queasy 0.2.0 → 0.3.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.
Files changed (98) hide show
  1. package/.github/workflows/check.yml +3 -0
  2. package/.github/workflows/publish.yml +3 -0
  3. package/CLAUDE.md +5 -4
  4. package/Readme.md +9 -4
  5. package/biome.json +5 -1
  6. package/dist/client.d.ts +33 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +199 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/constants.d.ts +10 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/{src → dist}/constants.js +2 -10
  13. package/dist/constants.js.map +1 -0
  14. package/dist/errors.d.ts +7 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/{src → dist}/errors.js +1 -13
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +3 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/manager.d.ts +19 -0
  23. package/dist/manager.d.ts.map +1 -0
  24. package/dist/manager.js +67 -0
  25. package/dist/manager.js.map +1 -0
  26. package/dist/pool.d.ts +29 -0
  27. package/dist/pool.d.ts.map +1 -0
  28. package/{src → dist}/pool.js +23 -82
  29. package/dist/pool.js.map +1 -0
  30. package/dist/queasy.lua +390 -0
  31. package/dist/queue.d.ts +22 -0
  32. package/dist/queue.d.ts.map +1 -0
  33. package/dist/queue.js +81 -0
  34. package/dist/queue.js.map +1 -0
  35. package/dist/types.d.ts +92 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils.d.ts +4 -0
  40. package/dist/utils.d.ts.map +1 -0
  41. package/dist/utils.js +24 -0
  42. package/dist/utils.js.map +1 -0
  43. package/dist/worker.d.ts +2 -0
  44. package/dist/worker.d.ts.map +1 -0
  45. package/dist/worker.js +42 -0
  46. package/dist/worker.js.map +1 -0
  47. package/docker-compose.yml +0 -2
  48. package/fuzztest/Readme.md +185 -0
  49. package/fuzztest/fuzz.ts +356 -0
  50. package/fuzztest/handlers/cascade-a.ts +90 -0
  51. package/fuzztest/handlers/cascade-b.ts +71 -0
  52. package/fuzztest/handlers/fail-handler.ts +47 -0
  53. package/fuzztest/handlers/periodic.ts +89 -0
  54. package/fuzztest/process.ts +100 -0
  55. package/fuzztest/shared/chaos.ts +29 -0
  56. package/fuzztest/shared/stream.ts +40 -0
  57. package/package.json +8 -7
  58. package/plans/redis-options.md +279 -0
  59. package/src/client.ts +246 -0
  60. package/src/constants.ts +33 -0
  61. package/src/errors.ts +13 -0
  62. package/src/index.ts +2 -0
  63. package/src/manager.ts +78 -0
  64. package/src/pool.ts +129 -0
  65. package/src/queasy.lua +2 -3
  66. package/src/queue.ts +108 -0
  67. package/src/types.ts +16 -0
  68. package/src/{utils.js → utils.ts} +3 -20
  69. package/src/{worker.js → worker.ts} +5 -12
  70. package/test/{client.test.js → client.test.ts} +6 -7
  71. package/test/{errors.test.js → errors.test.ts} +1 -1
  72. package/test/fixtures/always-fail-handler.ts +5 -0
  73. package/test/fixtures/data-logger-handler.ts +11 -0
  74. package/test/fixtures/failure-handler.ts +6 -0
  75. package/test/fixtures/permanent-error-handler.ts +6 -0
  76. package/test/fixtures/slow-handler.ts +6 -0
  77. package/test/fixtures/success-handler.js +0 -5
  78. package/test/fixtures/success-handler.ts +6 -0
  79. package/test/fixtures/with-failure-handler.ts +5 -0
  80. package/test/{guards.test.js → guards.test.ts} +21 -34
  81. package/test/{manager.test.js → manager.test.ts} +26 -34
  82. package/test/{pool.test.js → pool.test.ts} +14 -16
  83. package/test/{queue.test.js → queue.test.ts} +21 -21
  84. package/test/{redis-functions.test.js → redis-functions.test.ts} +14 -20
  85. package/test/{utils.test.js → utils.test.ts} +1 -1
  86. package/tsconfig.json +20 -0
  87. package/jsconfig.json +0 -17
  88. package/src/client.js +0 -258
  89. package/src/index.js +0 -2
  90. package/src/manager.js +0 -94
  91. package/src/queue.js +0 -154
  92. package/test/fixtures/always-fail-handler.js +0 -8
  93. package/test/fixtures/data-logger-handler.js +0 -19
  94. package/test/fixtures/failure-handler.js +0 -9
  95. package/test/fixtures/permanent-error-handler.js +0 -10
  96. package/test/fixtures/slow-handler.js +0 -9
  97. package/test/fixtures/with-failure-handler.js +0 -8
  98. /package/test/fixtures/{no-handle-handler.js → no-handle-handler.ts} +0 -0
