@zukhruf/mutex 0.3.0 → 0.3.2
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 +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lock-stores/file-system/caller.d.ts +15 -0
- package/dist/lock-stores/file-system/caller.d.ts.map +1 -0
- package/dist/lock-stores/file-system/caller.js +35 -0
- package/dist/lock-stores/file-system/caller.js.map +1 -0
- package/dist/lock-stores/file-system/file-lock-store.d.ts +7 -6
- package/dist/lock-stores/file-system/file-lock-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/file-lock-store.js +40 -14
- package/dist/lock-stores/file-system/file-lock-store.js.map +1 -1
- package/dist/lock-stores/file-system/lock-file-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/lock-file-store.js +41 -15
- package/dist/lock-stores/file-system/lock-file-store.js.map +1 -1
- package/dist/lock-stores/file-system/presence.d.ts +37 -0
- package/dist/lock-stores/file-system/presence.d.ts.map +1 -0
- package/dist/lock-stores/file-system/presence.js +112 -0
- package/dist/lock-stores/file-system/presence.js.map +1 -0
- package/dist/lock-stores/file-system/ticket-queue-file-store.d.ts +1 -1
- package/dist/lock-stores/file-system/ticket-queue-file-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/ticket-queue-file-store.js +78 -50
- package/dist/lock-stores/file-system/ticket-queue-file-store.js.map +1 -1
- package/dist/lock-stores/socket/electing-connector.d.ts.map +1 -1
- package/dist/lock-stores/socket/electing-connector.js +61 -5
- package/dist/lock-stores/socket/electing-connector.js.map +1 -1
- package/dist/lock-stores/socket/handshake.d.ts +27 -0
- package/dist/lock-stores/socket/handshake.d.ts.map +1 -0
- package/dist/lock-stores/socket/handshake.js +80 -0
- package/dist/lock-stores/socket/handshake.js.map +1 -0
- package/dist/lock-stores/socket/lock-server.d.ts.map +1 -1
- package/dist/lock-stores/socket/lock-server.js +7 -1
- package/dist/lock-stores/socket/lock-server.js.map +1 -1
- package/dist/lock-stores/socket/protocol-version-error.d.ts +12 -0
- package/dist/lock-stores/socket/protocol-version-error.d.ts.map +1 -0
- package/dist/lock-stores/socket/protocol-version-error.js +19 -0
- package/dist/lock-stores/socket/protocol-version-error.js.map +1 -0
- package/dist/shared/sqlite/is-cant-open.d.ts +3 -0
- package/dist/shared/sqlite/is-cant-open.d.ts.map +1 -0
- package/dist/shared/sqlite/is-cant-open.js +9 -0
- package/dist/shared/sqlite/is-cant-open.js.map +1 -0
- package/dist/shared/sqlite/is-not-a-database.d.ts +3 -0
- package/dist/shared/sqlite/is-not-a-database.d.ts.map +1 -0
- package/dist/shared/sqlite/is-not-a-database.js +9 -0
- package/dist/shared/sqlite/is-not-a-database.js.map +1 -0
- package/package.json +2 -1
- package/dist/lock-stores/file-system/owner.d.ts +0 -16
- package/dist/lock-stores/file-system/owner.d.ts.map +0 -1
- package/dist/lock-stores/file-system/owner.js +0 -47
- package/dist/lock-stores/file-system/owner.js.map +0 -1
|
@@ -2,47 +2,46 @@ import { appendFile, readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { atomicWrite } from "../../shared/fs/atomic-write.js";
|
|
3
3
|
import { isErrno } from "../../shared/fs/errno.js";
|
|
4
4
|
import { patiently } from "../../shared/fs/patiently.js";
|
|
5
|
+
import { Caller } from "./caller.js";
|
|
5
6
|
import { FileLockStore } from "./file-lock-store.js";
|
|
6
|
-
import {
|
|
7
|
+
import { Presence } from "./presence.js";
|
|
7
8
|
/**
|
|
8
9
|
* FIFO queue in one file: each waiter appends its ticket line and holds the
|
|
9
10
|
* lock once its ticket is first.
|
|
10
11
|
*
|
|
11
12
|
* Appends are atomic (O_APPEND), and the file is only ever replaced by the
|
|
12
|
-
* live head releasing or by the reclaim-lock holder evicting a
|
|
13
|
+
* live head releasing or by the reclaim-lock holder evicting a gone head, so
|
|
13
14
|
* two rewrites never race. A rewrite racing an append can drop that ticket;
|
|
14
15
|
* its waiter notices and appends it again.
|
|
15
16
|
*/
|
|
16
17
|
export class TicketQueueFileStore extends FileLockStore {
|
|
17
18
|
async lock(path, signal) {
|
|
18
|
-
const me =
|
|
19
|
+
const me = Caller.current();
|
|
20
|
+
await using leaving = new AsyncDisposableStack();
|
|
21
|
+
const presence = Presence.claim(Presence.pathOf(path, me));
|
|
22
|
+
leaving.defer(() => leave(path, me, presence));
|
|
19
23
|
await enqueue(path, me);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
catch (error) {
|
|
24
|
-
this.#leaveWhenFirst(path, me);
|
|
25
|
-
throw error;
|
|
26
|
-
}
|
|
24
|
+
await this.poll(() => this.#attempt(path, me), { signal });
|
|
25
|
+
leaving.move();
|
|
26
|
+
return holding(path, me, presence);
|
|
27
27
|
}
|
|
28
28
|
async tryLock(path) {
|
|
29
29
|
const [head] = await readTickets(path);
|
|
30
|
-
|
|
30
|
+
if (head)
|
|
31
|
+
await this.#evictGoneHeads(path, head);
|
|
31
32
|
if ((await readTickets(path)).length > 0)
|
|
32
33
|
return undefined;
|
|
33
|
-
const me =
|
|
34
|
+
const me = Caller.current();
|
|
35
|
+
await using leaving = new AsyncDisposableStack();
|
|
36
|
+
const presence = Presence.claim(Presence.pathOf(path, me));
|
|
37
|
+
leaving.defer(() => leave(path, me, presence));
|
|
34
38
|
await enqueue(path, me);
|
|
35
39
|
const [first] = await readTickets(path);
|
|
36
|
-
if (first?.id === me.id)
|
|
37
|
-
return holding(path, me);
|
|
38
40
|
// Another waiter appended at the same moment and is first.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (head && !head.isAlive()) {
|
|
44
|
-
await this.withReclaimLock(path, () => evictDeadHead(path));
|
|
45
|
-
}
|
|
41
|
+
if (first?.id !== me.id)
|
|
42
|
+
return undefined;
|
|
43
|
+
leaving.move();
|
|
44
|
+
return holding(path, me, presence);
|
|
46
45
|
}
|
|
47
46
|
async #attempt(path, me) {
|
|
48
47
|
const tickets = await readTickets(path);
|
|
@@ -52,53 +51,82 @@ export class TicketQueueFileStore extends FileLockStore {
|
|
|
52
51
|
}
|
|
53
52
|
const [head] = tickets;
|
|
54
53
|
if (head?.id === me.id)
|
|
55
|
-
return
|
|
56
|
-
|
|
54
|
+
return true;
|
|
55
|
+
if (head)
|
|
56
|
+
await this.#evictGoneHeads(path, head);
|
|
57
57
|
return undefined;
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
* Evicts the run of gone callers at the head of the queue. Each caller is
|
|
61
|
+
* found gone before the queue is read: a gone caller never writes again, so
|
|
62
|
+
* a queue that still starts with it is current, and writing it back cannot
|
|
63
|
+
* drop a later holder's ticket.
|
|
64
64
|
*/
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
async #evictGoneHeads(path, head) {
|
|
66
|
+
if (!(await isGone(path, head)))
|
|
67
|
+
return;
|
|
68
|
+
await this.withReclaimLock(path, async () => {
|
|
69
|
+
let gone = head;
|
|
70
|
+
for (;;) {
|
|
71
|
+
const tickets = await readTickets(path);
|
|
72
|
+
if (tickets[0]?.id !== gone.id)
|
|
73
|
+
return;
|
|
74
|
+
const rest = tickets.filter((ticket) => ticket.id !== gone.id);
|
|
75
|
+
await replace(path, rest);
|
|
76
|
+
await Presence.delete(Presence.pathOf(path, gone));
|
|
77
|
+
const [next] = rest;
|
|
78
|
+
if (!next || !(await isGone(path, next)))
|
|
79
|
+
return;
|
|
80
|
+
gone = next;
|
|
74
81
|
}
|
|
75
|
-
await this.#evictIfDead(path, head);
|
|
76
|
-
return undefined;
|
|
77
|
-
}, { keepAlive: false }).catch(() => {
|
|
78
|
-
// The directory is gone, so the ticket is gone too.
|
|
79
82
|
});
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
/** Whether the caller at the head of the queue at `path` no longer runs. */
|
|
86
|
+
async function isGone(path, head) {
|
|
87
|
+
const presence = Presence.pathOf(path, head);
|
|
88
|
+
const state = Presence.check(presence);
|
|
89
|
+
if (state === 'missing') {
|
|
90
|
+
// A ticket leaves the queue before its presence file, so a ticket still first never had one.
|
|
91
|
+
if ((await readTickets(path))[0]?.id === head.id) {
|
|
92
|
+
throw Presence.missing(path, presence);
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return state === 'gone';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A waiter that gives up keeps its ticket in line: only the head may rewrite
|
|
100
|
+
* the queue. Its presence ends, so the ticket reads as gone and whoever finds
|
|
101
|
+
* it first evicts it, together with the presence file.
|
|
102
|
+
*/
|
|
103
|
+
async function leave(path, me, presence) {
|
|
104
|
+
const tickets = await readTickets(path).catch((error) => {
|
|
105
|
+
presence.end();
|
|
106
|
+
throw error;
|
|
107
|
+
});
|
|
108
|
+
if (tickets.some((ticket) => ticket.id === me.id))
|
|
109
|
+
presence.end();
|
|
110
|
+
else
|
|
111
|
+
await presence.withdraw();
|
|
112
|
+
}
|
|
113
|
+
function holding(path, me, presence) {
|
|
114
|
+
return {
|
|
115
|
+
[Symbol.asyncDispose]: () => presence.releaseAfter(() => removeTicket(path, me)),
|
|
116
|
+
};
|
|
84
117
|
}
|
|
85
118
|
async function removeTicket(path, me) {
|
|
86
119
|
const remaining = await readTickets(path);
|
|
87
120
|
await replace(path, remaining.filter((ticket) => ticket.id !== me.id));
|
|
88
121
|
}
|
|
89
|
-
async function
|
|
90
|
-
|
|
91
|
-
if (head && !head.isAlive())
|
|
92
|
-
await replace(path, rest);
|
|
93
|
-
}
|
|
94
|
-
async function enqueue(path, owner) {
|
|
95
|
-
await patiently(() => appendFile(path, `${owner.serialize()}\n`));
|
|
122
|
+
async function enqueue(path, caller) {
|
|
123
|
+
await patiently(() => appendFile(path, `${caller.serialize()}\n`));
|
|
96
124
|
}
|
|
97
125
|
async function readTickets(path) {
|
|
98
126
|
try {
|
|
99
127
|
const content = await patiently(() => readFile(path, 'utf8'));
|
|
100
128
|
// The last segment is empty or a ticket still being appended.
|
|
101
|
-
return content.split('\n').slice(0, -1).map(
|
|
129
|
+
return content.split('\n').slice(0, -1).map(Caller.parse);
|
|
102
130
|
}
|
|
103
131
|
catch (error) {
|
|
104
132
|
if (isErrno(error, 'ENOENT'))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticket-queue-file-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/ticket-queue-file-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ticket-queue-file-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/ticket-queue-file-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electing-connector.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"electing-connector.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgBhF,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,yEAAyE;IACzE,SAAS,IAAI,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,eAAe;;gBAG3C,OAAO,EAAE,wBAAwB;IAIvC,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAyE9D"}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { connect } from 'node:net';
|
|
2
2
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { untilAborted } from "../../shared/until-aborted.js";
|
|
3
4
|
import { isLockResponse, } from "../remote/protocol.js";
|
|
5
|
+
import { PROTOCOL_VERSION, greet } from "./handshake.js";
|
|
6
|
+
import { ProtocolVersionError } from "./protocol-version-error.js";
|
|
4
7
|
import { SocketConnection } from "./socket-connection.js";
|
|
8
|
+
/**
|
|
9
|
+
* How long a leader that hung up on the hello may take to end its term. One
|
|
10
|
+
* that stops ends it within moments; one from before the handshake keeps it.
|
|
11
|
+
*/
|
|
12
|
+
const HANDSHAKE_PATIENCE = 1000;
|
|
5
13
|
/**
|
|
6
14
|
* Reaches the leader's socket, and when nobody serves it, campaigns to become
|
|
7
15
|
* the leader. Never gives up: a lost leader is always replaced by a candidate.
|
|
@@ -13,14 +21,17 @@ export class ElectingConnector {
|
|
|
13
21
|
this.#options = options;
|
|
14
22
|
}
|
|
15
23
|
async connect(signal) {
|
|
16
|
-
const { socketPath, election, pollInterval, serve
|
|
24
|
+
const { socketPath, election, pollInterval, serve } = this.#options;
|
|
17
25
|
for (;;) {
|
|
18
26
|
const leader = await reachUnlessAborted(socketPath, signal);
|
|
19
27
|
if (leader) {
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
const connection = await this.#follow(leader, signal);
|
|
29
|
+
if (connection)
|
|
30
|
+
return connection;
|
|
22
31
|
}
|
|
23
|
-
const leadership = await election.campaign({
|
|
32
|
+
const leadership = await election.campaign({
|
|
33
|
+
timeout: leader ? HANDSHAKE_PATIENCE : pollInterval,
|
|
34
|
+
});
|
|
24
35
|
if (signal.aborted)
|
|
25
36
|
await leadership?.resign();
|
|
26
37
|
signal.throwIfAborted();
|
|
@@ -36,7 +47,22 @@ export class ElectingConnector {
|
|
|
36
47
|
// This process leads now, so it reaches its own server without following anyone.
|
|
37
48
|
const own = await reachUnlessAborted(socketPath, signal);
|
|
38
49
|
if (own) {
|
|
39
|
-
|
|
50
|
+
if ((await greetUnlessAborted(own, signal)).kind === 'welcome') {
|
|
51
|
+
return new SocketConnection(own, isLockResponse);
|
|
52
|
+
}
|
|
53
|
+
own.destroy();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (leader) {
|
|
57
|
+
// The term is still held: either the leader that hung up predates the
|
|
58
|
+
// handshake, or a successor took over while it stopped. Only the
|
|
59
|
+
// successor answers a second hello.
|
|
60
|
+
const again = await reachUnlessAborted(socketPath, signal);
|
|
61
|
+
if (again) {
|
|
62
|
+
const connection = await this.#follow(again, signal);
|
|
63
|
+
if (connection)
|
|
64
|
+
return connection;
|
|
65
|
+
throw new ProtocolVersionError(PROTOCOL_VERSION, undefined);
|
|
40
66
|
}
|
|
41
67
|
}
|
|
42
68
|
else {
|
|
@@ -44,6 +70,26 @@ export class ElectingConnector {
|
|
|
44
70
|
}
|
|
45
71
|
}
|
|
46
72
|
}
|
|
73
|
+
/** Follows the leader on `socket` if it speaks this protocol; `undefined` when it hung up on the hello. */
|
|
74
|
+
async #follow(socket, signal) {
|
|
75
|
+
const greeting = await greetUnlessAborted(socket, signal);
|
|
76
|
+
if (greeting.kind !== 'welcome') {
|
|
77
|
+
socket.destroy();
|
|
78
|
+
if (greeting.kind === 'refused') {
|
|
79
|
+
throw new ProtocolVersionError(PROTOCOL_VERSION, greeting.version);
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
this.#options.connected();
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// Nobody will use this socket, and an open one would keep the process alive.
|
|
88
|
+
socket.destroy();
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
return new SocketConnection(socket, isLockResponse);
|
|
92
|
+
}
|
|
47
93
|
}
|
|
48
94
|
/** Like `reach`, but a socket reached after `signal` aborted is destroyed, not returned. */
|
|
49
95
|
async function reachUnlessAborted(socketPath, signal) {
|
|
@@ -69,4 +115,14 @@ function reach(socketPath) {
|
|
|
69
115
|
});
|
|
70
116
|
});
|
|
71
117
|
}
|
|
118
|
+
/** Greets the leader on `socket`; a store that closes meanwhile gives the socket up. */
|
|
119
|
+
async function greetUnlessAborted(socket, signal) {
|
|
120
|
+
try {
|
|
121
|
+
return await untilAborted(greet(socket), signal);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
socket.destroy();
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
72
128
|
//# sourceMappingURL=electing-connector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electing-connector.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,OAAO,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"electing-connector.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Socket } from 'node:net';
|
|
2
|
+
/**
|
|
3
|
+
* The version of the messages a socket store's processes exchange. It changes
|
|
4
|
+
* only when they change, so processes that run different package versions
|
|
5
|
+
* with the same protocol still share a directory.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
8
|
+
/** What a leader answered to this process's `hello`. */
|
|
9
|
+
export type Greeting = {
|
|
10
|
+
kind: 'welcome';
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'refused';
|
|
13
|
+
version: number;
|
|
14
|
+
}
|
|
15
|
+
/** The leader closed the connection without an answer: it stops, or it predates the handshake. */
|
|
16
|
+
| {
|
|
17
|
+
kind: 'closed';
|
|
18
|
+
};
|
|
19
|
+
/** Says which protocol this process speaks, and reads the leader's answer. */
|
|
20
|
+
export declare function greet(socket: Socket): Promise<Greeting>;
|
|
21
|
+
/**
|
|
22
|
+
* Reads a new peer's `hello`. A peer that speaks this protocol is welcomed;
|
|
23
|
+
* any other first line is refused with this leader's version, and the
|
|
24
|
+
* connection ends once that answer is written.
|
|
25
|
+
*/
|
|
26
|
+
export declare function welcome(socket: Socket): Promise<boolean>;
|
|
27
|
+
//# sourceMappingURL=handshake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/handshake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,wDAAwD;AACxD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACtC,kGAAkG;GAChG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB,8EAA8E;AAC9E,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAc7D;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { isRecord } from "../../shared/is-record.js";
|
|
2
|
+
/**
|
|
3
|
+
* The version of the messages a socket store's processes exchange. It changes
|
|
4
|
+
* only when they change, so processes that run different package versions
|
|
5
|
+
* with the same protocol still share a directory.
|
|
6
|
+
*/
|
|
7
|
+
export const PROTOCOL_VERSION = 1;
|
|
8
|
+
/** Says which protocol this process speaks, and reads the leader's answer. */
|
|
9
|
+
export async function greet(socket) {
|
|
10
|
+
socket.write(`${JSON.stringify({ op: 'hello', version: PROTOCOL_VERSION })}\n`);
|
|
11
|
+
const answer = parse(await readLine(socket));
|
|
12
|
+
if (isRecord(answer) && answer.op === 'welcome')
|
|
13
|
+
return { kind: 'welcome' };
|
|
14
|
+
if (isRecord(answer) &&
|
|
15
|
+
answer.op === 'refused' &&
|
|
16
|
+
typeof answer.version === 'number') {
|
|
17
|
+
return { kind: 'refused', version: answer.version };
|
|
18
|
+
}
|
|
19
|
+
return { kind: 'closed' };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reads a new peer's `hello`. A peer that speaks this protocol is welcomed;
|
|
23
|
+
* any other first line is refused with this leader's version, and the
|
|
24
|
+
* connection ends once that answer is written.
|
|
25
|
+
*/
|
|
26
|
+
export async function welcome(socket) {
|
|
27
|
+
const hello = parse(await readLine(socket));
|
|
28
|
+
if (isRecord(hello) &&
|
|
29
|
+
hello.op === 'hello' &&
|
|
30
|
+
hello.version === PROTOCOL_VERSION) {
|
|
31
|
+
socket.write(`${JSON.stringify({ op: 'welcome' })}\n`);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
// Destroyed once the answer is written: a paused socket would never see a silent peer hang up.
|
|
35
|
+
socket.end(`${JSON.stringify({ op: 'refused', version: PROTOCOL_VERSION })}\n`, () => socket.destroy());
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
function parse(line) {
|
|
39
|
+
if (line === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(line);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reads exactly one line and puts back whatever arrived after it, so the
|
|
50
|
+
* reader that takes over the socket next sees every later message. Resolves
|
|
51
|
+
* `undefined` when the socket closes first.
|
|
52
|
+
*/
|
|
53
|
+
function readLine(socket) {
|
|
54
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
55
|
+
const chunks = [];
|
|
56
|
+
const onData = (chunk) => {
|
|
57
|
+
const end = chunk.indexOf(0x0a);
|
|
58
|
+
if (end === -1) {
|
|
59
|
+
chunks.push(chunk);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
socket.off('data', onData);
|
|
63
|
+
socket.off('close', onClose);
|
|
64
|
+
socket.pause();
|
|
65
|
+
const rest = chunk.subarray(end + 1);
|
|
66
|
+
if (rest.length > 0)
|
|
67
|
+
socket.unshift(rest);
|
|
68
|
+
resolve(Buffer.concat([...chunks, chunk.subarray(0, end)]).toString('utf8'));
|
|
69
|
+
};
|
|
70
|
+
const onClose = () => {
|
|
71
|
+
socket.off('data', onData);
|
|
72
|
+
resolve(undefined);
|
|
73
|
+
};
|
|
74
|
+
// Every error is followed by `close`, which reports the lost peer as no answer.
|
|
75
|
+
socket.on('error', () => { });
|
|
76
|
+
socket.on('data', onData);
|
|
77
|
+
socket.once('close', onClose);
|
|
78
|
+
return promise;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=handshake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/handshake.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-server.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"lock-server.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAWtE,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,UAAU;;IAKrB,OAAO;WAUM,KAAK,CAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,EAAE,WAAW,EAAE,EAAE,iBAAiB,GACjC,OAAO,CAAC,UAAU,CAAC;IAwCtB;;;;;;OAMG;IACG,KAAK;CAQZ"}
|
|
@@ -4,6 +4,7 @@ import { EpochTokenSource } from "../../fencing/epoch-token-source.js";
|
|
|
4
4
|
import { isErrno } from "../../shared/fs/errno.js";
|
|
5
5
|
import { LockCoordinator } from "../remote/lock-coordinator.js";
|
|
6
6
|
import { isLockRequest, } from "../remote/protocol.js";
|
|
7
|
+
import { welcome } from "./handshake.js";
|
|
7
8
|
import { SocketConnection } from "./socket-connection.js";
|
|
8
9
|
/**
|
|
9
10
|
* Serves a `LockCoordinator` on a Unix socket for one leadership term. Tokens
|
|
@@ -37,7 +38,12 @@ export class LockServer {
|
|
|
37
38
|
socket.unref();
|
|
38
39
|
connections.add(socket);
|
|
39
40
|
socket.once('close', () => connections.delete(socket));
|
|
40
|
-
coordinator
|
|
41
|
+
// A peer that speaks another protocol is refused before it can send anything this coordinator would misread.
|
|
42
|
+
void welcome(socket).then((speaksOurs) => {
|
|
43
|
+
if (!speaksOurs)
|
|
44
|
+
return;
|
|
45
|
+
coordinator.serve(new SocketConnection(socket, isLockRequest));
|
|
46
|
+
});
|
|
41
47
|
});
|
|
42
48
|
await new Promise((resolve, reject) => {
|
|
43
49
|
server.once('error', reject);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-server.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAA4B,YAAY,EAAE,MAAM,UAAU,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAEvE,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAGL,aAAa,GACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO1D;;;GAGG;AACH,MAAM,OAAO,UAAU;IACZ,OAAO,CAAS;IAChB,YAAY,CAAc;IAC1B,WAAW,CAAa;IAEjC,YACE,MAAc,EACd,WAAwB,EACxB,UAAsB;QAEtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,KAAK,CAChB,UAAkB,EAClB,UAAsB,EACtB,EAAE,WAAW,EAAqB;QAElC,iGAAiG;QACjG,sFAAsF;QACtF,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAChD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;oBAAE,MAAM,KAAK,CAAC;YAC7C,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;YACtC,MAAM,EAAE,IAAI,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC;YAC9C,WAAW;SACZ,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,gHAAgH;QAChH,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACvD,WAAW,CAAC,KAAK,CACf,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"lock-server.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The socket store's leader speaks another protocol, so this process cannot
|
|
3
|
+
* use it. Run one version of the protocol per lock directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ProtocolVersionError extends Error {
|
|
6
|
+
/** The protocol this process speaks. */
|
|
7
|
+
readonly ours: number;
|
|
8
|
+
/** The protocol the leader speaks, or `undefined` when it closed the handshake without saying. */
|
|
9
|
+
readonly theirs: number | undefined;
|
|
10
|
+
constructor(ours: number, theirs: number | undefined);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=protocol-version-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-version-error.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/protocol-version-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAExB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;CAUrD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The socket store's leader speaks another protocol, so this process cannot
|
|
3
|
+
* use it. Run one version of the protocol per lock directory.
|
|
4
|
+
*/
|
|
5
|
+
export class ProtocolVersionError extends Error {
|
|
6
|
+
/** The protocol this process speaks. */
|
|
7
|
+
ours;
|
|
8
|
+
/** The protocol the leader speaks, or `undefined` when it closed the handshake without saying. */
|
|
9
|
+
theirs;
|
|
10
|
+
constructor(ours, theirs) {
|
|
11
|
+
super(theirs === undefined
|
|
12
|
+
? `The socket store's leader closed the handshake without an answer while it still leads, so it predates protocol version ${ours}. Stop the processes that run the older version of @zukhruf/mutex.`
|
|
13
|
+
: `The socket store's leader speaks protocol version ${theirs}, and this process speaks version ${ours}. Run one version per lock directory.`);
|
|
14
|
+
this.name = 'ProtocolVersionError';
|
|
15
|
+
this.ours = ours;
|
|
16
|
+
this.theirs = theirs;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=protocol-version-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-version-error.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/protocol-version-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,wCAAwC;IAC/B,IAAI,CAAS;IACtB,kGAAkG;IACzF,MAAM,CAAqB;IAEpC,YAAY,IAAY,EAAE,MAA0B;QAClD,KAAK,CACH,MAAM,KAAK,SAAS;YAClB,CAAC,CAAC,0HAA0H,IAAI,oEAAoE;YACpM,CAAC,CAAC,qDAAqD,MAAM,qCAAqC,IAAI,uCAAuC,CAChJ,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-cant-open.d.ts","sourceRoot":"","sources":["../../../src/shared/sqlite/is-cant-open.ts"],"names":[],"mappings":"AAEA,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOlD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const SQLITE_CANTOPEN = 14;
|
|
2
|
+
/** Whether `error` means SQLite could not open the file, for example because it does not exist. */
|
|
3
|
+
export function isCantOpen(error) {
|
|
4
|
+
return (error instanceof Error &&
|
|
5
|
+
'errcode' in error &&
|
|
6
|
+
typeof error.errcode === 'number' &&
|
|
7
|
+
(error.errcode & 0xff) === SQLITE_CANTOPEN);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=is-cant-open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-cant-open.js","sourceRoot":"","sources":["../../../src/shared/sqlite/is-cant-open.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,mGAAmG;AACnG,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,SAAS,IAAI,KAAK;QAClB,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,eAAe,CAC3C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-not-a-database.d.ts","sourceRoot":"","sources":["../../../src/shared/sqlite/is-not-a-database.ts"],"names":[],"mappings":"AAEA,+GAA+G;AAC/G,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOtD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const SQLITE_NOTADB = 26;
|
|
2
|
+
/** Whether `error` means the file is not a SQLite database, for example a file that an older version wrote. */
|
|
3
|
+
export function isNotADatabase(error) {
|
|
4
|
+
return (error instanceof Error &&
|
|
5
|
+
'errcode' in error &&
|
|
6
|
+
typeof error.errcode === 'number' &&
|
|
7
|
+
(error.errcode & 0xff) === SQLITE_NOTADB);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=is-not-a-database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-not-a-database.js","sourceRoot":"","sources":["../../../src/shared/sqlite/is-not-a-database.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,+GAA+G;AAC/G,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,SAAS,IAAI,KAAK;QAClB,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,aAAa,CACzC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zukhruf/mutex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "A mutex with interchangeable lock stores, from one object to every process on a host, with fencing tokens on every lease.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
+
"@zukhruf/testing": "^0.3.1",
|
|
50
51
|
"hono": "^4.13.13"
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Identifies one lock request and the process that made it, so waiters can
|
|
3
|
-
* evict a holder whose process died. A reused pid only delays recovery; it
|
|
4
|
-
* never breaks exclusion. Holders on another host are never evicted.
|
|
5
|
-
*/
|
|
6
|
-
export declare class Owner {
|
|
7
|
-
readonly pid: number;
|
|
8
|
-
readonly host: string;
|
|
9
|
-
readonly id: string;
|
|
10
|
-
constructor(pid: number, host: string, id: string);
|
|
11
|
-
static current(): Owner;
|
|
12
|
-
static parse(serialized: string): Owner;
|
|
13
|
-
serialize(): string;
|
|
14
|
-
isAlive(): boolean;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=owner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/owner.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,qBAAa,KAAK;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAER,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAMjD,MAAM,CAAC,OAAO,IAAI,KAAK;IAIvB,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK;IAavC,SAAS,IAAI,MAAM;IAInB,OAAO,IAAI,OAAO;CASnB"}
|