bunderstack 0.23.0 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to `bunderstack` will be documented in this file.
4
4
 
5
+ ## [0.23.1] - 2026-08-31
6
+
7
+ ### Changed
8
+
9
+ - **Continuous background-job concurrency.** Workers now fill declared
10
+ concurrency above the internal ten-row claim batch and refill each freed slot
11
+ immediately instead of waiting for the slowest job in a wave. Deterministic
12
+ fixture ticks retain complete-wave execution.
13
+
5
14
  ## [0.23.0] - 2026-08-29
6
15
 
7
16
  ### Added
@@ -3,7 +3,7 @@ export type { AnyJobDefinition, BackgroundDefinition, BackgroundDefs, Bunderstac
3
3
  export { enqueueJob } from './queue.js';
4
4
  export { createJobRunner } from './worker.js';
5
5
  export { startJobWorker } from './runtime.js';
6
- export type { StartWorkerOptions, RunWorkerOptions, WorkerHandle, } from './runtime.js';
6
+ export type { StartWorkerOptions, RunWorkerOptions, WorkerCycleResult, WorkerHandle, } from './runtime.js';
7
7
  export { parseCron, cronMatches } from './cron.js';
8
8
  export { slotsDue, floorSlot, CRON_PREFIX, SLOT_MS } from './slots.js';
9
9
  export type { CatchUp } from './slots.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAA;AAkBjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAM1C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA","sourcesContent":["// src/jobs/index.ts — module surface consumed by bunderstack.\nexport {\n createJobsBuilder,\n validateBackgroundDefs,\n validateJobsDefs,\n DEFAULT_RETRIES,\n DEFAULT_TIMEOUT_MS,\n} from './define'\nexport type {\n AnyJobDefinition,\n BackgroundDefinition,\n BackgroundDefs,\n BunderstackJobContext,\n BunderstackJobsBuilder,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n QueueJobDefinition,\n CronDefinition,\n CronInvocation,\n QueueJobKeys,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n} from './define'\nexport { enqueueJob } from './queue'\nexport { createJobRunner } from './worker'\nexport { startJobWorker } from './runtime'\nexport type {\n StartWorkerOptions,\n RunWorkerOptions,\n WorkerHandle,\n} from './runtime'\nexport { parseCron, cronMatches } from './cron'\nexport { slotsDue, floorSlot, CRON_PREFIX, SLOT_MS } from './slots'\nexport type { CatchUp } from './slots'\nexport type { TickResult } from './define'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAA;AAkBjB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAO1C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA","sourcesContent":["// src/jobs/index.ts — module surface consumed by bunderstack.\nexport {\n createJobsBuilder,\n validateBackgroundDefs,\n validateJobsDefs,\n DEFAULT_RETRIES,\n DEFAULT_TIMEOUT_MS,\n} from './define'\nexport type {\n AnyJobDefinition,\n BackgroundDefinition,\n BackgroundDefs,\n BunderstackJobContext,\n BunderstackJobsBuilder,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n QueueJobDefinition,\n CronDefinition,\n CronInvocation,\n QueueJobKeys,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n} from './define'\nexport { enqueueJob } from './queue'\nexport { createJobRunner } from './worker'\nexport { startJobWorker } from './runtime'\nexport type {\n StartWorkerOptions,\n RunWorkerOptions,\n WorkerCycleResult,\n WorkerHandle,\n} from './runtime'\nexport { parseCron, cronMatches } from './cron'\nexport { slotsDue, floorSlot, CRON_PREFIX, SLOT_MS } from './slots'\nexport type { CatchUp } from './slots'\nexport type { TickResult } from './define'\n"]}
@@ -2,10 +2,14 @@ export type WorkerHandle = {
2
2
  readonly closed: Promise<void>;
3
3
  close(): Promise<void>;
4
4
  };
5
+ export type WorkerCycleResult = {
6
+ wake?: Promise<void>;
7
+ };
5
8
  export type StartWorkerOptions = {
6
9
  signal?: AbortSignal;
7
10
  pollIntervalMs?: number;
8
- tick: (now: number) => Promise<void>;
11
+ tick: (now: number) => Promise<void | WorkerCycleResult>;
12
+ drain?: () => Promise<void>;
9
13
  onError?: (error: Error) => void;
10
14
  };
11
15
  export type RunWorkerOptions = StartWorkerOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/jobs/runtime.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAmBjD,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CA0BxE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/jobs/runtime.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAC,CAAA;IACxD,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AA+BjD,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CAiCxE"}
@@ -1,17 +1,25 @@
1
1
  function toError(error) {
2
2
  return error instanceof Error ? error : new Error(String(error));
3
3
  }
4
- function wait(ms, signal) {
4
+ function waitForNext(ms, signal, wake, onError) {
5
5
  return new Promise((resolve) => {
6
- if (signal.aborted)
7
- return resolve();
6
+ let settled = false;
8
7
  const timer = setTimeout(done, ms);
9
8
  function done() {
9
+ if (settled)
10
+ return;
11
+ settled = true;
10
12
  clearTimeout(timer);
11
13
  signal.removeEventListener('abort', done);
12
14
  resolve();
13
15
  }
14
16
  signal.addEventListener('abort', done, { once: true });
17
+ void wake?.then(done, (error) => {
18
+ onError?.(toError(error));
19
+ done();
20
+ });
21
+ if (signal.aborted)
22
+ done();
15
23
  });
16
24
  }
17
25
  export function startJobWorker(options) {
@@ -24,17 +32,19 @@ export function startJobWorker(options) {
24
32
  const closed = (async () => {
25
33
  try {
26
34
  while (!controller.signal.aborted) {
35
+ let cycle = undefined;
27
36
  try {
28
- await options.tick(Date.now());
37
+ cycle = await options.tick(Date.now());
29
38
  }
30
39
  catch (error) {
31
40
  options.onError?.(toError(error));
32
41
  }
33
- await wait(pollIntervalMs, controller.signal);
42
+ await waitForNext(pollIntervalMs, controller.signal, cycle?.wake, options.onError);
34
43
  }
35
44
  }
36
45
  finally {
37
46
  options.signal?.removeEventListener('abort', abort);
47
+ await options.drain?.();
38
48
  }
39
49
  })();
40
50
  const close = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/jobs/runtime.ts"],"names":[],"mappings":"AAcA,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,SAAS,IAAI,CAAC,EAAU,EAAE,MAAmB;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,OAAO,EAAE,CAAA;QACpC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAClC,SAAS,IAAI;YACX,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACzC,OAAO,EAAE,CAAA;QACX,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAA2B;IACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAA;IACtD,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;IACtC,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAChE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,KAAK,EAAE,CAAA;IAEpC,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,CAAC;oBACH,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;gBAChC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;gBACnC,CAAC;gBACD,MAAM,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;YAC/C,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrD,CAAC;IACH,CAAC,CAAC,EAAE,CAAA;IACJ,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC","sourcesContent":["export type WorkerHandle = {\n readonly closed: Promise<void>\n close(): Promise<void>\n}\n\nexport type StartWorkerOptions = {\n signal?: AbortSignal\n pollIntervalMs?: number\n tick: (now: number) => Promise<void>\n onError?: (error: Error) => void\n}\n\nexport type RunWorkerOptions = StartWorkerOptions\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) return resolve()\n const timer = setTimeout(done, ms)\n function done() {\n clearTimeout(timer)\n signal.removeEventListener('abort', done)\n resolve()\n }\n signal.addEventListener('abort', done, { once: true })\n })\n}\n\nexport function startJobWorker(options: StartWorkerOptions): WorkerHandle {\n const controller = new AbortController()\n const pollIntervalMs = options.pollIntervalMs ?? 1_000\n const abort = () => controller.abort()\n options.signal?.addEventListener('abort', abort, { once: true })\n if (options.signal?.aborted) abort()\n\n const closed = (async () => {\n try {\n while (!controller.signal.aborted) {\n try {\n await options.tick(Date.now())\n } catch (error) {\n options.onError?.(toError(error))\n }\n await wait(pollIntervalMs, controller.signal)\n }\n } finally {\n options.signal?.removeEventListener('abort', abort)\n }\n })()\n const close = () => {\n controller.abort()\n return closed\n }\n return { closed, close }\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/jobs/runtime.ts"],"names":[],"mappings":"AAiBA,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,SAAS,WAAW,CAClB,EAAU,EACV,MAAmB,EACnB,IAA+B,EAC/B,OAA6C;IAE7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAClC,SAAS,IAAI;YACX,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACzC,OAAO,EAAE,CAAA;QACX,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACtD,KAAK,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9B,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;YACzB,IAAI,EAAE,CAAA;QACR,CAAC,CAAC,CAAA;QACF,IAAI,MAAM,CAAC,OAAO;YAAE,IAAI,EAAE,CAAA;IAC5B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAA2B;IACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAA;IACtD,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;IACtC,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAChE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,KAAK,EAAE,CAAA;IAEpC,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,KAAK,GAA6B,SAAS,CAAA;gBAC/C,IAAI,CAAC;oBACH,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;gBACxC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;gBACnC,CAAC;gBACD,MAAM,WAAW,CACf,cAAc,EACd,UAAU,CAAC,MAAM,EACjB,KAAK,EAAE,IAAI,EACX,OAAO,CAAC,OAAO,CAChB,CAAA;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YACnD,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAA;QACzB,CAAC;IACH,CAAC,CAAC,EAAE,CAAA;IACJ,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC","sourcesContent":["export type WorkerHandle = {\n readonly closed: Promise<void>\n close(): Promise<void>\n}\n\nexport type WorkerCycleResult = { wake?: Promise<void> }\n\nexport type StartWorkerOptions = {\n signal?: AbortSignal\n pollIntervalMs?: number\n tick: (now: number) => Promise<void | WorkerCycleResult>\n drain?: () => Promise<void>\n onError?: (error: Error) => void\n}\n\nexport type RunWorkerOptions = StartWorkerOptions\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nfunction waitForNext(\n ms: number,\n signal: AbortSignal,\n wake: Promise<void> | undefined,\n onError: ((error: Error) => void) | undefined,\n): Promise<void> {\n return new Promise((resolve) => {\n let settled = false\n const timer = setTimeout(done, ms)\n function done() {\n if (settled) return\n settled = true\n clearTimeout(timer)\n signal.removeEventListener('abort', done)\n resolve()\n }\n signal.addEventListener('abort', done, { once: true })\n void wake?.then(done, (error) => {\n onError?.(toError(error))\n done()\n })\n if (signal.aborted) done()\n })\n}\n\nexport function startJobWorker(options: StartWorkerOptions): WorkerHandle {\n const controller = new AbortController()\n const pollIntervalMs = options.pollIntervalMs ?? 1_000\n const abort = () => controller.abort()\n options.signal?.addEventListener('abort', abort, { once: true })\n if (options.signal?.aborted) abort()\n\n const closed = (async () => {\n try {\n while (!controller.signal.aborted) {\n let cycle: void | WorkerCycleResult = undefined\n try {\n cycle = await options.tick(Date.now())\n } catch (error) {\n options.onError?.(toError(error))\n }\n await waitForNext(\n pollIntervalMs,\n controller.signal,\n cycle?.wake,\n options.onError,\n )\n }\n } finally {\n options.signal?.removeEventListener('abort', abort)\n await options.drain?.()\n }\n })()\n const close = () => {\n controller.abort()\n return closed\n }\n return { closed, close }\n}\n"]}
@@ -1,6 +1,9 @@
1
1
  import type { AnyDb } from '../dialect.js';
2
2
  import type { BunderstackLogger } from '../logging.js';
3
3
  import type { JobsDefs, JobsRuntimeFacade, TickResult } from './define.js';
4
+ type PumpResult = {
5
+ wake?: Promise<void>;
6
+ };
4
7
  export declare function createJobRunner(deps: {
5
8
  db: AnyDb;
6
9
  defs: JobsDefs;
@@ -9,6 +12,8 @@ export declare function createJobRunner(deps: {
9
12
  logger?: BunderstackLogger;
10
13
  }): {
11
14
  tick(now?: number): Promise<TickResult>;
15
+ pump: (now?: number) => Promise<PumpResult>;
16
+ drain: () => Promise<void>;
12
17
  inspect(now: number): Promise<{
13
18
  runnable: any;
14
19
  failed: {
@@ -30,4 +35,5 @@ export declare function createJobRunner(deps: {
30
35
  }>;
31
36
  setJobsFacade(f: JobsRuntimeFacade): void;
32
37
  };
38
+ export {};
33
39
  //# sourceMappingURL=worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AAiDjB,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;eA+SmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAStC,MAAM;;;;;;;;;;;;oBAiEb,SAAS,GACT,SAAS,GACT,WAAW,GACX,QAAQ;;;;;;;qBAQH,iBAAiB;EAIrC"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AA4BjB,KAAK,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAmC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;eA4WmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAnBlC,MAAM,KAAgB,OAAO,CAAC,UAAU,CAAC;iBAa1C,OAAO,CAAC,IAAI,CAAC;iBAYhB,MAAM;;;;;;;;;;;;oBAiEb,SAAS,GACT,SAAS,GACT,WAAW,GACX,QAAQ;;;;;;;qBAQH,iBAAiB;EAIrC"}
@@ -12,6 +12,11 @@ import { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots.js';
12
12
  const CLAIM_BATCH = 10;
13
13
  const SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000;
14
14
  const REAP_INTERVAL_MS = 60 * 60_000;
15
+ function capacityFor(def) {
16
+ return def.kind === 'job' && def.concurrency !== undefined
17
+ ? def.concurrency
18
+ : CLAIM_BATCH;
19
+ }
15
20
  function toError(err) {
16
21
  return err instanceof Error ? err : new Error(String(err));
17
22
  }
@@ -38,6 +43,7 @@ export function createJobRunner(deps) {
38
43
  const t = jobsTableFor(db);
39
44
  const ctx = { ...deps.ctx };
40
45
  let lastReapAt = 0;
46
+ const active = new Map();
41
47
  /** Cron rows carry no payload — their handler input is the slot itself. */
42
48
  function resolveInput(def, row) {
43
49
  if (def.kind === 'cron') {
@@ -184,6 +190,27 @@ export function createJobRunner(deps) {
184
190
  });
185
191
  return rows;
186
192
  }
193
+ async function claimAvailable(type, def, now) {
194
+ const runningRows = await db
195
+ .select({ id: t.id })
196
+ .from(t)
197
+ .where(and(eq(t.type, type), eq(t.status, 'running')));
198
+ let available = capacityFor(def) - runningRows.length;
199
+ if (available <= 0)
200
+ return [];
201
+ const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS);
202
+ const work = [];
203
+ while (available > 0) {
204
+ const limit = Math.min(CLAIM_BATCH, available);
205
+ const rows = await claim(type, limit, now, leaseUntil);
206
+ for (const row of rows)
207
+ work.push({ row, def, leaseUntil });
208
+ available -= rows.length;
209
+ if (rows.length < limit)
210
+ break;
211
+ }
212
+ return work;
213
+ }
187
214
  // `now` is the tick's injected clock: retry runAt math uses it so tests can
188
215
  // drive backoff deterministically. finishedAt uses the real clock (a handler
189
216
  // may run long past the tick's start).
@@ -267,22 +294,9 @@ export function createJobRunner(deps) {
267
294
  let totalClaimed = 0;
268
295
  for (const [name, def] of Object.entries(defs)) {
269
296
  const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name;
270
- let limit = CLAIM_BATCH;
271
- if (def.kind === 'job' && def.concurrency !== undefined) {
272
- const runningRows = await db
273
- .select({ id: t.id })
274
- .from(t)
275
- .where(and(eq(t.type, type), eq(t.status, 'running')));
276
- const capacity = def.concurrency - runningRows.length;
277
- if (capacity <= 0)
278
- continue;
279
- limit = Math.min(limit, capacity);
280
- }
281
- const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS);
282
- const claimed = await claim(type, limit, now, leaseUntil);
297
+ const claimed = await claimAvailable(type, def, now);
283
298
  totalClaimed += claimed.length;
284
- for (const row of claimed)
285
- claimedWork.push({ row, def, leaseUntil });
299
+ claimedWork.push(...claimed);
286
300
  }
287
301
  // Claim the whole tick snapshot before starting any handler. Work enqueued
288
302
  // by a handler therefore belongs to the next tick, regardless of the
@@ -298,16 +312,58 @@ export function createJobRunner(deps) {
298
312
  }
299
313
  return { claimed: totalClaimed, ran, failed };
300
314
  }
315
+ async function maintain(now) {
316
+ await materializeCronSlots(now);
317
+ await recoverExpiredLeases(now);
318
+ if (now - lastReapAt >= REAP_INTERVAL_MS) {
319
+ lastReapAt = now;
320
+ await reapSucceeded(now);
321
+ }
322
+ }
323
+ function startWork(type, work, claimedAt) {
324
+ let tasks = active.get(type);
325
+ if (!tasks) {
326
+ tasks = new Set();
327
+ active.set(type, tasks);
328
+ }
329
+ let task;
330
+ task = runJob(work.row, work.def, claimedAt, work.leaseUntil)
331
+ .then(() => undefined)
332
+ .catch((error) => {
333
+ logger.error('[bunderstack] background job execution failed:', error);
334
+ })
335
+ .finally(() => {
336
+ tasks.delete(task);
337
+ if (tasks.size === 0)
338
+ active.delete(type);
339
+ });
340
+ tasks.add(task);
341
+ return task;
342
+ }
343
+ async function pump(now = Date.now()) {
344
+ await maintain(now);
345
+ for (const [name, def] of Object.entries(defs)) {
346
+ const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name;
347
+ const work = await claimAvailable(type, def, now);
348
+ for (const item of work)
349
+ startWork(type, item, now);
350
+ }
351
+ const snapshot = [...active.values()].flatMap((tasks) => [...tasks]);
352
+ return snapshot.length === 0
353
+ ? {}
354
+ : { wake: Promise.race(snapshot).then(() => undefined) };
355
+ }
356
+ async function drain() {
357
+ const snapshot = [...active.values()].flatMap((tasks) => [...tasks]);
358
+ await Promise.allSettled(snapshot);
359
+ }
301
360
  return {
302
361
  async tick(now = Date.now()) {
303
- await materializeCronSlots(now);
304
- await recoverExpiredLeases(now);
305
- if (now - lastReapAt >= REAP_INTERVAL_MS) {
306
- lastReapAt = now;
307
- await reapSucceeded(now);
308
- }
362
+ await maintain(now);
309
363
  return runClaimable(now);
310
364
  },
365
+ pump,
366
+ drain,
311
367
  async inspect(now) {
312
368
  const runnableRows = await db
313
369
  .select({ id: t.id })
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport { and, eq, inArray, is, isNotNull, lt, lte, max, sql } from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type { BunderstackLogger } from '../logging'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { consoleLogger } from '../logging'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './define'\nimport { enqueueJob } from './queue'\nimport { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Terminal queue rows release their dedupe key. */\nfunction terminalPatch() {\n return { dedupeKey: null }\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n logger?: BunderstackLogger\n}) {\n const { db, defs } = deps\n const logger = deps.logger ?? consoleLogger\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /**\n * The watermark is the newest slot we already stored for this cron. When no\n * rows exist — a newly declared cron, or one whose rows were reaped — anchor\n * one slot before now so the current minute is eligible and nothing older is.\n */\n async function cronWatermark(type: string, now: number): Promise<number> {\n const rows = await db\n .select({ latest: max(t.runAt) })\n .from(t)\n .where(eq(t.type, type))\n const latest = rows[0]?.latest\n return latest == null ? floorSlot(now) - SLOT_MS : Number(latest)\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const from = await cronWatermark(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from,\n to: now,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n }\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n logger.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & { lastError: string | null })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(eq(t.id, row.id))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(),\n })\n .where(eq(t.id, row.id))\n await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(eq(t.id, row.id))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n leaseUntil: number,\n ): Promise<'ran' | 'failed' | 'lost'> {\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(input, ctx)\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n return 'ran'\n } catch (err) {\n const e = toError(err)\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: Array<{\n row: JobRow\n def: AnyBackgroundDefinition\n leaseUntil: number\n }> = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n let limit = CLAIM_BATCH\n if (def.kind === 'job' && def.concurrency !== undefined) {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n const capacity = def.concurrency - runningRows.length\n if (capacity <= 0) continue\n limit = Math.min(limit, capacity)\n }\n const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS)\n const claimed = await claim(type, limit, now, leaseUntil)\n totalClaimed += claimed.length\n for (const row of claimed) claimedWork.push({ row, def, leaseUntil })\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, leaseUntil }) =>\n runJob(row, def, now, leaseUntil),\n ),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n return runClaimable(now)\n },\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n const rows: Array<{\n id: string\n type: string\n status: string\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n createdAt: number\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n status: t.status,\n attempts: t.attempts,\n runAt: t.runAt,\n dedupeKey: t.dedupeKey,\n lastError: t.lastError,\n createdAt: t.createdAt,\n })\n .from(t)\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n jobs: rows\n .sort(\n (left, right) =>\n Number(left.createdAt) - Number(right.createdAt) ||\n String(left.id).localeCompare(String(right.id)),\n )\n .map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n kind: row.type.startsWith(CRON_PREFIX)\n ? ('cron' as const)\n : ('job' as const),\n status: row.status as\n | 'pending'\n | 'running'\n | 'succeeded'\n | 'failed',\n attempts: Number(row.attempts),\n runAt: Number(row.runAt),\n dedupeKey: row.dedupeKey,\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport { and, eq, inArray, is, isNotNull, lt, lte, max, sql } from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type { BunderstackLogger } from '../logging'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { consoleLogger } from '../logging'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './define'\nimport { enqueueJob } from './queue'\nimport { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\ntype ClaimedWork = {\n row: JobRow\n def: AnyBackgroundDefinition\n leaseUntil: number\n}\n\ntype PumpResult = { wake?: Promise<void> }\n\nfunction capacityFor(def: AnyBackgroundDefinition): number {\n return def.kind === 'job' && def.concurrency !== undefined\n ? def.concurrency\n : CLAIM_BATCH\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Terminal queue rows release their dedupe key. */\nfunction terminalPatch() {\n return { dedupeKey: null }\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n logger?: BunderstackLogger\n}) {\n const { db, defs } = deps\n const logger = deps.logger ?? consoleLogger\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n const active = new Map<string, Set<Promise<void>>>()\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /**\n * The watermark is the newest slot we already stored for this cron. When no\n * rows exist — a newly declared cron, or one whose rows were reaped — anchor\n * one slot before now so the current minute is eligible and nothing older is.\n */\n async function cronWatermark(type: string, now: number): Promise<number> {\n const rows = await db\n .select({ latest: max(t.runAt) })\n .from(t)\n .where(eq(t.type, type))\n const latest = rows[0]?.latest\n return latest == null ? floorSlot(now) - SLOT_MS : Number(latest)\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const from = await cronWatermark(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from,\n to: now,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n }\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n logger.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & { lastError: string | null })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(eq(t.id, row.id))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(),\n })\n .where(eq(t.id, row.id))\n await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(eq(t.id, row.id))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n async function claimAvailable(\n type: string,\n def: AnyBackgroundDefinition,\n now: number,\n ): Promise<ClaimedWork[]> {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n let available = capacityFor(def) - runningRows.length\n if (available <= 0) return []\n\n const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS)\n const work: ClaimedWork[] = []\n while (available > 0) {\n const limit = Math.min(CLAIM_BATCH, available)\n const rows = await claim(type, limit, now, leaseUntil)\n for (const row of rows) work.push({ row, def, leaseUntil })\n available -= rows.length\n if (rows.length < limit) break\n }\n return work\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n leaseUntil: number,\n ): Promise<'ran' | 'failed' | 'lost'> {\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(input, ctx)\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n return 'ran'\n } catch (err) {\n const e = toError(err)\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: ClaimedWork[] = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const claimed = await claimAvailable(type, def, now)\n totalClaimed += claimed.length\n claimedWork.push(...claimed)\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, leaseUntil }) =>\n runJob(row, def, now, leaseUntil),\n ),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n async function maintain(now: number) {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n }\n\n function startWork(\n type: string,\n work: ClaimedWork,\n claimedAt: number,\n ): Promise<void> {\n let tasks = active.get(type)\n if (!tasks) {\n tasks = new Set()\n active.set(type, tasks)\n }\n let task: Promise<void>\n task = runJob(work.row, work.def, claimedAt, work.leaseUntil)\n .then(() => undefined)\n .catch((error) => {\n logger.error('[bunderstack] background job execution failed:', error)\n })\n .finally(() => {\n tasks!.delete(task)\n if (tasks!.size === 0) active.delete(type)\n })\n tasks.add(task)\n return task\n }\n\n async function pump(now: number = Date.now()): Promise<PumpResult> {\n await maintain(now)\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n const work = await claimAvailable(type, def, now)\n for (const item of work) startWork(type, item, now)\n }\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n return snapshot.length === 0\n ? {}\n : { wake: Promise.race(snapshot).then(() => undefined) }\n }\n\n async function drain(): Promise<void> {\n const snapshot = [...active.values()].flatMap((tasks) => [...tasks])\n await Promise.allSettled(snapshot)\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await maintain(now)\n return runClaimable(now)\n },\n pump,\n drain,\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n const rows: Array<{\n id: string\n type: string\n status: string\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n createdAt: number\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n status: t.status,\n attempts: t.attempts,\n runAt: t.runAt,\n dedupeKey: t.dedupeKey,\n lastError: t.lastError,\n createdAt: t.createdAt,\n })\n .from(t)\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n jobs: rows\n .sort(\n (left, right) =>\n Number(left.createdAt) - Number(right.createdAt) ||\n String(left.id).localeCompare(String(right.id)),\n )\n .map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n kind: row.type.startsWith(CRON_PREFIX)\n ? ('cron' as const)\n : ('job' as const),\n status: row.status as\n | 'pending'\n | 'running'\n | 'succeeded'\n | 'failed',\n attempts: Number(row.attempts),\n runAt: Number(row.runAt),\n dedupeKey: row.dedupeKey,\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
package/dist/runtime.d.ts CHANGED
@@ -62,7 +62,7 @@ export interface StorageFacade {
62
62
  filename?: string;
63
63
  }): Promise<void>;
64
64
  }
65
- export type AppStartWorkerOptions = Omit<StartWorkerOptions, 'tick'>;
65
+ export type AppStartWorkerOptions = Omit<StartWorkerOptions, 'tick' | 'drain'>;
66
66
  export type AppRunWorkerOptions = AppStartWorkerOptions & {
67
67
  /**
68
68
  * Permit process-local realtime in a standalone worker.
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -298,11 +298,8 @@ export async function materializeBunderstack(options, source, overrides = {}) {
298
298
  const handle = startJobWorker({
299
299
  ...options,
300
300
  signal,
301
- // The runtime loop only cares that a tick completed; TickResult is for
302
- // callers that invoke tick() directly.
303
- tick: async (now) => {
304
- await jobRunner.tick(now);
305
- },
301
+ tick: (now) => jobRunner.pump(now),
302
+ drain: () => jobRunner.drain(),
306
303
  });
307
304
  const unregister = lifecycle.add(() => handle.close());
308
305
  void handle.closed.finally(unregister);