Perform deferred jobs on shutdown

This commit is contained in:
Hidekazu Kobayashi 2024-09-16 11:35:22 +00:00
parent 7466ec4777
commit ae0b237e3b
3 changed files with 41 additions and 16 deletions

View File

@ -1050,12 +1050,13 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public dispose(): void { public async dispose(): Promise<void> {
this.#shutdownController.abort(); this.#shutdownController.abort();
await this.updateNotesCountQueue.performAllNow();
} }
@bindThis @bindThis
public onApplicationShutdown(signal?: string | undefined): void { public async onApplicationShutdown(signal?: string | undefined): Promise<void> {
this.dispose(); await this.dispose();
} }
} }

View File

@ -3,27 +3,41 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
type Job<V> = {
value: V;
timer: NodeJS.Timeout;
};
export class CollapsedQueue<K, V> { export class CollapsedQueue<K, V> {
private jobs: Map<K, V> = new Map(); private jobs: Map<K, Job<V>> = new Map();
constructor( constructor(
private timeout: number, private timeout: number,
private collapse: (oldValue: V, newValue: V) => V, private collapse: (oldValue: V, newValue: V) => V,
private doJob: (key: K, value: V) => void, private perform: (key: K, value: V) => Promise<void>,
) {} ) {}
enqueue(key: K, value: V) { enqueue(key: K, value: V) {
if (this.jobs.has(key)) { if (this.jobs.has(key)) {
const old = this.jobs.get(key)!; const old = this.jobs.get(key)!;
const merged = this.collapse(old, value); const merged = this.collapse(old.value, value);
this.jobs.set(key, merged); this.jobs.set(key, { ...old, value: merged });
} else { } else {
this.jobs.set(key, value); const timer = setTimeout(() => {
setTimeout(() => { const job = this.jobs.get(key)!;
const value = this.jobs.get(key)!;
this.jobs.delete(key); this.jobs.delete(key);
this.doJob(key, value); this.perform(key, job.value);
}, this.timeout); }, this.timeout);
this.jobs.set(key, { value, timer });
} }
} }
async performAllNow() {
const entries = [...this.jobs.entries()];
this.jobs.clear();
for (const [_key, job] of entries) {
clearTimeout(job.timer);
}
await Promise.allSettled(entries.map(([key, job]) => this.perform(key, job.value)));
}
} }

View File

@ -4,7 +4,7 @@
*/ */
import { URL } from 'node:url'; import { URL } from 'node:url';
import { Injectable } from '@nestjs/common'; import { Injectable, OnApplicationShutdown } from '@nestjs/common';
import httpSignature from '@peertube/http-signature'; import httpSignature from '@peertube/http-signature';
import * as Bull from 'bullmq'; import * as Bull from 'bullmq';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
@ -26,10 +26,10 @@ import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdentifiableError } from '@/misc/identifiable-error.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 { CollapsedQueue } from '@/misc/collapsed-queue.js';
import { MiNote } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type { InboxJobData } from '../types.js';
type UpdateInstanceJob = { type UpdateInstanceJob = {
latestRequestReceivedAt: Date, latestRequestReceivedAt: Date,
@ -37,7 +37,7 @@ type UpdateInstanceJob = {
}; };
@Injectable() @Injectable()
export class InboxProcessorService { export class InboxProcessorService implements OnApplicationShutdown {
private logger: Logger; private logger: Logger;
private updateInstanceQueue: CollapsedQueue<MiNote['id'], UpdateInstanceJob>; private updateInstanceQueue: CollapsedQueue<MiNote['id'], UpdateInstanceJob>;
@ -254,4 +254,14 @@ export class InboxProcessorService {
suspensionState: job.shouldUnsuspend ? 'none' : undefined, suspensionState: job.shouldUnsuspend ? 'none' : undefined,
}); });
} }
@bindThis
public async dispose(): Promise<void> {
await this.updateInstanceQueue.performAllNow();
}
@bindThis
async onApplicationShutdown(signal?: string) {
await this.dispose();
}
} }