dflockd-client 1.7.2 → 1.8.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/README.md +2 -0
- package/dist/client.cjs +91 -25
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +29 -9
- package/dist/client.d.ts +29 -9
- package/dist/client.js +91 -25
- package/dist/client.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -112,6 +112,7 @@ without blocking. If the lock is contended, `enqueue()` returns `"queued"` and
|
|
|
112
112
|
| `renewRatio` | `number` | `0.5` | Renew at `lease * ratio` seconds (e.g. 50% of TTL)|
|
|
113
113
|
| `tls` | `tls.ConnectionOptions` | `undefined` | TLS options; pass `{}` for default system CA |
|
|
114
114
|
| `auth` | `string` | `undefined` | Auth token for servers started with `--auth-token` |
|
|
115
|
+
| `onLockLost` | `(key: string, token: string) => void` | `undefined` | Called when background lease renewal fails and the lock is lost |
|
|
115
116
|
|
|
116
117
|
### Multi-server sharding
|
|
117
118
|
|
|
@@ -240,6 +241,7 @@ try {
|
|
|
240
241
|
| `renewRatio` | `number` | `0.5` | Renew at `lease * ratio` seconds (e.g. 50% of TTL)|
|
|
241
242
|
| `tls` | `tls.ConnectionOptions` | `undefined` | TLS options; pass `{}` for default system CA |
|
|
242
243
|
| `auth` | `string` | `undefined` | Auth token for servers started with `--auth-token` |
|
|
244
|
+
| `onLockLost` | `(key: string, token: string) => void` | `undefined` | Called when background lease renewal fails and the slot is lost |
|
|
243
245
|
|
|
244
246
|
## Stats
|
|
245
247
|
|
package/dist/client.cjs
CHANGED
|
@@ -53,18 +53,18 @@ var net = __toESM(require("net"), 1);
|
|
|
53
53
|
var tls = __toESM(require("tls"), 1);
|
|
54
54
|
var DEFAULT_HOST = "127.0.0.1";
|
|
55
55
|
var DEFAULT_PORT = 6388;
|
|
56
|
-
var AcquireTimeoutError = class extends Error {
|
|
57
|
-
constructor(key) {
|
|
58
|
-
super(`timeout acquiring '${key}'`);
|
|
59
|
-
this.name = "AcquireTimeoutError";
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
56
|
var LockError = class extends Error {
|
|
63
57
|
constructor(message) {
|
|
64
58
|
super(message);
|
|
65
59
|
this.name = "LockError";
|
|
66
60
|
}
|
|
67
61
|
};
|
|
62
|
+
var AcquireTimeoutError = class extends LockError {
|
|
63
|
+
constructor(key) {
|
|
64
|
+
super(`timeout acquiring '${key}'`);
|
|
65
|
+
this.name = "AcquireTimeoutError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
68
|
var AuthError = class extends LockError {
|
|
69
69
|
constructor() {
|
|
70
70
|
super("authentication failed");
|
|
@@ -74,15 +74,25 @@ var AuthError = class extends LockError {
|
|
|
74
74
|
function encodeLines(...lines) {
|
|
75
75
|
return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
|
|
76
76
|
}
|
|
77
|
+
var _readlineBuf = /* @__PURE__ */ new WeakMap();
|
|
77
78
|
function readline(sock) {
|
|
78
79
|
return new Promise((resolve, reject) => {
|
|
79
|
-
let buf = "";
|
|
80
|
+
let buf = _readlineBuf.get(sock) ?? "";
|
|
81
|
+
const existing = buf.indexOf("\n");
|
|
82
|
+
if (existing !== -1) {
|
|
83
|
+
const line = buf.slice(0, existing).replace(/\r$/, "");
|
|
84
|
+
_readlineBuf.set(sock, buf.slice(existing + 1));
|
|
85
|
+
resolve(line);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
80
88
|
const onData = (chunk) => {
|
|
81
89
|
buf += chunk.toString("utf-8");
|
|
82
90
|
const idx = buf.indexOf("\n");
|
|
83
91
|
if (idx !== -1) {
|
|
84
92
|
cleanup();
|
|
85
|
-
|
|
93
|
+
const line = buf.slice(0, idx).replace(/\r$/, "");
|
|
94
|
+
_readlineBuf.set(sock, buf.slice(idx + 1));
|
|
95
|
+
resolve(line);
|
|
86
96
|
}
|
|
87
97
|
};
|
|
88
98
|
const onError = (err) => {
|
|
@@ -106,14 +116,22 @@ function readline(sock) {
|
|
|
106
116
|
async function connect2(host, port, tlsOptions, auth) {
|
|
107
117
|
const sock = await new Promise((resolve, reject) => {
|
|
108
118
|
if (tlsOptions) {
|
|
109
|
-
const s = tls.connect({ host, port, ...tlsOptions }, () =>
|
|
119
|
+
const s = tls.connect({ host, port, ...tlsOptions }, () => {
|
|
120
|
+
s.removeListener("error", reject);
|
|
121
|
+
resolve(s);
|
|
122
|
+
});
|
|
110
123
|
s.on("error", reject);
|
|
111
124
|
} else {
|
|
112
|
-
const s = net.createConnection({ host, port }, () =>
|
|
125
|
+
const s = net.createConnection({ host, port }, () => {
|
|
126
|
+
s.removeListener("error", reject);
|
|
127
|
+
resolve(s);
|
|
128
|
+
});
|
|
113
129
|
s.on("error", reject);
|
|
114
130
|
}
|
|
115
131
|
});
|
|
116
|
-
|
|
132
|
+
sock.on("error", () => {
|
|
133
|
+
});
|
|
134
|
+
if (auth != null && auth !== "") {
|
|
117
135
|
sock.write(encodeLines("auth", "_", auth));
|
|
118
136
|
const resp = await readline(sock);
|
|
119
137
|
if (resp === "ok") {
|
|
@@ -174,7 +192,7 @@ async function renew(sock, key, token, leaseTtlS) {
|
|
|
174
192
|
if (parts.length >= 2 && /^\d+$/.test(parts[1])) {
|
|
175
193
|
return parseInt(parts[1], 10);
|
|
176
194
|
}
|
|
177
|
-
|
|
195
|
+
throw new LockError(`renew: malformed response: '${resp}'`);
|
|
178
196
|
}
|
|
179
197
|
async function enqueue(sock, key, leaseTtlS) {
|
|
180
198
|
const arg = leaseTtlS == null ? "" : String(leaseTtlS);
|
|
@@ -241,7 +259,7 @@ async function semRenew(sock, key, token, leaseTtlS) {
|
|
|
241
259
|
if (parts.length >= 2 && /^\d+$/.test(parts[1])) {
|
|
242
260
|
return parseInt(parts[1], 10);
|
|
243
261
|
}
|
|
244
|
-
|
|
262
|
+
throw new LockError(`sem_renew: malformed response: '${resp}'`);
|
|
245
263
|
}
|
|
246
264
|
async function semEnqueue(sock, key, limit, leaseTtlS) {
|
|
247
265
|
const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
|
|
@@ -307,6 +325,7 @@ var DistributedLock = class {
|
|
|
307
325
|
renewRatio;
|
|
308
326
|
tls;
|
|
309
327
|
auth;
|
|
328
|
+
onLockLost;
|
|
310
329
|
token = null;
|
|
311
330
|
lease = 0;
|
|
312
331
|
sock = null;
|
|
@@ -318,6 +337,7 @@ var DistributedLock = class {
|
|
|
318
337
|
this.leaseTtlS = opts.leaseTtlS;
|
|
319
338
|
this.tls = opts.tls;
|
|
320
339
|
this.auth = opts.auth;
|
|
340
|
+
this.onLockLost = opts.onLockLost;
|
|
321
341
|
if (opts.servers) {
|
|
322
342
|
if (opts.servers.length === 0) {
|
|
323
343
|
throw new LockError("servers list must not be empty");
|
|
@@ -331,10 +351,24 @@ var DistributedLock = class {
|
|
|
331
351
|
}
|
|
332
352
|
pickServer() {
|
|
333
353
|
const idx = this.shardingStrategy(this.key, this.servers.length);
|
|
354
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {
|
|
355
|
+
throw new LockError(
|
|
356
|
+
`shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
334
359
|
return this.servers[idx];
|
|
335
360
|
}
|
|
336
|
-
/**
|
|
337
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Acquire the lock. Returns `true` on success, `false` on timeout.
|
|
363
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
364
|
+
*/
|
|
365
|
+
async acquire(opts) {
|
|
366
|
+
if (this.sock && !this.closed) {
|
|
367
|
+
if (!opts?.force) {
|
|
368
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
369
|
+
}
|
|
370
|
+
await this.close();
|
|
371
|
+
}
|
|
338
372
|
this.closed = false;
|
|
339
373
|
const [host, port] = this.pickServer();
|
|
340
374
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -370,8 +404,15 @@ var DistributedLock = class {
|
|
|
370
404
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
371
405
|
* Returns `"acquired"` (fast-path, lock is already held) or `"queued"`.
|
|
372
406
|
* If acquired immediately, the renew loop starts automatically.
|
|
407
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
373
408
|
*/
|
|
374
|
-
async enqueue() {
|
|
409
|
+
async enqueue(opts) {
|
|
410
|
+
if (this.sock && !this.closed) {
|
|
411
|
+
if (!opts?.force) {
|
|
412
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
413
|
+
}
|
|
414
|
+
await this.close();
|
|
415
|
+
}
|
|
375
416
|
this.closed = false;
|
|
376
417
|
const [host, port] = this.pickServer();
|
|
377
418
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -453,10 +494,11 @@ var DistributedLock = class {
|
|
|
453
494
|
try {
|
|
454
495
|
await renew(this.sock, this.key, this.token, this.leaseTtlS);
|
|
455
496
|
} catch {
|
|
456
|
-
|
|
457
|
-
`lock lost (renew failed): key=${this.key} token=${this.token}`
|
|
458
|
-
);
|
|
497
|
+
const lostToken = this.token;
|
|
459
498
|
this.token = null;
|
|
499
|
+
if (this.onLockLost) {
|
|
500
|
+
this.onLockLost(this.key, lostToken);
|
|
501
|
+
}
|
|
460
502
|
return;
|
|
461
503
|
}
|
|
462
504
|
this.renewTimer = setTimeout(loop, interval);
|
|
@@ -480,6 +522,7 @@ var DistributedSemaphore = class {
|
|
|
480
522
|
renewRatio;
|
|
481
523
|
tls;
|
|
482
524
|
auth;
|
|
525
|
+
onLockLost;
|
|
483
526
|
token = null;
|
|
484
527
|
lease = 0;
|
|
485
528
|
sock = null;
|
|
@@ -492,6 +535,7 @@ var DistributedSemaphore = class {
|
|
|
492
535
|
this.leaseTtlS = opts.leaseTtlS;
|
|
493
536
|
this.tls = opts.tls;
|
|
494
537
|
this.auth = opts.auth;
|
|
538
|
+
this.onLockLost = opts.onLockLost;
|
|
495
539
|
if (opts.servers) {
|
|
496
540
|
if (opts.servers.length === 0) {
|
|
497
541
|
throw new LockError("servers list must not be empty");
|
|
@@ -505,10 +549,24 @@ var DistributedSemaphore = class {
|
|
|
505
549
|
}
|
|
506
550
|
pickServer() {
|
|
507
551
|
const idx = this.shardingStrategy(this.key, this.servers.length);
|
|
552
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {
|
|
553
|
+
throw new LockError(
|
|
554
|
+
`shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
508
557
|
return this.servers[idx];
|
|
509
558
|
}
|
|
510
|
-
/**
|
|
511
|
-
|
|
559
|
+
/**
|
|
560
|
+
* Acquire a semaphore slot. Returns `true` on success, `false` on timeout.
|
|
561
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
562
|
+
*/
|
|
563
|
+
async acquire(opts) {
|
|
564
|
+
if (this.sock && !this.closed) {
|
|
565
|
+
if (!opts?.force) {
|
|
566
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
567
|
+
}
|
|
568
|
+
await this.close();
|
|
569
|
+
}
|
|
512
570
|
this.closed = false;
|
|
513
571
|
const [host, port] = this.pickServer();
|
|
514
572
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -545,8 +603,15 @@ var DistributedSemaphore = class {
|
|
|
545
603
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
546
604
|
* Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
|
|
547
605
|
* If acquired immediately, the renew loop starts automatically.
|
|
606
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
548
607
|
*/
|
|
549
|
-
async enqueue() {
|
|
608
|
+
async enqueue(opts) {
|
|
609
|
+
if (this.sock && !this.closed) {
|
|
610
|
+
if (!opts?.force) {
|
|
611
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
612
|
+
}
|
|
613
|
+
await this.close();
|
|
614
|
+
}
|
|
550
615
|
this.closed = false;
|
|
551
616
|
const [host, port] = this.pickServer();
|
|
552
617
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -628,10 +693,11 @@ var DistributedSemaphore = class {
|
|
|
628
693
|
try {
|
|
629
694
|
await semRenew(this.sock, this.key, this.token, this.leaseTtlS);
|
|
630
695
|
} catch {
|
|
631
|
-
|
|
632
|
-
`semaphore lost (renew failed): key=${this.key} token=${this.token}`
|
|
633
|
-
);
|
|
696
|
+
const lostToken = this.token;
|
|
634
697
|
this.token = null;
|
|
698
|
+
if (this.onLockLost) {
|
|
699
|
+
this.onLockLost(this.key, lostToken);
|
|
700
|
+
}
|
|
635
701
|
return;
|
|
636
702
|
}
|
|
637
703
|
this.renewTimer = setTimeout(loop, interval);
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => resolve(s));\n s.on(\"error\", reject);\n }\n });\n\n if (auth != null) {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n throw new LockError(`renew: malformed response: '${resp}'`);\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n throw new LockError(`sem_renew: malformed response: '${resp}'`);\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n onLockLost?: (key: string, token: string) => void;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n readonly onLockLost: ((key: string, token: string) => void) | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n this.onLockLost = opts.onLockLost;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n onLockLost?: (key: string, token: string) => void;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n readonly onLockLost: ((key: string, token: string) => void) | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n this.onLockLost = opts.onLockLost;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
|
package/dist/client.d.cts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
2
|
import * as tls from 'tls';
|
|
3
3
|
|
|
4
|
-
declare class AcquireTimeoutError extends Error {
|
|
5
|
-
constructor(key: string);
|
|
6
|
-
}
|
|
7
4
|
declare class LockError extends Error {
|
|
8
5
|
constructor(message: string);
|
|
9
6
|
}
|
|
7
|
+
declare class AcquireTimeoutError extends LockError {
|
|
8
|
+
constructor(key: string);
|
|
9
|
+
}
|
|
10
10
|
declare class AuthError extends LockError {
|
|
11
11
|
constructor();
|
|
12
12
|
}
|
|
@@ -99,6 +99,7 @@ interface DistributedLockOptions {
|
|
|
99
99
|
renewRatio?: number;
|
|
100
100
|
tls?: tls.ConnectionOptions;
|
|
101
101
|
auth?: string;
|
|
102
|
+
onLockLost?: (key: string, token: string) => void;
|
|
102
103
|
}
|
|
103
104
|
declare class DistributedLock {
|
|
104
105
|
readonly key: string;
|
|
@@ -109,6 +110,7 @@ declare class DistributedLock {
|
|
|
109
110
|
readonly renewRatio: number;
|
|
110
111
|
readonly tls: tls.ConnectionOptions | undefined;
|
|
111
112
|
readonly auth: string | undefined;
|
|
113
|
+
readonly onLockLost: ((key: string, token: string) => void) | undefined;
|
|
112
114
|
token: string | null;
|
|
113
115
|
lease: number;
|
|
114
116
|
private sock;
|
|
@@ -116,16 +118,24 @@ declare class DistributedLock {
|
|
|
116
118
|
private closed;
|
|
117
119
|
constructor(opts: DistributedLockOptions);
|
|
118
120
|
private pickServer;
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Acquire the lock. Returns `true` on success, `false` on timeout.
|
|
123
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
124
|
+
*/
|
|
125
|
+
acquire(opts?: {
|
|
126
|
+
force?: boolean;
|
|
127
|
+
}): Promise<boolean>;
|
|
121
128
|
/** Release the lock and close the connection. */
|
|
122
129
|
release(): Promise<void>;
|
|
123
130
|
/**
|
|
124
131
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
125
132
|
* Returns `"acquired"` (fast-path, lock is already held) or `"queued"`.
|
|
126
133
|
* If acquired immediately, the renew loop starts automatically.
|
|
134
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
127
135
|
*/
|
|
128
|
-
enqueue(
|
|
136
|
+
enqueue(opts?: {
|
|
137
|
+
force?: boolean;
|
|
138
|
+
}): Promise<"acquired" | "queued">;
|
|
129
139
|
/**
|
|
130
140
|
* Two-phase step 2: block until the lock is granted.
|
|
131
141
|
* Returns `true` if granted, `false` on timeout.
|
|
@@ -162,6 +172,7 @@ interface DistributedSemaphoreOptions {
|
|
|
162
172
|
renewRatio?: number;
|
|
163
173
|
tls?: tls.ConnectionOptions;
|
|
164
174
|
auth?: string;
|
|
175
|
+
onLockLost?: (key: string, token: string) => void;
|
|
165
176
|
}
|
|
166
177
|
declare class DistributedSemaphore {
|
|
167
178
|
readonly key: string;
|
|
@@ -173,6 +184,7 @@ declare class DistributedSemaphore {
|
|
|
173
184
|
readonly renewRatio: number;
|
|
174
185
|
readonly tls: tls.ConnectionOptions | undefined;
|
|
175
186
|
readonly auth: string | undefined;
|
|
187
|
+
readonly onLockLost: ((key: string, token: string) => void) | undefined;
|
|
176
188
|
token: string | null;
|
|
177
189
|
lease: number;
|
|
178
190
|
private sock;
|
|
@@ -180,16 +192,24 @@ declare class DistributedSemaphore {
|
|
|
180
192
|
private closed;
|
|
181
193
|
constructor(opts: DistributedSemaphoreOptions);
|
|
182
194
|
private pickServer;
|
|
183
|
-
/**
|
|
184
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Acquire a semaphore slot. Returns `true` on success, `false` on timeout.
|
|
197
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
198
|
+
*/
|
|
199
|
+
acquire(opts?: {
|
|
200
|
+
force?: boolean;
|
|
201
|
+
}): Promise<boolean>;
|
|
185
202
|
/** Release the semaphore slot and close the connection. */
|
|
186
203
|
release(): Promise<void>;
|
|
187
204
|
/**
|
|
188
205
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
189
206
|
* Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
|
|
190
207
|
* If acquired immediately, the renew loop starts automatically.
|
|
208
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
191
209
|
*/
|
|
192
|
-
enqueue(
|
|
210
|
+
enqueue(opts?: {
|
|
211
|
+
force?: boolean;
|
|
212
|
+
}): Promise<"acquired" | "queued">;
|
|
193
213
|
/**
|
|
194
214
|
* Two-phase step 2: block until a semaphore slot is granted.
|
|
195
215
|
* Returns `true` if granted, `false` on timeout.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
2
|
import * as tls from 'tls';
|
|
3
3
|
|
|
4
|
-
declare class AcquireTimeoutError extends Error {
|
|
5
|
-
constructor(key: string);
|
|
6
|
-
}
|
|
7
4
|
declare class LockError extends Error {
|
|
8
5
|
constructor(message: string);
|
|
9
6
|
}
|
|
7
|
+
declare class AcquireTimeoutError extends LockError {
|
|
8
|
+
constructor(key: string);
|
|
9
|
+
}
|
|
10
10
|
declare class AuthError extends LockError {
|
|
11
11
|
constructor();
|
|
12
12
|
}
|
|
@@ -99,6 +99,7 @@ interface DistributedLockOptions {
|
|
|
99
99
|
renewRatio?: number;
|
|
100
100
|
tls?: tls.ConnectionOptions;
|
|
101
101
|
auth?: string;
|
|
102
|
+
onLockLost?: (key: string, token: string) => void;
|
|
102
103
|
}
|
|
103
104
|
declare class DistributedLock {
|
|
104
105
|
readonly key: string;
|
|
@@ -109,6 +110,7 @@ declare class DistributedLock {
|
|
|
109
110
|
readonly renewRatio: number;
|
|
110
111
|
readonly tls: tls.ConnectionOptions | undefined;
|
|
111
112
|
readonly auth: string | undefined;
|
|
113
|
+
readonly onLockLost: ((key: string, token: string) => void) | undefined;
|
|
112
114
|
token: string | null;
|
|
113
115
|
lease: number;
|
|
114
116
|
private sock;
|
|
@@ -116,16 +118,24 @@ declare class DistributedLock {
|
|
|
116
118
|
private closed;
|
|
117
119
|
constructor(opts: DistributedLockOptions);
|
|
118
120
|
private pickServer;
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Acquire the lock. Returns `true` on success, `false` on timeout.
|
|
123
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
124
|
+
*/
|
|
125
|
+
acquire(opts?: {
|
|
126
|
+
force?: boolean;
|
|
127
|
+
}): Promise<boolean>;
|
|
121
128
|
/** Release the lock and close the connection. */
|
|
122
129
|
release(): Promise<void>;
|
|
123
130
|
/**
|
|
124
131
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
125
132
|
* Returns `"acquired"` (fast-path, lock is already held) or `"queued"`.
|
|
126
133
|
* If acquired immediately, the renew loop starts automatically.
|
|
134
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
127
135
|
*/
|
|
128
|
-
enqueue(
|
|
136
|
+
enqueue(opts?: {
|
|
137
|
+
force?: boolean;
|
|
138
|
+
}): Promise<"acquired" | "queued">;
|
|
129
139
|
/**
|
|
130
140
|
* Two-phase step 2: block until the lock is granted.
|
|
131
141
|
* Returns `true` if granted, `false` on timeout.
|
|
@@ -162,6 +172,7 @@ interface DistributedSemaphoreOptions {
|
|
|
162
172
|
renewRatio?: number;
|
|
163
173
|
tls?: tls.ConnectionOptions;
|
|
164
174
|
auth?: string;
|
|
175
|
+
onLockLost?: (key: string, token: string) => void;
|
|
165
176
|
}
|
|
166
177
|
declare class DistributedSemaphore {
|
|
167
178
|
readonly key: string;
|
|
@@ -173,6 +184,7 @@ declare class DistributedSemaphore {
|
|
|
173
184
|
readonly renewRatio: number;
|
|
174
185
|
readonly tls: tls.ConnectionOptions | undefined;
|
|
175
186
|
readonly auth: string | undefined;
|
|
187
|
+
readonly onLockLost: ((key: string, token: string) => void) | undefined;
|
|
176
188
|
token: string | null;
|
|
177
189
|
lease: number;
|
|
178
190
|
private sock;
|
|
@@ -180,16 +192,24 @@ declare class DistributedSemaphore {
|
|
|
180
192
|
private closed;
|
|
181
193
|
constructor(opts: DistributedSemaphoreOptions);
|
|
182
194
|
private pickServer;
|
|
183
|
-
/**
|
|
184
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Acquire a semaphore slot. Returns `true` on success, `false` on timeout.
|
|
197
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
198
|
+
*/
|
|
199
|
+
acquire(opts?: {
|
|
200
|
+
force?: boolean;
|
|
201
|
+
}): Promise<boolean>;
|
|
185
202
|
/** Release the semaphore slot and close the connection. */
|
|
186
203
|
release(): Promise<void>;
|
|
187
204
|
/**
|
|
188
205
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
189
206
|
* Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
|
|
190
207
|
* If acquired immediately, the renew loop starts automatically.
|
|
208
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
191
209
|
*/
|
|
192
|
-
enqueue(
|
|
210
|
+
enqueue(opts?: {
|
|
211
|
+
force?: boolean;
|
|
212
|
+
}): Promise<"acquired" | "queued">;
|
|
193
213
|
/**
|
|
194
214
|
* Two-phase step 2: block until a semaphore slot is granted.
|
|
195
215
|
* Returns `true` if granted, `false` on timeout.
|
package/dist/client.js
CHANGED
|
@@ -3,18 +3,18 @@ import * as net from "net";
|
|
|
3
3
|
import * as tls from "tls";
|
|
4
4
|
var DEFAULT_HOST = "127.0.0.1";
|
|
5
5
|
var DEFAULT_PORT = 6388;
|
|
6
|
-
var AcquireTimeoutError = class extends Error {
|
|
7
|
-
constructor(key) {
|
|
8
|
-
super(`timeout acquiring '${key}'`);
|
|
9
|
-
this.name = "AcquireTimeoutError";
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
6
|
var LockError = class extends Error {
|
|
13
7
|
constructor(message) {
|
|
14
8
|
super(message);
|
|
15
9
|
this.name = "LockError";
|
|
16
10
|
}
|
|
17
11
|
};
|
|
12
|
+
var AcquireTimeoutError = class extends LockError {
|
|
13
|
+
constructor(key) {
|
|
14
|
+
super(`timeout acquiring '${key}'`);
|
|
15
|
+
this.name = "AcquireTimeoutError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
18
|
var AuthError = class extends LockError {
|
|
19
19
|
constructor() {
|
|
20
20
|
super("authentication failed");
|
|
@@ -24,15 +24,25 @@ var AuthError = class extends LockError {
|
|
|
24
24
|
function encodeLines(...lines) {
|
|
25
25
|
return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
|
|
26
26
|
}
|
|
27
|
+
var _readlineBuf = /* @__PURE__ */ new WeakMap();
|
|
27
28
|
function readline(sock) {
|
|
28
29
|
return new Promise((resolve, reject) => {
|
|
29
|
-
let buf = "";
|
|
30
|
+
let buf = _readlineBuf.get(sock) ?? "";
|
|
31
|
+
const existing = buf.indexOf("\n");
|
|
32
|
+
if (existing !== -1) {
|
|
33
|
+
const line = buf.slice(0, existing).replace(/\r$/, "");
|
|
34
|
+
_readlineBuf.set(sock, buf.slice(existing + 1));
|
|
35
|
+
resolve(line);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
30
38
|
const onData = (chunk) => {
|
|
31
39
|
buf += chunk.toString("utf-8");
|
|
32
40
|
const idx = buf.indexOf("\n");
|
|
33
41
|
if (idx !== -1) {
|
|
34
42
|
cleanup();
|
|
35
|
-
|
|
43
|
+
const line = buf.slice(0, idx).replace(/\r$/, "");
|
|
44
|
+
_readlineBuf.set(sock, buf.slice(idx + 1));
|
|
45
|
+
resolve(line);
|
|
36
46
|
}
|
|
37
47
|
};
|
|
38
48
|
const onError = (err) => {
|
|
@@ -56,14 +66,22 @@ function readline(sock) {
|
|
|
56
66
|
async function connect2(host, port, tlsOptions, auth) {
|
|
57
67
|
const sock = await new Promise((resolve, reject) => {
|
|
58
68
|
if (tlsOptions) {
|
|
59
|
-
const s = tls.connect({ host, port, ...tlsOptions }, () =>
|
|
69
|
+
const s = tls.connect({ host, port, ...tlsOptions }, () => {
|
|
70
|
+
s.removeListener("error", reject);
|
|
71
|
+
resolve(s);
|
|
72
|
+
});
|
|
60
73
|
s.on("error", reject);
|
|
61
74
|
} else {
|
|
62
|
-
const s = net.createConnection({ host, port }, () =>
|
|
75
|
+
const s = net.createConnection({ host, port }, () => {
|
|
76
|
+
s.removeListener("error", reject);
|
|
77
|
+
resolve(s);
|
|
78
|
+
});
|
|
63
79
|
s.on("error", reject);
|
|
64
80
|
}
|
|
65
81
|
});
|
|
66
|
-
|
|
82
|
+
sock.on("error", () => {
|
|
83
|
+
});
|
|
84
|
+
if (auth != null && auth !== "") {
|
|
67
85
|
sock.write(encodeLines("auth", "_", auth));
|
|
68
86
|
const resp = await readline(sock);
|
|
69
87
|
if (resp === "ok") {
|
|
@@ -124,7 +142,7 @@ async function renew(sock, key, token, leaseTtlS) {
|
|
|
124
142
|
if (parts.length >= 2 && /^\d+$/.test(parts[1])) {
|
|
125
143
|
return parseInt(parts[1], 10);
|
|
126
144
|
}
|
|
127
|
-
|
|
145
|
+
throw new LockError(`renew: malformed response: '${resp}'`);
|
|
128
146
|
}
|
|
129
147
|
async function enqueue(sock, key, leaseTtlS) {
|
|
130
148
|
const arg = leaseTtlS == null ? "" : String(leaseTtlS);
|
|
@@ -191,7 +209,7 @@ async function semRenew(sock, key, token, leaseTtlS) {
|
|
|
191
209
|
if (parts.length >= 2 && /^\d+$/.test(parts[1])) {
|
|
192
210
|
return parseInt(parts[1], 10);
|
|
193
211
|
}
|
|
194
|
-
|
|
212
|
+
throw new LockError(`sem_renew: malformed response: '${resp}'`);
|
|
195
213
|
}
|
|
196
214
|
async function semEnqueue(sock, key, limit, leaseTtlS) {
|
|
197
215
|
const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
|
|
@@ -257,6 +275,7 @@ var DistributedLock = class {
|
|
|
257
275
|
renewRatio;
|
|
258
276
|
tls;
|
|
259
277
|
auth;
|
|
278
|
+
onLockLost;
|
|
260
279
|
token = null;
|
|
261
280
|
lease = 0;
|
|
262
281
|
sock = null;
|
|
@@ -268,6 +287,7 @@ var DistributedLock = class {
|
|
|
268
287
|
this.leaseTtlS = opts.leaseTtlS;
|
|
269
288
|
this.tls = opts.tls;
|
|
270
289
|
this.auth = opts.auth;
|
|
290
|
+
this.onLockLost = opts.onLockLost;
|
|
271
291
|
if (opts.servers) {
|
|
272
292
|
if (opts.servers.length === 0) {
|
|
273
293
|
throw new LockError("servers list must not be empty");
|
|
@@ -281,10 +301,24 @@ var DistributedLock = class {
|
|
|
281
301
|
}
|
|
282
302
|
pickServer() {
|
|
283
303
|
const idx = this.shardingStrategy(this.key, this.servers.length);
|
|
304
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {
|
|
305
|
+
throw new LockError(
|
|
306
|
+
`shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
284
309
|
return this.servers[idx];
|
|
285
310
|
}
|
|
286
|
-
/**
|
|
287
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Acquire the lock. Returns `true` on success, `false` on timeout.
|
|
313
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
314
|
+
*/
|
|
315
|
+
async acquire(opts) {
|
|
316
|
+
if (this.sock && !this.closed) {
|
|
317
|
+
if (!opts?.force) {
|
|
318
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
319
|
+
}
|
|
320
|
+
await this.close();
|
|
321
|
+
}
|
|
288
322
|
this.closed = false;
|
|
289
323
|
const [host, port] = this.pickServer();
|
|
290
324
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -320,8 +354,15 @@ var DistributedLock = class {
|
|
|
320
354
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
321
355
|
* Returns `"acquired"` (fast-path, lock is already held) or `"queued"`.
|
|
322
356
|
* If acquired immediately, the renew loop starts automatically.
|
|
357
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
323
358
|
*/
|
|
324
|
-
async enqueue() {
|
|
359
|
+
async enqueue(opts) {
|
|
360
|
+
if (this.sock && !this.closed) {
|
|
361
|
+
if (!opts?.force) {
|
|
362
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
363
|
+
}
|
|
364
|
+
await this.close();
|
|
365
|
+
}
|
|
325
366
|
this.closed = false;
|
|
326
367
|
const [host, port] = this.pickServer();
|
|
327
368
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -403,10 +444,11 @@ var DistributedLock = class {
|
|
|
403
444
|
try {
|
|
404
445
|
await renew(this.sock, this.key, this.token, this.leaseTtlS);
|
|
405
446
|
} catch {
|
|
406
|
-
|
|
407
|
-
`lock lost (renew failed): key=${this.key} token=${this.token}`
|
|
408
|
-
);
|
|
447
|
+
const lostToken = this.token;
|
|
409
448
|
this.token = null;
|
|
449
|
+
if (this.onLockLost) {
|
|
450
|
+
this.onLockLost(this.key, lostToken);
|
|
451
|
+
}
|
|
410
452
|
return;
|
|
411
453
|
}
|
|
412
454
|
this.renewTimer = setTimeout(loop, interval);
|
|
@@ -430,6 +472,7 @@ var DistributedSemaphore = class {
|
|
|
430
472
|
renewRatio;
|
|
431
473
|
tls;
|
|
432
474
|
auth;
|
|
475
|
+
onLockLost;
|
|
433
476
|
token = null;
|
|
434
477
|
lease = 0;
|
|
435
478
|
sock = null;
|
|
@@ -442,6 +485,7 @@ var DistributedSemaphore = class {
|
|
|
442
485
|
this.leaseTtlS = opts.leaseTtlS;
|
|
443
486
|
this.tls = opts.tls;
|
|
444
487
|
this.auth = opts.auth;
|
|
488
|
+
this.onLockLost = opts.onLockLost;
|
|
445
489
|
if (opts.servers) {
|
|
446
490
|
if (opts.servers.length === 0) {
|
|
447
491
|
throw new LockError("servers list must not be empty");
|
|
@@ -455,10 +499,24 @@ var DistributedSemaphore = class {
|
|
|
455
499
|
}
|
|
456
500
|
pickServer() {
|
|
457
501
|
const idx = this.shardingStrategy(this.key, this.servers.length);
|
|
502
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {
|
|
503
|
+
throw new LockError(
|
|
504
|
+
`shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
458
507
|
return this.servers[idx];
|
|
459
508
|
}
|
|
460
|
-
/**
|
|
461
|
-
|
|
509
|
+
/**
|
|
510
|
+
* Acquire a semaphore slot. Returns `true` on success, `false` on timeout.
|
|
511
|
+
* @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.
|
|
512
|
+
*/
|
|
513
|
+
async acquire(opts) {
|
|
514
|
+
if (this.sock && !this.closed) {
|
|
515
|
+
if (!opts?.force) {
|
|
516
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
517
|
+
}
|
|
518
|
+
await this.close();
|
|
519
|
+
}
|
|
462
520
|
this.closed = false;
|
|
463
521
|
const [host, port] = this.pickServer();
|
|
464
522
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -495,8 +553,15 @@ var DistributedSemaphore = class {
|
|
|
495
553
|
* Two-phase step 1: connect and join the FIFO queue.
|
|
496
554
|
* Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
|
|
497
555
|
* If acquired immediately, the renew loop starts automatically.
|
|
556
|
+
* @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.
|
|
498
557
|
*/
|
|
499
|
-
async enqueue() {
|
|
558
|
+
async enqueue(opts) {
|
|
559
|
+
if (this.sock && !this.closed) {
|
|
560
|
+
if (!opts?.force) {
|
|
561
|
+
throw new LockError("already connected; call release() or close() first, or pass { force: true }");
|
|
562
|
+
}
|
|
563
|
+
await this.close();
|
|
564
|
+
}
|
|
500
565
|
this.closed = false;
|
|
501
566
|
const [host, port] = this.pickServer();
|
|
502
567
|
this.sock = await connect2(host, port, this.tls, this.auth);
|
|
@@ -578,10 +643,11 @@ var DistributedSemaphore = class {
|
|
|
578
643
|
try {
|
|
579
644
|
await semRenew(this.sock, this.key, this.token, this.leaseTtlS);
|
|
580
645
|
} catch {
|
|
581
|
-
|
|
582
|
-
`semaphore lost (renew failed): key=${this.key} token=${this.token}`
|
|
583
|
-
);
|
|
646
|
+
const lostToken = this.token;
|
|
584
647
|
this.token = null;
|
|
648
|
+
if (this.onLockLost) {
|
|
649
|
+
this.onLockLost(this.key, lostToken);
|
|
650
|
+
}
|
|
585
651
|
return;
|
|
586
652
|
}
|
|
587
653
|
this.renewTimer = setTimeout(loop, interval);
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => resolve(s));\n s.on(\"error\", reject);\n }\n });\n\n if (auth != null) {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n throw new LockError(`renew: malformed response: '${resp}'`);\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n throw new LockError(`sem_renew: malformed response: '${resp}'`);\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n onLockLost?: (key: string, token: string) => void;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n readonly onLockLost: ((key: string, token: string) => void) | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n this.onLockLost = opts.onLockLost;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n onLockLost?: (key: string, token: string) => void;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n readonly onLockLost: ((key: string, token: string) => void) | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n this.onLockLost = opts.onLockLost;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dflockd-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "TypeScript client for the dflockd distributed lock daemon",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/mtingers/dflockd-client-ts.git"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^
|
|
47
|
+
"@types/node": "^25.3.0",
|
|
48
48
|
"tsup": "^8.0.0",
|
|
49
49
|
"typescript": "^5.7.0"
|
|
50
50
|
}
|