@zukhruf/mutex 0.3.16 → 0.3.18
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 +7 -8
- package/dist/index.d.ts +3 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -7
- package/dist/index.js.map +1 -1
- package/dist/lock-stores/file-system/file-lock-store.d.ts +4 -9
- package/dist/lock-stores/file-system/file-lock-store.d.ts.map +1 -1
- package/dist/lock-stores/file-system/file-lock-store.js +21 -38
- package/dist/lock-stores/file-system/file-lock-store.js.map +1 -1
- package/dist/lock-stores/file-system/lock-file-store.d.ts +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 +15 -24
- package/dist/lock-stores/file-system/lock-file-store.js.map +1 -1
- package/dist/lock-stores/file-system/presence.d.ts +10 -17
- package/dist/lock-stores/file-system/presence.d.ts.map +1 -1
- package/dist/lock-stores/file-system/presence.js +30 -69
- package/dist/lock-stores/file-system/presence.js.map +1 -1
- package/dist/lock-stores/file-system/record-file.d.ts +10 -0
- package/dist/lock-stores/file-system/record-file.d.ts.map +1 -0
- package/dist/lock-stores/file-system/record-file.js +21 -0
- package/dist/lock-stores/file-system/record-file.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 +25 -32
- package/dist/lock-stores/file-system/ticket-queue-file-store.js.map +1 -1
- package/dist/lock-stores/ipc/child-process-connection.d.ts +3 -4
- package/dist/lock-stores/ipc/child-process-connection.d.ts.map +1 -1
- package/dist/lock-stores/ipc/child-process-connection.js +11 -27
- package/dist/lock-stores/ipc/child-process-connection.js.map +1 -1
- package/dist/lock-stores/ipc/ipc-lock-coordinator.d.ts +3 -7
- package/dist/lock-stores/ipc/ipc-lock-coordinator.d.ts.map +1 -1
- package/dist/lock-stores/ipc/ipc-lock-coordinator.js +6 -12
- package/dist/lock-stores/ipc/ipc-lock-coordinator.js.map +1 -1
- package/dist/lock-stores/ipc/ipc-store.d.ts +2 -7
- package/dist/lock-stores/ipc/ipc-store.d.ts.map +1 -1
- package/dist/lock-stores/ipc/ipc-store.js +9 -15
- package/dist/lock-stores/ipc/ipc-store.js.map +1 -1
- package/dist/lock-stores/ipc/process-channel-connection.d.ts +4 -4
- package/dist/lock-stores/ipc/process-channel-connection.d.ts.map +1 -1
- package/dist/lock-stores/ipc/process-channel-connection.js +13 -25
- package/dist/lock-stores/ipc/process-channel-connection.js.map +1 -1
- package/dist/lock-stores/memory/memory-store.d.ts +1 -1
- package/dist/lock-stores/memory/memory-store.d.ts.map +1 -1
- package/dist/lock-stores/memory/memory-store.js +1 -1
- package/dist/lock-stores/memory/memory-store.js.map +1 -1
- package/dist/lock-stores/remote/connection-supervisor.d.ts +4 -4
- package/dist/lock-stores/remote/connection-supervisor.d.ts.map +1 -1
- package/dist/lock-stores/remote/connection-supervisor.js.map +1 -1
- package/dist/lock-stores/remote/connection.d.ts +4 -3
- package/dist/lock-stores/remote/connection.d.ts.map +1 -1
- package/dist/lock-stores/remote/connector.d.ts +5 -5
- package/dist/lock-stores/remote/connector.d.ts.map +1 -1
- package/dist/lock-stores/remote/delegating-lock-store.d.ts +14 -0
- package/dist/lock-stores/remote/delegating-lock-store.d.ts.map +1 -0
- package/dist/lock-stores/remote/delegating-lock-store.js +20 -0
- package/dist/lock-stores/remote/delegating-lock-store.js.map +1 -0
- package/dist/lock-stores/remote/lock-coordinator.d.ts +3 -4
- package/dist/lock-stores/remote/lock-coordinator.d.ts.map +1 -1
- package/dist/lock-stores/remote/lock-coordinator.js +23 -12
- package/dist/lock-stores/remote/lock-coordinator.js.map +1 -1
- package/dist/lock-stores/remote/protocol.d.ts +6 -2
- package/dist/lock-stores/remote/protocol.d.ts.map +1 -1
- package/dist/lock-stores/remote/protocol.js +14 -8
- package/dist/lock-stores/remote/protocol.js.map +1 -1
- package/dist/lock-stores/remote/queries.d.ts +2 -2
- package/dist/lock-stores/remote/queries.d.ts.map +1 -1
- package/dist/lock-stores/remote/queries.js.map +1 -1
- package/dist/lock-stores/remote/remote-lock-client.d.ts +2 -2
- package/dist/lock-stores/remote/remote-lock-client.d.ts.map +1 -1
- package/dist/lock-stores/remote/remote-lock-client.js +15 -8
- package/dist/lock-stores/remote/remote-lock-client.js.map +1 -1
- package/dist/lock-stores/remote/shared-channel-connection.d.ts +27 -0
- package/dist/lock-stores/remote/shared-channel-connection.d.ts.map +1 -0
- package/dist/lock-stores/remote/shared-channel-connection.js +41 -0
- package/dist/lock-stores/remote/shared-channel-connection.js.map +1 -0
- package/dist/lock-stores/socket/advertised-ops-connection.d.ts +3 -3
- package/dist/lock-stores/socket/advertised-ops-connection.d.ts.map +1 -1
- package/dist/lock-stores/socket/advertised-ops-connection.js +4 -1
- package/dist/lock-stores/socket/advertised-ops-connection.js.map +1 -1
- package/dist/lock-stores/socket/electing-connector.d.ts +4 -5
- package/dist/lock-stores/socket/electing-connector.d.ts.map +1 -1
- package/dist/lock-stores/socket/electing-connector.js +43 -50
- package/dist/lock-stores/socket/electing-connector.js.map +1 -1
- package/dist/lock-stores/socket/handshake.d.ts.map +1 -1
- package/dist/lock-stores/socket/handshake.js +6 -5
- package/dist/lock-stores/socket/handshake.js.map +1 -1
- package/dist/lock-stores/socket/json-line.d.ts +6 -0
- package/dist/lock-stores/socket/json-line.d.ts.map +1 -0
- package/dist/lock-stores/socket/json-line.js +8 -0
- package/dist/lock-stores/socket/json-line.js.map +1 -0
- package/dist/lock-stores/socket/leave-errors-to-close.d.ts +9 -0
- package/dist/lock-stores/socket/leave-errors-to-close.d.ts.map +1 -0
- package/dist/lock-stores/socket/leave-errors-to-close.js +10 -0
- package/dist/lock-stores/socket/leave-errors-to-close.js.map +1 -0
- package/dist/lock-stores/socket/local-directory-connector.d.ts.map +1 -1
- package/dist/lock-stores/socket/local-directory-connector.js +1 -1
- package/dist/lock-stores/socket/local-directory-connector.js.map +1 -1
- package/dist/lock-stores/socket/lock-server.d.ts +7 -4
- package/dist/lock-stores/socket/lock-server.d.ts.map +1 -1
- package/dist/lock-stores/socket/lock-server.js +29 -26
- package/dist/lock-stores/socket/lock-server.js.map +1 -1
- package/dist/lock-stores/socket/socket-connection.d.ts +5 -5
- package/dist/lock-stores/socket/socket-connection.d.ts.map +1 -1
- package/dist/lock-stores/socket/socket-connection.js +21 -23
- package/dist/lock-stores/socket/socket-connection.js.map +1 -1
- package/dist/lock-stores/socket/socket-election.d.ts +9 -0
- package/dist/lock-stores/socket/socket-election.d.ts.map +1 -0
- package/dist/lock-stores/socket/socket-election.js +16 -0
- package/dist/lock-stores/socket/socket-election.js.map +1 -0
- package/dist/lock-stores/socket/socket-store.d.ts +1 -1
- package/dist/lock-stores/socket/socket-store.d.ts.map +1 -1
- package/dist/lock-stores/socket/socket-store.js +6 -6
- package/dist/lock-stores/socket/socket-store.js.map +1 -1
- package/dist/lock-stores/sqlite/holder-record.d.ts.map +1 -1
- package/dist/lock-stores/sqlite/holder-record.js +9 -23
- package/dist/lock-stores/sqlite/holder-record.js.map +1 -1
- package/dist/lock-stores/sqlite/sqlite-store.d.ts +6 -7
- package/dist/lock-stores/sqlite/sqlite-store.d.ts.map +1 -1
- package/dist/lock-stores/sqlite/sqlite-store.js +31 -101
- package/dist/lock-stores/sqlite/sqlite-store.js.map +1 -1
- package/dist/lock-stores/thread/parent-port-connection.d.ts +3 -4
- package/dist/lock-stores/thread/parent-port-connection.d.ts.map +1 -1
- package/dist/lock-stores/thread/parent-port-connection.js +7 -13
- package/dist/lock-stores/thread/parent-port-connection.js.map +1 -1
- package/dist/lock-stores/thread/thread-lock-coordinator.d.ts +3 -7
- package/dist/lock-stores/thread/thread-lock-coordinator.d.ts.map +1 -1
- package/dist/lock-stores/thread/thread-lock-coordinator.js +6 -12
- package/dist/lock-stores/thread/thread-lock-coordinator.js.map +1 -1
- package/dist/lock-stores/thread/thread-store.d.ts +2 -7
- package/dist/lock-stores/thread/thread-store.d.ts.map +1 -1
- package/dist/lock-stores/thread/thread-store.js +3 -12
- package/dist/lock-stores/thread/thread-store.js.map +1 -1
- package/dist/lock-stores/thread/worker-connection.d.ts +3 -4
- package/dist/lock-stores/thread/worker-connection.d.ts.map +1 -1
- package/dist/lock-stores/thread/worker-connection.js +6 -18
- package/dist/lock-stores/thread/worker-connection.js.map +1 -1
- package/dist/mutex/key.d.ts +3 -3
- package/dist/mutex/key.d.ts.map +1 -1
- package/dist/mutex/key.js.map +1 -1
- package/dist/mutex/lease.d.ts +2 -9
- package/dist/mutex/lease.d.ts.map +1 -1
- package/dist/mutex/lease.js +17 -12
- package/dist/mutex/lease.js.map +1 -1
- package/dist/mutex/mutex.d.ts +3 -3
- package/dist/mutex/mutex.d.ts.map +1 -1
- package/dist/mutex/mutex.js +2 -2
- package/dist/mutex/mutex.js.map +1 -1
- package/package.json +6 -9
- package/dist/fencing/counter-token-source.d.ts +0 -11
- package/dist/fencing/counter-token-source.d.ts.map +0 -1
- package/dist/fencing/counter-token-source.js +0 -13
- package/dist/fencing/counter-token-source.js.map +0 -1
- package/dist/fencing/epoch-token-source.d.ts +0 -13
- package/dist/fencing/epoch-token-source.d.ts.map +0 -1
- package/dist/fencing/epoch-token-source.js +0 -27
- package/dist/fencing/epoch-token-source.js.map +0 -1
- package/dist/fencing/fencing-token.d.ts +0 -12
- package/dist/fencing/fencing-token.d.ts.map +0 -1
- package/dist/fencing/fencing-token.js +0 -18
- package/dist/fencing/fencing-token.js.map +0 -1
- package/dist/fencing/file-token-source.d.ts +0 -13
- package/dist/fencing/file-token-source.d.ts.map +0 -1
- package/dist/fencing/file-token-source.js +0 -36
- package/dist/fencing/file-token-source.js.map +0 -1
- package/dist/fencing/monotonic-clock-token-source.d.ts +0 -12
- package/dist/fencing/monotonic-clock-token-source.d.ts.map +0 -1
- package/dist/fencing/monotonic-clock-token-source.js +0 -15
- package/dist/fencing/monotonic-clock-token-source.js.map +0 -1
- package/dist/fencing/token-source.d.ts +0 -9
- package/dist/fencing/token-source.d.ts.map +0 -1
- package/dist/fencing/token-source.js +0 -2
- package/dist/fencing/token-source.js.map +0 -1
- package/dist/leader-election/index.d.ts +0 -4
- package/dist/leader-election/index.d.ts.map +0 -1
- package/dist/leader-election/index.js +0 -4
- package/dist/leader-election/index.js.map +0 -1
- package/dist/leader-election/leader-election.d.ts +0 -23
- package/dist/leader-election/leader-election.d.ts.map +0 -1
- package/dist/leader-election/leader-election.js +0 -79
- package/dist/leader-election/leader-election.js.map +0 -1
- package/dist/leader-election/leadership.d.ts +0 -14
- package/dist/leader-election/leadership.d.ts.map +0 -1
- package/dist/leader-election/leadership.js +0 -23
- package/dist/leader-election/leadership.js.map +0 -1
- package/dist/local-directory/local-directory.d.ts +0 -7
- package/dist/local-directory/local-directory.d.ts.map +0 -1
- package/dist/local-directory/local-directory.js +0 -75
- package/dist/local-directory/local-directory.js.map +0 -1
- package/dist/local-directory/network-directory-error.d.ts +0 -8
- package/dist/local-directory/network-directory-error.d.ts.map +0 -1
- package/dist/local-directory/network-directory-error.js +0 -13
- package/dist/local-directory/network-directory-error.js.map +0 -1
- package/dist/mutex/lock-lost-error.d.ts +0 -6
- package/dist/mutex/lock-lost-error.d.ts.map +0 -1
- package/dist/mutex/lock-lost-error.js +0 -10
- package/dist/mutex/lock-lost-error.js.map +0 -1
- package/dist/shared/fs/atomic-write.d.ts +0 -3
- package/dist/shared/fs/atomic-write.d.ts.map +0 -1
- package/dist/shared/fs/atomic-write.js +0 -10
- package/dist/shared/fs/atomic-write.js.map +0 -1
- package/dist/shared/fs/create-exclusive.d.ts +0 -6
- package/dist/shared/fs/create-exclusive.d.ts.map +0 -1
- package/dist/shared/fs/create-exclusive.js +0 -29
- package/dist/shared/fs/create-exclusive.js.map +0 -1
- package/dist/shared/fs/durable-write.d.ts +0 -9
- package/dist/shared/fs/durable-write.d.ts.map +0 -1
- package/dist/shared/fs/durable-write.js +0 -49
- package/dist/shared/fs/durable-write.js.map +0 -1
- package/dist/shared/fs/errno.d.ts +0 -2
- package/dist/shared/fs/errno.d.ts.map +0 -1
- package/dist/shared/fs/errno.js +0 -4
- package/dist/shared/fs/errno.js.map +0 -1
- package/dist/shared/fs/patiently.d.ts +0 -3
- package/dist/shared/fs/patiently.d.ts.map +0 -1
- package/dist/shared/fs/patiently.js +0 -30
- package/dist/shared/fs/patiently.js.map +0 -1
- package/dist/shared/fs/replace-file.d.ts +0 -8
- package/dist/shared/fs/replace-file.d.ts.map +0 -1
- package/dist/shared/fs/replace-file.js +0 -12
- package/dist/shared/fs/replace-file.js.map +0 -1
- package/dist/shared/fs/safe-file-name.d.ts +0 -16
- package/dist/shared/fs/safe-file-name.d.ts.map +0 -1
- package/dist/shared/fs/safe-file-name.js +0 -37
- package/dist/shared/fs/safe-file-name.js.map +0 -1
- package/dist/shared/sqlite/is-busy.d.ts +0 -3
- package/dist/shared/sqlite/is-busy.d.ts.map +0 -1
- package/dist/shared/sqlite/is-busy.js +0 -10
- package/dist/shared/sqlite/is-busy.js.map +0 -1
- package/dist/shared/sqlite/is-cant-open.d.ts +0 -3
- package/dist/shared/sqlite/is-cant-open.d.ts.map +0 -1
- package/dist/shared/sqlite/is-cant-open.js +0 -9
- package/dist/shared/sqlite/is-cant-open.js.map +0 -1
- package/dist/shared/sqlite/is-not-a-database.d.ts +0 -3
- package/dist/shared/sqlite/is-not-a-database.d.ts.map +0 -1
- package/dist/shared/sqlite/is-not-a-database.js +0 -9
- package/dist/shared/sqlite/is-not-a-database.js.map +0 -1
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ await mutex.acquire('product:42', async (lease) => {
|
|
|
98
98
|
});
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
Sometimes the lock store sees the loss, for example after a failover of `SocketStore`. Then the signal of the lease aborts with `
|
|
101
|
+
Sometimes the lock store sees the loss, for example after a failover of `SocketStore`. Then the signal of the lease aborts with `LeaseLostError`, and the task can stop before its next write. When the task ends, the call rejects with `LeaseLostError`:
|
|
102
102
|
|
|
103
103
|
```ts
|
|
104
104
|
await mutex.acquire('product:42', async (lease) => {
|
|
@@ -111,6 +111,8 @@ await mutex.acquire('product:42', async (lease) => {
|
|
|
111
111
|
|
|
112
112
|
The signal is a warning. A frozen holder can write before its lock store sees the loss, so the fencing token is still the protection.
|
|
113
113
|
|
|
114
|
+
The lease is a `FencedLease` of [`@zukhruf/fencing`](../fencing/README.md). Import `FencingToken` and the token sources from `@zukhruf/fencing`. `LeaseLostError` is from [`@zukhruf/lease`](../lease/README.md), and this package also exports it, because its calls reject with it.
|
|
115
|
+
|
|
114
116
|
See [Fencing tokens](./docs/concepts/fencing-tokens.md) and the recipe [Protect a database from stale holders](./docs/recipes/fence-a-database.md).
|
|
115
117
|
|
|
116
118
|
## Documentation
|
|
@@ -120,7 +122,7 @@ See [Fencing tokens](./docs/concepts/fencing-tokens.md) and the recipe [Protect
|
|
|
120
122
|
- [Reach](./docs/concepts/reach.md): who can share a lock, and how to select it.
|
|
121
123
|
- [Acquire modes](./docs/concepts/acquire-modes.md): wait or skip while a key is busy, and cancel a wait.
|
|
122
124
|
- [Fencing tokens](./docs/concepts/fencing-tokens.md): how a resource refuses a stale holder.
|
|
123
|
-
- [Leader election](
|
|
125
|
+
- [Leader election](../election/docs/concepts/leader-election.md): how `SocketStore` selects its coordinator, with `SqliteElection` of `@zukhruf/election`.
|
|
124
126
|
- [Failure modes](./docs/concepts/failure-modes.md): what each lock store does when something stops.
|
|
125
127
|
|
|
126
128
|
**Lock stores**: one page for each lock store, with What, Why, When, When not, How it works, Acquire modes, Failure modes, Options, and Evidence. See the table above.
|
|
@@ -133,7 +135,7 @@ See [Fencing tokens](./docs/concepts/fencing-tokens.md) and the recipe [Protect
|
|
|
133
135
|
4. [Worker threads that share a lock](./docs/recipes/worker-threads.md)
|
|
134
136
|
5. [Protect a database from stale holders](./docs/recipes/fence-a-database.md)
|
|
135
137
|
6. [Survive a crashed holder](./docs/recipes/survive-a-crashed-holder.md)
|
|
136
|
-
7. [Run a job in only one process](
|
|
138
|
+
7. [Run a job in only one process](../election/docs/recipes/singleton-job-with-leader-election.md), with `@zukhruf/election`
|
|
137
139
|
8. [Write your own lock store](./docs/recipes/write-your-own-lock-store.md)
|
|
138
140
|
9. [Skip a job that is already running](./docs/recipes/skip-a-job-that-is-already-running.md)
|
|
139
141
|
10. [Compute a value once and share it](./docs/recipes/compute-once-and-share-it.md)
|
|
@@ -150,7 +152,6 @@ npm install @zukhruf/mutex
|
|
|
150
152
|
|
|
151
153
|
```ts
|
|
152
154
|
import { Mutex, SqliteStore } from '@zukhruf/mutex';
|
|
153
|
-
import { LeaderElection } from '@zukhruf/mutex/leader-election';
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
## Development
|
|
@@ -169,12 +170,10 @@ The tests run from `src/`, not from `dist/`: they start workers and child proces
|
|
|
169
170
|
|
|
170
171
|
```
|
|
171
172
|
src/
|
|
172
|
-
mutex/ Mutex, Key, acquire modes,
|
|
173
|
-
fencing/ fencing tokens and token sources
|
|
173
|
+
mutex/ Mutex, Key, acquire modes, LockHandle, LockStore
|
|
174
174
|
lock-stores/ one folder for each lock store
|
|
175
175
|
remote/ the coordinator and client that ThreadStore, IpcStore and SocketStore share
|
|
176
|
-
|
|
177
|
-
shared/ small file system and SQLite helpers
|
|
176
|
+
shared/ a record check (key file names, file writes and file locks are in @zukhruf/fs)
|
|
178
177
|
testing/ test helpers and the matrix of lock stores (not published)
|
|
179
178
|
docs/
|
|
180
179
|
concepts/ stores/ recipes/ adr/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
export { Mutex } from './mutex/mutex.ts';
|
|
2
2
|
export { Key } from './mutex/key.ts';
|
|
3
|
-
export { leaseFor, type
|
|
3
|
+
export { leaseFor, type LockHandle } from './mutex/lease.ts';
|
|
4
4
|
export type { AcquireOptions, LockStore } from './mutex/lock-store.ts';
|
|
5
|
-
export {
|
|
5
|
+
export { LeaseLostError } from '@zukhruf/lease';
|
|
6
6
|
export type { Acquired, AcquireMode, ModeResult, NotAcquired, Outcome, OutcomeResults, } from './mutex/acquire-mode.ts';
|
|
7
7
|
export { Modes } from './mutex/acquire-modes/modes.ts';
|
|
8
8
|
export { WaitMode } from './mutex/acquire-modes/wait-mode.ts';
|
|
9
9
|
export { SkipIfBusyMode, type SkipIfBusyOptions, } from './mutex/acquire-modes/skip-if-busy-mode.ts';
|
|
10
|
-
export { FencingToken } from './fencing/fencing-token.ts';
|
|
11
|
-
export type { TokenSource } from './fencing/token-source.ts';
|
|
12
|
-
export { CounterTokenSource } from './fencing/counter-token-source.ts';
|
|
13
|
-
export { FileTokenSource } from './fencing/file-token-source.ts';
|
|
14
|
-
export { MonotonicClockTokenSource } from './fencing/monotonic-clock-token-source.ts';
|
|
15
|
-
export { EpochTokenSource } from './fencing/epoch-token-source.ts';
|
|
16
10
|
export { MemoryStore, type MemoryStoreOptions, } from './lock-stores/memory/memory-store.ts';
|
|
17
11
|
export { ThreadLockCoordinator, type ThreadLockCoordinatorOptions, } from './lock-stores/thread/thread-lock-coordinator.ts';
|
|
18
12
|
export { ThreadStore } from './lock-stores/thread/thread-store.ts';
|
|
@@ -20,7 +14,7 @@ export { IpcLockCoordinator, type IpcLockCoordinatorOptions, } from './lock-stor
|
|
|
20
14
|
export { IpcStore } from './lock-stores/ipc/ipc-store.ts';
|
|
21
15
|
export { CoordinatorUnavailableError } from './lock-stores/remote/coordinator-unavailable-error.ts';
|
|
22
16
|
export { UnsupportedRequestError } from './lock-stores/remote/unsupported-request-error.ts';
|
|
23
|
-
export { NetworkDirectoryError } from '
|
|
17
|
+
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
24
18
|
export { FileLockStore, type FileLockStoreOptions, } from './lock-stores/file-system/file-lock-store.ts';
|
|
25
19
|
export { TicketQueueFileStore } from './lock-stores/file-system/ticket-queue-file-store.ts';
|
|
26
20
|
export { LockFileStore } from './lock-stores/file-system/lock-file-store.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,OAAO,EACP,cAAc,GACf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,KAAK,iBAAiB,GACvB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,GAC1B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,WAAW,EACX,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
export { Mutex } from "./mutex/mutex.js";
|
|
2
2
|
export { Key } from "./mutex/key.js";
|
|
3
3
|
export { leaseFor } from "./mutex/lease.js";
|
|
4
|
-
export {
|
|
4
|
+
export { LeaseLostError } from '@zukhruf/lease';
|
|
5
5
|
export { Modes } from "./mutex/acquire-modes/modes.js";
|
|
6
6
|
export { WaitMode } from "./mutex/acquire-modes/wait-mode.js";
|
|
7
7
|
export { SkipIfBusyMode, } from "./mutex/acquire-modes/skip-if-busy-mode.js";
|
|
8
|
-
export { FencingToken } from "./fencing/fencing-token.js";
|
|
9
|
-
export { CounterTokenSource } from "./fencing/counter-token-source.js";
|
|
10
|
-
export { FileTokenSource } from "./fencing/file-token-source.js";
|
|
11
|
-
export { MonotonicClockTokenSource } from "./fencing/monotonic-clock-token-source.js";
|
|
12
|
-
export { EpochTokenSource } from "./fencing/epoch-token-source.js";
|
|
13
8
|
export { MemoryStore, } from "./lock-stores/memory/memory-store.js";
|
|
14
9
|
export { ThreadLockCoordinator, } from "./lock-stores/thread/thread-lock-coordinator.js";
|
|
15
10
|
export { ThreadStore } from "./lock-stores/thread/thread-store.js";
|
|
@@ -17,7 +12,7 @@ export { IpcLockCoordinator, } from "./lock-stores/ipc/ipc-lock-coordinator.js";
|
|
|
17
12
|
export { IpcStore } from "./lock-stores/ipc/ipc-store.js";
|
|
18
13
|
export { CoordinatorUnavailableError } from "./lock-stores/remote/coordinator-unavailable-error.js";
|
|
19
14
|
export { UnsupportedRequestError } from "./lock-stores/remote/unsupported-request-error.js";
|
|
20
|
-
export { NetworkDirectoryError } from
|
|
15
|
+
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
21
16
|
export { FileLockStore, } from "./lock-stores/file-system/file-lock-store.js";
|
|
22
17
|
export { TicketQueueFileStore } from "./lock-stores/file-system/ticket-queue-file-store.js";
|
|
23
18
|
export { LockFileStore } from "./lock-stores/file-system/lock-file-store.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAShD,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,WAAW,GAEZ,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,GAEtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,GAEnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,aAAa,GAEd,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,WAAW,GAGZ,MAAM,sCAAsC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TokenSource } from '@zukhruf/fencing';
|
|
2
2
|
import { type LockHandle } from '../../mutex/lease.ts';
|
|
3
3
|
import type { AcquireOptions, LockStore } from '../../mutex/lock-store.ts';
|
|
4
4
|
export interface FileLockStoreOptions {
|
|
@@ -7,10 +7,6 @@ export interface FileLockStoreOptions {
|
|
|
7
7
|
/** Defaults to durable per-key counter files beside the locks. */
|
|
8
8
|
tokens?: TokenSource;
|
|
9
9
|
}
|
|
10
|
-
export interface PollOptions {
|
|
11
|
-
/** Stops between attempts when it aborts; the poll then rejects with `signal.reason`. */
|
|
12
|
-
signal?: AbortSignal | undefined;
|
|
13
|
-
}
|
|
14
10
|
/** Shares one lock directory between processes: each key maps to `<directory>/<key>.lock`. */
|
|
15
11
|
export declare abstract class FileLockStore implements LockStore {
|
|
16
12
|
#private;
|
|
@@ -31,15 +27,14 @@ export declare abstract class FileLockStore implements LockStore {
|
|
|
31
27
|
protected abstract tryLock(path: string): Promise<AsyncDisposable | undefined>;
|
|
32
28
|
/** Whether the lock at `path` has a holder now, learned without a lock that a caller of `lock` or `tryLock` needs. */
|
|
33
29
|
protected abstract isHeldAt(path: string): Promise<boolean>;
|
|
34
|
-
|
|
30
|
+
/** Stops between attempts once `signal` aborts, and then rejects with `signal.reason`. */
|
|
31
|
+
protected poll<T>(attempt: () => Promise<T | undefined>, signal: AbortSignal | undefined): Promise<T>;
|
|
35
32
|
/**
|
|
36
33
|
* Serializes evicting a dead holder. Without it, two waiters that saw the
|
|
37
34
|
* same dead holder could each remove a lock the other had just taken. The
|
|
38
35
|
* kernel holds this lock, so a waiter that dies while it evicts frees it at
|
|
39
36
|
* once. Its file stays for the next eviction: deleting it while waiters use
|
|
40
|
-
* it would let two of them lock two different files.
|
|
41
|
-
* file except through SQLite: closing any other handle to it drops this
|
|
42
|
-
* process's lock without a word.
|
|
37
|
+
* it would let two of them lock two different files.
|
|
43
38
|
*/
|
|
44
39
|
protected withReclaimLock(path: string, task: () => Promise<void>): Promise<void>;
|
|
45
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-lock-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3E,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,8FAA8F;AAC9F,8BAAsB,aAAc,YAAW,SAAS;;gBAMpD,SAAS,EAAE,MAAM,EACjB,EACE,YAAiB,EACjB,MAAuC,GACxC,GAAE,oBAAyB;IAOxB,OAAO,CACX,GAAG,EAAE,MAAM,EACX,EAAE,MAAM,EAAE,GAAE,cAAmB,GAC9B,OAAO,CAAC,UAAU,CAAC;IAMhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAK9D,kFAAkF;IAC5E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK3C;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAElD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,CAAC,IAAI,CACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,eAAe,CAAC;IAE3B,mEAAmE;IACnE,SAAS,CAAC,QAAQ,CAAC,OAAO,CACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAEvC,sHAAsH;IACtH,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAE3D,0FAA0F;cAC1E,IAAI,CAAC,CAAC,EACpB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,EACrC,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,CAAC,CAAC;IAWb;;;;;;OAMG;cACa,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;CAiBxE"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { mkdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
4
3
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { untilAborted } from '@zukhruf/async';
|
|
5
|
+
import { FileTokenSource } from '@zukhruf/fencing';
|
|
6
|
+
import { FileLock, assertLocalDirectory, safeFileName } from '@zukhruf/fs';
|
|
7
7
|
import { leaseFor } from "../../mutex/lease.js";
|
|
8
|
-
import { safeFileName } from "../../shared/fs/safe-file-name.js";
|
|
9
|
-
import { isBusy } from "../../shared/sqlite/is-busy.js";
|
|
10
|
-
import { isNotADatabase } from "../../shared/sqlite/is-not-a-database.js";
|
|
11
8
|
/** Shares one lock directory between processes: each key maps to `<directory>/<key>.lock`. */
|
|
12
9
|
export class FileLockStore {
|
|
13
10
|
#directory;
|
|
@@ -32,18 +29,13 @@ export class FileLockStore {
|
|
|
32
29
|
await assertLocalDirectory(this.#directory);
|
|
33
30
|
return this.isHeldAt(this.#pathFor(key));
|
|
34
31
|
}
|
|
35
|
-
|
|
32
|
+
/** Stops between attempts once `signal` aborts, and then rejects with `signal.reason`. */
|
|
33
|
+
async poll(attempt, signal) {
|
|
36
34
|
for (;;) {
|
|
37
35
|
const result = await attempt();
|
|
38
36
|
if (result !== undefined)
|
|
39
37
|
return result;
|
|
40
|
-
|
|
41
|
-
await delay(this.#pollInterval, undefined, { signal });
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
signal?.throwIfAborted();
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
38
|
+
await untilAborted(delay(this.#pollInterval, undefined, { signal }), signal);
|
|
47
39
|
}
|
|
48
40
|
}
|
|
49
41
|
/**
|
|
@@ -51,26 +43,14 @@ export class FileLockStore {
|
|
|
51
43
|
* same dead holder could each remove a lock the other had just taken. The
|
|
52
44
|
* kernel holds this lock, so a waiter that dies while it evicts frees it at
|
|
53
45
|
* once. Its file stays for the next eviction: deleting it while waiters use
|
|
54
|
-
* it would let two of them lock two different files.
|
|
55
|
-
* file except through SQLite: closing any other handle to it drops this
|
|
56
|
-
* process's lock without a word.
|
|
46
|
+
* it would let two of them lock two different files.
|
|
57
47
|
*/
|
|
58
48
|
async withReclaimLock(path, task) {
|
|
59
49
|
const reclaimPath = `${path}.reclaim`;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
await task();
|
|
66
|
-
}
|
|
67
|
-
finally {
|
|
68
|
-
reclaim.exec('ROLLBACK');
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
finally {
|
|
72
|
-
reclaim.close();
|
|
73
|
-
}
|
|
50
|
+
using reclaim = FileLock.open(reclaimPath);
|
|
51
|
+
if (!claim(reclaim, reclaimPath))
|
|
52
|
+
return;
|
|
53
|
+
await task();
|
|
74
54
|
}
|
|
75
55
|
async #prepare(key) {
|
|
76
56
|
await assertLocalDirectory(this.#directory);
|
|
@@ -82,21 +62,24 @@ export class FileLockStore {
|
|
|
82
62
|
return join(this.#directory, `${name}.lock`);
|
|
83
63
|
}
|
|
84
64
|
}
|
|
85
|
-
/** Whether this
|
|
65
|
+
/** Whether this handle now holds the reclaim lock; another waiter is evicting if not. */
|
|
86
66
|
function claim(reclaim, path) {
|
|
87
67
|
try {
|
|
88
|
-
|
|
89
|
-
reclaim.exec('PRAGMA journal_mode = MEMORY');
|
|
90
|
-
reclaim.exec('BEGIN EXCLUSIVE');
|
|
91
|
-
return true;
|
|
68
|
+
return reclaim.tryLock();
|
|
92
69
|
}
|
|
93
70
|
catch (error) {
|
|
94
|
-
if (isBusy(error))
|
|
95
|
-
return false;
|
|
96
71
|
if (isNotADatabase(error)) {
|
|
97
72
|
throw new Error(`The reclaim file ${JSON.stringify(path)} was left by an older version of @zukhruf/mutex. Stop the processes that run that version, then delete the file.`, { cause: error });
|
|
98
73
|
}
|
|
99
74
|
throw error;
|
|
100
75
|
}
|
|
101
76
|
}
|
|
77
|
+
const SQLITE_NOTADB = 26;
|
|
78
|
+
/** Whether `error` means the file is not a SQLite database, for example a file that an older version wrote. */
|
|
79
|
+
function isNotADatabase(error) {
|
|
80
|
+
return (error instanceof Error &&
|
|
81
|
+
'errcode' in error &&
|
|
82
|
+
typeof error.errcode === 'number' &&
|
|
83
|
+
(error.errcode & 0xff) === SQLITE_NOTADB);
|
|
84
|
+
}
|
|
102
85
|
//# sourceMappingURL=file-lock-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"file-lock-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { FileLockStore } from './file-lock-store.ts';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class LockFileStore extends FileLockStore {
|
|
7
7
|
#private;
|
|
8
|
-
/**
|
|
8
|
+
/** The longer of a presence file and the draft that `createExclusive` writes beside the record. */
|
|
9
9
|
protected readonly longestSuffix: number;
|
|
10
10
|
protected lock(path: string, signal: AbortSignal | undefined): Promise<AsyncDisposable>;
|
|
11
11
|
protected tryLock(path: string): Promise<AsyncDisposable | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-file-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/lock-file-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lock-file-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/lock-file-store.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIrD;;;GAGG;AACH,qBAAa,aAAc,SAAQ,aAAa;;IAC9C,mGAAmG;IACnG,SAAS,CAAC,QAAQ,CAAC,aAAa,SAG9B;IAEF,SAAS,CAAC,IAAI,CACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,eAAe,CAAC;cAKX,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IA6B3E,kEAAkE;IAClE,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAanD"}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createExclusive } from
|
|
3
|
-
import { isErrno } from "../../shared/fs/errno.js";
|
|
4
|
-
import { patiently } from "../../shared/fs/patiently.js";
|
|
1
|
+
import { unlink } from 'node:fs/promises';
|
|
2
|
+
import { createExclusive, draftSuffixLength, patiently } from '@zukhruf/fs';
|
|
5
3
|
import { Caller } from "./caller.js";
|
|
6
4
|
import { FileLockStore } from "./file-lock-store.js";
|
|
7
5
|
import { Presence } from "./presence.js";
|
|
6
|
+
import { readRecord } from "./record-file.js";
|
|
8
7
|
/**
|
|
9
8
|
* Whoever creates the lock file holds the key; releasing deletes it. Waiters
|
|
10
9
|
* retry in no particular order, so this store is not FIFO.
|
|
11
10
|
*/
|
|
12
11
|
export class LockFileStore extends FileLockStore {
|
|
13
|
-
/**
|
|
14
|
-
longestSuffix = Presence.suffixLength;
|
|
12
|
+
/** The longer of a presence file and the draft that `createExclusive` writes beside the record. */
|
|
13
|
+
longestSuffix = Math.max(Presence.suffixLength, draftSuffixLength);
|
|
15
14
|
lock(path, signal) {
|
|
16
15
|
const me = Caller.current();
|
|
17
|
-
return this.poll(() => this.#attempt(path, me),
|
|
16
|
+
return this.poll(() => this.#attempt(path, me), signal);
|
|
18
17
|
}
|
|
19
18
|
async tryLock(path) {
|
|
20
19
|
const me = Caller.current();
|
|
@@ -28,15 +27,11 @@ export class LockFileStore extends FileLockStore {
|
|
|
28
27
|
await this.#evictIfGone(path, holder);
|
|
29
28
|
return undefined;
|
|
30
29
|
}
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
throw error;
|
|
35
|
-
});
|
|
36
|
-
if (!created) {
|
|
37
|
-
await presence.withdraw();
|
|
30
|
+
await using claiming = new AsyncDisposableStack();
|
|
31
|
+
const presence = claiming.adopt(Presence.claim(path, me), (claimed) => claimed.withdraw());
|
|
32
|
+
if (!(await createExclusive(path, me.serialize())))
|
|
38
33
|
return undefined;
|
|
39
|
-
|
|
34
|
+
claiming.move();
|
|
40
35
|
return {
|
|
41
36
|
[Symbol.asyncDispose]: () => presence.releaseAfter(() => removeLockFile(path)),
|
|
42
37
|
};
|
|
@@ -46,7 +41,7 @@ export class LockFileStore extends FileLockStore {
|
|
|
46
41
|
return Presence.isNamedCallerPresent(path, () => readHolder(path));
|
|
47
42
|
}
|
|
48
43
|
async #evictIfGone(path, holder) {
|
|
49
|
-
const state = await Presence.judge(path, holder,
|
|
44
|
+
const state = await Presence.judge(path, holder, () => readHolder(path));
|
|
50
45
|
if (state !== 'gone')
|
|
51
46
|
return;
|
|
52
47
|
await this.withReclaimLock(path, async () => {
|
|
@@ -61,13 +56,9 @@ function removeLockFile(path) {
|
|
|
61
56
|
return patiently(() => unlink(path));
|
|
62
57
|
}
|
|
63
58
|
async function readHolder(path) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (isErrno(error, 'ENOENT'))
|
|
69
|
-
return undefined;
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
59
|
+
const content = await readRecord(path);
|
|
60
|
+
if (content === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
return Caller.parse(content);
|
|
72
63
|
}
|
|
73
64
|
//# sourceMappingURL=lock-file-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-file-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/lock-file-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"lock-file-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/lock-file-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Caller } from './caller.ts';
|
|
2
2
|
/**
|
|
3
|
-
* A caller's presence:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* except through SQLite: closing any other handle to it drops this process's
|
|
9
|
-
* lock without a word.
|
|
3
|
+
* A caller's presence: the file lock of a file of its own, held from before
|
|
4
|
+
* the caller's record (a lock file, a ticket, or the holder record of
|
|
5
|
+
* `SqliteStore`) appears until after it is gone. The kernel ends it when the
|
|
6
|
+
* caller's process or thread stops, however it stops, so a waiter that can
|
|
7
|
+
* check the file knows the caller is gone.
|
|
10
8
|
*/
|
|
11
9
|
export declare class Presence {
|
|
12
10
|
#private;
|
|
@@ -15,20 +13,15 @@ export declare class Presence {
|
|
|
15
13
|
static readonly suffixLength: number;
|
|
16
14
|
/** The presence file of `caller`, whose record is at `record`. */
|
|
17
15
|
static pathOf(record: string, caller: Caller): string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* Whether the caller behind `path` still runs. Reading needs a lock that the
|
|
21
|
-
* caller's exclusive transaction refuses, and opening read-only never creates
|
|
22
|
-
* the file.
|
|
23
|
-
*/
|
|
24
|
-
static check(path: string): 'present' | 'gone' | 'missing';
|
|
16
|
+
/** Claims the presence of `caller`, whose record is at `record`. */
|
|
17
|
+
static claim(record: string, caller: Caller): Presence;
|
|
25
18
|
/**
|
|
26
19
|
* Whether `caller`, whom the record at `record` named, still runs. A caller's
|
|
27
20
|
* record goes before its presence file, so a missing presence file is a
|
|
28
|
-
* fault while `
|
|
29
|
-
* caller `moved` on when it does not.
|
|
21
|
+
* fault while `readNamed` still reads the caller from the record, and means
|
|
22
|
+
* the caller `moved` on when it does not.
|
|
30
23
|
*/
|
|
31
|
-
static judge(record: string, caller: Caller,
|
|
24
|
+
static judge(record: string, caller: Caller, readNamed: () => Promise<Caller | undefined>): Promise<'present' | 'gone' | 'moved'>;
|
|
32
25
|
/** Whether the caller that `readNamed` reads from the record at `record` still runs. Writes nothing. */
|
|
33
26
|
static isNamedCallerPresent(record: string, readNamed: () => Promise<Caller | undefined>): Promise<boolean>;
|
|
34
27
|
/** Deletes the file of a presence that has ended. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/presence.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/presence.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;GAMG;AACH,qBAAa,QAAQ;;IAInB,OAAO;IAKP,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,CAAC,YAAY,SAAgD;IAE5E,kEAAkE;IAClE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAIrD,oEAAoE;IACpE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ;IAatD;;;;;OAKG;WACU,KAAK,CAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAC3C,OAAO,CAAC,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IAWxC,wGAAwG;WAC3F,oBAAoB,CAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAC3C,OAAO,CAAC,OAAO,CAAC;IASnB,qDAAqD;WACxC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD,sGAAsG;IACtG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK;IAMnD,uFAAuF;IACvF,GAAG,IAAI,IAAI;IAIX;;;;OAIG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpE,uEAAuE;IACjE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAIhC"}
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
1
|
import { unlink } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
4
|
-
import { patiently } from "../../shared/fs/patiently.js";
|
|
5
|
-
import { isBusy } from "../../shared/sqlite/is-busy.js";
|
|
6
|
-
import { isCantOpen } from "../../shared/sqlite/is-cant-open.js";
|
|
2
|
+
import { FileLock, patiently } from '@zukhruf/fs';
|
|
7
3
|
import { Caller } from "./caller.js";
|
|
8
|
-
/** Garbage collection closes a connection nobody references, which would end a presence its caller still holds. */
|
|
9
|
-
const open = new Set();
|
|
10
4
|
/**
|
|
11
|
-
* A caller's presence:
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* except through SQLite: closing any other handle to it drops this process's
|
|
17
|
-
* lock without a word.
|
|
5
|
+
* A caller's presence: the file lock of a file of its own, held from before
|
|
6
|
+
* the caller's record (a lock file, a ticket, or the holder record of
|
|
7
|
+
* `SqliteStore`) appears until after it is gone. The kernel ends it when the
|
|
8
|
+
* caller's process or thread stops, however it stops, so a waiter that can
|
|
9
|
+
* check the file knows the caller is gone.
|
|
18
10
|
*/
|
|
19
11
|
export class Presence {
|
|
20
12
|
#path;
|
|
21
|
-
#
|
|
22
|
-
constructor(path,
|
|
13
|
+
#lock;
|
|
14
|
+
constructor(path, lock) {
|
|
23
15
|
this.#path = path;
|
|
24
|
-
this.#
|
|
16
|
+
this.#lock = lock;
|
|
25
17
|
}
|
|
26
18
|
/** The length of what `pathOf` adds to a record's path. */
|
|
27
19
|
static suffixLength = Presence.pathOf('', Caller.current()).length;
|
|
@@ -29,62 +21,33 @@ export class Presence {
|
|
|
29
21
|
static pathOf(record, caller) {
|
|
30
22
|
return `${record}.${caller.id}.presence`;
|
|
31
23
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
catch (error) {
|
|
40
|
-
database.close();
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
open.add(database);
|
|
44
|
-
return new Presence(path, database);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Whether the caller behind `path` still runs. Reading needs a lock that the
|
|
48
|
-
* caller's exclusive transaction refuses, and opening read-only never creates
|
|
49
|
-
* the file.
|
|
50
|
-
*/
|
|
51
|
-
static check(path) {
|
|
52
|
-
let database;
|
|
53
|
-
try {
|
|
54
|
-
database = new DatabaseSync(path, { readOnly: true, timeout: 0 });
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
// A file that exists but cannot be opened is a fault, not a missing presence.
|
|
58
|
-
if (isCantOpen(error) && !existsSync(path))
|
|
59
|
-
return 'missing';
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
database.prepare('SELECT count(*) FROM sqlite_schema').get();
|
|
64
|
-
return 'gone';
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
if (isBusy(error))
|
|
68
|
-
return 'present';
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
finally {
|
|
72
|
-
database.close();
|
|
24
|
+
/** Claims the presence of `caller`, whose record is at `record`. */
|
|
25
|
+
static claim(record, caller) {
|
|
26
|
+
const path = Presence.pathOf(record, caller);
|
|
27
|
+
using opened = new DisposableStack();
|
|
28
|
+
const lock = opened.use(FileLock.open(path));
|
|
29
|
+
if (!lock.tryLock()) {
|
|
30
|
+
throw new Error(`The presence file ${JSON.stringify(path)} is locked already, but it belongs to one caller only. Another program opened it.`);
|
|
73
31
|
}
|
|
32
|
+
opened.move();
|
|
33
|
+
return new Presence(path, lock);
|
|
74
34
|
}
|
|
75
35
|
/**
|
|
76
36
|
* Whether `caller`, whom the record at `record` named, still runs. A caller's
|
|
77
37
|
* record goes before its presence file, so a missing presence file is a
|
|
78
|
-
* fault while `
|
|
79
|
-
* caller `moved` on when it does not.
|
|
38
|
+
* fault while `readNamed` still reads the caller from the record, and means
|
|
39
|
+
* the caller `moved` on when it does not.
|
|
80
40
|
*/
|
|
81
|
-
static async judge(record, caller,
|
|
41
|
+
static async judge(record, caller, readNamed) {
|
|
82
42
|
const path = Presence.pathOf(record, caller);
|
|
83
|
-
const state =
|
|
84
|
-
if (state
|
|
85
|
-
return
|
|
86
|
-
if (
|
|
43
|
+
const state = FileLock.check(path);
|
|
44
|
+
if (state === 'locked')
|
|
45
|
+
return 'present';
|
|
46
|
+
if (state === 'unlocked')
|
|
47
|
+
return 'gone';
|
|
48
|
+
if ((await readNamed())?.id === caller.id) {
|
|
87
49
|
throw Presence.missing(record, path);
|
|
50
|
+
}
|
|
88
51
|
return 'moved';
|
|
89
52
|
}
|
|
90
53
|
/** Whether the caller that `readNamed` reads from the record at `record` still runs. Writes nothing. */
|
|
@@ -93,7 +56,7 @@ export class Presence {
|
|
|
93
56
|
const caller = await readNamed();
|
|
94
57
|
if (!caller)
|
|
95
58
|
return false;
|
|
96
|
-
const state = await Presence.judge(record, caller,
|
|
59
|
+
const state = await Presence.judge(record, caller, readNamed);
|
|
97
60
|
if (state !== 'moved')
|
|
98
61
|
return state === 'present';
|
|
99
62
|
}
|
|
@@ -113,9 +76,7 @@ export class Presence {
|
|
|
113
76
|
}
|
|
114
77
|
/** Lets the kernel lock go. The file stays for whoever removes the caller's record. */
|
|
115
78
|
end() {
|
|
116
|
-
|
|
117
|
-
this.#database.exec('ROLLBACK');
|
|
118
|
-
this.#database.close();
|
|
79
|
+
this.#lock[Symbol.dispose]();
|
|
119
80
|
}
|
|
120
81
|
/**
|
|
121
82
|
* Removes the caller's record, then ends this presence and deletes its file.
|