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.
- package/.github/workflows/check.yml +3 -0
- package/.github/workflows/publish.yml +3 -0
- package/CLAUDE.md +5 -4
- package/Readme.md +9 -4
- package/biome.json +5 -1
- package/dist/client.d.ts +33 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +199 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/{src → dist}/constants.js +2 -10
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/{src → dist}/errors.js +1 -13
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +19 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +67 -0
- package/dist/manager.js.map +1 -0
- package/dist/pool.d.ts +29 -0
- package/dist/pool.d.ts.map +1 -0
- package/{src → dist}/pool.js +23 -82
- package/dist/pool.js.map +1 -0
- package/dist/queasy.lua +390 -0
- package/dist/queue.d.ts +22 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +81 -0
- package/dist/queue.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +24 -0
- package/dist/utils.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +42 -0
- package/dist/worker.js.map +1 -0
- package/docker-compose.yml +0 -2
- package/fuzztest/Readme.md +185 -0
- package/fuzztest/fuzz.ts +356 -0
- package/fuzztest/handlers/cascade-a.ts +90 -0
- package/fuzztest/handlers/cascade-b.ts +71 -0
- package/fuzztest/handlers/fail-handler.ts +47 -0
- package/fuzztest/handlers/periodic.ts +89 -0
- package/fuzztest/process.ts +100 -0
- package/fuzztest/shared/chaos.ts +29 -0
- package/fuzztest/shared/stream.ts +40 -0
- package/package.json +8 -7
- package/plans/redis-options.md +279 -0
- package/src/client.ts +246 -0
- package/src/constants.ts +33 -0
- package/src/errors.ts +13 -0
- package/src/index.ts +2 -0
- package/src/manager.ts +78 -0
- package/src/pool.ts +129 -0
- package/src/queasy.lua +2 -3
- package/src/queue.ts +108 -0
- package/src/types.ts +16 -0
- package/src/{utils.js → utils.ts} +3 -20
- package/src/{worker.js → worker.ts} +5 -12
- package/test/{client.test.js → client.test.ts} +6 -7
- package/test/{errors.test.js → errors.test.ts} +1 -1
- package/test/fixtures/always-fail-handler.ts +5 -0
- package/test/fixtures/data-logger-handler.ts +11 -0
- package/test/fixtures/failure-handler.ts +6 -0
- package/test/fixtures/permanent-error-handler.ts +6 -0
- package/test/fixtures/slow-handler.ts +6 -0
- package/test/fixtures/success-handler.js +0 -5
- package/test/fixtures/success-handler.ts +6 -0
- package/test/fixtures/with-failure-handler.ts +5 -0
- package/test/{guards.test.js → guards.test.ts} +21 -34
- package/test/{manager.test.js → manager.test.ts} +26 -34
- package/test/{pool.test.js → pool.test.ts} +14 -16
- package/test/{queue.test.js → queue.test.ts} +21 -21
- package/test/{redis-functions.test.js → redis-functions.test.ts} +14 -20
- package/test/{utils.test.js → utils.test.ts} +1 -1
- package/tsconfig.json +20 -0
- package/jsconfig.json +0 -17
- package/src/client.js +0 -258
- package/src/index.js +0 -2
- package/src/manager.js +0 -94
- package/src/queue.js +0 -154
- package/test/fixtures/always-fail-handler.js +0 -8
- package/test/fixtures/data-logger-handler.js +0 -19
- package/test/fixtures/failure-handler.js +0 -9
- package/test/fixtures/permanent-error-handler.js +0 -10
- package/test/fixtures/slow-handler.js +0 -9
- package/test/fixtures/with-failure-handler.js +0 -8
- /package/test/fixtures/{no-handle-handler.js → no-handle-handler.ts} +0 -0
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
|
|
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
|
-
- **
|
|
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
|
-
-
|
|
81
|
-
- Biome for formatting (
|
|
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
|
|
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
|
-
### `
|
|
59
|
-
Returns a Queasy client.
|
|
60
|
-
- `
|
|
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
|
},
|
package/dist/client.d.ts
ADDED
|
@@ -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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -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"}
|
package/{src → dist}/errors.js
RENAMED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/dist/manager.js
ADDED
|
@@ -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"}
|