@@ -36,6 +36,9 @@ jobs:
36
36
  - name: Typecheck
37
37
  run: npm run typecheck
38
38
 
39
+ - name: Build
40
+ run: npm run build
41
+
39
42
  - name: Test with coverage
40
43
  run: npm run test:coverage
41
44
 
@@ -24,6 +24,9 @@ jobs:
24
24
 
25
25
  - run: npm ci
26
26
 
27
+ - name: Build
28
+ run: npm run build
29
+
27
30
  - name: Extract version
28
31
  id: version
29
32
  run: echo "version=$(node -e "console.log(require('./package.json').version)")" >> "$GITHUB_OUTPUT"
package/CLAUDE.md CHANGED
@@ -10,7 +10,8 @@ npm test -- --test-name-pattern="pattern" # Run tests matching a pattern
10
10
  node --test test/queue.test.js # Run a single test file
11
11
  npm run lint # Lint with Biome
12
12
  npm run format # Auto-format with Biome
13
- npm run typecheck # TypeScript check via jsconfig.json (JSDoc types)
13
+ npm run build # Compile TypeScript to dist/
14
+ npm run typecheck # TypeScript type-check without emitting
14
15
  npm run docker:up # Start Redis via Docker Compose
15
16
  npm run docker:down # Stop Redis
