@rvoh/psychic-workers 0.3.2 → 0.4.1

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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dream_1 = require("@rvoh/dream");
4
4
  const index_js_1 = require("./index.js");
5
+ const durationToSeconds_js_1 = require("../helpers/durationToSeconds.js");
5
6
  class BaseBackgroundedModel extends dream_1.Dream {
6
7
  /**
7
8
  * A getter meant to be overridden in child classes. This does
@@ -76,11 +77,12 @@ class BaseBackgroundedModel extends dream_1.Dream {
76
77
  * @param methodName - the name of the static method you wish to run in the background
77
78
  * @param args - a variadic list of arguments to be sent to your method
78
79
  */
79
- static async backgroundWithDelay(delaySeconds, methodName, ...args) {
80
+ static async backgroundWithDelay(delay, methodName, ...args) {
80
81
  const safeThis = this;
81
82
  return await index_js_1.default.staticMethod(safeThis, methodName, {
82
83
  globalName: safeThis.globalName,
83
- delaySeconds,
84
+ delaySeconds: (0, durationToSeconds_js_1.default)(delay),
85
+ jobId: delay.jobId,
84
86
  args,
85
87
  jobConfig: safeThis.backgroundJobConfig,
86
88
  });
@@ -143,11 +145,12 @@ class BaseBackgroundedModel extends dream_1.Dream {
143
145
  * @param methodName - the name of the static method you wish to run in the background
144
146
  * @param args - a variadic list of arguments to be sent to your method
145
147
  */
146
- async backgroundWithDelay(delaySeconds, methodName, ...args) {
148
+ async backgroundWithDelay(delay, methodName, ...args) {
147
149
  const safeThis = this;
148
150
  return await index_js_1.default.modelInstanceMethod(safeThis, methodName, {
149
151
  args,
150
- delaySeconds,
152
+ delaySeconds: (0, durationToSeconds_js_1.default)(delay),
153
+ jobId: delay.jobId,
151
154
  jobConfig: safeThis.backgroundJobConfig,
152
155
  });
153
156
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dream_1 = require("@rvoh/dream");
4
4
  const index_js_1 = require("./index.js");
5
+ const durationToSeconds_js_1 = require("../helpers/durationToSeconds.js");
5
6
  class BaseBackgroundedService {
6
7
  /**
7
8
  * A getter meant to be overridden in child classes. This does
@@ -88,11 +89,12 @@ class BaseBackgroundedService {
88
89
  * @param methodName - the name of the static method you wish to run in the background
89
90
  * @param args - a variadic list of arguments to be sent to your method
90
91
  */
91
- static async backgroundWithDelay(delaySeconds, methodName, ...args) {
92
+ static async backgroundWithDelay(delay, methodName, ...args) {
92
93
  const safeThis = this;
93
94
  return await index_js_1.default.staticMethod(safeThis, methodName, {
94
95
  globalName: safeThis.globalName,
95
- delaySeconds,
96
+ delaySeconds: (0, durationToSeconds_js_1.default)(delay),
97
+ jobId: delay.jobId,
96
98
  args,
97
99
  jobConfig: safeThis.backgroundJobConfig,
98
100
  });
@@ -14,6 +14,7 @@ const NoQueueForSpecifiedWorkstream_js_1 = require("../error/background/NoQueueF
14
14
  const EnvInternal_js_1 = require("../helpers/EnvInternal.js");
15
15
  const index_js_1 = require("../psychic-app-workers/index.js");
16
16
  const nameToRedisQueueName_js_1 = require("./helpers/nameToRedisQueueName.js");
17
+ const DEFAULT_CONCURRENCY = 10;
17
18
  /**
18
19
  * the underlying class driving the `background` singleton,
19
20
  * available as an import from `psychic-workers`.
@@ -130,13 +131,15 @@ class Background {
130
131
  }
131
132
  async shutdownAndExit() {
132
133
  await this.shutdown();
133
- process.exit();
134
+ // https://docs.bullmq.io/guide/going-to-production#gracefully-shut-down-workers
135
+ process.exit(0);
134
136
  }
135
137
  /**
136
138
  * Shuts down workers, closes all redis connections
137
139
  */
138
140
  async shutdown() {
139
- await Promise.all(this._workers.map(worker => worker.close()));
141
+ if (!EnvInternal_js_1.default.isTest)
142
+ psychic_1.PsychicApp.log(`[psychic-workers] shutdown`);
140
143
  const psychicWorkersApp = index_js_1.default.getOrFail();
141
144
  for (const hook of psychicWorkersApp.hooks.workerShutdown) {
142
145
  await hook();
@@ -148,18 +151,18 @@ class Background {
148
151
  * closes all redis connections for workers and queues
149
152
  */
150
153
  async closeAllRedisConnections() {
151
- for (const queue of this.queues) {
152
- await queue.close();
153
- }
154
+ if (!EnvInternal_js_1.default.isTest)
155
+ psychic_1.PsychicApp.log(`[psychic-workers] closeAllRedisConnections`);
154
156
  for (const worker of this.workers) {
155
157
  await worker.close();
156
158
  }
157
159
  for (const connection of this.redisConnections) {
158
160
  try {
159
- connection.disconnect();
161
+ await connection.quit();
160
162
  }
161
- catch {
162
- // noop
163
+ catch (error) {
164
+ if (!EnvInternal_js_1.default.isTest)
165
+ psychic_1.PsychicApp.logWithLevel('error', `[psychic-workers] error quitting Redis:`, error);
163
166
  }
164
167
  }
165
168
  }
@@ -205,7 +208,7 @@ class Background {
205
208
  for (let i = 0; i < workerCount; i++) {
206
209
  this._workers.push(new Background.Worker(formattedQueueName, async (job) => await this.doWork(job), {
207
210
  connection: defaultWorkerConnection,
208
- concurrency: backgroundOptions.defaultWorkstream?.concurrency || 1,
211
+ concurrency: backgroundOptions.defaultWorkstream?.concurrency || DEFAULT_CONCURRENCY,
209
212
  }));
210
213
  }
211
214
  }
@@ -252,7 +255,7 @@ class Background {
252
255
  limit: namedWorkstream.rateLimit,
253
256
  },
254
257
  connection: namedWorkstreamWorkerConnection,
255
- concurrency: namedWorkstream.concurrency || 1,
258
+ concurrency: namedWorkstream.concurrency || DEFAULT_CONCURRENCY,
256
259
  // explicitly typing as WorkerOptions because Psychic can't be aware of BullMQ Pro options
257
260
  }));
258
261
  }
@@ -371,11 +374,15 @@ class Background {
371
374
  work() {
372
375
  this.connect({ activateWorkers: true });
373
376
  process.on('SIGTERM', () => {
377
+ if (!EnvInternal_js_1.default.isTest)
378
+ psychic_1.PsychicApp.log('[psychic-workers] handle SIGTERM');
374
379
  void this.shutdownAndExit()
375
380
  .then(() => { })
376
381
  .catch(() => { });
377
382
  });
378
383
  process.on('SIGINT', () => {
384
+ if (!EnvInternal_js_1.default.isTest)
385
+ psychic_1.PsychicApp.log('[psychic-workers] handle SIGINT');
379
386
  void this.shutdownAndExit()
380
387
  .then(() => { })
381
388
  .catch(() => { });
@@ -392,7 +399,7 @@ class Background {
392
399
  * @param importKey - (optional) the import key for the class
393
400
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
394
401
  */
395
- async staticMethod(ObjectClass, method, { globalName, delaySeconds, args = [], jobConfig = {}, }) {
402
+ async staticMethod(ObjectClass, method, { globalName, delaySeconds, jobId, args = [], jobConfig = {}, }) {
396
403
  this.connect();
397
404
  await this._addToQueue(`BackgroundJobQueueStaticJob`, {
398
405
  globalName,
@@ -400,6 +407,7 @@ class Background {
400
407
  args,
401
408
  }, {
402
409
  delaySeconds,
410
+ jobId,
403
411
  jobConfig,
404
412
  groupId: this.jobConfigToGroupId(jobConfig),
405
413
  priority: this.jobConfigToPriority(jobConfig),
@@ -472,7 +480,7 @@ class Background {
472
480
  * @param importKey - (optional) the import key for the class
473
481
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
474
482
  */
475
- async modelInstanceMethod(modelInstance, method, { delaySeconds, args = [], jobConfig = {}, }) {
483
+ async modelInstanceMethod(modelInstance, method, { delaySeconds, jobId, args = [], jobConfig = {}, }) {
476
484
  this.connect();
477
485
  await this._addToQueue('BackgroundJobQueueModelInstanceJob', {
478
486
  id: modelInstance.primaryKeyValue,
@@ -481,16 +489,19 @@ class Background {
481
489
  args,
482
490
  }, {
483
491
  delaySeconds,
492
+ jobId,
484
493
  jobConfig,
485
494
  groupId: this.jobConfigToGroupId(jobConfig),
486
495
  priority: this.jobConfigToPriority(jobConfig),
487
496
  });
488
497
  }
489
498
  // should be private, but public so we can test
490
- async _addToQueue(jobType, jobData, { delaySeconds, jobConfig, priority, groupId, }) {
499
+ async _addToQueue(jobType, jobData, { delaySeconds, jobId, jobConfig, priority, groupId, }) {
491
500
  // set this variable out side of the conditional so that
492
501
  // mismatches will raise exceptions even in tests
493
502
  const queueInstance = this.queueInstance(jobConfig);
503
+ // if delaySeconds is 0, we will intentionally treat
504
+ // this as `undefined`
494
505
  const delay = delaySeconds ? delaySeconds * 1000 : undefined;
495
506
  if (EnvInternal_js_1.default.isTest && !EnvInternal_js_1.default.boolean('REALLY_TEST_BACKGROUND_QUEUE')) {
496
507
  const queue = new Background.Queue('TestQueue', { connection: {} });
@@ -504,6 +515,7 @@ class Background {
504
515
  if (groupId && priority) {
505
516
  await queueInstance.add(jobType, jobData, {
506
517
  delay,
518
+ jobId,
507
519
  group: {
508
520
  ...this.groupIdToGroupConfig(groupId),
509
521
  priority: this.mapPriorityWordToPriorityNumber(priority),
@@ -515,6 +527,7 @@ class Background {
515
527
  else {
516
528
  await queueInstance.add(jobType, jobData, {
517
529
  delay,
530
+ jobId,
518
531
  group: this.groupIdToGroupConfig(groupId),
519
532
  priority: this.mapPriorityWordToPriorityNumber(priority),
520
533
  // explicitly typing as JobsOptions because Psychic can't be aware of BullMQ Pro options
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = durationToSeconds;
4
+ function durationToSeconds(duration) {
5
+ return ((duration.seconds ? duration.seconds : 0) +
6
+ (duration.minutes ? duration.minutes * 60 : 0) +
7
+ (duration.hours ? duration.hours * 60 * 60 : 0) +
8
+ (duration.days ? duration.days * 60 * 60 * 24 : 0));
9
+ }
@@ -1,5 +1,6 @@
1
1
  import { Dream } from '@rvoh/dream';
2
2
  import background from './index.js';
3
+ import durationToSeconds from '../helpers/durationToSeconds.js';
3
4
  export default class BaseBackgroundedModel extends Dream {
4
5
  /**
5
6
  * A getter meant to be overridden in child classes. This does
@@ -74,11 +75,12 @@ export default class BaseBackgroundedModel extends Dream {
74
75
  * @param methodName - the name of the static method you wish to run in the background
75
76
  * @param args - a variadic list of arguments to be sent to your method
76
77
  */
77
- static async backgroundWithDelay(delaySeconds, methodName, ...args) {
78
+ static async backgroundWithDelay(delay, methodName, ...args) {
78
79
  const safeThis = this;
79
80
  return await background.staticMethod(safeThis, methodName, {
80
81
  globalName: safeThis.globalName,
81
- delaySeconds,
82
+ delaySeconds: durationToSeconds(delay),
83
+ jobId: delay.jobId,
82
84
  args,
83
85
  jobConfig: safeThis.backgroundJobConfig,
84
86
  });
@@ -141,11 +143,12 @@ export default class BaseBackgroundedModel extends Dream {
141
143
  * @param methodName - the name of the static method you wish to run in the background
142
144
  * @param args - a variadic list of arguments to be sent to your method
143
145
  */
144
- async backgroundWithDelay(delaySeconds, methodName, ...args) {
146
+ async backgroundWithDelay(delay, methodName, ...args) {
145
147
  const safeThis = this;
146
148
  return await background.modelInstanceMethod(safeThis, methodName, {
147
149
  args,
148
- delaySeconds,
150
+ delaySeconds: durationToSeconds(delay),
151
+ jobId: delay.jobId,
149
152
  jobConfig: safeThis.backgroundJobConfig,
150
153
  });
151
154
  }
@@ -1,5 +1,6 @@
1
1
  import { GlobalNameNotSet } from '@rvoh/dream';
2
2
  import background from './index.js';
3
+ import durationToSeconds from '../helpers/durationToSeconds.js';
3
4
  export default class BaseBackgroundedService {
4
5
  /**
5
6
  * A getter meant to be overridden in child classes. This does
@@ -86,11 +87,12 @@ export default class BaseBackgroundedService {
86
87
  * @param methodName - the name of the static method you wish to run in the background
87
88
  * @param args - a variadic list of arguments to be sent to your method
88
89
  */
89
- static async backgroundWithDelay(delaySeconds, methodName, ...args) {
90
+ static async backgroundWithDelay(delay, methodName, ...args) {
90
91
  const safeThis = this;
91
92
  return await background.staticMethod(safeThis, methodName, {
92
93
  globalName: safeThis.globalName,
93
- delaySeconds,
94
+ delaySeconds: durationToSeconds(delay),
95
+ jobId: delay.jobId,
94
96
  args,
95
97
  jobConfig: safeThis.backgroundJobConfig,
96
98
  });
@@ -10,6 +10,7 @@ import NoQueueForSpecifiedWorkstream from '../error/background/NoQueueForSpecifi
10
10
  import EnvInternal from '../helpers/EnvInternal.js';
11
11
  import PsychicAppWorkers from '../psychic-app-workers/index.js';
12
12
  import nameToRedisQueueName from './helpers/nameToRedisQueueName.js';
13
+ const DEFAULT_CONCURRENCY = 10;
13
14
  /**
14
15
  * the underlying class driving the `background` singleton,
15
16
  * available as an import from `psychic-workers`.
@@ -126,13 +127,15 @@ export class Background {
126
127
  }
127
128
  async shutdownAndExit() {
128
129
  await this.shutdown();
129
- process.exit();
130
+ // https://docs.bullmq.io/guide/going-to-production#gracefully-shut-down-workers
131
+ process.exit(0);
130
132
  }
131
133
  /**
132
134
  * Shuts down workers, closes all redis connections
133
135
  */
134
136
  async shutdown() {
135
- await Promise.all(this._workers.map(worker => worker.close()));
137
+ if (!EnvInternal.isTest)
138
+ PsychicApp.log(`[psychic-workers] shutdown`);
136
139
  const psychicWorkersApp = PsychicAppWorkers.getOrFail();
137
140
  for (const hook of psychicWorkersApp.hooks.workerShutdown) {
138
141
  await hook();
@@ -144,18 +147,18 @@ export class Background {
144
147
  * closes all redis connections for workers and queues
145
148
  */
146
149
  async closeAllRedisConnections() {
147
- for (const queue of this.queues) {
148
- await queue.close();
149
- }
150
+ if (!EnvInternal.isTest)
151
+ PsychicApp.log(`[psychic-workers] closeAllRedisConnections`);
150
152
  for (const worker of this.workers) {
151
153
  await worker.close();
152
154
  }
153
155
  for (const connection of this.redisConnections) {
154
156
  try {
155
- connection.disconnect();
157
+ await connection.quit();
156
158
  }
157
- catch {
158
- // noop
159
+ catch (error) {
160
+ if (!EnvInternal.isTest)
161
+ PsychicApp.logWithLevel('error', `[psychic-workers] error quitting Redis:`, error);
159
162
  }
160
163
  }
161
164
  }
@@ -201,7 +204,7 @@ export class Background {
201
204
  for (let i = 0; i < workerCount; i++) {
202
205
  this._workers.push(new Background.Worker(formattedQueueName, async (job) => await this.doWork(job), {
203
206
  connection: defaultWorkerConnection,
204
- concurrency: backgroundOptions.defaultWorkstream?.concurrency || 1,
207
+ concurrency: backgroundOptions.defaultWorkstream?.concurrency || DEFAULT_CONCURRENCY,
205
208
  }));
206
209
  }
207
210
  }
@@ -248,7 +251,7 @@ export class Background {
248
251
  limit: namedWorkstream.rateLimit,
249
252
  },
250
253
  connection: namedWorkstreamWorkerConnection,
251
- concurrency: namedWorkstream.concurrency || 1,
254
+ concurrency: namedWorkstream.concurrency || DEFAULT_CONCURRENCY,
252
255
  // explicitly typing as WorkerOptions because Psychic can't be aware of BullMQ Pro options
253
256
  }));
254
257
  }
@@ -367,11 +370,15 @@ export class Background {
367
370
  work() {
368
371
  this.connect({ activateWorkers: true });
369
372
  process.on('SIGTERM', () => {
373
+ if (!EnvInternal.isTest)
374
+ PsychicApp.log('[psychic-workers] handle SIGTERM');
370
375
  void this.shutdownAndExit()
371
376
  .then(() => { })
372
377
  .catch(() => { });
373
378
  });
374
379
  process.on('SIGINT', () => {
380
+ if (!EnvInternal.isTest)
381
+ PsychicApp.log('[psychic-workers] handle SIGINT');
375
382
  void this.shutdownAndExit()
376
383
  .then(() => { })
377
384
  .catch(() => { });
@@ -388,7 +395,7 @@ export class Background {
388
395
  * @param importKey - (optional) the import key for the class
389
396
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
390
397
  */
391
- async staticMethod(ObjectClass, method, { globalName, delaySeconds, args = [], jobConfig = {}, }) {
398
+ async staticMethod(ObjectClass, method, { globalName, delaySeconds, jobId, args = [], jobConfig = {}, }) {
392
399
  this.connect();
393
400
  await this._addToQueue(`BackgroundJobQueueStaticJob`, {
394
401
  globalName,
@@ -396,6 +403,7 @@ export class Background {
396
403
  args,
397
404
  }, {
398
405
  delaySeconds,
406
+ jobId,
399
407
  jobConfig,
400
408
  groupId: this.jobConfigToGroupId(jobConfig),
401
409
  priority: this.jobConfigToPriority(jobConfig),
@@ -468,7 +476,7 @@ export class Background {
468
476
  * @param importKey - (optional) the import key for the class
469
477
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
470
478
  */
471
- async modelInstanceMethod(modelInstance, method, { delaySeconds, args = [], jobConfig = {}, }) {
479
+ async modelInstanceMethod(modelInstance, method, { delaySeconds, jobId, args = [], jobConfig = {}, }) {
472
480
  this.connect();
473
481
  await this._addToQueue('BackgroundJobQueueModelInstanceJob', {
474
482
  id: modelInstance.primaryKeyValue,
@@ -477,16 +485,19 @@ export class Background {
477
485
  args,
478
486
  }, {
479
487
  delaySeconds,
488
+ jobId,
480
489
  jobConfig,
481
490
  groupId: this.jobConfigToGroupId(jobConfig),
482
491
  priority: this.jobConfigToPriority(jobConfig),
483
492
  });
484
493
  }
485
494
  // should be private, but public so we can test
486
- async _addToQueue(jobType, jobData, { delaySeconds, jobConfig, priority, groupId, }) {
495
+ async _addToQueue(jobType, jobData, { delaySeconds, jobId, jobConfig, priority, groupId, }) {
487
496
  // set this variable out side of the conditional so that
488
497
  // mismatches will raise exceptions even in tests
489
498
  const queueInstance = this.queueInstance(jobConfig);
499
+ // if delaySeconds is 0, we will intentionally treat
500
+ // this as `undefined`
490
501
  const delay = delaySeconds ? delaySeconds * 1000 : undefined;
491
502
  if (EnvInternal.isTest && !EnvInternal.boolean('REALLY_TEST_BACKGROUND_QUEUE')) {
492
503
  const queue = new Background.Queue('TestQueue', { connection: {} });
@@ -500,6 +511,7 @@ export class Background {
500
511
  if (groupId && priority) {
501
512
  await queueInstance.add(jobType, jobData, {
502
513
  delay,
514
+ jobId,
503
515
  group: {
504
516
  ...this.groupIdToGroupConfig(groupId),
505
517
  priority: this.mapPriorityWordToPriorityNumber(priority),
@@ -511,6 +523,7 @@ export class Background {
511
523
  else {
512
524
  await queueInstance.add(jobType, jobData, {
513
525
  delay,
526
+ jobId,
514
527
  group: this.groupIdToGroupConfig(groupId),
515
528
  priority: this.mapPriorityWordToPriorityNumber(priority),
516
529
  // explicitly typing as JobsOptions because Psychic can't be aware of BullMQ Pro options
@@ -0,0 +1,6 @@
1
+ export default function durationToSeconds(duration) {
2
+ return ((duration.seconds ? duration.seconds : 0) +
3
+ (duration.minutes ? duration.minutes * 60 : 0) +
4
+ (duration.hours ? duration.hours * 60 * 60 : 0) +
5
+ (duration.days ? duration.days * 60 * 60 * 24 : 0));
6
+ }
@@ -1,5 +1,5 @@
1
1
  import { Dream } from '@rvoh/dream';
2
- import { BackgroundJobConfig } from '../types/background.js';
2
+ import { BackgroundJobConfig, 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 {
@@ -64,7 +64,7 @@ export default class BaseBackgroundedModel extends Dream {
64
64
  * @param methodName - the name of the static method you wish to run in the background
65
65
  * @param args - a variadic list of arguments to be sent to your method
66
66
  */
67
- static backgroundWithDelay<T, MethodName extends PsychicBackgroundedModelStaticMethods<T & typeof BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delaySeconds: number, methodName: MethodName, ...args: MethodArgs): Promise<void>;
67
+ 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>;
68
68
  /**
69
69
  * types composed by psychic must be provided, since psychic-workers leverages
70
70
  * the sync command in psychic to read your backgroundable services and extract
@@ -114,7 +114,7 @@ export default class BaseBackgroundedModel extends Dream {
114
114
  * @param methodName - the name of the static method you wish to run in the background
115
115
  * @param args - a variadic list of arguments to be sent to your method
116
116
  */
117
- backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceInstanceMethods<T & BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delaySeconds: number, methodName: MethodName, ...args: MethodArgs): Promise<void>;
117
+ 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>;
118
118
  }
119
119
  export type PsychicBackgroundedModelStaticMethods<T extends typeof BaseBackgroundedModel> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedModel>>;
120
120
  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 } from '../types/background.js';
2
+ import { BackgroundJobConfig, DelayedJobOpts } from '../types/background.js';
3
3
  import { FunctionPropertyNames } from '../types/utils.js';
4
4
  export default class BaseBackgroundedService {
5
5
  /**
@@ -72,7 +72,7 @@ export default class BaseBackgroundedService {
72
72
  * @param methodName - the name of the static method you wish to run in the background
73
73
  * @param args - a variadic list of arguments to be sent to your method
74
74
  */
75
- static backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceStaticMethods<T & typeof BaseBackgroundedService>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delaySeconds: number, methodName: MethodName, ...args: MethodArgs): Promise<void>;
75
+ 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>;
76
76
  /**
77
77
  * types composed by psychic must be provided, since psychic-workers leverages
78
78
  * the sync command in psychic to read your backgroundable services and extract
@@ -124,11 +124,12 @@ export declare class Background {
124
124
  * @param importKey - (optional) the import key for the class
125
125
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
126
126
  */
127
- staticMethod(ObjectClass: Record<'name', string>, method: string, { globalName, delaySeconds, args, jobConfig, }: {
127
+ staticMethod(ObjectClass: Record<'name', string>, method: string, { globalName, delaySeconds, jobId, args, jobConfig, }: {
128
128
  globalName: string;
129
129
  args?: any[];
130
130
  filepath?: string;
131
131
  delaySeconds?: number;
132
+ jobId?: string | undefined;
132
133
  importKey?: string;
133
134
  jobConfig?: BackgroundJobConfig<any>;
134
135
  }): Promise<void>;
@@ -162,14 +163,16 @@ export declare class Background {
162
163
  * @param importKey - (optional) the import key for the class
163
164
  * @param jobConfig - (optional) the background job config to use when backgrounding this method
164
165
  */
165
- modelInstanceMethod(modelInstance: Dream, method: string, { delaySeconds, args, jobConfig, }: {
166
+ modelInstanceMethod(modelInstance: Dream, method: string, { delaySeconds, jobId, args, jobConfig, }: {
166
167
  delaySeconds?: number;
168
+ jobId?: string | undefined;
167
169
  importKey?: string;
168
170
  args?: any[];
169
171
  jobConfig?: BackgroundJobConfig<any>;
170
172
  }): Promise<void>;
171
- _addToQueue(jobType: JobTypes, jobData: BackgroundJobData, { delaySeconds, jobConfig, priority, groupId, }: {
173
+ _addToQueue(jobType: JobTypes, jobData: BackgroundJobData, { delaySeconds, jobId, jobConfig, priority, groupId, }: {
172
174
  delaySeconds?: number | undefined;
175
+ jobId?: string | undefined;
173
176
  jobConfig: BackgroundJobConfig<any>;
174
177
  priority: BackgroundQueuePriority;
175
178
  groupId?: string | undefined;
@@ -1,6 +1,6 @@
1
1
  import { Env } from '@rvoh/dream';
2
2
  declare const EnvInternal: Env<{
3
3
  string: "NODE_ENV" | "PSYCHIC_CORE_DEVELOPMENT";
4
- boolean: "DEBUG" | "PSYCHIC_CORE_DEVELOPMENT" | "REALLY_TEST_BACKGROUND_QUEUE";
5
- }, "NODE_ENV" | "PSYCHIC_CORE_DEVELOPMENT", never, "PSYCHIC_CORE_DEVELOPMENT" | "DEBUG" | "REALLY_TEST_BACKGROUND_QUEUE">;
4
+ boolean: "REALLY_TEST_BACKGROUND_QUEUE";
5
+ }, "NODE_ENV" | "PSYCHIC_CORE_DEVELOPMENT", never, "REALLY_TEST_BACKGROUND_QUEUE">;
6
6
  export default EnvInternal;
@@ -0,0 +1,2 @@
1
+ import { DelayedJobDuration } from '../types/background.js';
2
+ export default function durationToSeconds(duration: DelayedJobDuration): number;
@@ -30,6 +30,20 @@ export interface BackgroundJobData {
30
30
  */
31
31
  globalName?: string;
32
32
  }
33
+ export type DelayedJobOpts = DelayedJobDuration & {
34
+ /**
35
+ * a unique identifier for your job. this identifier will be
36
+ * used to debounce, leveraging the internal throttling mechanisms
37
+ * provided by BullMQ
38
+ */
39
+ jobId?: string;
40
+ };
41
+ export interface DelayedJobDuration {
42
+ seconds?: number;
43
+ minutes?: number;
44
+ hours?: number;
45
+ days?: number;
46
+ }
33
47
  export type JobTypes = 'BackgroundJobQueueFunctionJob' | 'BackgroundJobQueueStaticJob' | 'BackgroundJobQueueModelInstanceJob';
34
48
  export type BackgroundQueuePriority = 'default' | 'urgent' | 'not_urgent' | 'last';
35
49
  interface BaseBackgroundJobConfig {
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": "0.3.2",
5
+ "version": "0.4.1",
6
6
  "author": "RVO Health",
7
7
  "repository": {
8
8
  "type": "git",
@@ -73,4 +73,4 @@
73
73
  "vitest": "^3.1.1"
74
74
  },
75
75
  "packageManager": "yarn@4.7.0"
76
- }
76
+ }