@rvoh/psychic-workers 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/background/BaseBackgroundedModel.js +69 -0
- package/dist/cjs/src/background/BaseBackgroundedService.js +52 -0
- package/dist/cjs/src/background/BaseScheduledService.js +58 -0
- package/dist/cjs/src/background/index.js +35 -7
- package/dist/cjs/src/test-utils/WorkerTestUtils.js +1 -1
- package/dist/esm/src/background/BaseBackgroundedModel.js +69 -0
- package/dist/esm/src/background/BaseBackgroundedService.js +52 -0
- package/dist/esm/src/background/BaseScheduledService.js +58 -0
- package/dist/esm/src/background/index.js +35 -7
- package/dist/esm/src/test-utils/WorkerTestUtils.js +1 -1
- package/dist/types/src/background/BaseBackgroundedModel.d.ts +54 -1
- package/dist/types/src/background/BaseBackgroundedService.d.ts +40 -1
- package/dist/types/src/background/BaseScheduledService.d.ts +53 -0
- package/dist/types/src/background/index.d.ts +28 -0
- package/dist/types/src/types/background.d.ts +14 -0
- package/package.json +9 -8
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dream } from '@rvoh/dream';
|
|
2
|
+
import { mergeBackgroundWithOptsIntoJobConfig } from './BaseBackgroundedService.js';
|
|
2
3
|
import background from './index.js';
|
|
3
4
|
import durationToSeconds from '../helpers/durationToSeconds.js';
|
|
4
5
|
export default class BaseBackgroundedModel extends Dream {
|
|
@@ -71,6 +72,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
71
72
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
72
73
|
* before making assertions.
|
|
73
74
|
*
|
|
75
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
76
|
+
*
|
|
74
77
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
75
78
|
* @param methodName - the name of the static method you wish to run in the background
|
|
76
79
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -85,6 +88,38 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
85
88
|
jobConfig: safeThis.backgroundJobConfig,
|
|
86
89
|
});
|
|
87
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
93
|
+
* sending in the provided args, along with an options object which can
|
|
94
|
+
* be used to delay the job and/or override the priority provided by
|
|
95
|
+
* the `backgroundJobConfig` getter on this model.
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
99
|
+
* ```
|
|
100
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
101
|
+
* is an indication that the job was put in the queue, not that it has
|
|
102
|
+
* completed.
|
|
103
|
+
*
|
|
104
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
105
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
106
|
+
* before making assertions.
|
|
107
|
+
*
|
|
108
|
+
* @param opts - options for backgrounding this job
|
|
109
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
110
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
111
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
112
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
113
|
+
*/
|
|
114
|
+
static async backgroundWith(opts, methodName, ...args) {
|
|
115
|
+
const safeThis = this;
|
|
116
|
+
return await background.staticMethod(safeThis, methodName, {
|
|
117
|
+
globalName: safeThis.globalName,
|
|
118
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
119
|
+
args,
|
|
120
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
88
123
|
/**
|
|
89
124
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
90
125
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -139,6 +174,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
139
174
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
140
175
|
* before making assertions.
|
|
141
176
|
*
|
|
177
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
178
|
+
*
|
|
142
179
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
143
180
|
* @param methodName - the name of the static method you wish to run in the background
|
|
144
181
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -152,4 +189,36 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
152
189
|
jobConfig: safeThis.backgroundJobConfig,
|
|
153
190
|
});
|
|
154
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
194
|
+
* sending in the provided args, along with an options object which can
|
|
195
|
+
* be used to delay the job and/or override the priority provided by
|
|
196
|
+
* the `backgroundJobConfig` getter on this model.
|
|
197
|
+
*
|
|
198
|
+
* ```ts
|
|
199
|
+
* const user = await User.lastOrFail()
|
|
200
|
+
* await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
201
|
+
* ```
|
|
202
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
203
|
+
* is an indication that the job was put in the queue, not that it has
|
|
204
|
+
* completed.
|
|
205
|
+
*
|
|
206
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
207
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
208
|
+
* before making assertions.
|
|
209
|
+
*
|
|
210
|
+
* @param opts - options for backgrounding this job
|
|
211
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
212
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
213
|
+
* @param methodName - the name of the instance method you wish to run in the background
|
|
214
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
215
|
+
*/
|
|
216
|
+
async backgroundWith(opts, methodName, ...args) {
|
|
217
|
+
const safeThis = this;
|
|
218
|
+
return await background.modelInstanceMethod(safeThis, methodName, {
|
|
219
|
+
args,
|
|
220
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
221
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
155
224
|
}
|
|
@@ -83,6 +83,8 @@ export default class BaseBackgroundedService {
|
|
|
83
83
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
84
84
|
* before making assertions.
|
|
85
85
|
*
|
|
86
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
87
|
+
*
|
|
86
88
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
87
89
|
* @param methodName - the name of the static method you wish to run in the background
|
|
88
90
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -97,6 +99,43 @@ export default class BaseBackgroundedService {
|
|
|
97
99
|
jobConfig: safeThis.backgroundJobConfig,
|
|
98
100
|
});
|
|
99
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
104
|
+
* sending in the provided args, along with an options object which can
|
|
105
|
+
* be used to delay the job and/or override the priority provided by
|
|
106
|
+
* the `backgroundJobConfig` getter on this service.
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* await MyBackgroundableClass.backgroundWith(
|
|
110
|
+
* { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
|
|
111
|
+
* 'myMethod',
|
|
112
|
+
* 'abc',
|
|
113
|
+
* 123,
|
|
114
|
+
* )
|
|
115
|
+
* ```
|
|
116
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
117
|
+
* is an indication that the job was put in the queue, not that it has
|
|
118
|
+
* completed.
|
|
119
|
+
*
|
|
120
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
121
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
122
|
+
* before making assertions.
|
|
123
|
+
*
|
|
124
|
+
* @param opts - options for backgrounding this job
|
|
125
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
126
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
127
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
128
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
129
|
+
*/
|
|
130
|
+
static async backgroundWith(opts, methodName, ...args) {
|
|
131
|
+
const safeThis = this;
|
|
132
|
+
return await background.staticMethod(safeThis, methodName, {
|
|
133
|
+
globalName: safeThis.globalName,
|
|
134
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
135
|
+
args,
|
|
136
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
100
139
|
/**
|
|
101
140
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
102
141
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -108,3 +147,16 @@ export default class BaseBackgroundedService {
|
|
|
108
147
|
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
|
|
109
148
|
}
|
|
110
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* @internal
|
|
152
|
+
*
|
|
153
|
+
* returns a copy of the provided job config, with the priority
|
|
154
|
+
* replaced by the priority found in the `backgroundWith` opts,
|
|
155
|
+
* if one was provided.
|
|
156
|
+
*/
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
|
+
export function mergeBackgroundWithOptsIntoJobConfig(jobConfig, opts) {
|
|
159
|
+
if (!opts.priority)
|
|
160
|
+
return jobConfig;
|
|
161
|
+
return { ...jobConfig, priority: opts.priority };
|
|
162
|
+
}
|
|
@@ -66,6 +66,64 @@ export default class BaseScheduledService {
|
|
|
66
66
|
jobConfig: safeThis.backgroundJobConfig,
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the id that `schedule` registers a job under, which can be passed
|
|
71
|
+
* to {@link BaseScheduledService.unschedule} to remove that job.
|
|
72
|
+
*
|
|
73
|
+
* The id is derived from the service's global name and the method name. It
|
|
74
|
+
* performs no queue construction and no redis I/O, so it is safe to call
|
|
75
|
+
* anywhere, including from a console in development.
|
|
76
|
+
*
|
|
77
|
+
* The intended workflow for deleting a scheduled service is to read the id
|
|
78
|
+
* off the class before removing it:
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* MyScheduledService.unscheduleId('myHourlyMethod')
|
|
82
|
+
* // => 'services/MyScheduledService:myHourlyMethod'
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* then check that string into a seed or migration and call `unschedule` with
|
|
86
|
+
* it, which allows the class itself to be deleted in the same deploy.
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @param methodName - the name of the static method that was scheduled
|
|
93
|
+
* @returns the id `schedule` registered this method under
|
|
94
|
+
*/
|
|
95
|
+
static unscheduleId(methodName) {
|
|
96
|
+
const safeThis = this;
|
|
97
|
+
return background.jobSchedulerId(safeThis.globalName, methodName);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Removes a scheduled job, preventing it from being run again.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* Since the id is just a string, it can also be checked into a seed or
|
|
107
|
+
* migration, which allows the scheduled service class to be deleted in the
|
|
108
|
+
* same deploy that stops its job:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* Every queue in your application is checked, so this finds the job whether
|
|
115
|
+
* or not the service's workstream has changed since it was scheduled, and
|
|
116
|
+
* whether it landed in a current or a transitional queue.
|
|
117
|
+
*
|
|
118
|
+
* NOTE: unscheduling stops future runs. It does not cancel a run that has
|
|
119
|
+
* already been placed on a queue, so a final invocation may still happen.
|
|
120
|
+
*
|
|
121
|
+
* @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
|
|
122
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
123
|
+
*/
|
|
124
|
+
static async unschedule(id) {
|
|
125
|
+
return await background.unschedule(id);
|
|
126
|
+
}
|
|
69
127
|
/**
|
|
70
128
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
71
129
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -500,13 +500,7 @@ export class Background {
|
|
|
500
500
|
*/
|
|
501
501
|
async scheduledMethod(ObjectClass, pattern, method, { globalName, args = [], jobConfig = {}, scheduleOpts = {}, }) {
|
|
502
502
|
this.connect();
|
|
503
|
-
|
|
504
|
-
// Since the name is really a job type and never changes, the `jobId` is the only
|
|
505
|
-
// way to allow multiple jobs with the same cron repeat pattern. Uniqueness will
|
|
506
|
-
// now be enforced by combining class name, method name, and cron repeat pattern.
|
|
507
|
-
//
|
|
508
|
-
// See: https://docs.bullmq.io/guide/jobs/repeatable
|
|
509
|
-
const schedulerId = `${globalName}:${method}`;
|
|
503
|
+
const schedulerId = this.jobSchedulerId(globalName, method);
|
|
510
504
|
const queueInstance = this.queueInstance(jobConfig);
|
|
511
505
|
if (!queueInstance)
|
|
512
506
|
throw new Error(`Missing queue for: ${jobConfig.queue?.toString()}`);
|
|
@@ -520,6 +514,40 @@ export class Background {
|
|
|
520
514
|
},
|
|
521
515
|
});
|
|
522
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* @internal
|
|
519
|
+
*
|
|
520
|
+
* Returns the id a scheduled job is registered with BullMQ under.
|
|
521
|
+
*
|
|
522
|
+
* `jobId` is used to determine uniqueness along with name and repeat pattern.
|
|
523
|
+
* Since the name is really a job type and never changes, the `jobId` is the only
|
|
524
|
+
* way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
|
|
525
|
+
* enforced by combining the global name and the method name.
|
|
526
|
+
*
|
|
527
|
+
* See: https://docs.bullmq.io/guide/jobs/repeatable
|
|
528
|
+
*
|
|
529
|
+
* @param globalName - the globalName of the class the method belongs to
|
|
530
|
+
* @param method - the name of the scheduled method
|
|
531
|
+
*/
|
|
532
|
+
jobSchedulerId(globalName, method) {
|
|
533
|
+
return `${globalName}:${method}`;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* removes a scheduled job from BullMQ, preventing it from being run again
|
|
537
|
+
*
|
|
538
|
+
* Every queue is checked, rather than the one the job's current
|
|
539
|
+
* `backgroundJobConfig` routes to, so that a job can still be unscheduled
|
|
540
|
+
* after its workstream has changed, or after the class that scheduled it has
|
|
541
|
+
* been deleted.
|
|
542
|
+
*
|
|
543
|
+
* @param jobSchedulerId - the id the job was scheduled under
|
|
544
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
545
|
+
*/
|
|
546
|
+
async unschedule(jobSchedulerId) {
|
|
547
|
+
this.connect();
|
|
548
|
+
const removed = await Promise.all(this.queues.map(queue => queue.removeJobScheduler(jobSchedulerId)));
|
|
549
|
+
return removed.some(Boolean);
|
|
550
|
+
}
|
|
523
551
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
524
552
|
queueInstance(values) {
|
|
525
553
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dream } from '@rvoh/dream';
|
|
2
|
+
import { mergeBackgroundWithOptsIntoJobConfig } from './BaseBackgroundedService.js';
|
|
2
3
|
import background from './index.js';
|
|
3
4
|
import durationToSeconds from '../helpers/durationToSeconds.js';
|
|
4
5
|
export default class BaseBackgroundedModel extends Dream {
|
|
@@ -71,6 +72,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
71
72
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
72
73
|
* before making assertions.
|
|
73
74
|
*
|
|
75
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
76
|
+
*
|
|
74
77
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
75
78
|
* @param methodName - the name of the static method you wish to run in the background
|
|
76
79
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -85,6 +88,38 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
85
88
|
jobConfig: safeThis.backgroundJobConfig,
|
|
86
89
|
});
|
|
87
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
93
|
+
* sending in the provided args, along with an options object which can
|
|
94
|
+
* be used to delay the job and/or override the priority provided by
|
|
95
|
+
* the `backgroundJobConfig` getter on this model.
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
99
|
+
* ```
|
|
100
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
101
|
+
* is an indication that the job was put in the queue, not that it has
|
|
102
|
+
* completed.
|
|
103
|
+
*
|
|
104
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
105
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
106
|
+
* before making assertions.
|
|
107
|
+
*
|
|
108
|
+
* @param opts - options for backgrounding this job
|
|
109
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
110
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
111
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
112
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
113
|
+
*/
|
|
114
|
+
static async backgroundWith(opts, methodName, ...args) {
|
|
115
|
+
const safeThis = this;
|
|
116
|
+
return await background.staticMethod(safeThis, methodName, {
|
|
117
|
+
globalName: safeThis.globalName,
|
|
118
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
119
|
+
args,
|
|
120
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
88
123
|
/**
|
|
89
124
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
90
125
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -139,6 +174,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
139
174
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
140
175
|
* before making assertions.
|
|
141
176
|
*
|
|
177
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
178
|
+
*
|
|
142
179
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
143
180
|
* @param methodName - the name of the static method you wish to run in the background
|
|
144
181
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -152,4 +189,36 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
152
189
|
jobConfig: safeThis.backgroundJobConfig,
|
|
153
190
|
});
|
|
154
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
194
|
+
* sending in the provided args, along with an options object which can
|
|
195
|
+
* be used to delay the job and/or override the priority provided by
|
|
196
|
+
* the `backgroundJobConfig` getter on this model.
|
|
197
|
+
*
|
|
198
|
+
* ```ts
|
|
199
|
+
* const user = await User.lastOrFail()
|
|
200
|
+
* await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
201
|
+
* ```
|
|
202
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
203
|
+
* is an indication that the job was put in the queue, not that it has
|
|
204
|
+
* completed.
|
|
205
|
+
*
|
|
206
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
207
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
208
|
+
* before making assertions.
|
|
209
|
+
*
|
|
210
|
+
* @param opts - options for backgrounding this job
|
|
211
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
212
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
213
|
+
* @param methodName - the name of the instance method you wish to run in the background
|
|
214
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
215
|
+
*/
|
|
216
|
+
async backgroundWith(opts, methodName, ...args) {
|
|
217
|
+
const safeThis = this;
|
|
218
|
+
return await background.modelInstanceMethod(safeThis, methodName, {
|
|
219
|
+
args,
|
|
220
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
221
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
155
224
|
}
|
|
@@ -83,6 +83,8 @@ export default class BaseBackgroundedService {
|
|
|
83
83
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
84
84
|
* before making assertions.
|
|
85
85
|
*
|
|
86
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
87
|
+
*
|
|
86
88
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
87
89
|
* @param methodName - the name of the static method you wish to run in the background
|
|
88
90
|
* @param args - a variadic list of arguments to be sent to your method
|
|
@@ -97,6 +99,43 @@ export default class BaseBackgroundedService {
|
|
|
97
99
|
jobConfig: safeThis.backgroundJobConfig,
|
|
98
100
|
});
|
|
99
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
104
|
+
* sending in the provided args, along with an options object which can
|
|
105
|
+
* be used to delay the job and/or override the priority provided by
|
|
106
|
+
* the `backgroundJobConfig` getter on this service.
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* await MyBackgroundableClass.backgroundWith(
|
|
110
|
+
* { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
|
|
111
|
+
* 'myMethod',
|
|
112
|
+
* 'abc',
|
|
113
|
+
* 123,
|
|
114
|
+
* )
|
|
115
|
+
* ```
|
|
116
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
117
|
+
* is an indication that the job was put in the queue, not that it has
|
|
118
|
+
* completed.
|
|
119
|
+
*
|
|
120
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
121
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
122
|
+
* before making assertions.
|
|
123
|
+
*
|
|
124
|
+
* @param opts - options for backgrounding this job
|
|
125
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
126
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
127
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
128
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
129
|
+
*/
|
|
130
|
+
static async backgroundWith(opts, methodName, ...args) {
|
|
131
|
+
const safeThis = this;
|
|
132
|
+
return await background.staticMethod(safeThis, methodName, {
|
|
133
|
+
globalName: safeThis.globalName,
|
|
134
|
+
...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
|
|
135
|
+
args,
|
|
136
|
+
jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
100
139
|
/**
|
|
101
140
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
102
141
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -108,3 +147,16 @@ export default class BaseBackgroundedService {
|
|
|
108
147
|
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
|
|
109
148
|
}
|
|
110
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* @internal
|
|
152
|
+
*
|
|
153
|
+
* returns a copy of the provided job config, with the priority
|
|
154
|
+
* replaced by the priority found in the `backgroundWith` opts,
|
|
155
|
+
* if one was provided.
|
|
156
|
+
*/
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
|
+
export function mergeBackgroundWithOptsIntoJobConfig(jobConfig, opts) {
|
|
159
|
+
if (!opts.priority)
|
|
160
|
+
return jobConfig;
|
|
161
|
+
return { ...jobConfig, priority: opts.priority };
|
|
162
|
+
}
|
|
@@ -66,6 +66,64 @@ export default class BaseScheduledService {
|
|
|
66
66
|
jobConfig: safeThis.backgroundJobConfig,
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the id that `schedule` registers a job under, which can be passed
|
|
71
|
+
* to {@link BaseScheduledService.unschedule} to remove that job.
|
|
72
|
+
*
|
|
73
|
+
* The id is derived from the service's global name and the method name. It
|
|
74
|
+
* performs no queue construction and no redis I/O, so it is safe to call
|
|
75
|
+
* anywhere, including from a console in development.
|
|
76
|
+
*
|
|
77
|
+
* The intended workflow for deleting a scheduled service is to read the id
|
|
78
|
+
* off the class before removing it:
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* MyScheduledService.unscheduleId('myHourlyMethod')
|
|
82
|
+
* // => 'services/MyScheduledService:myHourlyMethod'
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* then check that string into a seed or migration and call `unschedule` with
|
|
86
|
+
* it, which allows the class itself to be deleted in the same deploy.
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @param methodName - the name of the static method that was scheduled
|
|
93
|
+
* @returns the id `schedule` registered this method under
|
|
94
|
+
*/
|
|
95
|
+
static unscheduleId(methodName) {
|
|
96
|
+
const safeThis = this;
|
|
97
|
+
return background.jobSchedulerId(safeThis.globalName, methodName);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Removes a scheduled job, preventing it from being run again.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* Since the id is just a string, it can also be checked into a seed or
|
|
107
|
+
* migration, which allows the scheduled service class to be deleted in the
|
|
108
|
+
* same deploy that stops its job:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* Every queue in your application is checked, so this finds the job whether
|
|
115
|
+
* or not the service's workstream has changed since it was scheduled, and
|
|
116
|
+
* whether it landed in a current or a transitional queue.
|
|
117
|
+
*
|
|
118
|
+
* NOTE: unscheduling stops future runs. It does not cancel a run that has
|
|
119
|
+
* already been placed on a queue, so a final invocation may still happen.
|
|
120
|
+
*
|
|
121
|
+
* @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
|
|
122
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
123
|
+
*/
|
|
124
|
+
static async unschedule(id) {
|
|
125
|
+
return await background.unschedule(id);
|
|
126
|
+
}
|
|
69
127
|
/**
|
|
70
128
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
71
129
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -500,13 +500,7 @@ export class Background {
|
|
|
500
500
|
*/
|
|
501
501
|
async scheduledMethod(ObjectClass, pattern, method, { globalName, args = [], jobConfig = {}, scheduleOpts = {}, }) {
|
|
502
502
|
this.connect();
|
|
503
|
-
|
|
504
|
-
// Since the name is really a job type and never changes, the `jobId` is the only
|
|
505
|
-
// way to allow multiple jobs with the same cron repeat pattern. Uniqueness will
|
|
506
|
-
// now be enforced by combining class name, method name, and cron repeat pattern.
|
|
507
|
-
//
|
|
508
|
-
// See: https://docs.bullmq.io/guide/jobs/repeatable
|
|
509
|
-
const schedulerId = `${globalName}:${method}`;
|
|
503
|
+
const schedulerId = this.jobSchedulerId(globalName, method);
|
|
510
504
|
const queueInstance = this.queueInstance(jobConfig);
|
|
511
505
|
if (!queueInstance)
|
|
512
506
|
throw new Error(`Missing queue for: ${jobConfig.queue?.toString()}`);
|
|
@@ -520,6 +514,40 @@ export class Background {
|
|
|
520
514
|
},
|
|
521
515
|
});
|
|
522
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* @internal
|
|
519
|
+
*
|
|
520
|
+
* Returns the id a scheduled job is registered with BullMQ under.
|
|
521
|
+
*
|
|
522
|
+
* `jobId` is used to determine uniqueness along with name and repeat pattern.
|
|
523
|
+
* Since the name is really a job type and never changes, the `jobId` is the only
|
|
524
|
+
* way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
|
|
525
|
+
* enforced by combining the global name and the method name.
|
|
526
|
+
*
|
|
527
|
+
* See: https://docs.bullmq.io/guide/jobs/repeatable
|
|
528
|
+
*
|
|
529
|
+
* @param globalName - the globalName of the class the method belongs to
|
|
530
|
+
* @param method - the name of the scheduled method
|
|
531
|
+
*/
|
|
532
|
+
jobSchedulerId(globalName, method) {
|
|
533
|
+
return `${globalName}:${method}`;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* removes a scheduled job from BullMQ, preventing it from being run again
|
|
537
|
+
*
|
|
538
|
+
* Every queue is checked, rather than the one the job's current
|
|
539
|
+
* `backgroundJobConfig` routes to, so that a job can still be unscheduled
|
|
540
|
+
* after its workstream has changed, or after the class that scheduled it has
|
|
541
|
+
* been deleted.
|
|
542
|
+
*
|
|
543
|
+
* @param jobSchedulerId - the id the job was scheduled under
|
|
544
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
545
|
+
*/
|
|
546
|
+
async unschedule(jobSchedulerId) {
|
|
547
|
+
this.connect();
|
|
548
|
+
const removed = await Promise.all(this.queues.map(queue => queue.removeJobScheduler(jobSchedulerId)));
|
|
549
|
+
return removed.some(Boolean);
|
|
550
|
+
}
|
|
523
551
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
524
552
|
queueInstance(values) {
|
|
525
553
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dream } from '@rvoh/dream';
|
|
2
|
-
import { BackgroundJobConfig, DelayedJobOpts } from '../types/background.js';
|
|
2
|
+
import { BackgroundJobConfig, BackgroundWithOpts, DelayedJobOpts } from '../types/background.js';
|
|
3
3
|
import { FunctionPropertyNames } from '../types/utils.js';
|
|
4
4
|
import { BackgroundableMethodArgs } from './BaseBackgroundedService.js';
|
|
5
5
|
export default class BaseBackgroundedModel extends Dream {
|
|
@@ -60,11 +60,37 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
60
60
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
61
61
|
* before making assertions.
|
|
62
62
|
*
|
|
63
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
64
|
+
*
|
|
63
65
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
64
66
|
* @param methodName - the name of the static method you wish to run in the background
|
|
65
67
|
* @param args - a variadic list of arguments to be sent to your method
|
|
66
68
|
*/
|
|
67
69
|
static backgroundWithDelay<T, MethodName extends PsychicBackgroundedModelStaticMethods<T & typeof BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
72
|
+
* sending in the provided args, along with an options object which can
|
|
73
|
+
* be used to delay the job and/or override the priority provided by
|
|
74
|
+
* the `backgroundJobConfig` getter on this model.
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
78
|
+
* ```
|
|
79
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
80
|
+
* is an indication that the job was put in the queue, not that it has
|
|
81
|
+
* completed.
|
|
82
|
+
*
|
|
83
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
84
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
85
|
+
* before making assertions.
|
|
86
|
+
*
|
|
87
|
+
* @param opts - options for backgrounding this job
|
|
88
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
89
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
90
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
91
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
92
|
+
*/
|
|
93
|
+
static backgroundWith<T, MethodName extends PsychicBackgroundedModelStaticMethods<T & typeof BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
68
94
|
/**
|
|
69
95
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
70
96
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -110,11 +136,38 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
110
136
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
111
137
|
* before making assertions.
|
|
112
138
|
*
|
|
139
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
140
|
+
*
|
|
113
141
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
114
142
|
* @param methodName - the name of the static method you wish to run in the background
|
|
115
143
|
* @param args - a variadic list of arguments to be sent to your method
|
|
116
144
|
*/
|
|
117
145
|
backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceInstanceMethods<T & BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
148
|
+
* sending in the provided args, along with an options object which can
|
|
149
|
+
* be used to delay the job and/or override the priority provided by
|
|
150
|
+
* the `backgroundJobConfig` getter on this model.
|
|
151
|
+
*
|
|
152
|
+
* ```ts
|
|
153
|
+
* const user = await User.lastOrFail()
|
|
154
|
+
* await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
|
|
155
|
+
* ```
|
|
156
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
157
|
+
* is an indication that the job was put in the queue, not that it has
|
|
158
|
+
* completed.
|
|
159
|
+
*
|
|
160
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
161
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
162
|
+
* before making assertions.
|
|
163
|
+
*
|
|
164
|
+
* @param opts - options for backgrounding this job
|
|
165
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
166
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
167
|
+
* @param methodName - the name of the instance method you wish to run in the background
|
|
168
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
169
|
+
*/
|
|
170
|
+
backgroundWith<T, MethodName extends PsychicBackgroundedServiceInstanceMethods<T & BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
118
171
|
}
|
|
119
172
|
export type PsychicBackgroundedModelStaticMethods<T extends typeof BaseBackgroundedModel> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedModel>>;
|
|
120
173
|
export type PsychicBackgroundedServiceInstanceMethods<T extends BaseBackgroundedModel> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<BaseBackgroundedModel>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Job } from 'bullmq';
|
|
2
|
-
import { BackgroundJobConfig, DelayedJobOpts } from '../types/background.js';
|
|
2
|
+
import { BackgroundJobConfig, BackgroundWithOpts, DelayedJobOpts } from '../types/background.js';
|
|
3
3
|
import { FunctionPropertyNames } from '../types/utils.js';
|
|
4
4
|
export default class BaseBackgroundedService {
|
|
5
5
|
/**
|
|
@@ -68,11 +68,42 @@ export default class BaseBackgroundedService {
|
|
|
68
68
|
* method, preventing you from needing to explicitly wait for queues to flush
|
|
69
69
|
* before making assertions.
|
|
70
70
|
*
|
|
71
|
+
* @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
|
|
72
|
+
*
|
|
71
73
|
* @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
|
|
72
74
|
* @param methodName - the name of the static method you wish to run in the background
|
|
73
75
|
* @param args - a variadic list of arguments to be sent to your method
|
|
74
76
|
*/
|
|
75
77
|
static backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceStaticMethods<T & typeof BaseBackgroundedService>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* runs the specified method in a background queue, driven by BullMQ,
|
|
80
|
+
* sending in the provided args, along with an options object which can
|
|
81
|
+
* be used to delay the job and/or override the priority provided by
|
|
82
|
+
* the `backgroundJobConfig` getter on this service.
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* await MyBackgroundableClass.backgroundWith(
|
|
86
|
+
* { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
|
|
87
|
+
* 'myMethod',
|
|
88
|
+
* 'abc',
|
|
89
|
+
* 123,
|
|
90
|
+
* )
|
|
91
|
+
* ```
|
|
92
|
+
* though calling backgroundWith must be awaited, the resolution of the promise
|
|
93
|
+
* is an indication that the job was put in the queue, not that it has
|
|
94
|
+
* completed.
|
|
95
|
+
*
|
|
96
|
+
* NOTE: in test environments, psychic will immediately invoke the underlying
|
|
97
|
+
* method, preventing you from needing to explicitly wait for queues to flush
|
|
98
|
+
* before making assertions.
|
|
99
|
+
*
|
|
100
|
+
* @param opts - options for backgrounding this job
|
|
101
|
+
* @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
|
|
102
|
+
* @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
|
|
103
|
+
* @param methodName - the name of the static method you wish to run in the background
|
|
104
|
+
* @param args - a variadic list of arguments to be sent to your method
|
|
105
|
+
*/
|
|
106
|
+
static backgroundWith<T, MethodName extends PsychicBackgroundedServiceStaticMethods<T & typeof BaseBackgroundedService>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
76
107
|
/**
|
|
77
108
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
78
109
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -81,6 +112,14 @@ export default class BaseBackgroundedService {
|
|
|
81
112
|
*/
|
|
82
113
|
get psychicWorkerTypes(): any;
|
|
83
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* @internal
|
|
117
|
+
*
|
|
118
|
+
* returns a copy of the provided job config, with the priority
|
|
119
|
+
* replaced by the priority found in the `backgroundWith` opts,
|
|
120
|
+
* if one was provided.
|
|
121
|
+
*/
|
|
122
|
+
export declare function mergeBackgroundWithOptsIntoJobConfig<T extends BackgroundJobConfig<any>>(jobConfig: T, opts: BackgroundWithOpts): T;
|
|
84
123
|
export type PsychicBackgroundedServiceStaticMethods<T extends typeof BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedService>>;
|
|
85
124
|
export type PsychicBackgroundedServiceInstanceMethods<T extends BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<BaseBackgroundedService>>;
|
|
86
125
|
type OmitJobFromEndOfArguments<Original extends unknown[]> = Original extends [Job] ? Original extends [string] ? Original : [] : Original extends [...infer Rest, Job] ? Original extends [...unknown[], string] ? Original : Rest : Original;
|
|
@@ -51,6 +51,59 @@ export default class BaseScheduledService {
|
|
|
51
51
|
* @param args - a variadic list of arguments to be sent to your method
|
|
52
52
|
*/
|
|
53
53
|
static schedule<T, MethodName extends FunctionPropertyNames<Required<T>>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends MethodFunc extends (...args: any) => any ? Parameters<MethodFunc> : never>(this: T, pattern: string, methodName: MethodName, ...args: MethodArgs): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the id that `schedule` registers a job under, which can be passed
|
|
56
|
+
* to {@link BaseScheduledService.unschedule} to remove that job.
|
|
57
|
+
*
|
|
58
|
+
* The id is derived from the service's global name and the method name. It
|
|
59
|
+
* performs no queue construction and no redis I/O, so it is safe to call
|
|
60
|
+
* anywhere, including from a console in development.
|
|
61
|
+
*
|
|
62
|
+
* The intended workflow for deleting a scheduled service is to read the id
|
|
63
|
+
* off the class before removing it:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* MyScheduledService.unscheduleId('myHourlyMethod')
|
|
67
|
+
* // => 'services/MyScheduledService:myHourlyMethod'
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* then check that string into a seed or migration and call `unschedule` with
|
|
71
|
+
* it, which allows the class itself to be deleted in the same deploy.
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @param methodName - the name of the static method that was scheduled
|
|
78
|
+
* @returns the id `schedule` registered this method under
|
|
79
|
+
*/
|
|
80
|
+
static unscheduleId<T, MethodName extends FunctionPropertyNames<Required<T>>>(this: T, methodName: MethodName): string;
|
|
81
|
+
/**
|
|
82
|
+
* Removes a scheduled job, preventing it from being run again.
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Since the id is just a string, it can also be checked into a seed or
|
|
89
|
+
* migration, which allows the scheduled service class to be deleted in the
|
|
90
|
+
* same deploy that stops its job:
|
|
91
|
+
*
|
|
92
|
+
* ```ts
|
|
93
|
+
* await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* Every queue in your application is checked, so this finds the job whether
|
|
97
|
+
* or not the service's workstream has changed since it was scheduled, and
|
|
98
|
+
* whether it landed in a current or a transitional queue.
|
|
99
|
+
*
|
|
100
|
+
* NOTE: unscheduling stops future runs. It does not cancel a run that has
|
|
101
|
+
* already been placed on a queue, so a final invocation may still happen.
|
|
102
|
+
*
|
|
103
|
+
* @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
|
|
104
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
105
|
+
*/
|
|
106
|
+
static unschedule(id: string): Promise<boolean>;
|
|
54
107
|
/**
|
|
55
108
|
* types composed by psychic must be provided, since psychic-workers leverages
|
|
56
109
|
* the sync command in psychic to read your backgroundable services and extract
|
|
@@ -182,6 +182,34 @@ export declare class Background {
|
|
|
182
182
|
jobConfig?: BackgroundJobConfig<any>;
|
|
183
183
|
scheduleOpts?: JobSchedulerTemplateOptions;
|
|
184
184
|
}): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* @internal
|
|
187
|
+
*
|
|
188
|
+
* Returns the id a scheduled job is registered with BullMQ under.
|
|
189
|
+
*
|
|
190
|
+
* `jobId` is used to determine uniqueness along with name and repeat pattern.
|
|
191
|
+
* Since the name is really a job type and never changes, the `jobId` is the only
|
|
192
|
+
* way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
|
|
193
|
+
* enforced by combining the global name and the method name.
|
|
194
|
+
*
|
|
195
|
+
* See: https://docs.bullmq.io/guide/jobs/repeatable
|
|
196
|
+
*
|
|
197
|
+
* @param globalName - the globalName of the class the method belongs to
|
|
198
|
+
* @param method - the name of the scheduled method
|
|
199
|
+
*/
|
|
200
|
+
jobSchedulerId(globalName: string, method: string): string;
|
|
201
|
+
/**
|
|
202
|
+
* removes a scheduled job from BullMQ, preventing it from being run again
|
|
203
|
+
*
|
|
204
|
+
* Every queue is checked, rather than the one the job's current
|
|
205
|
+
* `backgroundJobConfig` routes to, so that a job can still be unscheduled
|
|
206
|
+
* after its workstream has changed, or after the class that scheduled it has
|
|
207
|
+
* been deleted.
|
|
208
|
+
*
|
|
209
|
+
* @param jobSchedulerId - the id the job was scheduled under
|
|
210
|
+
* @returns true if a scheduled job was removed, false if none was found
|
|
211
|
+
*/
|
|
212
|
+
unschedule(jobSchedulerId: string): Promise<boolean>;
|
|
185
213
|
private queueInstance;
|
|
186
214
|
/**
|
|
187
215
|
* adds the instance method of a provided dream model to BullMQ
|
|
@@ -45,6 +45,20 @@ export interface DelayedJobDuration {
|
|
|
45
45
|
}
|
|
46
46
|
export type JobTypes = 'BackgroundJobQueueFunctionJob' | 'BackgroundJobQueueStaticJob' | 'BackgroundJobQueueModelInstanceJob';
|
|
47
47
|
export type BackgroundQueuePriority = 'default' | 'urgent' | 'not_urgent' | 'last';
|
|
48
|
+
export interface BackgroundWithOpts {
|
|
49
|
+
/**
|
|
50
|
+
* an optional delay to hold off the job for a certain amount of
|
|
51
|
+
* time after it is entered into the queue. Accepts the same options
|
|
52
|
+
* as `backgroundWithDelay`, including an optional `jobId` which
|
|
53
|
+
* debounces repeated calls within the delay window.
|
|
54
|
+
*/
|
|
55
|
+
delay?: DelayedJobOpts;
|
|
56
|
+
/**
|
|
57
|
+
* an optional priority. When provided, this overrides the priority
|
|
58
|
+
* set on the `backgroundJobConfig` of the service or model.
|
|
59
|
+
*/
|
|
60
|
+
priority?: BackgroundQueuePriority;
|
|
61
|
+
}
|
|
48
62
|
interface BaseBackgroundJobConfig {
|
|
49
63
|
priority?: BackgroundQueuePriority;
|
|
50
64
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic-workers",
|
|
4
4
|
"description": "Background job system for Psychic applications",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.6.0",
|
|
6
6
|
"author": "RVO Health",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"ioredis": "^5.8.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@eslint/js": "^
|
|
64
|
+
"@eslint/js": "^10.0.1",
|
|
65
65
|
"@koa/cors": "^5.0.0",
|
|
66
66
|
"@koa/etag": "^5.0.2",
|
|
67
67
|
"@koa/router": "^15.4.0",
|
|
@@ -78,15 +78,15 @@
|
|
|
78
78
|
"@types/node": "^22.12.0",
|
|
79
79
|
"@types/pg": "^8",
|
|
80
80
|
"@types/supertest": "^6.0.3",
|
|
81
|
-
"@typescript-eslint/parser": "^8.
|
|
81
|
+
"@typescript-eslint/parser": "^8.67.0",
|
|
82
82
|
"bullmq": "^5.64.0",
|
|
83
|
-
"eslint": "^
|
|
83
|
+
"eslint": "^10.0.1",
|
|
84
84
|
"ioredis": "^5.8.2",
|
|
85
85
|
"koa": "^3.1.2",
|
|
86
86
|
"koa-bodyparser": "^4.4.1",
|
|
87
87
|
"koa-conditional-get": "^3.0.0",
|
|
88
|
-
"kysely": "^0.28.
|
|
89
|
-
"kysely-codegen": "
|
|
88
|
+
"kysely": "^0.28.17",
|
|
89
|
+
"kysely-codegen": "~0.20.0",
|
|
90
90
|
"luxon-jest-matchers": "^0.1.14",
|
|
91
91
|
"openapi-typescript": "^7.9.1",
|
|
92
92
|
"pg": "^8.13.1",
|
|
@@ -96,8 +96,9 @@
|
|
|
96
96
|
"tsx": "^4.20.6",
|
|
97
97
|
"typedoc": "^0.28.17",
|
|
98
98
|
"typescript": "^5.8.2",
|
|
99
|
-
"typescript-eslint": "^8.
|
|
100
|
-
"
|
|
99
|
+
"typescript-eslint": "^8.67.0",
|
|
100
|
+
"vite": "^7.3.6",
|
|
101
|
+
"vitest": "^4.1.11"
|
|
101
102
|
},
|
|
102
103
|
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
|
|
103
104
|
"dependencies": {
|