Defer instance metadata update

This commit is contained in:
KOBA789 2024-09-16 19:37:21 +09:00 committed by GitHub
parent 7d7a12d7d6
commit d5524e947f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 73 additions and 6 deletions

View File

@ -60,6 +60,7 @@ import { UserBlockingService } from '@/core/UserBlockingService.js';
import { isReply } from '@/misc/is-reply.js';
import { trackPromise } from '@/misc/promise-tracker.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { CollapsedQueue } from '@/misc/collapsed-queue.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@ -151,6 +152,7 @@ type Option = {
@Injectable()
export class NoteCreateService implements OnApplicationShutdown {
#shutdownController = new AbortController();
private updateNotesCountQueue: CollapsedQueue<MiNote['id'], number>;
constructor(
@Inject(DI.config)
@ -218,7 +220,9 @@ export class NoteCreateService implements OnApplicationShutdown {
private instanceChart: InstanceChart,
private utilityService: UtilityService,
private userBlockingService: UserBlockingService,
) { }
) {
this.updateNotesCountQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseNotesCount, this.performUpdateNotesCount);
}
@bindThis
public async create(user: {
@ -516,7 +520,7 @@ export class NoteCreateService implements OnApplicationShutdown {
// Register host
if (this.userEntityService.isRemoteUser(user)) {
this.federatedInstanceService.fetch(user.host).then(async i => {
this.instancesRepository.increment({ id: i.id }, 'notesCount', 1);
this.updateNotesCountQueue.enqueue(i.id, 1);
if ((await this.metaService.fetch()).enableChartsForFederatedInstances) {
this.instanceChart.updateNote(i.host, note, true);
}
@ -1035,6 +1039,16 @@ export class NoteCreateService implements OnApplicationShutdown {
return false;
}
@bindThis
private collapseNotesCount(oldValue: number, newValue: number) {
return oldValue + newValue;
}
@bindThis
private performUpdateNotesCount(id: MiNote['id'], incrBy: number) {
this.instancesRepository.increment({ id: id }, 'notesCount', incrBy);
}
@bindThis
public dispose(): void {
this.#shutdownController.abort();

View File

@ -0,0 +1,24 @@
export class CollapsedQueue<K, V> {
private jobs: Map<K, V> = new Map();
constructor(
private timeout: number,
private collapse: (oldValue: V, newValue: V) => V,
private doJob: (key: K, value: V) => void,
) { }
enqueue(key: K, value: V) {
if (this.jobs.has(key)) {
const old = this.jobs.get(key)!;
const merged = this.collapse(old, value);
this.jobs.set(key, merged);
} else {
this.jobs.set(key, value);
setTimeout(() => {
const value = this.jobs.get(key)!;
this.jobs.delete(key);
this.doJob(key, value);
}, this.timeout);
}
}
}

View File

@ -28,10 +28,18 @@ import { bindThis } from '@/decorators.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type { InboxJobData } from '../types.js';
import { CollapsedQueue } from "@/misc/collapsed-queue.js"
import { MiNote } from '@/models/Note.js';
type UpdateInstanceJob = {
latestRequestReceivedAt: Date,
shouldUnsuspend: boolean,
};
@Injectable()
export class InboxProcessorService {
private logger: Logger;
private updateInstanceQueue: CollapsedQueue<MiNote['id'], UpdateInstanceJob>;
constructor(
private utilityService: UtilityService,
@ -48,6 +56,7 @@ export class InboxProcessorService {
private queueLoggerService: QueueLoggerService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('inbox');
this.updateInstanceQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseUpdateInstanceJobs, this.performUpdateInstance);
}
@bindThis
@ -185,11 +194,9 @@ export class InboxProcessorService {
// Update stats
this.federatedInstanceService.fetch(authUser.user.host).then(i => {
this.federatedInstanceService.update(i.id, {
this.updateInstanceQueue.enqueue(i.id, {
latestRequestReceivedAt: new Date(),
isNotResponding: false,
// もしサーバーが死んでるために配信が止まっていた場合には自動的に復活させてあげる
suspensionState: i.suspensionState === 'autoSuspendedForNotResponding' ? 'none' : undefined,
shouldUnsuspend: job.suspensionState === 'autoSuspendedForNotResponding',
});
this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
@ -225,4 +232,26 @@ export class InboxProcessorService {
}
return 'ok';
}
@bindThis
public collapseUpdateInstanceJobs(oldJob: UpdateInstanceJob, newJob: UpdateInstanceJob) {
const latestRequestReceivedAt = oldJob.latestRequestReceivedAt < newJob.latestRequestReceivedAt
? newJob.latestRequestReceivedAt
: oldJob.latestRequestReceivedAt;
const shouldUnsuspend = oldJob.shouldUnsuspend || newJob.shouldUnsuspend;
return {
latestRequestReceivedAt,
shouldUnsuspend,
};
}
@bindThis
public performUpdateInstance(id: string, job: UpdateInstanceJob) {
this.federatedInstanceService.update(id, {
latestRequestReceivedAt: new Date(),
isNotResponding: false,
// もしサーバーが死んでるために配信が止まっていた場合には自動的に復活させてあげる
suspensionState: job.shouldUnsuspend ? 'none' : undefined,
});
}
}