@delali/sirannon-db 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backup-scheduler/index.d.ts +15 -1
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/baseline-D93hcIEE.d.ts +17 -0
- package/dist/{change-tracker-DKRVUC3l.d.ts → change-tracker-DDmXB754.d.ts} +56 -8
- package/dist/{chunk-5NOIGN5Y.mjs → chunk-2QLXDHAP.mjs} +1 -1
- package/dist/{chunk-LNY2VVHE.mjs → chunk-7C36BCSN.mjs} +1 -1
- package/dist/{chunk-NVQS53NT.mjs → chunk-7FQRQH5Z.mjs} +53 -64
- package/dist/{chunk-D7LAYTKN.mjs → chunk-7R4ER4FB.mjs} +1 -1
- package/dist/{chunk-FHWTZFI4.mjs → chunk-BQFQ65OL.mjs} +1 -1
- package/dist/{chunk-O7SLN3GI.mjs → chunk-BTTFW4Z4.mjs} +1 -1
- package/dist/{chunk-H6PIVVDN.mjs → chunk-CCZK6LCB.mjs} +38 -25
- package/dist/{chunk-JZGINXTN.mjs → chunk-HCCGEIZ2.mjs} +2 -2
- package/dist/{chunk-67M7KAH6.mjs → chunk-IWGIYDMZ.mjs} +1 -1
- package/dist/{chunk-LFZ37BSX.mjs → chunk-OUSWVNWT.mjs} +1 -1
- package/dist/{chunk-HR5CWTLC.mjs → chunk-P2VJYRVY.mjs} +60 -7
- package/dist/{chunk-UC3SCMIN.mjs → chunk-PBRXXISQ.mjs} +3 -0
- package/dist/{chunk-JU64Y7HM.mjs → chunk-SBL6GN43.mjs} +1 -1
- package/dist/{chunk-EBJXPQQO.mjs → chunk-UPKKSUPA.mjs} +2 -2
- package/dist/{chunk-TJF5GZSV.mjs → chunk-VOSJBZ6Q.mjs} +1 -1
- package/dist/{chunk-PIKHN33N.mjs → chunk-VOYGMAU7.mjs} +9 -1
- package/dist/{chunk-H237TXZW.mjs → chunk-WJ67DTD6.mjs} +48 -6
- package/dist/{chunk-OQVZBEBY.mjs → chunk-XF2HH5E6.mjs} +4 -61
- package/dist/client/index.d.ts +211 -12
- package/dist/client/index.mjs +155 -67
- package/dist/client/topology.d.ts +55 -7
- package/dist/client/topology.mjs +20 -1
- package/dist/{client-base-CLWmH5Ln.d.ts → client-base-CmZO0v3m.d.ts} +133 -24
- package/dist/codegen/cli.mjs +3 -3
- package/dist/codegen/index.d.ts +92 -2
- package/dist/codegen/index.mjs +3 -3
- package/dist/core/index.d.ts +177 -15
- package/dist/core/index.mjs +2481 -2226
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-B5Qv1-cU.d.ts +380 -0
- package/dist/driver/better-sqlite3.d.ts +18 -1
- package/dist/driver/better-sqlite3.mjs +5 -5
- package/dist/driver/bun.d.ts +28 -0
- package/dist/driver/expo.d.ts +17 -0
- package/dist/driver/node.d.ts +18 -1
- package/dist/driver/node.mjs +5 -5
- package/dist/driver/wa-sqlite.d.ts +18 -1
- package/dist/{errors-Bw5MdNCu.d.ts → errors-Dei4GdBb.d.ts} +80 -7
- package/dist/file-migrations/index.d.ts +54 -2
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/{operation-registry-9DcvxcE5.d.ts → operation-registry-hlbhqu7q.d.ts} +50 -1
- package/dist/{primary-wins-DPAm2AKG.d.ts → primary-wins-B0np8JS3.d.ts} +25 -1
- package/dist/protocol-rqANt-9Q.d.ts +152 -0
- package/dist/query-types-DL3LtPvY.d.ts +95 -0
- package/dist/react/index.d.ts +58 -3
- package/dist/replication/coordinator/etcd.d.ts +63 -3
- package/dist/replication/coordinator/etcd.mjs +82 -46
- package/dist/replication/index.d.ts +329 -95
- package/dist/replication/index.mjs +256 -141
- package/dist/server/index.d.ts +230 -12
- package/dist/server/index.mjs +63 -50
- package/dist/{server-options-1JHu8pid.d.ts → server-options-Dab_Jvd_.d.ts} +96 -12
- package/dist/sirannon-CMhiJa5Y.d.ts +111 -0
- package/dist/transport/grpc.d.ts +93 -9
- package/dist/transport/grpc.mjs +63 -20
- package/dist/transport/memory.d.ts +50 -20
- package/dist/transport/memory.mjs +25 -0
- package/dist/{types-CL6piSnD.d.ts → types-BCejqzNA.d.ts} +20 -0
- package/dist/types-CMBcFPhb.d.ts +336 -0
- package/dist/types-CjhxcjhA.d.ts +123 -0
- package/dist/types-DyrCiWuc.d.ts +499 -0
- package/dist/types-rVZKnKN-.d.ts +591 -0
- package/package.json +7 -1
- package/dist/baseline-Br77Fnhb.d.ts +0 -6
- package/dist/database-BY0L5Q2n.d.ts +0 -172
- package/dist/protocol-6KrSq2Hy.d.ts +0 -66
- package/dist/sirannon-DaQSyhbJ.d.ts +0 -36
- package/dist/types-B7gmEsZW.d.ts +0 -221
- package/dist/types-BsVabqSI.d.ts +0 -139
- package/dist/types-C_D8IhpO.d.ts +0 -60
- package/dist/types-zhnRXrsb.d.ts +0 -384
package/dist/react/index.d.ts
CHANGED
|
@@ -1,18 +1,73 @@
|
|
|
1
|
-
import { L as LiveQueryOptions, a as LiveQuery, b as LiveQueryState } from '../types-
|
|
2
|
-
import { O as OperationRef } from '../operation-registry-
|
|
3
|
-
import { P as Params } from '../types-
|
|
1
|
+
import { L as LiveQueryOptions, a as LiveQuery, b as LiveQueryState } from '../types-BCejqzNA.js';
|
|
2
|
+
import { O as OperationRef } from '../operation-registry-hlbhqu7q.js';
|
|
3
|
+
import { P as Params } from '../query-types-DL3LtPvY.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The one method these hooks need from a database, so both a local
|
|
7
|
+
* `Database` and a `RemoteDatabase` satisfy it.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
5
11
|
interface LiveDatabase {
|
|
12
|
+
/** Opens a live query for a registered read. */
|
|
6
13
|
live(operation: string | OperationRef<never, never>, args?: never, options?: LiveQueryOptions): Promise<LiveQuery<unknown>>;
|
|
7
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Settings for one call to `useLiveQuery`.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
8
20
|
interface UseLiveQueryOptions extends LiveQueryOptions {
|
|
21
|
+
/** Set false to hold the query closed, which suits a query that depends on data you do not have yet. */
|
|
9
22
|
enabled?: boolean;
|
|
10
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Subscribes a component to a registered read and re-renders it as the rows change.
|
|
26
|
+
*
|
|
27
|
+
* The query closes when the component unmounts.
|
|
28
|
+
*
|
|
29
|
+
* @param database - Database the read runs against.
|
|
30
|
+
* @param operation - Name of the registered read.
|
|
31
|
+
* @param args - Arguments the read takes.
|
|
32
|
+
* @param options - Whether the query runs, plus the live-query settings.
|
|
33
|
+
* @returns Whether the query is pending, ready with rows, or failed.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
11
37
|
declare function useLiveQuery<Row = Record<string, unknown>>(database: LiveDatabase, operation: string, args?: Params, options?: UseLiveQueryOptions): LiveQueryState<Row>;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribes a component to a registered read and re-renders it as the rows change.
|
|
40
|
+
*
|
|
41
|
+
* @param database - Database the read runs against.
|
|
42
|
+
* @param operation - Reference to the registered read, which carries its argument and row types.
|
|
43
|
+
* @param args - Arguments the read takes.
|
|
44
|
+
* @param options - Whether the query runs, plus the live-query settings.
|
|
45
|
+
* @returns Whether the query is pending, ready with rows, or failed.
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
12
49
|
declare function useLiveQuery<Args, Row>(database: LiveDatabase, operation: OperationRef<Args, Row>, args: Args, options?: UseLiveQueryOptions): LiveQueryState<Row>;
|
|
50
|
+
/**
|
|
51
|
+
* Returns a stable callback that runs a registered write.
|
|
52
|
+
*
|
|
53
|
+
* @param database - Database the write runs against.
|
|
54
|
+
* @param command - Reference to the registered write, which carries its argument type.
|
|
55
|
+
* @returns A callback that takes the write's arguments and resolves with its result.
|
|
56
|
+
*
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
13
59
|
declare function useCommand<Args, Result>(database: {
|
|
14
60
|
execute(operation: OperationRef<Args, unknown>, args: Args): Promise<Result>;
|
|
15
61
|
}, command: OperationRef<Args, unknown>): (args: Args) => Promise<Result>;
|
|
62
|
+
/**
|
|
63
|
+
* Returns a stable callback that runs a statement or a registered write by name.
|
|
64
|
+
*
|
|
65
|
+
* @param database - Database the write runs against.
|
|
66
|
+
* @param command - The statement to run, or the name of a registered write.
|
|
67
|
+
* @returns A callback that takes the parameters and resolves with the result.
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
16
71
|
declare function useCommand<Result>(database: {
|
|
17
72
|
execute(sql: string, params?: Params): Promise<Result>;
|
|
18
73
|
}, command: string): (params?: Params) => Promise<Result>;
|
|
@@ -1,18 +1,57 @@
|
|
|
1
1
|
import { IOptions } from 'etcd3';
|
|
2
|
-
import { C as ClusterCoordinator, A as AcquireControllerLeaseInput,
|
|
2
|
+
import { C as ClusterCoordinator, A as AcquireControllerLeaseInput, a as AcquireControllerLeaseResult, R as RegisterNodeSessionInput, b as CoordinatorNodeSession, S as SetReplicationGroupStateInput, c as ReplicationGroupState, d as ReplicationGroupWatcher, e as CoordinatorWatchDisposer, f as CompareAndAdvancePrimaryTermInput, g as CompareAndAdvancePrimaryTermResult, U as UpdateInSyncSetInput, h as AdmitNodeToInSyncSetInput, i as UpdateNodeMaintenanceInput, P as PromoteEligibleReplicaInput } from '../../types-CMBcFPhb.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @public
|
|
6
|
+
*
|
|
7
|
+
* Where the etcd coordinator connects, under which key prefix it stores group state, and how it authenticates.
|
|
8
|
+
*/
|
|
4
9
|
interface EtcdClusterCoordinatorOptions {
|
|
10
|
+
/**
|
|
11
|
+
* etcd endpoints to connect to. Production access requires https addresses.
|
|
12
|
+
*/
|
|
5
13
|
hosts: string | string[];
|
|
14
|
+
/**
|
|
15
|
+
* Prefix applied to every key this coordinator writes, which keeps clusters apart in one etcd.
|
|
16
|
+
*/
|
|
6
17
|
keyPrefix: string;
|
|
18
|
+
/**
|
|
19
|
+
* TLS material, which production access requires.
|
|
20
|
+
*/
|
|
7
21
|
credentials?: IOptions['credentials'];
|
|
22
|
+
/**
|
|
23
|
+
* Username and password, as an alternative to a client certificate.
|
|
24
|
+
*/
|
|
8
25
|
auth?: IOptions['auth'];
|
|
26
|
+
/**
|
|
27
|
+
* Options passed straight through to the underlying gRPC channel.
|
|
28
|
+
*/
|
|
9
29
|
grpcOptions?: IOptions['grpcOptions'];
|
|
30
|
+
/**
|
|
31
|
+
* Milliseconds to wait for a connection to etcd.
|
|
32
|
+
*/
|
|
10
33
|
dialTimeoutMs?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Milliseconds any single etcd call may take.
|
|
36
|
+
*/
|
|
11
37
|
defaultCallTimeoutMs?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Accepts plain http endpoints without credentials, which suits tests only.
|
|
40
|
+
*/
|
|
12
41
|
allowInsecure?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Called when a group watch fails so that a caller can log it or raise an alert.
|
|
44
|
+
*/
|
|
13
45
|
onWatcherError?: (error: Error) => void;
|
|
14
46
|
}
|
|
15
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @public
|
|
50
|
+
*
|
|
51
|
+
* Stores primary authority, node sessions, group state, and the in-sync set in etcd.
|
|
52
|
+
*
|
|
53
|
+
* Build one with {@link createEtcdCoordinator}.
|
|
54
|
+
*/
|
|
16
55
|
declare class EtcdClusterCoordinator implements ClusterCoordinator {
|
|
17
56
|
private readonly client;
|
|
18
57
|
private readonly namespace;
|
|
@@ -22,25 +61,46 @@ declare class EtcdClusterCoordinator implements ClusterCoordinator {
|
|
|
22
61
|
private readonly watchers;
|
|
23
62
|
private readonly groups;
|
|
24
63
|
constructor(options: EtcdClusterCoordinatorOptions);
|
|
64
|
+
/** Bids for the controller lease, and reports who holds it. */
|
|
25
65
|
tryAcquireControllerLease(input: AcquireControllerLeaseInput): Promise<AcquireControllerLeaseResult>;
|
|
66
|
+
/** Extends a lease, and reports false once it has already lapsed. */
|
|
26
67
|
renewLease(leaseId: string, ttlMs: number): Promise<boolean>;
|
|
68
|
+
/** Gives up a lease at once instead of waiting for it to lapse. */
|
|
27
69
|
releaseLease(leaseId: string): Promise<boolean>;
|
|
70
|
+
/** Records one node as a live member of the cluster. */
|
|
28
71
|
registerNodeSession(input: RegisterNodeSessionInput): Promise<CoordinatorNodeSession>;
|
|
72
|
+
/** Reads one node's session, and returns null once its lease has lapsed. */
|
|
29
73
|
getLiveNodeSession(clusterId: string, nodeId: string): Promise<CoordinatorNodeSession | null>;
|
|
74
|
+
/** Ends one node's membership at once. */
|
|
30
75
|
deregisterNodeSession(clusterId: string, nodeId: string): Promise<void>;
|
|
76
|
+
/** Writes the group's state, which seeds a new group or replaces an existing one. */
|
|
31
77
|
setReplicationGroupState(input: SetReplicationGroupStateInput): Promise<ReplicationGroupState>;
|
|
78
|
+
/** Reads the group's state, and returns null when the group is absent. */
|
|
32
79
|
getReplicationGroupState(clusterId: string, groupId: string): Promise<ReplicationGroupState | null>;
|
|
80
|
+
/** Calls back on each change to the group's state, and returns a function that stops the watch. */
|
|
33
81
|
watchReplicationGroup(clusterId: string, groupId: string, watcher: ReplicationGroupWatcher): Promise<CoordinatorWatchDisposer>;
|
|
82
|
+
/** Promotes a node only while the group is still at the term the caller read. */
|
|
34
83
|
compareAndAdvancePrimaryTerm(input: CompareAndAdvancePrimaryTermInput): Promise<CompareAndAdvancePrimaryTermResult>;
|
|
84
|
+
/** Replaces the group's in-sync set, and optionally moves its durability point. */
|
|
35
85
|
updateInSyncSet(input: UpdateInSyncSetInput): Promise<ReplicationGroupState | null>;
|
|
86
|
+
/** Adds one caught-up node to the in-sync set. */
|
|
36
87
|
admitNodeToInSyncSet(input: AdmitNodeToInSyncSetInput): Promise<ReplicationGroupState | null>;
|
|
88
|
+
/** Marks one node as being taken out of service, rebuilt, or quarantined. */
|
|
37
89
|
updateNodeMaintenance(input: UpdateNodeMaintenanceInput): Promise<ReplicationGroupState | null>;
|
|
90
|
+
/** Promotes whichever in-sync replica is safe to write. */
|
|
38
91
|
promoteEligibleReplica(input: PromoteEligibleReplicaInput): Promise<ReplicationGroupState>;
|
|
92
|
+
/** Closes the etcd client and stops every watch. */
|
|
39
93
|
close(): Promise<void>;
|
|
40
|
-
private discardSupersededLeases;
|
|
41
94
|
private getLeaseFromKey;
|
|
42
|
-
private trackLease;
|
|
43
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* @public
|
|
98
|
+
*
|
|
99
|
+
* Builds a coordinator backed by etcd.
|
|
100
|
+
*
|
|
101
|
+
* @param options - etcd endpoints, key prefix, credentials, and timeouts.
|
|
102
|
+
* @returns The coordinator, ready to pass to a replication engine.
|
|
103
|
+
*/
|
|
44
104
|
declare function createEtcdCoordinator(options: EtcdClusterCoordinatorOptions): EtcdClusterCoordinator;
|
|
45
105
|
|
|
46
106
|
export { EtcdClusterCoordinator, type EtcdClusterCoordinatorOptions, createEtcdCoordinator };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { compatibilityAllowsPromotion } from '../../chunk-ER7ODTDA.mjs';
|
|
2
|
-
import { CoordinatorError, NoSafePrimaryError } from '../../chunk-
|
|
3
|
-
import '../../chunk-
|
|
4
|
-
import '../../chunk-
|
|
2
|
+
import { CoordinatorError, NoSafePrimaryError } from '../../chunk-BTTFW4Z4.mjs';
|
|
3
|
+
import '../../chunk-2QLXDHAP.mjs';
|
|
4
|
+
import '../../chunk-PBRXXISQ.mjs';
|
|
5
5
|
import { Etcd3 } from 'etcd3';
|
|
6
6
|
|
|
7
7
|
// src/replication/coordinator/group-rules.ts
|
|
@@ -473,12 +473,65 @@ var EtcdGroupStore = class {
|
|
|
473
473
|
}
|
|
474
474
|
};
|
|
475
475
|
|
|
476
|
+
// src/replication/coordinator/etcd-lease-registry.ts
|
|
477
|
+
async function revokeLeaseQuietly(lease) {
|
|
478
|
+
try {
|
|
479
|
+
await lease.revoke();
|
|
480
|
+
} catch {
|
|
481
|
+
lease.release();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
var EtcdLeaseRegistry = class {
|
|
485
|
+
constructor(onLeaseLost) {
|
|
486
|
+
this.onLeaseLost = onLeaseLost;
|
|
487
|
+
}
|
|
488
|
+
entries = /* @__PURE__ */ new Map();
|
|
489
|
+
get(leaseId) {
|
|
490
|
+
return this.entries.get(leaseId);
|
|
491
|
+
}
|
|
492
|
+
forget(leaseId) {
|
|
493
|
+
this.entries.delete(leaseId);
|
|
494
|
+
}
|
|
495
|
+
leaseIdsForKey(key) {
|
|
496
|
+
const matches = [];
|
|
497
|
+
for (const [leaseId, entry] of this.entries) {
|
|
498
|
+
if (entry.key === key) matches.push(leaseId);
|
|
499
|
+
}
|
|
500
|
+
return matches;
|
|
501
|
+
}
|
|
502
|
+
track(lease, entry) {
|
|
503
|
+
this.entries.set(entry.leaseId, { ...entry, lease });
|
|
504
|
+
lease.on("lost", (err) => {
|
|
505
|
+
this.entries.delete(entry.leaseId);
|
|
506
|
+
this.onLeaseLost?.(err instanceof Error ? err : new CoordinatorError(String(err)));
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
async discardSuperseded(key, keepLeaseId) {
|
|
510
|
+
const superseded = [];
|
|
511
|
+
for (const [leaseId, entry] of this.entries) {
|
|
512
|
+
if (entry.key === key && leaseId !== keepLeaseId) {
|
|
513
|
+
this.entries.delete(leaseId);
|
|
514
|
+
superseded.push(entry);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
await Promise.allSettled(superseded.map((entry) => revokeLeaseQuietly(entry.lease)));
|
|
518
|
+
}
|
|
519
|
+
async revokeAll() {
|
|
520
|
+
const revokes = [];
|
|
521
|
+
for (const entry of this.entries.values()) {
|
|
522
|
+
revokes.push(revokeLeaseQuietly(entry.lease));
|
|
523
|
+
}
|
|
524
|
+
this.entries.clear();
|
|
525
|
+
await Promise.allSettled(revokes);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
476
529
|
// src/replication/coordinator/etcd.ts
|
|
477
530
|
var EtcdClusterCoordinator = class {
|
|
478
531
|
client;
|
|
479
532
|
namespace;
|
|
480
533
|
onWatcherError;
|
|
481
|
-
leases
|
|
534
|
+
leases;
|
|
482
535
|
grantedNodeSessionLeaseIds = /* @__PURE__ */ new Map();
|
|
483
536
|
watchers = /* @__PURE__ */ new Set();
|
|
484
537
|
groups;
|
|
@@ -487,8 +540,10 @@ var EtcdClusterCoordinator = class {
|
|
|
487
540
|
this.client = new Etcd3(toEtcdOptions(options));
|
|
488
541
|
this.namespace = this.client.namespace(normaliseKeyPrefix(options.keyPrefix));
|
|
489
542
|
this.onWatcherError = options.onWatcherError;
|
|
543
|
+
this.leases = new EtcdLeaseRegistry(this.onWatcherError);
|
|
490
544
|
this.groups = new EtcdGroupStore(this.namespace, this.watchers, this.onWatcherError);
|
|
491
545
|
}
|
|
546
|
+
/** Bids for the controller lease, and reports who holds it. */
|
|
492
547
|
async tryAcquireControllerLease(input) {
|
|
493
548
|
assertNonEmpty(input.clusterId, "clusterId");
|
|
494
549
|
assertNonEmpty(input.holderId, "holderId");
|
|
@@ -513,7 +568,7 @@ var EtcdClusterCoordinator = class {
|
|
|
513
568
|
const current = await this.getLeaseFromKey(key);
|
|
514
569
|
return { acquired: false, lease: current };
|
|
515
570
|
}
|
|
516
|
-
this.
|
|
571
|
+
this.leases.track(lease, {
|
|
517
572
|
leaseId,
|
|
518
573
|
key,
|
|
519
574
|
ttlMs: input.ttlMs,
|
|
@@ -526,6 +581,7 @@ var EtcdClusterCoordinator = class {
|
|
|
526
581
|
const parsed = parseLease(value);
|
|
527
582
|
return { acquired: true, lease: parsed };
|
|
528
583
|
}
|
|
584
|
+
/** Extends a lease, and reports false once it has already lapsed. */
|
|
529
585
|
async renewLease(leaseId, ttlMs) {
|
|
530
586
|
assertNonEmpty(leaseId, "leaseId");
|
|
531
587
|
assertPositiveTtl(ttlMs);
|
|
@@ -536,7 +592,7 @@ var EtcdClusterCoordinator = class {
|
|
|
536
592
|
try {
|
|
537
593
|
await entry.lease.keepaliveOnce();
|
|
538
594
|
} catch {
|
|
539
|
-
this.leases.
|
|
595
|
+
this.leases.forget(leaseId);
|
|
540
596
|
return false;
|
|
541
597
|
}
|
|
542
598
|
const renewedAtMs = Date.now();
|
|
@@ -559,19 +615,20 @@ var EtcdClusterCoordinator = class {
|
|
|
559
615
|
return true;
|
|
560
616
|
}
|
|
561
617
|
if (!refreshed) {
|
|
562
|
-
this.leases.
|
|
618
|
+
this.leases.forget(leaseId);
|
|
563
619
|
return false;
|
|
564
620
|
}
|
|
565
621
|
entry.ttlMs = ttlMs;
|
|
566
622
|
return true;
|
|
567
623
|
}
|
|
624
|
+
/** Gives up a lease at once instead of waiting for it to lapse. */
|
|
568
625
|
async releaseLease(leaseId) {
|
|
569
626
|
assertNonEmpty(leaseId, "leaseId");
|
|
570
627
|
const entry = this.leases.get(leaseId);
|
|
571
628
|
if (!entry) {
|
|
572
629
|
return false;
|
|
573
630
|
}
|
|
574
|
-
this.leases.
|
|
631
|
+
this.leases.forget(leaseId);
|
|
575
632
|
const currentValue = await this.namespace.get(entry.key).string();
|
|
576
633
|
const currentLeaseId = currentValue ? parseLeaseIdForEntry(entry.kind, currentValue) : null;
|
|
577
634
|
let released = false;
|
|
@@ -582,6 +639,7 @@ var EtcdClusterCoordinator = class {
|
|
|
582
639
|
await revokeLeaseQuietly(entry.lease);
|
|
583
640
|
return released;
|
|
584
641
|
}
|
|
642
|
+
/** Records one node as a live member of the cluster. */
|
|
585
643
|
async registerNodeSession(input) {
|
|
586
644
|
assertNonEmpty(input.clusterId, "clusterId");
|
|
587
645
|
assertNonEmpty(input.nodeId, "nodeId");
|
|
@@ -625,9 +683,9 @@ var EtcdClusterCoordinator = class {
|
|
|
625
683
|
await revokeLeaseQuietly(lease);
|
|
626
684
|
throw new CoordinatorError(`Node session '${input.nodeId}' registration conflicted with a concurrent write`);
|
|
627
685
|
}
|
|
628
|
-
await this.
|
|
686
|
+
await this.leases.discardSuperseded(key, leaseId);
|
|
629
687
|
this.grantedNodeSessionLeaseIds.set(key, leaseId);
|
|
630
|
-
this.
|
|
688
|
+
this.leases.track(lease, {
|
|
631
689
|
leaseId,
|
|
632
690
|
key,
|
|
633
691
|
ttlMs: input.ttlMs,
|
|
@@ -649,46 +707,54 @@ var EtcdClusterCoordinator = class {
|
|
|
649
707
|
});
|
|
650
708
|
return parseNodeSession(rawSession);
|
|
651
709
|
}
|
|
710
|
+
/** Reads one node's session, and returns null once its lease has lapsed. */
|
|
652
711
|
async getLiveNodeSession(clusterId, nodeId) {
|
|
653
712
|
assertNonEmpty(clusterId, "clusterId");
|
|
654
713
|
assertNonEmpty(nodeId, "nodeId");
|
|
655
714
|
const value = await this.namespace.get(nodeSessionKey(clusterId, nodeId)).string();
|
|
656
715
|
return value ? parseNodeSession(value) : null;
|
|
657
716
|
}
|
|
717
|
+
/** Ends one node's membership at once. */
|
|
658
718
|
async deregisterNodeSession(clusterId, nodeId) {
|
|
659
719
|
assertNonEmpty(clusterId, "clusterId");
|
|
660
720
|
assertNonEmpty(nodeId, "nodeId");
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
if (entry.key === key) {
|
|
664
|
-
await this.releaseLease(leaseId);
|
|
665
|
-
}
|
|
721
|
+
for (const leaseId of this.leases.leaseIdsForKey(nodeSessionKey(clusterId, nodeId))) {
|
|
722
|
+
await this.releaseLease(leaseId);
|
|
666
723
|
}
|
|
667
724
|
}
|
|
725
|
+
/** Writes the group's state, which seeds a new group or replaces an existing one. */
|
|
668
726
|
setReplicationGroupState(input) {
|
|
669
727
|
return this.groups.setReplicationGroupState(input);
|
|
670
728
|
}
|
|
729
|
+
/** Reads the group's state, and returns null when the group is absent. */
|
|
671
730
|
getReplicationGroupState(clusterId, groupId) {
|
|
672
731
|
return this.groups.getReplicationGroupState(clusterId, groupId);
|
|
673
732
|
}
|
|
733
|
+
/** Calls back on each change to the group's state, and returns a function that stops the watch. */
|
|
674
734
|
watchReplicationGroup(clusterId, groupId, watcher) {
|
|
675
735
|
return this.groups.watchReplicationGroup(clusterId, groupId, watcher);
|
|
676
736
|
}
|
|
737
|
+
/** Promotes a node only while the group is still at the term the caller read. */
|
|
677
738
|
compareAndAdvancePrimaryTerm(input) {
|
|
678
739
|
return this.groups.compareAndAdvancePrimaryTerm(input);
|
|
679
740
|
}
|
|
741
|
+
/** Replaces the group's in-sync set, and optionally moves its durability point. */
|
|
680
742
|
updateInSyncSet(input) {
|
|
681
743
|
return this.groups.updateInSyncSet(input);
|
|
682
744
|
}
|
|
745
|
+
/** Adds one caught-up node to the in-sync set. */
|
|
683
746
|
admitNodeToInSyncSet(input) {
|
|
684
747
|
return this.groups.admitNodeToInSyncSet(input);
|
|
685
748
|
}
|
|
749
|
+
/** Marks one node as being taken out of service, rebuilt, or quarantined. */
|
|
686
750
|
updateNodeMaintenance(input) {
|
|
687
751
|
return this.groups.updateNodeMaintenance(input);
|
|
688
752
|
}
|
|
753
|
+
/** Promotes whichever in-sync replica is safe to write. */
|
|
689
754
|
promoteEligibleReplica(input) {
|
|
690
755
|
return this.groups.promoteEligibleReplica(input);
|
|
691
756
|
}
|
|
757
|
+
/** Closes the etcd client and stops every watch. */
|
|
692
758
|
async close() {
|
|
693
759
|
const watcherCancels = [];
|
|
694
760
|
for (const watcher of this.watchers) {
|
|
@@ -696,47 +762,17 @@ var EtcdClusterCoordinator = class {
|
|
|
696
762
|
}
|
|
697
763
|
this.watchers.clear();
|
|
698
764
|
await Promise.allSettled(watcherCancels);
|
|
699
|
-
const leaseRevokes = [];
|
|
700
|
-
for (const entry of this.leases.values()) {
|
|
701
|
-
leaseRevokes.push(revokeLeaseQuietly(entry.lease));
|
|
702
|
-
}
|
|
703
|
-
this.leases.clear();
|
|
704
765
|
this.grantedNodeSessionLeaseIds.clear();
|
|
705
|
-
await
|
|
766
|
+
await this.leases.revokeAll();
|
|
706
767
|
this.client.close();
|
|
707
768
|
}
|
|
708
|
-
async discardSupersededLeases(key, keepLeaseId) {
|
|
709
|
-
const superseded = [];
|
|
710
|
-
for (const [leaseId, entry] of this.leases) {
|
|
711
|
-
if (entry.key === key && leaseId !== keepLeaseId) {
|
|
712
|
-
this.leases.delete(leaseId);
|
|
713
|
-
superseded.push(entry);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
await Promise.allSettled(superseded.map((entry) => revokeLeaseQuietly(entry.lease)));
|
|
717
|
-
}
|
|
718
769
|
async getLeaseFromKey(key) {
|
|
719
770
|
const value = await this.namespace.get(key).string();
|
|
720
771
|
return value ? parseLease(value) : null;
|
|
721
772
|
}
|
|
722
|
-
trackLease(lease, entry) {
|
|
723
|
-
const fullEntry = { ...entry, lease };
|
|
724
|
-
this.leases.set(entry.leaseId, fullEntry);
|
|
725
|
-
lease.on("lost", (err) => {
|
|
726
|
-
this.leases.delete(entry.leaseId);
|
|
727
|
-
this.onWatcherError?.(err instanceof Error ? err : new CoordinatorError(String(err)));
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
773
|
};
|
|
731
774
|
function createEtcdCoordinator(options) {
|
|
732
775
|
return new EtcdClusterCoordinator(options);
|
|
733
776
|
}
|
|
734
|
-
async function revokeLeaseQuietly(lease) {
|
|
735
|
-
try {
|
|
736
|
-
await lease.revoke();
|
|
737
|
-
} catch {
|
|
738
|
-
lease.release();
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
777
|
|
|
742
778
|
export { EtcdClusterCoordinator, createEtcdCoordinator };
|