mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-27 06:00:21 +01:00
job!
This commit is contained in:
parent
d2ea04fbf2
commit
239824c187
8 changed files with 54 additions and 37 deletions
|
@ -135,10 +135,14 @@ id: 'aid'
|
||||||
# Job concurrency per worker
|
# Job concurrency per worker
|
||||||
#deliverJobConcurrency: 128
|
#deliverJobConcurrency: 128
|
||||||
#inboxJobConcurrency: 16
|
#inboxJobConcurrency: 16
|
||||||
|
#relashionshipJobConcurrency: 16
|
||||||
|
# What's relashionshipJob?:
|
||||||
|
# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
|
||||||
|
|
||||||
# Job rate limiter
|
# Job rate limiter
|
||||||
#deliverJobPerSec: 128
|
#deliverJobPerSec: 128
|
||||||
#inboxJobPerSec: 16
|
#inboxJobPerSec: 16
|
||||||
|
#relashionshipJobPerSec: 64
|
||||||
|
|
||||||
# Job attempts
|
# Job attempts
|
||||||
#deliverJobMaxAttempts: 12
|
#deliverJobMaxAttempts: 12
|
||||||
|
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -6,5 +6,6 @@
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.test.ts": "typescript"
|
"*.test.ts": "typescript"
|
||||||
},
|
},
|
||||||
|
"jest.jestCommandLine": "pnpm run jest",
|
||||||
"jest.autoRun": "off"
|
"jest.autoRun": "off"
|
||||||
}
|
}
|
|
@ -84,8 +84,10 @@ export type Source = {
|
||||||
|
|
||||||
deliverJobConcurrency?: number;
|
deliverJobConcurrency?: number;
|
||||||
inboxJobConcurrency?: number;
|
inboxJobConcurrency?: number;
|
||||||
|
relashionshipJobConcurrency?: number;
|
||||||
deliverJobPerSec?: number;
|
deliverJobPerSec?: number;
|
||||||
inboxJobPerSec?: number;
|
inboxJobPerSec?: number;
|
||||||
|
relashionshipJobPerSec?: number;
|
||||||
deliverJobMaxAttempts?: number;
|
deliverJobMaxAttempts?: number;
|
||||||
inboxJobMaxAttempts?: number;
|
inboxJobMaxAttempts?: number;
|
||||||
|
|
||||||
|
|
|
@ -96,14 +96,14 @@ export class AccountMoveService {
|
||||||
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
|
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
|
||||||
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
|
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
|
||||||
|
|
||||||
// Unfollow
|
// Unfollow after 24 hours
|
||||||
const followings = await this.followingsRepository.findBy({
|
const followings = await this.followingsRepository.findBy({
|
||||||
followerId: src.id,
|
followerId: src.id,
|
||||||
});
|
});
|
||||||
this.queueService.createUnfollowJob(followings.map(following => ({
|
this.queueService.createDelayedUnfollowJob(followings.map(following => ({
|
||||||
from: { id: src.id },
|
from: { id: src.id },
|
||||||
to: { id: following.followeeId },
|
to: { id: following.followeeId },
|
||||||
})));
|
})), process.env.NODE_ENV === 'test' ? 10000 : 1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
await this.postMoveProcess(src, dst);
|
await this.postMoveProcess(src, dst);
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ const $db: Provider = {
|
||||||
|
|
||||||
const $relationship: Provider = {
|
const $relationship: Provider = {
|
||||||
provide: 'queue:relationship',
|
provide: 'queue:relationship',
|
||||||
useFactory: (config: Config) => q(config, 'relationship'),
|
useFactory: (config: Config) => q(config, 'relationship', config.relashionshipJobPerSec ?? 64),
|
||||||
inject: [DI.config],
|
inject: [DI.config],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -258,6 +258,12 @@ export class QueueService {
|
||||||
return this.relationshipQueue.addBulk(jobs);
|
return this.relationshipQueue.addBulk(jobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public createDelayedUnfollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string }[], delay: number) {
|
||||||
|
const jobs = followings.map(rel => this.generateRelationshipJobData('unfollow', rel, { delay }));
|
||||||
|
return this.relationshipQueue.addBulk(jobs);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createBlockJob(blockings: { from: ThinUser, to: ThinUser, silent?: boolean }[]) {
|
public createBlockJob(blockings: { from: ThinUser, to: ThinUser, silent?: boolean }[]) {
|
||||||
const jobs = blockings.map(rel => this.generateRelationshipJobData('block', rel));
|
const jobs = blockings.map(rel => this.generateRelationshipJobData('block', rel));
|
||||||
|
@ -271,7 +277,7 @@ export class QueueService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private generateRelationshipJobData(name: 'follow' | 'unfollow' | 'block' | 'unblock', data: RelationshipJobData): {
|
private generateRelationshipJobData(name: 'follow' | 'unfollow' | 'block' | 'unblock', data: RelationshipJobData, opts?: Bull.JobOptions): {
|
||||||
name: string,
|
name: string,
|
||||||
data: RelationshipJobData,
|
data: RelationshipJobData,
|
||||||
opts: Bull.JobOptions,
|
opts: Bull.JobOptions,
|
||||||
|
@ -287,6 +293,7 @@ export class QueueService {
|
||||||
opts: {
|
opts: {
|
||||||
removeOnComplete: true,
|
removeOnComplete: true,
|
||||||
removeOnFail: true,
|
removeOnFail: true,
|
||||||
|
...opts,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class RelationshipQueueProcessorsService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public start(q: Bull.Queue): void {
|
public start(q: Bull.Queue): void {
|
||||||
const maxJobs = (this.config.deliverJobConcurrency ?? 128) / 4; // conservative?
|
const maxJobs = this.config.relashionshipJobConcurrency ?? 16;
|
||||||
q.process('follow', maxJobs, (job) => this.relationshipProcessorService.processFollow(job));
|
q.process('follow', maxJobs, (job) => this.relationshipProcessorService.processFollow(job));
|
||||||
q.process('unfollow', maxJobs, (job) => this.relationshipProcessorService.processUnfollow(job));
|
q.process('unfollow', maxJobs, (job) => this.relationshipProcessorService.processUnfollow(job));
|
||||||
q.process('block', maxJobs, (job) => this.relationshipProcessorService.processBlock(job));
|
q.process('block', maxJobs, (job) => this.relationshipProcessorService.processBlock(job));
|
||||||
|
|
|
@ -266,11 +266,12 @@ describe('Account Move', () => {
|
||||||
|
|
||||||
await sleep(1000 * 3); // wait for jobs to finish
|
await sleep(1000 * 3); // wait for jobs to finish
|
||||||
|
|
||||||
|
// Unfollow delayed?
|
||||||
const aliceFollowings = await api('/users/following', {
|
const aliceFollowings = await api('/users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(aliceFollowings.status, 200);
|
assert.strictEqual(aliceFollowings.status, 200);
|
||||||
assert.strictEqual(aliceFollowings.body.length, 0);
|
assert.strictEqual(aliceFollowings.body.length, 3);
|
||||||
|
|
||||||
const carolFollowings = await api('/users/following', {
|
const carolFollowings = await api('/users/following', {
|
||||||
userId: carol.id,
|
userId: carol.id,
|
||||||
|
@ -304,16 +305,35 @@ describe('Account Move', () => {
|
||||||
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
|
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to move if the destination account has already moved.', async () => {
|
test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
|
||||||
await api('/i/move', {
|
await successfulApiCall({
|
||||||
moveToAccount: `@bob@${url.hostname}`,
|
endpoint: '/following/create',
|
||||||
|
parameters: {
|
||||||
|
userId: frank.id,
|
||||||
|
},
|
||||||
|
user: bob,
|
||||||
|
});
|
||||||
|
const followers = await api('/users/followers', {
|
||||||
|
userId: frank.id,
|
||||||
|
}, frank);
|
||||||
|
|
||||||
|
assert.strictEqual(followers.status, 200);
|
||||||
|
assert.strictEqual(followers.body.length, 2);
|
||||||
|
assert.strictEqual(followers.body[0].followerId, bob.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Unfollowed after 10 sec (24 hours in production).', async () => {
|
||||||
|
await sleep(1000 * 8);
|
||||||
|
|
||||||
|
const following = await api('/users/following', {
|
||||||
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const newAlice = await Users.findOneByOrFail({ id: alice.id });
|
assert.strictEqual(following.status, 200);
|
||||||
assert.strictEqual(newAlice.movedToUri, `${url.origin}/users/${bob.id}`);
|
assert.strictEqual(following.body.length, 0);
|
||||||
assert.strictEqual(newAlice.alsoKnownAs?.length, 1);
|
});
|
||||||
assert.strictEqual(newAlice.alsoKnownAs[0], `${url.origin}/users/${bob.id}`);
|
|
||||||
|
|
||||||
|
test('Unable to move if the destination account has already moved.', async () => {
|
||||||
const res = await api('/i/move', {
|
const res = await api('/i/move', {
|
||||||
moveToAccount: `@alice@${url.hostname}`,
|
moveToAccount: `@alice@${url.hostname}`,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -345,23 +365,6 @@ describe('Account Move', () => {
|
||||||
assert.strictEqual(newEve.followersCount, 1);
|
assert.strictEqual(newEve.followersCount, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
|
|
||||||
await successfulApiCall({
|
|
||||||
endpoint: '/following/create',
|
|
||||||
parameters: {
|
|
||||||
userId: frank.id,
|
|
||||||
},
|
|
||||||
user: bob,
|
|
||||||
});
|
|
||||||
const followers = await api('/users/followers', {
|
|
||||||
userId: frank.id,
|
|
||||||
}, frank);
|
|
||||||
|
|
||||||
assert.strictEqual(followers.status, 200);
|
|
||||||
assert.strictEqual(followers.body.length, 2);
|
|
||||||
assert.strictEqual(followers.body[0].followerId, bob.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
'/antennas/create',
|
'/antennas/create',
|
||||||
'/channels/create',
|
'/channels/create',
|
||||||
|
|
Loading…
Reference in a new issue