@orion-js/dogs 4.6.2 → 4.6.5

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/index.cjs CHANGED
@@ -92,27 +92,169 @@ __export(index_exports, {
92
92
  startWorkers: () => startWorkers
93
93
  });
94
94
  module.exports = __toCommonJS(index_exports);
95
- var import_services5 = require("@orion-js/services");
95
+ var import_services6 = require("@orion-js/services");
96
96
 
97
- // src/services/EventsService.ts
98
- var import_logger2 = require("@orion-js/logger");
99
- var import_services = require("@orion-js/services");
97
+ // src/repos/JobsHistoryRepo.ts
98
+ var import_mongodb = require("@orion-js/mongodb");
99
+
100
+ // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/isInteger.js
101
+ function _isInteger(n) {
102
+ return n << 0 === n;
103
+ }
104
+ var isInteger = Number.isInteger || _isInteger;
105
+
106
+ // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/createPath.js
107
+ function createPath(path, delimiter = ".") {
108
+ return typeof path === "string" ? path.split(delimiter).map((x) => isInteger(x) ? Number(x) : x) : path;
109
+ }
110
+
111
+ // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/compare.js
112
+ function compare(a, b) {
113
+ return String(a) === String(b);
114
+ }
115
+
116
+ // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/includes.js
117
+ function includes(a, list) {
118
+ let index = -1;
119
+ const { length } = list;
120
+ while (++index < length)
121
+ if (compare(list[index], a))
122
+ return true;
123
+ return false;
124
+ }
125
+
126
+ // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/omit.js
127
+ function omit(propsToOmit, obj) {
128
+ if (arguments.length === 1) return (_obj) => omit(propsToOmit, _obj);
129
+ if (obj === null || obj === void 0)
130
+ return void 0;
131
+ const propsToOmitValue = createPath(propsToOmit, ",");
132
+ const willReturn = {};
133
+ for (const key in obj)
134
+ if (!includes(key, propsToOmitValue))
135
+ willReturn[key] = obj[key];
136
+ return willReturn;
137
+ }
138
+
139
+ // src/types/HistoryRecord.ts
140
+ var import_schema = require("@orion-js/schema");
141
+ var HistoryRecordSchema = (0, import_schema.schemaWithName)("HistoryRecord", {
142
+ _id: { type: "string" },
143
+ jobId: { type: "string" },
144
+ executionId: { type: "string" },
145
+ jobName: { type: "string" },
146
+ type: { type: "string" },
147
+ priority: { type: "number" },
148
+ tries: { type: "number" },
149
+ uniqueIdentifier: { type: "string", optional: true },
150
+ startedAt: { type: "date" },
151
+ endedAt: { type: "date" },
152
+ duration: { type: "number" },
153
+ expiresAt: { type: "date", optional: true },
154
+ status: { type: "string", enum: ["success", "error", "stale"] },
155
+ errorMessage: { type: "string", optional: true },
156
+ params: { type: "blackbox", optional: true },
157
+ result: { type: "any", optional: true }
158
+ });
159
+
160
+ // src/repos/JobsHistoryRepo.ts
161
+ var _history_dec, _JobsHistoryRepo_decorators, _init;
162
+ _JobsHistoryRepo_decorators = [(0, import_mongodb.Repository)()], _history_dec = [(0, import_mongodb.MongoCollection)({
163
+ name: "orionjs.jobs_dogs_history",
164
+ idGeneration: "uuid",
165
+ schema: HistoryRecordSchema,
166
+ indexes: [
167
+ {
168
+ keys: {
169
+ jobName: 1,
170
+ startedAt: 1
171
+ }
172
+ },
173
+ {
174
+ keys: {
175
+ executionId: 1
176
+ }
177
+ },
178
+ {
179
+ keys: {
180
+ expiresAt: 1
181
+ },
182
+ options: {
183
+ expireAfterSeconds: 0
184
+ }
185
+ }
186
+ ]
187
+ })];
188
+ var JobsHistoryRepo = class {
189
+ constructor() {
190
+ __publicField(this, "history", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
191
+ }
192
+ async saveExecution(record) {
193
+ await this.history.upsert(
194
+ { executionId: record.executionId },
195
+ {
196
+ $setOnInsert: {
197
+ status: record.status
198
+ },
199
+ $set: {
200
+ ...omit(["status"], record)
201
+ }
202
+ }
203
+ );
204
+ }
205
+ async getExecutions(jobName, limit, skip) {
206
+ const cursor = this.history.find({ jobName }).sort({ startedAt: -1 });
207
+ if (skip) {
208
+ cursor.skip(skip);
209
+ }
210
+ if (limit) {
211
+ cursor.limit(limit);
212
+ }
213
+ return await cursor.toArray();
214
+ }
215
+ };
216
+ _init = __decoratorStart(null);
217
+ __decorateElement(_init, 5, "history", _history_dec, JobsHistoryRepo);
218
+ JobsHistoryRepo = __decorateElement(_init, 0, "JobsHistoryRepo", _JobsHistoryRepo_decorators, JobsHistoryRepo);
219
+ __runInitializers(_init, 1, JobsHistoryRepo);
100
220
 
101
221
  // src/repos/JobsRepo.ts
102
222
  var import_helpers = require("@orion-js/helpers");
103
223
  var import_logger = require("@orion-js/logger");
104
- var import_mongodb = require("@orion-js/mongodb");
224
+ var import_mongodb2 = require("@orion-js/mongodb");
225
+
226
+ // src/services/partitions.ts
227
+ function validatePartitionsCount(nPartitions) {
228
+ if (!Number.isInteger(nPartitions) || nPartitions <= 0) {
229
+ throw new Error("nPartitions must be a positive integer");
230
+ }
231
+ }
232
+ function getRandomPartition(nPartitions) {
233
+ return Math.floor(Math.random() * nPartitions);
234
+ }
235
+ function createShuffledPartitions(nPartitions) {
236
+ const partitions = Array.from({ length: nPartitions }, (_, partition) => partition);
237
+ for (let index = partitions.length - 1; index > 0; index--) {
238
+ const swapIndex = Math.floor(Math.random() * (index + 1));
239
+ [partitions[index], partitions[swapIndex]] = [partitions[swapIndex], partitions[index]];
240
+ }
241
+ return partitions;
242
+ }
105
243
 
106
244
  // src/types/JobRecord.ts
107
- var import_schema = require("@orion-js/schema");
108
- var JobStatusEnum = (0, import_schema.createEnum)("JobStatus", ["pending", "maxTriesReached"]);
109
- var JobRecordSchema = (0, import_schema.schemaWithName)("JobRecord", {
245
+ var import_schema2 = require("@orion-js/schema");
246
+ var JobStatusEnum = (0, import_schema2.createEnum)("JobStatus", ["pending", "maxTriesReached"]);
247
+ var JobRecordSchema = (0, import_schema2.schemaWithName)("JobRecord", {
110
248
  _id: { type: "string" },
111
249
  jobName: { type: "string" },
112
- type: { type: (0, import_schema.createEnum)("JobType", ["recurrent", "event"]) },
250
+ type: { type: (0, import_schema2.createEnum)("JobType", ["recurrent", "event"]) },
113
251
  priority: { type: "number" },
114
252
  uniqueIdentifier: { type: "string", optional: true },
115
253
  nextRunAt: { type: "date" },
254
+ /**
255
+ * Acquisition partition. Optional so records created by previous versions can be reconciled.
256
+ */
257
+ partition: { type: "number", optional: true },
116
258
  lastRunAt: { type: "date", optional: true },
117
259
  lockedUntil: { type: "date", optional: true },
118
260
  /**
@@ -120,6 +262,11 @@ var JobRecordSchema = (0, import_schema.schemaWithName)("JobRecord", {
120
262
  * this value so a stale execution cannot modify a newer claim.
121
263
  */
122
264
  lockId: { type: "string", optional: true },
265
+ /**
266
+ * Internal acquisition metadata used when MongoDB supports sorted updateOne. It preserves the
267
+ * pre-update stale state because updateOne intentionally does not return the previous document.
268
+ */
269
+ claimWasStale: { type: "boolean", optional: true },
123
270
  tries: { type: "number", optional: true },
124
271
  params: { type: "blackbox", optional: true },
125
272
  /**
@@ -144,23 +291,30 @@ var DEFAULT_MAX_TRIES_REACHED_RETENTION_MS = 1e3 * 60 * 60 * 24 * 7;
144
291
  // src/repos/JobsRepo.ts
145
292
  var JOB_ACQUISITION_HINTS = {
146
293
  byJobName: {
294
+ partition: 1,
147
295
  jobName: 1,
148
296
  priority: -1,
149
297
  nextRunAt: 1
150
298
  },
151
299
  globalPriority: {
300
+ partition: 1,
152
301
  priority: -1,
153
302
  nextRunAt: 1
154
303
  }
155
304
  };
156
305
  var INITIAL_JOB_ACQUISITION_HINT = "byJobName";
157
306
  var CANDIDATE_JOB_ACQUISITION_HINT = "globalPriority";
307
+ var SORTED_UPDATE_ONE_MIN_WIRE_VERSION = 25;
308
+ var LOCK_ID_INDEX_NAME = "jobs_dogs_records_lock_id";
309
+ var PARTITION_JOB_NAME_INDEX_NAME = "jobs_dogs_records_partition_job_name_priority_next_run";
310
+ var PARTITION_PRIORITY_INDEX_NAME = "jobs_dogs_records_partition_priority_next_run";
158
311
  var JOB_ACQUISITION_SORT = {
159
312
  priority: -1,
160
313
  nextRunAt: 1
161
314
  };
162
- function getJobAcquisitionSelector(jobNames, now) {
315
+ function getJobAcquisitionSelector(jobNames, now, partition) {
163
316
  return {
317
+ partition,
164
318
  jobName: { $in: jobNames },
165
319
  nextRunAt: { $lte: now },
166
320
  $and: [
@@ -171,17 +325,19 @@ function getJobAcquisitionSelector(jobNames, now) {
171
325
  ]
172
326
  };
173
327
  }
174
- var _jobs_dec, _JobsRepo_decorators, _init;
175
- _JobsRepo_decorators = [(0, import_mongodb.Repository)()], _jobs_dec = [(0, import_mongodb.MongoCollection)({
328
+ var _jobs_dec, _JobsRepo_decorators, _init2;
329
+ _JobsRepo_decorators = [(0, import_mongodb2.Repository)()], _jobs_dec = [(0, import_mongodb2.MongoCollection)({
176
330
  idGeneration: "uuid",
177
331
  name: "orionjs.jobs_dogs_records",
178
332
  schema: JobRecordSchema,
179
333
  indexes: [
180
334
  {
181
- keys: JOB_ACQUISITION_HINTS.byJobName
335
+ keys: JOB_ACQUISITION_HINTS.byJobName,
336
+ options: { name: PARTITION_JOB_NAME_INDEX_NAME }
182
337
  },
183
338
  {
184
- keys: JOB_ACQUISITION_HINTS.globalPriority
339
+ keys: JOB_ACQUISITION_HINTS.globalPriority,
340
+ options: { name: PARTITION_PRIORITY_INDEX_NAME }
185
341
  },
186
342
  {
187
343
  keys: {
@@ -201,6 +357,15 @@ _JobsRepo_decorators = [(0, import_mongodb.Repository)()], _jobs_dec = [(0, impo
201
357
  sparse: true
202
358
  }
203
359
  },
360
+ {
361
+ keys: {
362
+ lockId: 1
363
+ },
364
+ options: {
365
+ name: LOCK_ID_INDEX_NAME,
366
+ sparse: true
367
+ }
368
+ },
204
369
  {
205
370
  keys: {
206
371
  expiresAt: 1
@@ -212,14 +377,55 @@ _JobsRepo_decorators = [(0, import_mongodb.Repository)()], _jobs_dec = [(0, impo
212
377
  })];
213
378
  var JobsRepo = class {
214
379
  constructor() {
215
- __publicField(this, "jobs", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
380
+ __publicField(this, "jobs", __runInitializers(_init2, 8, this)), __runInitializers(_init2, 11, this);
381
+ __publicField(this, "sortedUpdateOneSupportPromise");
382
+ __publicField(this, "partitionsByJobName", /* @__PURE__ */ new Map());
383
+ }
384
+ configureJobPartitions(jobNames, nPartitions) {
385
+ for (const jobName of jobNames) {
386
+ this.partitionsByJobName.set(jobName, nPartitions);
387
+ }
388
+ }
389
+ getJobPartitionsCount(jobName) {
390
+ return this.partitionsByJobName.get(jobName);
391
+ }
392
+ async supportsSortedUpdateOne() {
393
+ if (!this.sortedUpdateOneSupportPromise) {
394
+ this.sortedUpdateOneSupportPromise = this.detectSortedUpdateOneSupport();
395
+ }
396
+ return this.sortedUpdateOneSupportPromise;
397
+ }
398
+ async detectSortedUpdateOneSupport() {
399
+ const rawCollection = await this.jobs.getRawCollection();
400
+ await this.jobs.createIndexesPromise;
401
+ const hello = await rawCollection.db.admin().command({ hello: 1 });
402
+ const supportsSortedUpdateOne = typeof hello.maxWireVersion === "number" && hello.maxWireVersion >= SORTED_UPDATE_ONE_MIN_WIRE_VERSION;
403
+ if (!supportsSortedUpdateOne) {
404
+ import_logger.logger.info("MongoDB does not support sorted updateOne; using findOneAndUpdate for jobs.");
405
+ return false;
406
+ }
407
+ const hasLockIdIndex = await rawCollection.indexExists(LOCK_ID_INDEX_NAME);
408
+ if (!hasLockIdIndex) {
409
+ import_logger.logger.warn(
410
+ `MongoDB supports sorted updateOne but index "${LOCK_ID_INDEX_NAME}" is unavailable; using findOneAndUpdate for jobs.`
411
+ );
412
+ return false;
413
+ }
414
+ import_logger.logger.info("MongoDB supports sorted updateOne; using it for job acquisition.");
415
+ return true;
416
+ }
417
+ async getJobAndLock(jobNames, lockTime, hint = INITIAL_JOB_ACQUISITION_HINT, partition = 0) {
418
+ if (await this.supportsSortedUpdateOne()) {
419
+ return this.getJobAndLockWithSortedUpdateOne(jobNames, lockTime, hint, partition);
420
+ }
421
+ return this.getJobAndLockWithFindOneAndUpdate(jobNames, lockTime, hint, partition);
216
422
  }
217
- async getJobAndLock(jobNames, lockTime, hint = INITIAL_JOB_ACQUISITION_HINT) {
423
+ async getJobAndLockWithFindOneAndUpdate(jobNames, lockTime, hint, partition) {
218
424
  const executionId = (0, import_helpers.generateId)();
219
425
  const now = /* @__PURE__ */ new Date();
220
426
  const lockedUntil = new Date(now.getTime() + lockTime);
221
427
  const job = await this.jobs.findOneAndUpdate(
222
- getJobAcquisitionSelector(jobNames, now),
428
+ getJobAcquisitionSelector(jobNames, now, partition),
223
429
  {
224
430
  $set: { lockId: executionId, lockedUntil, lastRunAt: now },
225
431
  $inc: { tries: 1 }
@@ -235,6 +441,37 @@ var JobsRepo = class {
235
441
  if (!job) return;
236
442
  const tries = (job.tries || 0) + 1;
237
443
  const wasStale = Boolean(job.lockedUntil);
444
+ return this.toJobToRun(job, executionId, lockTime, tries, wasStale);
445
+ }
446
+ async getJobAndLockWithSortedUpdateOne(jobNames, lockTime, hint, partition) {
447
+ const executionId = (0, import_helpers.generateId)();
448
+ const now = /* @__PURE__ */ new Date();
449
+ const lockedUntil = new Date(now.getTime() + lockTime);
450
+ const rawCollection = await this.jobs.getRawCollection();
451
+ const result = await rawCollection.updateOne(
452
+ getJobAcquisitionSelector(jobNames, now, partition),
453
+ [
454
+ {
455
+ $set: {
456
+ lockId: { $literal: executionId },
457
+ lockedUntil: { $literal: lockedUntil },
458
+ lastRunAt: { $literal: now },
459
+ tries: { $add: [{ $ifNull: ["$tries", 0] }, 1] },
460
+ claimWasStale: { $eq: [{ $type: "$lockedUntil" }, "date"] }
461
+ }
462
+ }
463
+ ],
464
+ {
465
+ hint: JOB_ACQUISITION_HINTS[hint],
466
+ sort: JOB_ACQUISITION_SORT
467
+ }
468
+ );
469
+ if (result.matchedCount === 0) return;
470
+ const job = await rawCollection.findOne({ lockId: executionId });
471
+ if (!job) return;
472
+ return this.toJobToRun(job, executionId, lockTime, job.tries || 0, Boolean(job.claimWasStale));
473
+ }
474
+ toJobToRun(job, executionId, lockTime, tries, wasStale) {
238
475
  if (wasStale) {
239
476
  import_logger.logger.info(`Running job "${job.jobName}" that was staled`);
240
477
  }
@@ -252,9 +489,9 @@ var JobsRepo = class {
252
489
  wasStale
253
490
  };
254
491
  }
255
- async getJobAcquisitionExecutionTime(jobNames, hint) {
492
+ async getJobAcquisitionExecutionTime(jobNames, hint, partition = 0) {
256
493
  var _a;
257
- const explain = await this.jobs.find(getJobAcquisitionSelector(jobNames, /* @__PURE__ */ new Date()), {
494
+ const explain = await this.jobs.find(getJobAcquisitionSelector(jobNames, /* @__PURE__ */ new Date(), partition), {
258
495
  hint: JOB_ACQUISITION_HINTS[hint],
259
496
  maxTimeMS: 1e3,
260
497
  readPreference: "primary"
@@ -278,7 +515,7 @@ var JobsRepo = class {
278
515
  priority: options.priority,
279
516
  ...options.resetTries ? { tries: 0 } : {}
280
517
  },
281
- $unset: { lockId: "", lockedUntil: "" }
518
+ $unset: { lockId: "", lockedUntil: "", claimWasStale: "" }
282
519
  };
283
520
  const result = await this.jobs.updateOne(
284
521
  options.lockId ? { _id: options.jobId, lockId: options.lockId } : options.jobId,
@@ -312,6 +549,7 @@ var JobsRepo = class {
312
549
  $unset: {
313
550
  lockId: "",
314
551
  lockedUntil: "",
552
+ claimWasStale: "",
315
553
  ...retentionMs === null ? { expiresAt: "" } : {}
316
554
  }
317
555
  }
@@ -368,12 +606,12 @@ var JobsRepo = class {
368
606
  lockedUntil: { $exists: true }
369
607
  },
370
608
  {
371
- $unset: { lockId: "", lockedUntil: "" }
609
+ $unset: { lockId: "", lockedUntil: "", claimWasStale: "" }
372
610
  }
373
611
  );
374
612
  return result.modifiedCount;
375
613
  }
376
- async ensureJobRecord(job) {
614
+ async ensureJobRecord(job, partition = 0) {
377
615
  const result = await this.jobs.upsert(
378
616
  {
379
617
  jobName: job.name
@@ -387,7 +625,8 @@ var JobsRepo = class {
387
625
  status: ""
388
626
  },
389
627
  $setOnInsert: {
390
- nextRunAt: /* @__PURE__ */ new Date()
628
+ nextRunAt: /* @__PURE__ */ new Date(),
629
+ partition
391
630
  }
392
631
  }
393
632
  );
@@ -405,7 +644,8 @@ var JobsRepo = class {
405
644
  params: options.params,
406
645
  nextRunAt: options.nextRunAt,
407
646
  priority: options.priority,
408
- type: "event"
647
+ type: "event",
648
+ ...options.partition === void 0 ? {} : { partition: options.partition }
409
649
  });
410
650
  } catch (error) {
411
651
  if (error.isValidationError && Object.values(error.validationErrors).includes("notUnique") && options.uniqueIdentifier) {
@@ -433,7 +673,8 @@ var JobsRepo = class {
433
673
  params: job.params,
434
674
  nextRunAt: job.nextRunAt,
435
675
  priority: job.priority,
436
- type: "event"
676
+ type: "event",
677
+ ...job.partition === void 0 ? {} : { partition: job.partition }
437
678
  });
438
679
  scheduledCount++;
439
680
  } catch (error) {
@@ -458,11 +699,55 @@ var JobsRepo = class {
458
699
  errors
459
700
  };
460
701
  }
702
+ async reconcilePartitions(options) {
703
+ if (options.jobNames.length === 0) return 0;
704
+ const rawCollection = await this.jobs.getRawCollection();
705
+ await this.jobs.createIndexesPromise;
706
+ const now = /* @__PURE__ */ new Date();
707
+ const invalidPartition = {
708
+ $or: [
709
+ { partition: { $exists: false } },
710
+ { partition: { $lt: 0 } },
711
+ { partition: { $gte: options.nPartitions } }
712
+ ]
713
+ };
714
+ const availableLock = {
715
+ $or: [{ lockedUntil: { $exists: false } }, { lockedUntil: { $lte: now } }]
716
+ };
717
+ const runnableJob = {
718
+ $or: [{ type: { $ne: "event" } }, { status: { $ne: "maxTriesReached" } }]
719
+ };
720
+ const jobs = await rawCollection.find({
721
+ jobName: { $in: options.jobNames },
722
+ $and: [invalidPartition, availableLock, runnableJob]
723
+ }).project({ _id: 1 }).limit(options.batchSize).toArray();
724
+ if (jobs.length === 0) return 0;
725
+ const result = await rawCollection.bulkWrite(
726
+ jobs.map((job) => ({
727
+ updateOne: {
728
+ filter: {
729
+ _id: job._id,
730
+ $and: [invalidPartition, availableLock, runnableJob]
731
+ },
732
+ update: { $set: { partition: getRandomPartition(options.nPartitions) } }
733
+ }
734
+ })),
735
+ { ordered: false }
736
+ );
737
+ return result.modifiedCount;
738
+ }
461
739
  };
462
- _init = __decoratorStart(null);
463
- __decorateElement(_init, 5, "jobs", _jobs_dec, JobsRepo);
464
- JobsRepo = __decorateElement(_init, 0, "JobsRepo", _JobsRepo_decorators, JobsRepo);
465
- __runInitializers(_init, 1, JobsRepo);
740
+ _init2 = __decoratorStart(null);
741
+ __decorateElement(_init2, 5, "jobs", _jobs_dec, JobsRepo);
742
+ JobsRepo = __decorateElement(_init2, 0, "JobsRepo", _JobsRepo_decorators, JobsRepo);
743
+ __runInitializers(_init2, 1, JobsRepo);
744
+
745
+ // src/schedule.ts
746
+ var import_services2 = require("@orion-js/services");
747
+
748
+ // src/services/EventsService.ts
749
+ var import_logger2 = require("@orion-js/logger");
750
+ var import_services = require("@orion-js/services");
466
751
 
467
752
  // src/services/getNextRunDate.ts
468
753
  var import_cron_parser = require("cron-parser");
@@ -492,176 +777,65 @@ var getNextRunDate = (options) => {
492
777
  };
493
778
 
494
779
  // src/services/EventsService.ts
495
- var _jobsRepo_dec, _EventsService_decorators, _init2;
780
+ var _jobsRepo_dec, _EventsService_decorators, _init3;
496
781
  _EventsService_decorators = [(0, import_services.Service)()], _jobsRepo_dec = [(0, import_services.Inject)(() => JobsRepo)];
497
782
  var EventsService = class {
498
783
  constructor() {
499
- __publicField(this, "jobsRepo", __runInitializers(_init2, 8, this)), __runInitializers(_init2, 11, this);
784
+ __publicField(this, "jobsRepo", __runInitializers(_init3, 8, this)), __runInitializers(_init3, 11, this);
500
785
  }
501
- async scheduleJob(options) {
786
+ async scheduleJob(options, nPartitions) {
502
787
  import_logger2.logger.debug("Scheduling job...", options);
788
+ const configuredPartitions = nPartitions ?? this.jobsRepo.getJobPartitionsCount(options.name);
503
789
  await this.jobsRepo.scheduleJob({
504
790
  name: options.name,
505
791
  priority: options.priority || 100,
506
792
  nextRunAt: getNextRunDate(options),
507
793
  params: options.params || null,
508
- uniqueIdentifier: options.uniqueIdentifier
794
+ uniqueIdentifier: options.uniqueIdentifier,
795
+ partition: configuredPartitions === void 0 ? void 0 : getRandomPartition(configuredPartitions)
509
796
  });
510
797
  }
511
- async scheduleJobs(jobs) {
798
+ async scheduleJobs(jobs, nPartitions) {
512
799
  import_logger2.logger.debug(`Scheduling ${jobs.length} jobs...`);
513
- const jobRecords = jobs.map((options) => ({
514
- name: options.name,
515
- priority: options.priority || 100,
516
- nextRunAt: getNextRunDate(options),
517
- params: options.params || null,
518
- uniqueIdentifier: options.uniqueIdentifier
519
- }));
800
+ const jobRecords = jobs.map((options) => {
801
+ const configuredPartitions = nPartitions ?? this.jobsRepo.getJobPartitionsCount(options.name);
802
+ return {
803
+ name: options.name,
804
+ priority: options.priority || 100,
805
+ nextRunAt: getNextRunDate(options),
806
+ params: options.params || null,
807
+ uniqueIdentifier: options.uniqueIdentifier,
808
+ partition: configuredPartitions === void 0 ? void 0 : getRandomPartition(configuredPartitions)
809
+ };
810
+ });
520
811
  return await this.jobsRepo.scheduleJobs(jobRecords);
521
812
  }
522
813
  };
523
- _init2 = __decoratorStart(null);
524
- __decorateElement(_init2, 5, "jobsRepo", _jobsRepo_dec, EventsService);
525
- EventsService = __decorateElement(_init2, 0, "EventsService", _EventsService_decorators, EventsService);
526
- __runInitializers(_init2, 1, EventsService);
814
+ _init3 = __decoratorStart(null);
815
+ __decorateElement(_init3, 5, "jobsRepo", _jobsRepo_dec, EventsService);
816
+ EventsService = __decorateElement(_init3, 0, "EventsService", _EventsService_decorators, EventsService);
817
+ __runInitializers(_init3, 1, EventsService);
818
+
819
+ // src/schedule.ts
820
+ var eventsService = (0, import_services2.getInstance)(EventsService);
821
+ function scheduleJobInternal(options, nPartitions) {
822
+ return eventsService.scheduleJob(options, nPartitions);
823
+ }
824
+ function scheduleJobsInternal(jobs, nPartitions) {
825
+ return eventsService.scheduleJobs(jobs, nPartitions);
826
+ }
527
827
 
528
828
  // src/services/WorkerService.ts
529
829
  var import_helpers2 = require("@orion-js/helpers");
530
830
  var import_logger4 = require("@orion-js/logger");
531
- var import_services3 = require("@orion-js/services");
831
+ var import_services4 = require("@orion-js/services");
532
832
 
533
833
  // src/services/Executor.ts
534
834
  var import_api = require("@opentelemetry/api");
535
835
  var import_logger3 = require("@orion-js/logger");
536
- var import_services2 = require("@orion-js/services");
537
-
538
- // src/repos/JobsHistoryRepo.ts
539
- var import_mongodb2 = require("@orion-js/mongodb");
540
-
541
- // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/isInteger.js
542
- function _isInteger(n) {
543
- return n << 0 === n;
544
- }
545
- var isInteger = Number.isInteger || _isInteger;
546
-
547
- // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/createPath.js
548
- function createPath(path, delimiter = ".") {
549
- return typeof path === "string" ? path.split(delimiter).map((x) => isInteger(x) ? Number(x) : x) : path;
550
- }
551
-
552
- // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/compare.js
553
- function compare(a, b) {
554
- return String(a) === String(b);
555
- }
556
-
557
- // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/_internals/includes.js
558
- function includes(a, list) {
559
- let index = -1;
560
- const { length } = list;
561
- while (++index < length)
562
- if (compare(list[index], a))
563
- return true;
564
- return false;
565
- }
566
-
567
- // ../../node_modules/.bun/rambdax@11.3.1/node_modules/rambdax/src/omit.js
568
- function omit(propsToOmit, obj) {
569
- if (arguments.length === 1) return (_obj) => omit(propsToOmit, _obj);
570
- if (obj === null || obj === void 0)
571
- return void 0;
572
- const propsToOmitValue = createPath(propsToOmit, ",");
573
- const willReturn = {};
574
- for (const key in obj)
575
- if (!includes(key, propsToOmitValue))
576
- willReturn[key] = obj[key];
577
- return willReturn;
578
- }
579
-
580
- // src/types/HistoryRecord.ts
581
- var import_schema2 = require("@orion-js/schema");
582
- var HistoryRecordSchema = (0, import_schema2.schemaWithName)("HistoryRecord", {
583
- _id: { type: "string" },
584
- jobId: { type: "string" },
585
- executionId: { type: "string" },
586
- jobName: { type: "string" },
587
- type: { type: "string" },
588
- priority: { type: "number" },
589
- tries: { type: "number" },
590
- uniqueIdentifier: { type: "string", optional: true },
591
- startedAt: { type: "date" },
592
- endedAt: { type: "date" },
593
- duration: { type: "number" },
594
- expiresAt: { type: "date", optional: true },
595
- status: { type: "string", enum: ["success", "error", "stale"] },
596
- errorMessage: { type: "string", optional: true },
597
- params: { type: "blackbox", optional: true },
598
- result: { type: "any", optional: true }
599
- });
600
-
601
- // src/repos/JobsHistoryRepo.ts
602
- var _history_dec, _JobsHistoryRepo_decorators, _init3;
603
- _JobsHistoryRepo_decorators = [(0, import_mongodb2.Repository)()], _history_dec = [(0, import_mongodb2.MongoCollection)({
604
- name: "orionjs.jobs_dogs_history",
605
- idGeneration: "uuid",
606
- schema: HistoryRecordSchema,
607
- indexes: [
608
- {
609
- keys: {
610
- jobName: 1,
611
- startedAt: 1
612
- }
613
- },
614
- {
615
- keys: {
616
- executionId: 1
617
- }
618
- },
619
- {
620
- keys: {
621
- expiresAt: 1
622
- },
623
- options: {
624
- expireAfterSeconds: 0
625
- }
626
- }
627
- ]
628
- })];
629
- var JobsHistoryRepo = class {
630
- constructor() {
631
- __publicField(this, "history", __runInitializers(_init3, 8, this)), __runInitializers(_init3, 11, this);
632
- }
633
- async saveExecution(record) {
634
- await this.history.upsert(
635
- { executionId: record.executionId },
636
- {
637
- $setOnInsert: {
638
- status: record.status
639
- },
640
- $set: {
641
- ...omit(["status"], record)
642
- }
643
- }
644
- );
645
- }
646
- async getExecutions(jobName, limit, skip) {
647
- const cursor = this.history.find({ jobName }).sort({ startedAt: -1 });
648
- if (skip) {
649
- cursor.skip(skip);
650
- }
651
- if (limit) {
652
- cursor.limit(limit);
653
- }
654
- return await cursor.toArray();
655
- }
656
- };
657
- _init3 = __decoratorStart(null);
658
- __decorateElement(_init3, 5, "history", _history_dec, JobsHistoryRepo);
659
- JobsHistoryRepo = __decorateElement(_init3, 0, "JobsHistoryRepo", _JobsHistoryRepo_decorators, JobsHistoryRepo);
660
- __runInitializers(_init3, 1, JobsHistoryRepo);
661
-
662
- // src/services/Executor.ts
836
+ var import_services3 = require("@orion-js/services");
663
837
  var _jobsHistoryRepo_dec, _jobsRepo_dec2, _Executor_decorators, _init4;
664
- _Executor_decorators = [(0, import_services2.Service)()], _jobsRepo_dec2 = [(0, import_services2.Inject)(() => JobsRepo)], _jobsHistoryRepo_dec = [(0, import_services2.Inject)(() => JobsHistoryRepo)];
838
+ _Executor_decorators = [(0, import_services3.Service)()], _jobsRepo_dec2 = [(0, import_services3.Inject)(() => JobsRepo)], _jobsHistoryRepo_dec = [(0, import_services3.Inject)(() => JobsHistoryRepo)];
665
839
  var Executor = class {
666
840
  constructor() {
667
841
  __publicField(this, "jobsRepo", __runInitializers(_init4, 8, this)), __runInitializers(_init4, 11, this);
@@ -999,11 +1173,16 @@ Executor = __decorateElement(_init4, 0, "Executor", _Executor_decorators, Execut
999
1173
  __runInitializers(_init4, 1, Executor);
1000
1174
 
1001
1175
  // src/services/WorkerService.ts
1176
+ var FILTERED_JOB_NAMES_ACQUISITION_HINT = "byJobName";
1002
1177
  var _executor_dec, _jobsRepo_dec3, _WorkerService_decorators, _init5;
1003
- _WorkerService_decorators = [(0, import_services3.Service)()], _jobsRepo_dec3 = [(0, import_services3.Inject)(() => JobsRepo)], _executor_dec = [(0, import_services3.Inject)(() => Executor)];
1178
+ _WorkerService_decorators = [(0, import_services4.Service)()], _jobsRepo_dec3 = [(0, import_services4.Inject)(() => JobsRepo)], _executor_dec = [(0, import_services4.Inject)(() => Executor)];
1004
1179
  var WorkerService = class {
1005
1180
  constructor() {
1006
1181
  __publicField(this, "jobAcquisitionHintProbeIntervalMs", 30 * 60 * 1e3);
1182
+ __publicField(this, "partitionReconcilerIntervalMs", 5 * 60 * 1e3);
1183
+ __publicField(this, "partitionReconcilerJitterMs", 30 * 1e3);
1184
+ __publicField(this, "partitionReconcilerBatchPauseMs", 250);
1185
+ __publicField(this, "partitionReconcilerBatchSize", 500);
1007
1186
  __publicField(this, "jobsRepo", __runInitializers(_init5, 8, this)), __runInitializers(_init5, 11, this);
1008
1187
  __publicField(this, "executor", __runInitializers(_init5, 12, this)), __runInitializers(_init5, 15, this);
1009
1188
  }
@@ -1088,6 +1267,15 @@ var WorkerService = class {
1088
1267
  if (capacityPromises.length === 0) return;
1089
1268
  await Promise.race([...capacityPromises, workersInstance.stopSignal]);
1090
1269
  }
1270
+ getNextPartition(config, workersInstance) {
1271
+ const partition = workersInstance.partitionOrder[workersInstance.partitionCursor];
1272
+ workersInstance.partitionCursor++;
1273
+ if (workersInstance.partitionCursor >= config.nPartitions) {
1274
+ workersInstance.partitionOrder = createShuffledPartitions(config.nPartitions);
1275
+ workersInstance.partitionCursor = 0;
1276
+ }
1277
+ return partition;
1278
+ }
1091
1279
  async runSchedulerLoop(config, workersInstance) {
1092
1280
  const jobNames = this.getJobNames(config.jobs);
1093
1281
  const executeConfig = {
@@ -1097,6 +1285,7 @@ var WorkerService = class {
1097
1285
  onMaxTriesReached: config.onMaxTriesReached
1098
1286
  };
1099
1287
  import_logger4.logger.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
1288
+ let consecutivePartitionMisses = 0;
1100
1289
  while (workersInstance.running) {
1101
1290
  let didMiss = false;
1102
1291
  let allJobNamesAtCapacity = false;
@@ -1107,21 +1296,31 @@ var WorkerService = class {
1107
1296
  allJobNamesAtCapacity = workersInstance.activeExecutions.size > 0;
1108
1297
  break;
1109
1298
  }
1299
+ const jobAcquisitionHint = availableJobNames.length < jobNames.length ? FILTERED_JOB_NAMES_ACQUISITION_HINT : workersInstance.jobAcquisitionHint;
1300
+ const partition = this.getNextPartition(config, workersInstance);
1110
1301
  const jobToRun = await this.jobsRepo.getJobAndLock(
1111
1302
  availableJobNames,
1112
1303
  config.defaultLockTime,
1113
- workersInstance.jobAcquisitionHint
1304
+ jobAcquisitionHint,
1305
+ partition
1114
1306
  );
1115
1307
  if (!jobToRun) {
1116
- import_logger4.logger.debug("No job to run");
1117
- didMiss = true;
1118
- break;
1308
+ consecutivePartitionMisses++;
1309
+ import_logger4.logger.debug(`No job to run in partition ${partition}`);
1310
+ if (consecutivePartitionMisses >= config.nPartitions) {
1311
+ consecutivePartitionMisses = 0;
1312
+ didMiss = true;
1313
+ break;
1314
+ }
1315
+ continue;
1119
1316
  }
1317
+ consecutivePartitionMisses = 0;
1120
1318
  this.startExecution(executeConfig, workersInstance, jobToRun);
1121
1319
  if (!workersInstance.running) break;
1122
1320
  }
1123
1321
  } catch (error) {
1124
1322
  import_logger4.logger.error("Error in job scheduler.", { error });
1323
+ consecutivePartitionMisses = 0;
1125
1324
  didMiss = true;
1126
1325
  }
1127
1326
  if (!workersInstance.running) return;
@@ -1157,11 +1356,13 @@ var WorkerService = class {
1157
1356
  workersInstance.jobAcquisitionHintProbeCycle++;
1158
1357
  try {
1159
1358
  for (let sample = 0; sample < 3; sample++) {
1359
+ const partition = getRandomPartition(config.nPartitions);
1160
1360
  for (const hint of [firstHint, secondHint]) {
1161
1361
  if (!workersInstance.running) return;
1162
1362
  const executionTimeMillis = await this.jobsRepo.getJobAcquisitionExecutionTime(
1163
1363
  jobNames,
1164
- hint
1364
+ hint,
1365
+ partition
1165
1366
  );
1166
1367
  if (!workersInstance.running) return;
1167
1368
  byHint[hint].push(executionTimeMillis);
@@ -1233,6 +1434,44 @@ var WorkerService = class {
1233
1434
  await this.waitForNextJobAcquisitionHintProbe(workersInstance);
1234
1435
  }
1235
1436
  }
1437
+ async reconcilePartitionBatch(config) {
1438
+ if (typeof this.jobsRepo.reconcilePartitions !== "function") return 0;
1439
+ try {
1440
+ return await this.jobsRepo.reconcilePartitions({
1441
+ jobNames: this.getJobNames(config.jobs),
1442
+ nPartitions: config.nPartitions,
1443
+ batchSize: this.partitionReconcilerBatchSize
1444
+ });
1445
+ } catch (error) {
1446
+ import_logger4.logger.warn("Job partition reconciliation failed; it will be retried.", { error });
1447
+ return 0;
1448
+ }
1449
+ }
1450
+ async waitForNextPartitionReconcile(workersInstance, delayMs) {
1451
+ await new Promise((resolve) => {
1452
+ let resolved = false;
1453
+ const finishWait = () => {
1454
+ if (resolved) return;
1455
+ resolved = true;
1456
+ clearTimeout(workersInstance.partitionReconcilerTimer);
1457
+ workersInstance.partitionReconcilerTimer = void 0;
1458
+ workersInstance.cancelPartitionReconcilerWait = void 0;
1459
+ resolve();
1460
+ };
1461
+ workersInstance.partitionReconcilerTimer = setTimeout(finishWait, delayMs);
1462
+ workersInstance.cancelPartitionReconcilerWait = finishWait;
1463
+ });
1464
+ }
1465
+ async runPartitionReconcilerLoop(config, workersInstance, initialReconcilePromise) {
1466
+ let modifiedCount = await initialReconcilePromise;
1467
+ while (workersInstance.running) {
1468
+ const didFillBatch = modifiedCount >= this.partitionReconcilerBatchSize;
1469
+ const delayMs = didFillBatch ? this.partitionReconcilerBatchPauseMs : this.partitionReconcilerIntervalMs + Math.floor(Math.random() * (this.partitionReconcilerJitterMs + 1));
1470
+ await this.waitForNextPartitionReconcile(workersInstance, delayMs);
1471
+ if (!workersInstance.running) return;
1472
+ modifiedCount = await this.reconcilePartitionBatch(config);
1473
+ }
1474
+ }
1236
1475
  createWorkersInstanceDefinition(config) {
1237
1476
  let signalStop;
1238
1477
  const stopSignal = new Promise((resolve) => {
@@ -1248,7 +1487,10 @@ var WorkerService = class {
1248
1487
  jobAcquisitionHintProbeCycle: 0,
1249
1488
  hasCompletedInitialJobAcquisitionHintProbe: false,
1250
1489
  pendingJobAcquisitionHintWins: 0,
1490
+ partitionOrder: createShuffledPartitions(config.nPartitions),
1491
+ partitionCursor: 0,
1251
1492
  jobAcquisitionHintProbePromise: Promise.resolve(),
1493
+ partitionReconcilerPromise: Promise.resolve(),
1252
1494
  schedulerPromise: Promise.resolve(),
1253
1495
  stopSignal,
1254
1496
  signalStop,
@@ -1258,14 +1500,16 @@ var WorkerService = class {
1258
1500
  stop: async () => {
1259
1501
  if (workersInstance.stopPromise) return workersInstance.stopPromise;
1260
1502
  workersInstance.stopPromise = (async () => {
1261
- var _a;
1503
+ var _a, _b;
1262
1504
  import_logger4.logger.info("Stopping job scheduler...");
1263
1505
  workersInstance.running = false;
1264
1506
  workersInstance.signalStop();
1265
1507
  (_a = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a.call(workersInstance);
1508
+ (_b = workersInstance.cancelPartitionReconcilerWait) == null ? void 0 : _b.call(workersInstance);
1266
1509
  await Promise.all([
1267
1510
  workersInstance.schedulerPromise,
1268
- workersInstance.jobAcquisitionHintProbePromise
1511
+ workersInstance.jobAcquisitionHintProbePromise,
1512
+ workersInstance.partitionReconcilerPromise
1269
1513
  ]);
1270
1514
  const activeCapacity = Array.from(
1271
1515
  workersInstance.activeExecutions.values(),
@@ -1283,7 +1527,7 @@ var WorkerService = class {
1283
1527
  await Promise.all(
1284
1528
  jobs.filter((job) => job.type === "recurrent").map(async (job) => {
1285
1529
  import_logger4.logger.debug(`Ensuring records for job "${job.name}"...`);
1286
- await this.jobsRepo.ensureJobRecord(job);
1530
+ await this.jobsRepo.ensureJobRecord(job, getRandomPartition(config.nPartitions));
1287
1531
  })
1288
1532
  );
1289
1533
  }
@@ -1300,29 +1544,41 @@ var WorkerService = class {
1300
1544
  * @param userConfig - Configuration for the scheduler. Required field: jobs
1301
1545
  */
1302
1546
  startWorkers(userConfig) {
1547
+ var _a, _b;
1303
1548
  const maxTriesReachedRetentionMs = userConfig.maxTriesReachedRetentionMs === void 0 ? DEFAULT_MAX_TRIES_REACHED_RETENTION_MS : userConfig.maxTriesReachedRetentionMs;
1304
1549
  if (maxTriesReachedRetentionMs !== null && (!Number.isFinite(maxTriesReachedRetentionMs) || maxTriesReachedRetentionMs < 0)) {
1305
1550
  throw new Error("maxTriesReachedRetentionMs must be null, zero, or a positive number");
1306
1551
  }
1552
+ const nPartitions = userConfig.nPartitions ?? 1;
1553
+ validatePartitionsCount(nPartitions);
1307
1554
  const config = {
1308
1555
  pollInterval: 3e3,
1309
1556
  workersCount: 4,
1310
1557
  defaultLockTime: 30 * 1e3,
1311
1558
  ...userConfig,
1559
+ nPartitions,
1312
1560
  maxTriesReachedRetentionMs
1313
1561
  };
1314
- setNameToJobs(config.jobs);
1562
+ setNameToJobs(config.jobs, config.nPartitions);
1563
+ (_b = (_a = this.jobsRepo).configureJobPartitions) == null ? void 0 : _b.call(_a, this.getJobNames(config.jobs), config.nPartitions);
1315
1564
  const workersInstance = this.createWorkersInstanceDefinition(config);
1316
1565
  import_logger4.logger.debug("Starting job scheduler", config);
1317
1566
  workersInstance.jobAcquisitionHintProbePromise = this.runJobAcquisitionHintProbeLoop(
1318
1567
  config,
1319
1568
  workersInstance
1320
1569
  );
1570
+ const initialReconcilePromise = this.reconcilePartitionBatch(config);
1571
+ workersInstance.partitionReconcilerPromise = this.runPartitionReconcilerLoop(
1572
+ config,
1573
+ workersInstance,
1574
+ initialReconcilePromise
1575
+ );
1321
1576
  workersInstance.schedulerPromise = this.runScheduler(config, workersInstance).catch((error) => {
1322
- var _a;
1577
+ var _a2, _b2;
1323
1578
  workersInstance.running = false;
1324
1579
  workersInstance.signalStop();
1325
- (_a = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a.call(workersInstance);
1580
+ (_a2 = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a2.call(workersInstance);
1581
+ (_b2 = workersInstance.cancelPartitionReconcilerWait) == null ? void 0 : _b2.call(workersInstance);
1326
1582
  import_logger4.logger.error("Job scheduler stopped after an unexpected error.", { error });
1327
1583
  });
1328
1584
  return workersInstance;
@@ -1333,15 +1589,13 @@ __decorateElement(_init5, 5, "jobsRepo", _jobsRepo_dec3, WorkerService);
1333
1589
  __decorateElement(_init5, 5, "executor", _executor_dec, WorkerService);
1334
1590
  WorkerService = __decorateElement(_init5, 0, "WorkerService", _WorkerService_decorators, WorkerService);
1335
1591
  __runInitializers(_init5, 1, WorkerService);
1336
- function setNameToJobs(jobs) {
1592
+ function setNameToJobs(jobs, nPartitions) {
1337
1593
  for (const name of Object.keys(jobs)) {
1338
1594
  jobs[name].jobName = name;
1595
+ jobs[name].nPartitions = nPartitions;
1339
1596
  }
1340
1597
  }
1341
1598
 
1342
- // src/service/index.ts
1343
- var import_services4 = require("@orion-js/services");
1344
-
1345
1599
  // src/defineJob/index.ts
1346
1600
  var import_schema3 = require("@orion-js/schema");
1347
1601
  var import_cron_parser2 = require("cron-parser");
@@ -1358,11 +1612,14 @@ function createEventJob(options) {
1358
1612
  throw new Error("This job has not been registered in the workers");
1359
1613
  }
1360
1614
  const params = jobDefinition.params ? await (0, import_schema3.cleanAndValidate)(jobDefinition.params, scheduleOptions.params) : scheduleOptions.params;
1361
- return await scheduleJob({
1362
- ...scheduleOptions,
1363
- name: jobDefinition.jobName,
1364
- params
1365
- });
1615
+ return await scheduleJobInternal(
1616
+ {
1617
+ ...scheduleOptions,
1618
+ name: jobDefinition.jobName,
1619
+ params
1620
+ },
1621
+ jobDefinition.nPartitions
1622
+ );
1366
1623
  };
1367
1624
  jobDefinition.scheduleJobs = async (jobs) => {
1368
1625
  if (!jobDefinition.jobName) {
@@ -1378,7 +1635,7 @@ function createEventJob(options) {
1378
1635
  };
1379
1636
  })
1380
1637
  );
1381
- return await scheduleJobs(processedJobs);
1638
+ return await scheduleJobsInternal(processedJobs, jobDefinition.nPartitions);
1382
1639
  };
1383
1640
  return jobDefinition;
1384
1641
  }
@@ -1407,13 +1664,14 @@ var defineJob = (options) => {
1407
1664
  };
1408
1665
 
1409
1666
  // src/service/index.ts
1667
+ var import_services5 = require("@orion-js/services");
1410
1668
  var serviceMetadata = /* @__PURE__ */ new WeakMap();
1411
1669
  var jobsMetadata = /* @__PURE__ */ new Map();
1412
1670
  var jobEntriesByClass = /* @__PURE__ */ new Map();
1413
1671
  var pendingJobEntries = {};
1414
1672
  function Jobs() {
1415
1673
  return (target, context) => {
1416
- (0, import_services4.Service)()(target, context);
1674
+ (0, import_services5.Service)()(target, context);
1417
1675
  serviceMetadata.set(target, { _serviceType: "jobs" });
1418
1676
  if (Object.keys(pendingJobEntries).length > 0) {
1419
1677
  jobEntriesByClass.set(target, pendingJobEntries);
@@ -1462,7 +1720,7 @@ function initializeJobsIfNeeded(instance) {
1462
1720
  jobsMetadata.set(instance, jobs);
1463
1721
  }
1464
1722
  function getServiceJobs(target) {
1465
- const instance = (0, import_services4.getInstance)(target);
1723
+ const instance = (0, import_services5.getInstance)(target);
1466
1724
  if (!serviceMetadata.has(instance.constructor)) {
1467
1725
  throw new Error("You must pass a class decorated with @Jobs to getServiceJobs");
1468
1726
  }
@@ -1476,18 +1734,17 @@ function getServiceJobs(target) {
1476
1734
  }
1477
1735
 
1478
1736
  // src/index.ts
1479
- var workerService = (0, import_services5.getInstance)(WorkerService);
1480
- var eventsService = (0, import_services5.getInstance)(EventsService);
1481
- var jobsHistoryRepo = (0, import_services5.getInstance)(JobsHistoryRepo);
1482
- var jobsRepo = (0, import_services5.getInstance)(JobsRepo);
1737
+ var workerService = (0, import_services6.getInstance)(WorkerService);
1738
+ var jobsHistoryRepo = (0, import_services6.getInstance)(JobsHistoryRepo);
1739
+ var jobsRepo = (0, import_services6.getInstance)(JobsRepo);
1483
1740
  var startWorkers = (config) => {
1484
1741
  return workerService.startWorkers(config);
1485
1742
  };
1486
1743
  var scheduleJob = (options) => {
1487
- return eventsService.scheduleJob(options);
1744
+ return scheduleJobInternal(options);
1488
1745
  };
1489
1746
  var scheduleJobs = (jobs) => {
1490
- return eventsService.scheduleJobs(jobs);
1747
+ return scheduleJobsInternal(jobs);
1491
1748
  };
1492
1749
  // Annotate the CommonJS export names for ESM import in node:
1493
1750
  0 && (module.exports = {