@nicnocquee/dataqueue 1.39.0 → 1.40.0-beta.20260612032253

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.d.cts CHANGED
@@ -528,14 +528,20 @@ type JobHandlers<PayloadMap> = {
528
528
  interface ProcessorOptions {
529
529
  workerId?: string;
530
530
  /**
531
- * The number of jobs to process at a time.
532
- * - If not provided, the processor will process 10 jobs at a time.
533
- * - In serverless functions, it's better to process less jobs at a time since serverless functions are charged by the second and have a timeout.
531
+ * The maximum number of jobs to claim from the queue per poll.
532
+ * - If not provided, up to 10 jobs are claimed per poll.
533
+ * - With `startInBackground`, claims are capped to the number of free
534
+ * concurrency slots, so this only matters when it is below `concurrency`.
535
+ * - In serverless functions, it's better to claim fewer jobs at a time since serverless functions are charged by the second and have a timeout.
534
536
  */
535
537
  batchSize?: number;
536
538
  /**
537
- * The maximum number of jobs to process in parallel per batch.
538
- * - If not provided, all jobs in the batch are processed in parallel.
539
+ * The maximum number of jobs to process in parallel.
540
+ * - With `startInBackground`, this is the steady number of jobs kept in
541
+ * flight: each slot is refilled as soon as it frees, so a slow job never
542
+ * blocks the other slots.
543
+ * - With the one-shot `start`, this caps parallelism within the single batch.
544
+ * - If not provided, defaults to 3.
539
545
  * - Set to 1 to process jobs sequentially.
540
546
  * - Set to a lower value to avoid resource exhaustion.
541
547
  */
@@ -564,7 +570,8 @@ interface ProcessorOptions {
564
570
  interface Processor {
565
571
  /**
566
572
  * Start the job processor in the background.
567
- * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs (as many as batchSize) as they become available.
573
+ * - Keeps up to `concurrency` jobs in flight, refilling each slot as soon as it frees instead of waiting for a whole batch to settle.
574
+ * - Polls every pollInterval milliseconds (5 seconds if not provided) for new work as it becomes available.
568
575
  * - **You have to call the stop method to stop the processor.**
569
576
  * - Handlers are provided per-processor when calling createProcessor.
570
577
  * - In serverless functions, it's recommended to call start instead and await it to finish.
package/dist/index.d.ts CHANGED
@@ -528,14 +528,20 @@ type JobHandlers<PayloadMap> = {
528
528
  interface ProcessorOptions {
529
529
  workerId?: string;
530
530
  /**
531
- * The number of jobs to process at a time.
532
- * - If not provided, the processor will process 10 jobs at a time.
533
- * - In serverless functions, it's better to process less jobs at a time since serverless functions are charged by the second and have a timeout.
531
+ * The maximum number of jobs to claim from the queue per poll.
532
+ * - If not provided, up to 10 jobs are claimed per poll.
533
+ * - With `startInBackground`, claims are capped to the number of free
534
+ * concurrency slots, so this only matters when it is below `concurrency`.
535
+ * - In serverless functions, it's better to claim fewer jobs at a time since serverless functions are charged by the second and have a timeout.
534
536
  */
535
537
  batchSize?: number;
536
538
  /**
537
- * The maximum number of jobs to process in parallel per batch.
538
- * - If not provided, all jobs in the batch are processed in parallel.
539
+ * The maximum number of jobs to process in parallel.
540
+ * - With `startInBackground`, this is the steady number of jobs kept in
541
+ * flight: each slot is refilled as soon as it frees, so a slow job never
542
+ * blocks the other slots.
543
+ * - With the one-shot `start`, this caps parallelism within the single batch.
544
+ * - If not provided, defaults to 3.
539
545
  * - Set to 1 to process jobs sequentially.
540
546
  * - Set to a lower value to avoid resource exhaustion.
541
547
  */
@@ -564,7 +570,8 @@ interface ProcessorOptions {
564
570
  interface Processor {
565
571
  /**
566
572
  * Start the job processor in the background.
567
- * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs (as many as batchSize) as they become available.
573
+ * - Keeps up to `concurrency` jobs in flight, refilling each slot as soon as it frees instead of waiting for a whole batch to settle.
574
+ * - Polls every pollInterval milliseconds (5 seconds if not provided) for new work as it becomes available.
568
575
  * - **You have to call the stop method to stop the processor.**
569
576
  * - Handlers are provided per-processor when calling createProcessor.
570
577
  * - In serverless functions, it's recommended to call start instead and await it to finish.
package/dist/index.js CHANGED
@@ -586,8 +586,11 @@ var createProcessor = (backend, handlers, options = {}, onBeforeBatch, emit) =>
586
586
  );
587
587
  }
588
588
  let running = false;
589
- let intervalId = null;
590
- let currentBatchPromise = null;
589
+ let pollTimer = null;
590
+ let claimInProgress = false;
591
+ let pumpRequested = false;
592
+ let inFlight = 0;
593
+ const inFlightJobs = /* @__PURE__ */ new Set();
591
594
  setLogContext(options.verbose ?? false);
592
595
  const processJobs = async () => {
593
596
  if (!running) return 0;
@@ -629,29 +632,80 @@ var createProcessor = (backend, handlers, options = {}, onBeforeBatch, emit) =>
629
632
  return {
630
633
  /**
631
634
  * Start the job processor in the background.
632
- * - This will run periodically (every pollInterval milliseconds or 5 seconds if not provided) and process jobs as they become available.
635
+ * - Keeps up to `concurrency` jobs in flight, refilling each slot as soon as
636
+ * it frees instead of waiting for a whole batch to settle.
637
+ * - Polls every `pollInterval` milliseconds (5 seconds if not provided) for
638
+ * new work and to enqueue due cron jobs.
633
639
  * - You have to call the stop method to stop the processor.
634
640
  */
635
641
  startInBackground: () => {
636
642
  if (running) return;
637
643
  log(`Starting job processor with workerId: ${workerId}`);
638
644
  running = true;
639
- const scheduleNext = (immediate) => {
645
+ const pump = async () => {
640
646
  if (!running) return;
641
- if (immediate) {
642
- intervalId = setTimeout(loop, 0);
643
- } else {
644
- intervalId = setTimeout(loop, pollInterval);
647
+ if (claimInProgress) {
648
+ pumpRequested = true;
649
+ return;
650
+ }
651
+ if (inFlight >= concurrency) return;
652
+ claimInProgress = true;
653
+ pumpRequested = false;
654
+ let claimLimit = 0;
655
+ let claimed = 0;
656
+ try {
657
+ claimLimit = Math.min(concurrency - inFlight, batchSize);
658
+ const jobs = await backend.getNextBatch(workerId, claimLimit, jobType, groupConcurrency);
659
+ claimed = jobs.length;
660
+ for (const job of jobs) {
661
+ emit?.("job:processing", { jobId: job.id, jobType: job.jobType });
662
+ inFlight++;
663
+ const jobPromise = processJobWithHandlers(
664
+ backend,
665
+ job,
666
+ handlers,
667
+ emit
668
+ ).catch((err) => {
669
+ onError(err instanceof Error ? err : new Error(String(err)));
670
+ }).finally(() => {
671
+ inFlight--;
672
+ inFlightJobs.delete(jobPromise);
673
+ void pump();
674
+ });
675
+ inFlightJobs.add(jobPromise);
676
+ }
677
+ } catch (error) {
678
+ const err = error instanceof Error ? error : new Error(String(error));
679
+ onError(err);
680
+ emit?.("error", err);
681
+ } finally {
682
+ claimInProgress = false;
683
+ }
684
+ const moreLikely = claimed === claimLimit || pumpRequested;
685
+ if (running && moreLikely && inFlight < concurrency) {
686
+ void pump();
645
687
  }
646
688
  };
647
- const loop = async () => {
689
+ const tick = async () => {
648
690
  if (!running) return;
649
- currentBatchPromise = processJobs();
650
- const processed = await currentBatchPromise;
651
- currentBatchPromise = null;
652
- scheduleNext(processed === batchSize);
691
+ if (onBeforeBatch) {
692
+ try {
693
+ await onBeforeBatch();
694
+ } catch (hookError) {
695
+ log(`onBeforeBatch hook error: ${hookError}`);
696
+ const err = hookError instanceof Error ? hookError : new Error(String(hookError));
697
+ onError(err);
698
+ emit?.("error", err);
699
+ }
700
+ }
701
+ await pump();
702
+ if (running) {
703
+ pollTimer = setTimeout(() => {
704
+ void tick();
705
+ }, pollInterval);
706
+ }
653
707
  };
654
- loop();
708
+ void tick();
655
709
  },
656
710
  /**
657
711
  * Stop the job processor that runs in the background.
@@ -660,9 +714,9 @@ var createProcessor = (backend, handlers, options = {}, onBeforeBatch, emit) =>
660
714
  stop: () => {
661
715
  log(`Stopping job processor with workerId: ${workerId}`);
662
716
  running = false;
663
- if (intervalId) {
664
- clearTimeout(intervalId);
665
- intervalId = null;
717
+ if (pollTimer) {
718
+ clearTimeout(pollTimer);
719
+ pollTimer = null;
666
720
  }
667
721
  },
668
722
  /**
@@ -672,17 +726,15 @@ var createProcessor = (backend, handlers, options = {}, onBeforeBatch, emit) =>
672
726
  stopAndDrain: async (drainTimeoutMs = 3e4) => {
673
727
  log(`Stopping and draining job processor with workerId: ${workerId}`);
674
728
  running = false;
675
- if (intervalId) {
676
- clearTimeout(intervalId);
677
- intervalId = null;
729
+ if (pollTimer) {
730
+ clearTimeout(pollTimer);
731
+ pollTimer = null;
678
732
  }
679
- if (currentBatchPromise) {
733
+ if (inFlightJobs.size > 0) {
680
734
  await Promise.race([
681
- currentBatchPromise.catch(() => {
682
- }),
735
+ Promise.allSettled(Array.from(inFlightJobs)),
683
736
  new Promise((resolve) => setTimeout(resolve, drainTimeoutMs))
684
737
  ]);
685
- currentBatchPromise = null;
686
738
  }
687
739
  log(`Job processor ${workerId} drained`);
688
740
  },