@workglow/job-queue 0.0.109 → 0.0.110

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/bun.js CHANGED
@@ -513,6 +513,8 @@ class JobQueueWorker {
513
513
  pollIntervalMs;
514
514
  events = new EventEmitter2;
515
515
  running = false;
516
+ wakeResolve = null;
517
+ wakeTimer = null;
516
518
  activeJobAbortControllers = new Map;
517
519
  processingTimes = new Map;
518
520
  constructor(jobClass, options) {
@@ -532,11 +534,22 @@ class JobQueueWorker {
532
534
  this.processJobs();
533
535
  return this;
534
536
  }
537
+ notify() {
538
+ if (this.wakeResolve) {
539
+ if (this.wakeTimer) {
540
+ clearTimeout(this.wakeTimer);
541
+ this.wakeTimer = null;
542
+ }
543
+ this.wakeResolve();
544
+ this.wakeResolve = null;
545
+ }
546
+ }
535
547
  async stop() {
536
548
  if (!this.running) {
537
549
  return this;
538
550
  }
539
551
  this.running = false;
552
+ this.notify();
540
553
  const size = await this.storage.size(JobStatus3.PROCESSING);
541
554
  const sleepTime = Math.max(100, size * 2);
542
555
  await sleep(sleepTime);
@@ -586,26 +599,52 @@ class JobQueueWorker {
586
599
  return this.storageToClass(job);
587
600
  }
588
601
  async processJobs() {
589
- if (!this.running) {
590
- return;
591
- }
592
- try {
593
- await this.checkForAbortingJobs();
594
- const canProceed = await this.limiter.canProceed();
595
- if (canProceed) {
596
- const job = await this.next();
597
- if (job) {
598
- this.processSingleJob(job);
602
+ while (this.running) {
603
+ try {
604
+ await this.checkForAbortingJobs();
605
+ const canProceed = await this.limiter.canProceed();
606
+ if (canProceed) {
607
+ const job = await this.next();
608
+ if (job) {
609
+ this.processSingleJob(job);
610
+ continue;
611
+ }
612
+ }
613
+ if (canProceed) {
614
+ const delay = await this.getIdleDelay();
615
+ await this.waitForWakeOrTimeout(delay);
599
616
  } else {
600
- await sleep(this.pollIntervalMs);
617
+ await this.waitForWakeOrTimeout(this.pollIntervalMs);
601
618
  }
602
- }
603
- } finally {
604
- if (this.running) {
605
- setTimeout(() => this.processJobs(), this.pollIntervalMs);
619
+ } catch {
620
+ await sleep(this.pollIntervalMs);
606
621
  }
607
622
  }
608
623
  }
624
+ async getIdleDelay() {
625
+ try {
626
+ const pending = await this.storage.peek(JobStatus3.PENDING, 1);
627
+ if (pending.length > 0 && pending[0].run_after) {
628
+ const delay = new Date(pending[0].run_after).getTime() - Date.now();
629
+ if (delay > 0) {
630
+ return Math.min(delay, this.pollIntervalMs);
631
+ }
632
+ }
633
+ } catch {}
634
+ return this.pollIntervalMs;
635
+ }
636
+ waitForWakeOrTimeout(timeoutMs) {
637
+ return new Promise((resolve) => {
638
+ this.wakeTimer = setTimeout(() => {
639
+ this.wakeTimer = null;
640
+ this.wakeResolve = null;
641
+ resolve();
642
+ }, timeoutMs);
643
+ this.wakeResolve = () => {
644
+ resolve();
645
+ };
646
+ });
647
+ }
609
648
  async checkForAbortingJobs() {
610
649
  const abortingJobs = await this.storage.peek(JobStatus3.ABORTING);
611
650
  for (const jobData of abortingJobs) {
@@ -809,6 +848,7 @@ class JobQueueServer {
809
848
  clients = new Set;
810
849
  running = false;
811
850
  cleanupTimer = null;
851
+ storageUnsubscribe = null;
812
852
  stats = {
813
853
  totalJobs: 0,
814
854
  completedJobs: 0,
@@ -838,6 +878,13 @@ class JobQueueServer {
838
878
  this.running = true;
839
879
  this.events.emit("server_start", this.queueName);
840
880
  await this.fixupJobs();
881
+ try {
882
+ this.storageUnsubscribe = this.storage.subscribeToChanges((change) => {
883
+ if (change.type === "INSERT" || change.type === "UPDATE" && change.new?.status === JobStatus4.PENDING) {
884
+ this.notifyWorkers();
885
+ }
886
+ });
887
+ } catch {}
841
888
  await Promise.all(this.workers.map((worker) => worker.start()));
842
889
  this.startCleanupLoop();
843
890
  return this;
@@ -847,6 +894,10 @@ class JobQueueServer {
847
894
  return this;
848
895
  }
849
896
  this.running = false;
897
+ if (this.storageUnsubscribe) {
898
+ this.storageUnsubscribe();
899
+ this.storageUnsubscribe = null;
900
+ }
850
901
  if (this.cleanupTimer) {
851
902
  clearTimeout(this.cleanupTimer);
852
903
  this.cleanupTimer = null;
@@ -891,6 +942,11 @@ class JobQueueServer {
891
942
  removeClient(client) {
892
943
  this.clients.delete(client);
893
944
  }
945
+ notifyWorkers() {
946
+ for (const worker of this.workers) {
947
+ worker.notify();
948
+ }
949
+ }
894
950
  on(event, listener) {
895
951
  this.events.on(event, listener);
896
952
  }
@@ -923,6 +979,7 @@ class JobQueueServer {
923
979
  if (this.deleteAfterCompletionMs === 0) {
924
980
  await this.storage.delete(jobId);
925
981
  }
982
+ this.notifyWorkers();
926
983
  });
927
984
  worker.on("job_error", async (jobId, error, errorCode) => {
928
985
  this.stats = { ...this.stats, failedJobs: this.stats.failedJobs + 1 };
@@ -931,6 +988,7 @@ class JobQueueServer {
931
988
  if (this.deleteAfterFailureMs === 0) {
932
989
  await this.storage.delete(jobId);
933
990
  }
991
+ this.notifyWorkers();
934
992
  });
935
993
  worker.on("job_disabled", async (jobId) => {
936
994
  this.stats = { ...this.stats, disabledJobs: this.stats.disabledJobs + 1 };
@@ -939,6 +997,7 @@ class JobQueueServer {
939
997
  if (this.deleteAfterDisabledMs === 0) {
940
998
  await this.storage.delete(jobId);
941
999
  }
1000
+ this.notifyWorkers();
942
1001
  });
943
1002
  worker.on("job_retry", (jobId, runAfter) => {
944
1003
  this.stats = { ...this.stats, retriedJobs: this.stats.retriedJobs + 1 };
@@ -1092,26 +1151,21 @@ var CONCURRENT_JOB_LIMITER = createServiceToken2("jobqueue.limiter.concurrent");
1092
1151
  class ConcurrencyLimiter {
1093
1152
  currentRunningJobs = 0;
1094
1153
  maxConcurrentJobs;
1095
- timeSliceInMilliseconds;
1096
1154
  nextAllowedStartTime = new Date;
1097
- constructor(maxConcurrentJobs, timeSliceInMilliseconds = 1000) {
1155
+ constructor(maxConcurrentJobs) {
1098
1156
  this.maxConcurrentJobs = maxConcurrentJobs;
1099
- this.timeSliceInMilliseconds = timeSliceInMilliseconds;
1100
1157
  }
1101
1158
  async canProceed() {
1102
1159
  return this.currentRunningJobs < this.maxConcurrentJobs && Date.now() >= this.nextAllowedStartTime.getTime();
1103
1160
  }
1104
1161
  async recordJobStart() {
1105
- if (this.currentRunningJobs < this.maxConcurrentJobs) {
1106
- this.currentRunningJobs++;
1107
- this.nextAllowedStartTime = new Date(Date.now() + this.timeSliceInMilliseconds);
1108
- }
1162
+ this.currentRunningJobs++;
1109
1163
  }
1110
1164
  async recordJobCompletion() {
1111
1165
  this.currentRunningJobs = Math.max(0, this.currentRunningJobs - 1);
1112
1166
  }
1113
1167
  async getNextAvailableTime() {
1114
- return this.currentRunningJobs < this.maxConcurrentJobs ? new Date : new Date(Date.now() + this.timeSliceInMilliseconds);
1168
+ return this.currentRunningJobs > this.maxConcurrentJobs ? new Date : new Date(Date.now() - 1);
1115
1169
  }
1116
1170
  async setNextAvailableTime(date) {
1117
1171
  if (date > this.nextAllowedStartTime) {
@@ -1349,4 +1403,4 @@ export {
1349
1403
  AbortSignalJobError
1350
1404
  };
1351
1405
 
1352
- //# debugId=922F4A1C8172A8C264756E2164756E21
1406
+ //# debugId=FF64F110FAFE185564756E2164756E21