dflockd-client 1.2.0 → 1.4.0
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 +29 -0
- package/dist/client.cjs +21 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +43 -1
- package/dist/client.d.ts +43 -1
- package/dist/client.js +20 -0
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -235,6 +235,34 @@ try {
|
|
|
235
235
|
| `port` | `number` | `6388` | Server port *(deprecated — use `servers`)* |
|
|
236
236
|
| `renewRatio` | `number` | `0.5` | Renew at `lease * ratio` seconds (e.g. 50% of TTL)|
|
|
237
237
|
|
|
238
|
+
## Stats
|
|
239
|
+
|
|
240
|
+
Query server runtime statistics (active connections, held locks, semaphores,
|
|
241
|
+
idle entries).
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
import { stats } from "dflockd-client";
|
|
245
|
+
|
|
246
|
+
const s = await stats();
|
|
247
|
+
console.log(`connections: ${s.connections}`);
|
|
248
|
+
console.log(`locks held: ${s.locks.length}`);
|
|
249
|
+
console.log(`semaphores active: ${s.semaphores.length}`);
|
|
250
|
+
|
|
251
|
+
for (const lock of s.locks) {
|
|
252
|
+
console.log(` ${lock.key} owner=${lock.owner_conn_id} expires_in=${lock.lease_expires_in_s}s waiters=${lock.waiters}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
for (const sem of s.semaphores) {
|
|
256
|
+
console.log(` ${sem.key} holders=${sem.holders}/${sem.limit} waiters=${sem.waiters}`);
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Pass `{ host, port }` to query a specific server:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const s = await stats({ host: "10.0.0.1", port: 6388 });
|
|
264
|
+
```
|
|
265
|
+
|
|
238
266
|
### Error handling
|
|
239
267
|
|
|
240
268
|
```ts
|
|
@@ -260,6 +288,7 @@ import * as net from "net";
|
|
|
260
288
|
import {
|
|
261
289
|
acquire, enqueue, waitForLock, renew, release,
|
|
262
290
|
semAcquire, semEnqueue, semWaitForLock, semRenew, semRelease,
|
|
291
|
+
stats,
|
|
263
292
|
} from "dflockd-client";
|
|
264
293
|
|
|
265
294
|
const sock = net.createConnection({ host: "127.0.0.1", port: 6388 });
|
package/dist/client.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(client_exports, {
|
|
|
44
44
|
semRenew: () => semRenew,
|
|
45
45
|
semWaitForLock: () => semWaitForLock,
|
|
46
46
|
stableHashShard: () => stableHashShard,
|
|
47
|
+
stats: () => stats,
|
|
47
48
|
waitForLock: () => waitForLock
|
|
48
49
|
});
|
|
49
50
|
module.exports = __toCommonJS(client_exports);
|
|
@@ -252,6 +253,25 @@ async function semRelease(sock, key, token) {
|
|
|
252
253
|
throw new LockError(`sem_release failed: '${resp}'`);
|
|
253
254
|
}
|
|
254
255
|
}
|
|
256
|
+
async function statsProto(sock) {
|
|
257
|
+
sock.write(encodeLines("stats", "_", ""));
|
|
258
|
+
const resp = await readline(sock);
|
|
259
|
+
if (!resp.startsWith("ok ")) {
|
|
260
|
+
throw new LockError(`stats failed: '${resp}'`);
|
|
261
|
+
}
|
|
262
|
+
const json = resp.slice(3);
|
|
263
|
+
return JSON.parse(json);
|
|
264
|
+
}
|
|
265
|
+
async function stats(options) {
|
|
266
|
+
const host = options?.host ?? DEFAULT_HOST;
|
|
267
|
+
const port = options?.port ?? DEFAULT_PORT;
|
|
268
|
+
const sock = await connect(host, port);
|
|
269
|
+
try {
|
|
270
|
+
return await statsProto(sock);
|
|
271
|
+
} finally {
|
|
272
|
+
sock.destroy();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
255
275
|
var DistributedLock = class {
|
|
256
276
|
key;
|
|
257
277
|
acquireTimeoutS;
|
|
@@ -607,6 +627,7 @@ var DistributedSemaphore = class {
|
|
|
607
627
|
semRenew,
|
|
608
628
|
semWaitForLock,
|
|
609
629
|
stableHashShard,
|
|
630
|
+
stats,
|
|
610
631
|
waitForLock
|
|
611
632
|
});
|
|
612
633
|
//# sourceMappingURL=client.cjs.map
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\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\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\nfunction connect(host: string, port: number): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n });\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// 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// 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}\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\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\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);\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);\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}\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\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\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);\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);\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\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\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\nfunction connect(host: string, port: number): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n });\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 },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port);\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}\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\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\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);\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);\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}\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\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\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);\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);\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":[]}
|
package/dist/client.d.cts
CHANGED
|
@@ -8,6 +8,33 @@ declare class LockError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
type ShardingStrategy = (key: string, numServers: number) => number;
|
|
10
10
|
declare function stableHashShard(key: string, numServers: number): number;
|
|
11
|
+
interface StatsLock {
|
|
12
|
+
key: string;
|
|
13
|
+
owner_conn_id: number;
|
|
14
|
+
lease_expires_in_s: number;
|
|
15
|
+
waiters: number;
|
|
16
|
+
}
|
|
17
|
+
interface StatsSemaphore {
|
|
18
|
+
key: string;
|
|
19
|
+
limit: number;
|
|
20
|
+
holders: number;
|
|
21
|
+
waiters: number;
|
|
22
|
+
}
|
|
23
|
+
interface StatsIdleLock {
|
|
24
|
+
key: string;
|
|
25
|
+
idle_s: number;
|
|
26
|
+
}
|
|
27
|
+
interface StatsIdleSemaphore {
|
|
28
|
+
key: string;
|
|
29
|
+
idle_s: number;
|
|
30
|
+
}
|
|
31
|
+
interface Stats {
|
|
32
|
+
connections: number;
|
|
33
|
+
locks: StatsLock[];
|
|
34
|
+
semaphores: StatsSemaphore[];
|
|
35
|
+
idle_locks: StatsIdleLock[];
|
|
36
|
+
idle_semaphores: StatsIdleSemaphore[];
|
|
37
|
+
}
|
|
11
38
|
declare function acquire(sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number): Promise<{
|
|
12
39
|
token: string;
|
|
13
40
|
lease: number;
|
|
@@ -38,6 +65,21 @@ declare function semWaitForLock(sock: net.Socket, key: string, waitTimeoutS: num
|
|
|
38
65
|
lease: number;
|
|
39
66
|
}>;
|
|
40
67
|
declare function semRelease(sock: net.Socket, key: string, token: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Query server runtime statistics.
|
|
70
|
+
*
|
|
71
|
+
* Opens a short-lived connection, sends the `stats` command, and returns
|
|
72
|
+
* the parsed response.
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* const s = await stats();
|
|
76
|
+
* console.log(s.connections, s.locks.length);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function stats(options?: {
|
|
80
|
+
host?: string;
|
|
81
|
+
port?: number;
|
|
82
|
+
}): Promise<Stats>;
|
|
41
83
|
interface DistributedLockOptions {
|
|
42
84
|
key: string;
|
|
43
85
|
acquireTimeoutS?: number;
|
|
@@ -157,4 +199,4 @@ declare class DistributedSemaphore {
|
|
|
157
199
|
private stopRenew;
|
|
158
200
|
}
|
|
159
201
|
|
|
160
|
-
export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, waitForLock };
|
|
202
|
+
export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
|
package/dist/client.d.ts
CHANGED
|
@@ -8,6 +8,33 @@ declare class LockError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
type ShardingStrategy = (key: string, numServers: number) => number;
|
|
10
10
|
declare function stableHashShard(key: string, numServers: number): number;
|
|
11
|
+
interface StatsLock {
|
|
12
|
+
key: string;
|
|
13
|
+
owner_conn_id: number;
|
|
14
|
+
lease_expires_in_s: number;
|
|
15
|
+
waiters: number;
|
|
16
|
+
}
|
|
17
|
+
interface StatsSemaphore {
|
|
18
|
+
key: string;
|
|
19
|
+
limit: number;
|
|
20
|
+
holders: number;
|
|
21
|
+
waiters: number;
|
|
22
|
+
}
|
|
23
|
+
interface StatsIdleLock {
|
|
24
|
+
key: string;
|
|
25
|
+
idle_s: number;
|
|
26
|
+
}
|
|
27
|
+
interface StatsIdleSemaphore {
|
|
28
|
+
key: string;
|
|
29
|
+
idle_s: number;
|
|
30
|
+
}
|
|
31
|
+
interface Stats {
|
|
32
|
+
connections: number;
|
|
33
|
+
locks: StatsLock[];
|
|
34
|
+
semaphores: StatsSemaphore[];
|
|
35
|
+
idle_locks: StatsIdleLock[];
|
|
36
|
+
idle_semaphores: StatsIdleSemaphore[];
|
|
37
|
+
}
|
|
11
38
|
declare function acquire(sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number): Promise<{
|
|
12
39
|
token: string;
|
|
13
40
|
lease: number;
|
|
@@ -38,6 +65,21 @@ declare function semWaitForLock(sock: net.Socket, key: string, waitTimeoutS: num
|
|
|
38
65
|
lease: number;
|
|
39
66
|
}>;
|
|
40
67
|
declare function semRelease(sock: net.Socket, key: string, token: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Query server runtime statistics.
|
|
70
|
+
*
|
|
71
|
+
* Opens a short-lived connection, sends the `stats` command, and returns
|
|
72
|
+
* the parsed response.
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* const s = await stats();
|
|
76
|
+
* console.log(s.connections, s.locks.length);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function stats(options?: {
|
|
80
|
+
host?: string;
|
|
81
|
+
port?: number;
|
|
82
|
+
}): Promise<Stats>;
|
|
41
83
|
interface DistributedLockOptions {
|
|
42
84
|
key: string;
|
|
43
85
|
acquireTimeoutS?: number;
|
|
@@ -157,4 +199,4 @@ declare class DistributedSemaphore {
|
|
|
157
199
|
private stopRenew;
|
|
158
200
|
}
|
|
159
201
|
|
|
160
|
-
export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, waitForLock };
|
|
202
|
+
export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
|
package/dist/client.js
CHANGED
|
@@ -204,6 +204,25 @@ async function semRelease(sock, key, token) {
|
|
|
204
204
|
throw new LockError(`sem_release failed: '${resp}'`);
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
+
async function statsProto(sock) {
|
|
208
|
+
sock.write(encodeLines("stats", "_", ""));
|
|
209
|
+
const resp = await readline(sock);
|
|
210
|
+
if (!resp.startsWith("ok ")) {
|
|
211
|
+
throw new LockError(`stats failed: '${resp}'`);
|
|
212
|
+
}
|
|
213
|
+
const json = resp.slice(3);
|
|
214
|
+
return JSON.parse(json);
|
|
215
|
+
}
|
|
216
|
+
async function stats(options) {
|
|
217
|
+
const host = options?.host ?? DEFAULT_HOST;
|
|
218
|
+
const port = options?.port ?? DEFAULT_PORT;
|
|
219
|
+
const sock = await connect(host, port);
|
|
220
|
+
try {
|
|
221
|
+
return await statsProto(sock);
|
|
222
|
+
} finally {
|
|
223
|
+
sock.destroy();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
207
226
|
var DistributedLock = class {
|
|
208
227
|
key;
|
|
209
228
|
acquireTimeoutS;
|
|
@@ -558,6 +577,7 @@ export {
|
|
|
558
577
|
semRenew,
|
|
559
578
|
semWaitForLock,
|
|
560
579
|
stableHashShard,
|
|
580
|
+
stats,
|
|
561
581
|
waitForLock
|
|
562
582
|
};
|
|
563
583
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\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\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\nfunction connect(host: string, port: number): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n });\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// 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// 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}\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\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\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);\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);\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}\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\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\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);\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);\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\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\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\nfunction connect(host: string, port: number): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n });\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 },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port);\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}\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\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\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);\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);\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}\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\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\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);\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);\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":[]}
|