Make JobQueue an abstract class
This commit is contained in:
parent
d9e90e9ea8
commit
943bb38af1
4 changed files with 208 additions and 133 deletions
|
@ -15,7 +15,7 @@ const noopOnCompleteCallbacks = {
|
|||
reject: noop,
|
||||
};
|
||||
|
||||
type JobQueueOptions<T> = {
|
||||
type JobQueueOptions = {
|
||||
/**
|
||||
* The backing store for jobs. Typically a wrapper around the database.
|
||||
*/
|
||||
|
@ -32,38 +32,13 @@ type JobQueueOptions<T> = {
|
|||
* the job to fail; a value of 2 will allow the job to fail once; etc.
|
||||
*/
|
||||
maxAttempts: number;
|
||||
|
||||
/**
|
||||
* `parseData` will be called with the raw data from `store`. For example, if the job
|
||||
* takes a single number, `parseData` should throw if `data` is a number and should
|
||||
* return the number otherwise.
|
||||
*
|
||||
* If it throws, the job will be deleted from the store and the job will not be run.
|
||||
*
|
||||
* Will only be called once per job, even if `maxAttempts > 1`.
|
||||
*/
|
||||
parseData: (data: unknown) => T;
|
||||
|
||||
/**
|
||||
* Run the job, given data.
|
||||
*
|
||||
* If it resolves, the job will be deleted from the store.
|
||||
*
|
||||
* If it rejects, the job will be retried up to `maxAttempts - 1` times, after which it
|
||||
* will be deleted from the store.
|
||||
*/
|
||||
run: (job: Readonly<ParsedJob<T>>) => Promise<void>;
|
||||
};
|
||||
|
||||
export class JobQueue<T> {
|
||||
export abstract class JobQueue<T> {
|
||||
private readonly maxAttempts: number;
|
||||
|
||||
private readonly parseData: (data: unknown) => T;
|
||||
|
||||
private readonly queueType: string;
|
||||
|
||||
private readonly run: (job: Readonly<ParsedJob<T>>) => Promise<unknown>;
|
||||
|
||||
private readonly store: JobQueueStore;
|
||||
|
||||
private readonly logPrefix: string;
|
||||
|
@ -78,7 +53,7 @@ export class JobQueue<T> {
|
|||
|
||||
private started = false;
|
||||
|
||||
constructor(options: Readonly<JobQueueOptions<T>>) {
|
||||
constructor(options: Readonly<JobQueueOptions>) {
|
||||
assert(
|
||||
Number.isInteger(options.maxAttempts) && options.maxAttempts >= 1,
|
||||
'maxAttempts should be a positive integer'
|
||||
|
@ -93,14 +68,33 @@ export class JobQueue<T> {
|
|||
);
|
||||
|
||||
this.maxAttempts = options.maxAttempts;
|
||||
this.parseData = options.parseData;
|
||||
this.queueType = options.queueType;
|
||||
this.run = options.run;
|
||||
this.store = options.store;
|
||||
|
||||
this.logPrefix = `${this.queueType} job queue:`;
|
||||
}
|
||||
|
||||
/**
|
||||
* `parseData` will be called with the raw data from `store`. For example, if the job
|
||||
* takes a single number, `parseData` should throw if `data` is a number and should
|
||||
* return the number otherwise.
|
||||
*
|
||||
* If it throws, the job will be deleted from the store and the job will not be run.
|
||||
*
|
||||
* Will only be called once per job, even if `maxAttempts > 1`.
|
||||
*/
|
||||
protected abstract parseData(data: unknown): T;
|
||||
|
||||
/**
|
||||
* Run the job, given data.
|
||||
*
|
||||
* If it resolves, the job will be deleted from the store.
|
||||
*
|
||||
* If it rejects, the job will be retried up to `maxAttempts - 1` times, after which it
|
||||
* will be deleted from the store.
|
||||
*/
|
||||
protected abstract run(job: Readonly<ParsedJob<T>>): Promise<void>;
|
||||
|
||||
/**
|
||||
* Start streaming jobs from the store.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
|
@ -12,18 +13,12 @@ const removeStorageKeyJobDataSchema = z.object({
|
|||
|
||||
type RemoveStorageKeyJobData = z.infer<typeof removeStorageKeyJobDataSchema>;
|
||||
|
||||
export const removeStorageKeyJobQueue = new JobQueue<RemoveStorageKeyJobData>({
|
||||
store: jobQueueDatabaseStore,
|
||||
|
||||
queueType: 'remove storage key',
|
||||
|
||||
maxAttempts: 100,
|
||||
|
||||
parseData(data: unknown): RemoveStorageKeyJobData {
|
||||
export class RemoveStorageKeyJobQueue extends JobQueue<RemoveStorageKeyJobData> {
|
||||
protected parseData(data: unknown): RemoveStorageKeyJobData {
|
||||
return removeStorageKeyJobDataSchema.parse(data);
|
||||
},
|
||||
}
|
||||
|
||||
async run({
|
||||
protected async run({
|
||||
data,
|
||||
}: Readonly<{ data: RemoveStorageKeyJobData }>): Promise<void> {
|
||||
await new Promise<void>(resolve => {
|
||||
|
@ -31,5 +26,13 @@ export const removeStorageKeyJobQueue = new JobQueue<RemoveStorageKeyJobData>({
|
|||
});
|
||||
|
||||
await window.storage.remove(data.key);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const removeStorageKeyJobQueue = new RemoveStorageKeyJobQueue({
|
||||
store: jobQueueDatabaseStore,
|
||||
|
||||
queueType: 'remove storage key',
|
||||
|
||||
maxAttempts: 100,
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import * as z from 'zod';
|
||||
import * as moment from 'moment';
|
||||
|
@ -45,18 +46,14 @@ const reportSpamJobDataSchema = z.object({
|
|||
|
||||
export type ReportSpamJobData = z.infer<typeof reportSpamJobDataSchema>;
|
||||
|
||||
export const reportSpamJobQueue = new JobQueue<ReportSpamJobData>({
|
||||
store: jobQueueDatabaseStore,
|
||||
|
||||
queueType: 'report spam',
|
||||
|
||||
maxAttempts: 25,
|
||||
|
||||
parseData(data: unknown): ReportSpamJobData {
|
||||
export class ReportSpamJobQueue extends JobQueue<ReportSpamJobData> {
|
||||
protected parseData(data: unknown): ReportSpamJobData {
|
||||
return reportSpamJobDataSchema.parse(data);
|
||||
},
|
||||
}
|
||||
|
||||
async run({ data }: Readonly<{ data: ReportSpamJobData }>): Promise<void> {
|
||||
protected async run({
|
||||
data,
|
||||
}: Readonly<{ data: ReportSpamJobData }>): Promise<void> {
|
||||
const { e164, serverGuids } = data;
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
|
@ -115,5 +112,13 @@ export const reportSpamJobQueue = new JobQueue<ReportSpamJobData>({
|
|||
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const reportSpamJobQueue = new ReportSpamJobQueue({
|
||||
store: jobQueueDatabaseStore,
|
||||
|
||||
queueType: 'report spam',
|
||||
|
||||
maxAttempts: 25,
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue