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