16
17
  ```
@@ -21,7 +22,7 @@ Tests require a running Redis instance. Use `docker:up` first if needed.
21
22
 
22
23
  Queasy is a Redis-backed job queue with **at-least-once** delivery semantics. The core logic lives in two layers:
23
24
 
24
- - **JS layer** (`src/queue.js`): The `queue()` factory returns `{ dispatch, cancel, listen }`. On first use, it uploads the Lua script to Redis via `FUNCTION LOAD REPLACE`. A `WeakSet` (`initializedClients`) tracks which Redis clients have already had the functions loaded. `listen()` is currently a TODO stub.
25
+ - **TypeScript layer** (`src/client.ts`): The `Client` class accepts a `RedisOptions` object and constructs its own node-redis connection via `createClient` (plain object) or `createCluster` (object with `rootNodes`). On construction it connects, then uploads the Lua script to Redis via `FUNCTION LOAD REPLACE`. The connection is torn down in `close()` via `destroy()`. Source compiles to `dist/` via `npm run build`.
25
26
  - **Lua layer** (`src/queasy.lua`): All queue state mutations are atomic Redis functions registered under the `queasy` library. No queue logic should be duplicated in JS — the Lua functions are the single source of truth for state transitions.
26
27
 
27
28
  ### Redis data structures
@@ -77,7 +78,7 @@ Queue names in tests use `{curly-brace}` syntax (e.g., `{test-api-queue}`) to ke
77
78
  ## Conventions
78
79
 
79
80
  - ESM modules throughout (`"type": "module"` in package.json).
80
- - JSDoc types with a `types.ts` file for IDE support; run `npm run typecheck` to verify.
81
- - Biome for formatting (tabs, single quotes, 100-char width). Run `npm run format` before committing.
81
+ - TypeScript source in `src/`, compiled output in `dist/`. Run `npm run build` before running tests.
82
+ - Biome for formatting (spaces, single quotes, 100-char width). Run `npm run format` before committing.
82
83
  - All Lua booleans arrive as strings (`'true'`/`'false'`) from `redis.fCall` — comparisons in Lua must use string equality.
83
84
  - `redis.setresp(3)` is called in each registered function to get RESP3 map responses (needed for `HGETALL` returning `{ map: {...} }` instead of a flat array).
package/Readme.md CHANGED
@@ -13,7 +13,7 @@ A Redis-backed job queue for Node.js, featuring (in comparison with design inspi
13
13
 
14
14
  ### Terminology
15
15
 
16
- A _client_ is an instance of Queasy that connects to a Redis database. A _job_ is the basic unit of work that is _dispatched_ into a _queue_.
16
+ A _client_ is an instance of Queasy. It manages its own Redis connection. A _job_ is the basic unit of work that is _dispatched_ into a _queue_.
17
17
 
18
18
  A _handler_ is JavaScript code that performs work. There are two kinds of handlers: _task handlers_, which process jobs, and _fail handlers_, which are invoked when a job fails permanently. Handlers run on _workers_, which are Node.js worker threads. By default, a Queasy client automatically creates one worker per CPU.
19
19
 
@@ -55,9 +55,14 @@ The response of the heartbeat Lua function indicates whether the client had been
55
55
 
56
56
  ## API
57
57
 
58
- ### `client(redisConnection, workerCount)`
59
- Returns a Queasy client.
60
- - `redisConnection`: a node-redis connection object.
58
+ ### `new Client(options, workerCount)`
59
+ Returns a Queasy client. Queasy creates and manages its own Redis connection internally.
60
+ - `options`: connection options. Two forms are accepted:
61
+ - **Single node** (plain object): passed to node-redis `createClient`. Accepts `url`, `socket`, `username`, `password`, and `database`. Defaults to `{}` (connects to `localhost:6379`).
62
+ - **Cluster** (object with `rootNodes`): passed to node-redis `createCluster`. Accepts:
63
+ - `rootNodes`: array of per-node connection options (same fields as single-node form); at least three nodes are recommended.
64
+ - `defaults`: options shared across all nodes (e.g. auth and TLS).
65
+ - `nodeAddressMap`: address translation map for NAT environments.
61
66
  - `workerCount`: number; Size of the worker pool. If 0, or if called in a queasy worker thread, no pool is created. Defaults to the number of CPUs.
62
67
 
63
68
  The client object returned is an EventEmitter, which emits a 'disconnect' event when it fails permanently for any reason, such as library version mismatch between different workers connected to the same Redis insance, or a lost locks situation. When this happens, in general the application should exit the worker process and allow the supervisor to restart it.
package/biome.json CHANGED
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
3
+ "files": {
4
+ "includes": ["**", "!!dist"]
5
+ },
3
6
  "assist": { "actions": { "source": { "organizeImports": "on" } } },
4
7
  "linter": {
5
8
  "enabled": true,
@@ -9,7 +12,8 @@
9
12
  "noForEach": "off"
10
13
  },
11
14
  "style": {
12
- "useNodejsImportProtocol": "error"
15
+ "useNodejsImportProtocol": "error",
16
+ "noNonNullAssertion": "off"
13
17
  }
14
18
  }
15
19
  },
@@ -0,0 +1,33 @@
1
+ import EventEmitter from 'node:events';
2
+ import { createClient, createCluster } from 'redis';
3
+ import { Manager } from './manager.ts';
4
+ import { Pool } from './pool.ts';
5
+ import { Queue } from './queue.ts';
6
+ import type { Job, RedisOptions } from './types.ts';
7
+ type RedisConnection = ReturnType<typeof createClient> | ReturnType<typeof createCluster>;
8
+ interface QueueEntry {
9
+ queue: Queue;
10
+ bumpTimer?: NodeJS.Timeout;
11
+ }
12
+ export declare function parseJob(jobArray: string[]): Job | null;
13
+ export declare class Client extends EventEmitter {
14
+ redis: RedisConnection;
15
+ clientId: string;
16
+ queues: Record<string, QueueEntry>;
17
+ disconnected: boolean;
18
+ pool: Pool | undefined;
19
+ manager: Manager | undefined;
20
+ constructor(options?: RedisOptions, workerCount?: number, callback?: (client: Client) => unknown);
21
+ queue(name: string, isKey?: boolean): Queue;
22
+ scheduleBump(key: string): void;
23
+ bump(key: string): Promise<void>;
24
+ close(): Promise<void>;
25
+ dispatch(key: string, id: string, runAt: number, data: any, updateData: boolean, updateRunAt: boolean | string, resetCounts: boolean): Promise<void>;
26
+ cancel(key: string, id: string): Promise<boolean>;
27
+ dequeue(key: string, count: number): Promise<Job[]>;
28
+ finish(key: string, jobId: string): Promise<void>;
29
+ fail(key: string, failkey: string, jobId: string, failJobData: any): Promise<void>;
30
+ retry(key: string, jobId: string, retryAt: number): Promise<void>;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAC;AAMvC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AASpD,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AA6B1F,UAAU,UAAU;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CAC9B;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,IAAI,CAiBvD;AAED,qBAAa,MAAO,SAAQ,YAAY;IACpC,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;gBAGzB,OAAO,GAAE,YAAiB,EAC1B,WAAW,GAAE,MAAyB,EACtC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO;IA6B1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,KAAK;IAYzC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMzB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,QAAQ,CACV,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EAEb,IAAI,EAAE,GAAG,EACT,UAAU,EAAE,OAAO,EACnB,WAAW,EAAE,OAAO,GAAG,MAAM,EAC7B,WAAW,EAAE,OAAO,GACrB,OAAO,CAAC,IAAI,CAAC;IAeV,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYjD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAkBnD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,IAAI,CACN,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAEb,WAAW,EAAE,GAAG,GACjB,OAAO,CAAC,IAAI,CAAC;IAcV,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO1E"}
package/dist/client.js ADDED
@@ -0,0 +1,199 @@
1
+ import EventEmitter from 'node:events';
2
+ import { readFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { getEnvironmentData } from 'node:worker_threads';
7
+ import { createClient, createCluster } from 'redis';
8
+ import { HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT, LUA_FUNCTIONS_VERSION } from "./constants.js";
9
+ import { Manager } from "./manager.js";
10
+ import { Pool } from "./pool.js";
11
+ import { Queue } from "./queue.js";
12
+ import { compareSemver, generateId, parseVersion } from "./utils.js";
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const luaScript = readFileSync(join(__dirname, 'queasy.lua'), 'utf8').replace('__QUEASY_VERSION__', LUA_FUNCTIONS_VERSION);
15
+ async function installLuaFunctions(redis) {
16
+ const installedVersionString = (await redis
17
+ .fCall('queasy_version', { keys: [], arguments: [] })
18
+ .catch(() => null));
19
+ const installedVersion = parseVersion(installedVersionString);
20
+ const availableVersion = parseVersion(LUA_FUNCTIONS_VERSION);
21
+ if (compareSemver(availableVersion, installedVersion) > 0) {
22
+ await redis.sendCommand([
23
+ 'FUNCTION',
24
+ 'LOAD',
25
+ 'REPLACE',
26
+ luaScript,
27
+ ]);
28
+ return false;
29
+ }
30
+ return installedVersion[0] > availableVersion[0];
31
+ }
32
+ function buildRedisConnection(options) {
33
+ if ('rootNodes' in options) {
34
+ return createCluster(options);
35
+ }
36
+ return createClient(options);
37
+ }
38
+ export function parseJob(jobArray) {
39
+ if (!jobArray || jobArray.length === 0)
40
+ return null;
41
+ const job = {};
42
+ for (let i = 0; i < jobArray.length; i += 2) {
43
+ const key = jobArray[i];
44
+ const value = jobArray[i + 1];
45
+ job[key] = value;
46
+ }
47
+ return {
48
+ id: job.id,
49
+ data: job.data ? JSON.parse(job.data) : undefined,
50
+ runAt: job.run_at ? Number(job.run_at) : 0,
51
+ retryCount: Number(job.retry_count || 0),
52
+ stallCount: Number(job.stall_count || 0),
53
+ };
54
+ }
55
+ export class Client extends EventEmitter {
56
+ redis;
57
+ clientId;
58
+ queues;
59
+ disconnected;
60
+ pool;
61
+ manager;
62
+ constructor(options = {}, workerCount = os.cpus().length, callback) {
63
+ super();
64
+ this.redis = buildRedisConnection(options);
65
+ this.clientId = generateId();
66
+ this.queues = {};
67
+ this.disconnected = false;
68
+ const inWorker = getEnvironmentData('queasy_worker_context');
69
+ this.pool = !inWorker && workerCount !== 0 ? new Pool(workerCount) : undefined;
70
+ if (this.pool)
71
+ this.manager = new Manager(this.pool);
72
+ this.redis
73
+ .connect()
74
+ .then(() => installLuaFunctions(this.redis))
75
+ .then((disconnect) => {
76
+ this.disconnected = disconnect;
77
+ if (disconnect)
78
+ this.emit('disconnected', 'Redis has incompatible queasy version.');
79
+ else {
80
+ this.emit('connected');
81
+ if (callback)
82
+ callback(this);
83
+ }
84
+ })
85
+ .catch((err) => {
86
+ this.disconnected = true;
87
+ this.emit('disconnected', err.message);
88
+ });
89
+ }
90
+ queue(name, isKey = false) {
91
+ if (this.disconnected)
92
+ throw new Error("Can't add queue: client disconnected");
93
+ const key = isKey ? name : `{${name}}`;
94
+ if (!this.queues[key]) {
95
+ this.queues[key] = {
96
+ queue: new Queue(key, this, this.pool, this.manager),
97
+ };
98
+ }
99
+ return this.queues[key].queue;
100
+ }
101
+ scheduleBump(key) {
102
+ const queueEntry = this.queues[key];
103
+ if (queueEntry.bumpTimer)
104
+ clearTimeout(queueEntry.bumpTimer);
105
+ queueEntry.bumpTimer = setTimeout(() => this.bump(key), HEARTBEAT_INTERVAL);
106
+ }
107
+ async bump(key) {
108
+ if (this.disconnected)
109
+ return;
110
+ this.scheduleBump(key);
111
+ const now = Date.now();
112
+ const expiry = now + HEARTBEAT_TIMEOUT;
113
+ const bumped = await this.redis.fCall('queasy_bump', {
114
+ keys: [key],
115
+ arguments: [this.clientId, String(now), String(expiry)],
116
+ });
117
+ if (!bumped) {
118
+ await this.close();
119
+ this.emit('disconnected', 'Lost locks, possible main thread freeze');
120
+ }
121
+ }
122
+ async close() {
123
+ if (this.pool)
124
+ await this.pool.close();
125
+ if (this.manager)
126
+ await this.manager.close();
127
+ this.queues = {};
128
+ this.pool = undefined;
129
+ this.disconnected = true;
130
+ await this.redis.quit().catch(() => { });
131
+ }
132
+ async dispatch(key, id, runAt,
133
+ // biome-ignore lint/suspicious/noExplicitAny: Data is any serializable value
134
+ data, updateData, updateRunAt, resetCounts) {
135
+ await this.redis.fCall('queasy_dispatch', {
136
+ keys: [key],
137
+ arguments: [
138
+ id,
139
+ String(runAt),
140
+ JSON.stringify(data),
141
+ String(updateData),
142
+ String(updateRunAt),
143
+ String(resetCounts),
144
+ ],
145
+ });
146
+ this.emit('dispatch', key, { id, runAt, data, updateData, updateRunAt, resetCounts });
147
+ }
148
+ async cancel(key, id) {
149
+ const result = await this.redis.fCall('queasy_cancel', {
150
+ keys: [key],
151
+ arguments: [id],
152
+ });
153
+ this.emit('cancel', key, id);
154
+ return result === 1;
155
+ }
156
+ async dequeue(key, count) {
157
+ const now = Date.now();
158
+ const expiry = now + HEARTBEAT_TIMEOUT;
159
+ const result = (await this.redis.fCall('queasy_dequeue', {
160
+ keys: [key],
161
+ arguments: [this.clientId, String(now), String(expiry), String(count)],
162
+ }));
163
+ this.scheduleBump(key);
164
+ const jobs = result.map((jobArray) => parseJob(jobArray)).filter(Boolean);
165
+ for (const job of jobs)
166
+ this.emit('dequeue', key, job);
167
+ return jobs;
168
+ }
169
+ async finish(key, jobId) {
170
+ await this.redis.fCall('queasy_finish', {
171
+ keys: [key],
172
+ arguments: [jobId, this.clientId, String(Date.now())],
173
+ });
174
+ this.emit('finish', key, jobId);
175
+ }
176
+ async fail(key, failkey, jobId,
177
+ // biome-ignore lint/suspicious/noExplicitAny: Fail job data is any serializable value
178
+ failJobData) {
179
+ await this.redis.fCall('queasy_fail', {
180
+ keys: [key, failkey],
181
+ arguments: [
182
+ jobId,
183
+ this.clientId,
184
+ generateId(),
185
+ JSON.stringify(failJobData),
186
+ String(Date.now()),
187
+ ],
188
+ });
189
+ this.emit('fail', key, jobId);
190
+ }
191
+ async retry(key, jobId, retryAt) {
192
+ await this.redis.fCall('queasy_retry', {
193
+ keys: [key],
194
+ arguments: [jobId, this.clientId, String(retryAt), String(Date.now())],
195
+ });
196
+ this.emit('retry', key, jobId);
197
+ }
198
+ }
199
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { HandlerOptions, JobUpdateOptions } from './types.ts';
2
+ export declare const DEFAULT_RETRY_OPTIONS: Required<HandlerOptions>;
3
+ export declare const DEFAULT_UPDATE_OPTIONS: Required<JobUpdateOptions>;
4
+ export declare const FAILJOB_RETRY_OPTIONS: Required<HandlerOptions>;
5
+ export declare const LUA_FUNCTIONS_VERSION = "1.0.1";
6
+ export declare const HEARTBEAT_INTERVAL = 5000;
7
+ export declare const HEARTBEAT_TIMEOUT = 10000;
8
+ export declare const WORKER_CAPACITY = 10;
9
+ export declare const DEQUEUE_INTERVAL = 100;
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,cAAc,CAQ1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,gBAAgB,CAI7D,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,cAAc,CAQ1D,CAAC;AAEF,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAC7C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AACvC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAC"}
@@ -1,7 +1,3 @@
1
- /** @typedef {import('./types').HandlerOptions} HandlerOptions */
2
- /** @typedef {import('./types').JobUpdateOptions} JobUpdateOptions */
3
-
4
- /** @type {Required<HandlerOptions>} */
5
1
  export const DEFAULT_RETRY_OPTIONS = {
6
2
  maxRetries: 10,
7
3
  maxStalls: 3,
@@ -11,15 +7,11 @@ export const DEFAULT_RETRY_OPTIONS = {
11
7
  timeout: 60_000, // 1 minute
12
8
  priority: 100,
13
9
  };
14
-
15
- /** @type {Required<JobUpdateOptions>} */
16
10
  export const DEFAULT_UPDATE_OPTIONS = {
17
11
  updateData: true,
18
12
  updateRunAt: true,
19
13
  resetCounts: false,
20
14
  };
21
-
22
- /** @type {Required<HandlerOptions>} */
23
15
  export const FAILJOB_RETRY_OPTIONS = {
24
16
  maxRetries: 100,
25
17
  maxStalls: 3,
@@ -29,9 +21,9 @@ export const FAILJOB_RETRY_OPTIONS = {
29
21
  timeout: 60_000,
30
22
  priority: 100,
31
23
  };
32
-
33
- export const LUA_FUNCTIONS_VERSION = '1.0';
24
+ export const LUA_FUNCTIONS_VERSION = '1.0.1';
34
25
  export const HEARTBEAT_INTERVAL = 5000; // 5 seconds
35
26
  export const HEARTBEAT_TIMEOUT = 10000; // 10 seconds
36
27
  export const WORKER_CAPACITY = 10;
37
28
  export const DEQUEUE_INTERVAL = 100; // ms
29
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAA6B;IAC3D,UAAU,EAAE,EAAE;IACd,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,OAAO,EAAE,YAAY;IACjC,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,MAAM,EAAE,WAAW;IAC5B,QAAQ,EAAE,GAAG;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IAC9D,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,KAAK;CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAA6B;IAC3D,UAAU,EAAE,GAAG;IACf,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,OAAO,EAAE,aAAa;IAClC,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,GAAG;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,CAAC,YAAY;AACpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,CAAC,aAAa;AACrD,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC,CAAC,KAAK"}
@@ -0,0 +1,7 @@
1
+ export declare class PermanentError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class StallError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI9B;AAED,qBAAa,UAAW,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI9B"}
@@ -1,25 +1,13 @@
1
- /**
2
- * Error thrown to indicate a job should not be retried
3
- */
4
1
  export class PermanentError extends Error {
5
- /**
6
- * @param {string} message - Error message
7
- */
8
2
  constructor(message) {
9
3
  super(message);
10
4
  this.name = 'PermanentError';
11
5
  }
12
6
  }
13
-
14
- /**
15
- * Error indicating a job stalled (worker stopped sending heartbeats)
16
- */
17
7
  export class StallError extends Error {
18
- /**
19
- * @param {string} message - Error message
20
- */
21
8
  constructor(message) {
22
9
  super(message);
23
10
  this.name = 'StallError';
24
11
  }
25
12
  }
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IACjC,CAAC;CACJ;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IACjC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC7B,CAAC;CACJ"}
@@ -0,0 +1,3 @@
1
+ export { Client } from './client.ts';
2
+ export { PermanentError, StallError } from './errors.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Client } from "./client.js";
2
+ export { PermanentError, StallError } from "./errors.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Pool } from './pool.ts';
2
+ import type { Queue } from './queue.ts';
3
+ interface QueueEntry {
4
+ queue: Queue;
5
+ lastDequeuedAt: number;
6
+ isBusy: boolean;
7
+ }
8
+ export declare class Manager {
9
+ pool: Pool;
10
+ queues: QueueEntry[];
11
+ timer: NodeJS.Timeout | null;
12
+ busyCount: number;
13
+ constructor(pool: Pool);
14
+ addQueue(queue: Queue): void;
15
+ next(): Promise<void>;
16
+ close(): void;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,UAAU,UAAU;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,OAAO;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;gBAEN,IAAI,EAAE,IAAI;IAOtB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAMtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3B,KAAK,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,67 @@
1
+ import { DEQUEUE_INTERVAL } from "./constants.js";
2
+ export class Manager {
3
+ pool;
4
+ queues;
5
+ timer;
6
+ busyCount;
7
+ constructor(pool) {
8
+ this.pool = pool;
9
+ this.queues = [];
10
+ this.timer = null;
11
+ this.busyCount = 0;
12
+ }
13
+ addQueue(queue) {
14
+ this.queues.unshift({ queue, lastDequeuedAt: 0, isBusy: false });
15
+ this.busyCount += 1;
16
+ this.next();
17
+ }
18
+ async next() {
19
+ const entry = this.queues.shift();
20
+ if (!entry)
21
+ return;
22
+ if (this.timer) {
23
+ clearTimeout(this.timer);
24
+ this.timer = null;
25
+ }
26
+ const size = entry.queue.handlerOptions.size;
27
+ if (this.pool.capacity < size)
28
+ return;
29
+ const batchSize = Math.max(1, Math.floor(this.pool.capacity / this.busyCount / size));
30
+ entry.lastDequeuedAt = Date.now();
31
+ const { count } = await entry.queue.dequeue(batchSize);
32
+ const nowBusy = count >= batchSize;
33
+ this.busyCount += Number(nowBusy) - Number(entry.isBusy);
34
+ entry.isBusy = nowBusy;
35
+ this.queues.push(entry);
36
+ this.queues.sort(compareQueueEntries);
37
+ if (!this.timer && this.queues.length) {
38
+ const { isBusy, lastDequeuedAt } = this.queues[0];
39
+ const delay = isBusy ? 0 : Math.max(0, lastDequeuedAt - Date.now() + DEQUEUE_INTERVAL);
40
+ this.timer = setTimeout(() => this.next(), delay);
41
+ }
42
+ }
43
+ close() {
44
+ if (this.timer)
45
+ clearTimeout(this.timer);
46
+ }
47
+ }
48
+ function compareQueueEntries(a, b) {
49
+ if (a.isBusy > b.isBusy)
50
+ return -1;
51
+ if (a.isBusy < b.isBusy)
52
+ return 1;
53
+ if (a.queue.handlerOptions.priority > b.queue.handlerOptions.priority)
54
+ return 1;
55
+ if (a.queue.handlerOptions.priority < b.queue.handlerOptions.priority)
56
+ return -1;
57
+ if (a.lastDequeuedAt > b.lastDequeuedAt)
58
+ return -1;
59
+ if (a.lastDequeuedAt < b.lastDequeuedAt)
60
+ return 1;
61
+ if (a.queue.handlerOptions.size > b.queue.handlerOptions.size)
62
+ return 1;
63
+ if (a.queue.handlerOptions.size < b.queue.handlerOptions.size)
64
+ return -1;
65
+ return 0;
66
+ }
67
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
package/dist/pool.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { Worker } from 'node:worker_threads';
2
+ import type { DoneMessage, Job } from './types.ts';
3
+ interface WorkerEntry {
4
+ worker: Worker;
5
+ capacity: number;
6
+ id: string;
7
+ jobCount: number;
8
+ stalledJobs: Set<string>;
9
+ }
10
+ interface JobEntry {
11
+ resolve: (value: DoneMessage) => void;
12
+ reject: (reason: DoneMessage) => void;
13
+ size: number;
14
+ timer: NodeJS.Timeout;
15
+ }
16
+ export declare class Pool {
17
+ workers: Set<WorkerEntry>;
18
+ activeJobs: Map<string, JobEntry>;
19
+ capacity: number;
20
+ constructor(targetCount?: number | null);
21
+ createWorker(): void;
22
+ handleWorkerMessage(workerEntry: WorkerEntry, message: DoneMessage): void;
23
+ handleTimeout(workerEntry: WorkerEntry, jobId: string): void;
24
+ terminateIfEmpty({ stalledJobs, jobCount, worker }: WorkerEntry): Promise<void>;
25
+ process(handlerPath: string, job: Job, size: number, timeout: number): Promise<DoneMessage>;
26
+ close(): Promise<void>;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAGnD,UAAU,WAAW;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED,UAAU,QAAQ;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC;CACzB;AAED,qBAAa,IAAI;IACb,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;gBAEL,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IASvC,YAAY,IAAI,IAAI;IAgBpB,mBAAmB,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAoBzE,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAStD,gBAAgB,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAerF,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAmBrF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAc/B"}