@delali/sirannon-db 0.3.4-next.62 → 0.3.4
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 +79 -56
- package/dist/backup/index.d.ts +44 -45
- package/dist/backup/index.mjs +7 -7
- package/dist/backup-scheduler/index.d.ts +42 -41
- package/dist/backup-scheduler/index.mjs +4 -4
- package/dist/baseline-Co3iIsN_.d.ts +19 -0
- package/dist/change-tracker-DqCdeEyx.d.ts +128 -0
- package/dist/{chunk-VOSJBZ6Q.mjs → chunk-2ES3DT4A.mjs} +71 -33
- package/dist/{chunk-6CMCO7S4.mjs → chunk-2O3PP4CF.mjs} +1 -1
- package/dist/{chunk-E7Q7HE46.mjs → chunk-3BZLZ47R.mjs} +26 -26
- package/dist/chunk-4KASJKKK.mjs +2501 -0
- package/dist/{chunk-MYNI2WFS.mjs → chunk-52XW3Z76.mjs} +40 -8
- package/dist/chunk-55YRLSM2.mjs +57 -0
- package/dist/{chunk-KDAHYFW7.mjs → chunk-6AOJCNRU.mjs} +5 -5
- package/dist/{chunk-KXTYR2OQ.mjs → chunk-A3PHCO6W.mjs} +39 -111
- package/dist/chunk-ADNWEHGZ.mjs +299 -0
- package/dist/{chunk-2QLXDHAP.mjs → chunk-BLMRNSUG.mjs} +11 -2
- package/dist/chunk-BVMZJOVM.mjs +5 -0
- package/dist/{chunk-IWGIYDMZ.mjs → chunk-BZWDPF4G.mjs} +1 -1
- package/dist/{chunk-YVF2ZJTE.mjs → chunk-CZL4D3BM.mjs} +1 -1
- package/dist/{chunk-7SC2TZAS.mjs → chunk-DX4WGJL7.mjs} +2 -2
- package/dist/{chunk-45MKELYA.mjs → chunk-ESTZZPT5.mjs} +132 -13
- package/dist/{chunk-QP5L5DFJ.mjs → chunk-FJPR2CJX.mjs} +3 -3
- package/dist/chunk-HR7K3GZW.mjs +36 -0
- package/dist/{chunk-LXCGPZPA.mjs → chunk-IEKIYO4V.mjs} +1 -1
- package/dist/{chunk-TUD5CJ76.mjs → chunk-KB4OLJAD.mjs} +26 -19
- package/dist/{chunk-PBRXXISQ.mjs → chunk-KINDFBQP.mjs} +3 -2
- package/dist/{chunk-E6IMX7RS.mjs → chunk-KJLNYOOL.mjs} +2 -2
- package/dist/{chunk-BTTFW4Z4.mjs → chunk-KU32OT4H.mjs} +2 -10
- package/dist/{chunk-JQWGHSEH.mjs → chunk-NVSQ2T5Q.mjs} +71 -70
- package/dist/{chunk-W4EUTJNT.mjs → chunk-NZGSDALX.mjs} +94 -96
- package/dist/{chunk-U445A3C4.mjs → chunk-QGVRRKB2.mjs} +1 -1
- package/dist/{chunk-GYCEJNU4.mjs → chunk-R7GBXNTO.mjs} +1 -1
- package/dist/{chunk-7C36BCSN.mjs → chunk-VDN6FHU4.mjs} +1 -1
- package/dist/{chunk-6IYS4WLH.mjs → chunk-VO3ZWA2G.mjs} +142 -115
- package/dist/chunk-WOMADPM4.mjs +12 -0
- package/dist/{chunk-OUSWVNWT.mjs → chunk-YCMB6GR4.mjs} +1 -1
- package/dist/{chunk-YNTDOFTN.mjs → chunk-YW7IKQ7T.mjs} +1 -1
- package/dist/{chunk-M2BEAH5I.mjs → chunk-ZN4HI5FK.mjs} +126 -72
- package/dist/{chunk-SBL6GN43.mjs → chunk-ZYWI45EN.mjs} +1 -1
- package/dist/client/index.d.ts +135 -137
- package/dist/client/index.mjs +177 -82
- package/dist/client/topology.d.ts +31 -31
- package/dist/client/topology.mjs +42 -23
- package/dist/client-base-BxTocU1x.d.ts +398 -0
- package/dist/client-streams-YKANXMWO.mjs +112 -0
- package/dist/codegen/cli.mjs +4 -4
- package/dist/codegen/index.d.ts +36 -29
- package/dist/codegen/index.mjs +4 -4
- package/dist/core/index.d.ts +130 -115
- package/dist/core/index.mjs +387 -297
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-CGDsXW1K.d.ts +959 -0
- package/dist/database-backup-DcLhxoa1.d.ts +143 -0
- package/dist/driver/better-sqlite3.d.ts +11 -10
- package/dist/driver/better-sqlite3.mjs +11 -11
- package/dist/driver/bun.d.ts +9 -8
- package/dist/driver/expo.d.ts +6 -7
- package/dist/driver/node.d.ts +11 -10
- package/dist/driver/node.mjs +13 -11
- package/dist/driver/wa-sqlite.d.ts +6 -6
- package/dist/errors-CJUkQ-KL.d.ts +199 -0
- package/dist/file-migrations/index.d.ts +20 -18
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/forward-rpc-VRWFZXHN.mjs +44 -0
- package/dist/operation-registry-CGhNT-Sn.d.ts +96 -0
- package/dist/primary-wins-CHNOYuN7.d.ts +81 -0
- package/dist/protocol-DFpNNsIT.d.ts +153 -0
- package/dist/query-types-5B02ixH0.d.ts +129 -0
- package/dist/react/index.d.ts +22 -22
- package/dist/replication/coordinator/etcd.d.ts +37 -31
- package/dist/replication/coordinator/etcd.mjs +296 -131
- package/dist/replication/index.d.ts +201 -161
- package/dist/replication/index.mjs +630 -529
- package/dist/restore-options-CH8mE-Tb.d.ts +84 -0
- package/dist/server/index.d.ts +201 -191
- package/dist/server/index.mjs +736 -619
- package/dist/server-options-aYtCkefX.d.ts +317 -0
- package/dist/server-streams-24XL4T43.mjs +254 -0
- package/dist/sirannon-J3EgPVD7.d.ts +179 -0
- package/dist/transport/grpc.d.ts +37 -24
- package/dist/transport/grpc.mjs +83 -3199
- package/dist/transport/memory.d.ts +25 -26
- package/dist/transport/memory.mjs +18 -9
- package/dist/types-BrAiDGOb.d.ts +505 -0
- package/dist/types-CEJG0eFS.d.ts +88 -0
- package/dist/types-Dc8oWlxj.d.ts +1580 -0
- package/dist/types-DqODKeOq.d.ts +123 -0
- package/dist/types-guEKSzMr.d.ts +367 -0
- package/package.json +7 -7
- package/dist/baseline-D93hcIEE.d.ts +0 -17
- package/dist/change-tracker-CjRFuZdB.d.ts +0 -144
- package/dist/client-base-Cmd9EVme.d.ts +0 -397
- package/dist/database-BLQvqjsw.d.ts +0 -917
- package/dist/database-backup-B7oszQ2n.d.ts +0 -141
- package/dist/errors-Dei4GdBb.d.ts +0 -196
- package/dist/operation-registry-oiPuSYI6.d.ts +0 -94
- package/dist/primary-wins-B0np8JS3.d.ts +0 -85
- package/dist/protocol-HI-nY8DD.d.ts +0 -152
- package/dist/query-types-p1liOP-b.d.ts +0 -127
- package/dist/restore-options-CksYsH1l.d.ts +0 -83
- package/dist/server-options-Bj5iJnjx.d.ts +0 -292
- package/dist/sirannon-BPxC5tC2.d.ts +0 -178
- package/dist/types-B0EhgDbr.d.ts +0 -340
- package/dist/types-CbjfRwrK.d.ts +0 -499
- package/dist/types-CjhxcjhA.d.ts +0 -123
- package/dist/types-DPSmjU54.d.ts +0 -1518
- package/dist/types-DTO4CYhW.d.ts +0 -88
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { compatibilityAllowsPromotion } from '../../chunk-ER7ODTDA.mjs';
|
|
2
|
-
import { CoordinatorError, NoSafePrimaryError } from '../../chunk-
|
|
3
|
-
import '../../chunk-
|
|
4
|
-
import '../../chunk-
|
|
5
|
-
import
|
|
2
|
+
import { CoordinatorError, NoSafePrimaryError } from '../../chunk-KU32OT4H.mjs';
|
|
3
|
+
import { optionalDependencyError } from '../../chunk-WOMADPM4.mjs';
|
|
4
|
+
import '../../chunk-BLMRNSUG.mjs';
|
|
5
|
+
import '../../chunk-KINDFBQP.mjs';
|
|
6
6
|
|
|
7
7
|
// src/replication/coordinator/group-rules.ts
|
|
8
8
|
var MIN_AUTOMATIC_FAILOVER_VOTERS = 3;
|
|
@@ -287,7 +287,10 @@ function controllerLeaseKey(clusterId) {
|
|
|
287
287
|
return `clusters/${encodeKey(clusterId)}/controller`;
|
|
288
288
|
}
|
|
289
289
|
function nodeSessionKey(clusterId, nodeId) {
|
|
290
|
-
return
|
|
290
|
+
return `${nodeSessionPrefix(clusterId)}${encodeKey(nodeId)}`;
|
|
291
|
+
}
|
|
292
|
+
function nodeSessionPrefix(clusterId) {
|
|
293
|
+
return `clusters/${encodeKey(clusterId)}/nodes/`;
|
|
291
294
|
}
|
|
292
295
|
function replicationGroupKey(clusterId, groupId) {
|
|
293
296
|
return `clusters/${encodeKey(clusterId)}/groups/${encodeKey(groupId)}`;
|
|
@@ -299,6 +302,145 @@ function encodeKey(value) {
|
|
|
299
302
|
return encodeURIComponent(value);
|
|
300
303
|
}
|
|
301
304
|
|
|
305
|
+
// src/replication/coordinator/etcd-lease-registry.ts
|
|
306
|
+
async function readLeaseAt(namespace, key) {
|
|
307
|
+
const value = await namespace.get(key).string();
|
|
308
|
+
return value ? parseLease(value) : null;
|
|
309
|
+
}
|
|
310
|
+
async function revokeLeaseQuietly(lease) {
|
|
311
|
+
try {
|
|
312
|
+
await lease.revoke();
|
|
313
|
+
} catch {
|
|
314
|
+
lease.release();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
var EtcdLeaseRegistry = class {
|
|
318
|
+
constructor(onLeaseLost) {
|
|
319
|
+
this.onLeaseLost = onLeaseLost;
|
|
320
|
+
}
|
|
321
|
+
entries = /* @__PURE__ */ new Map();
|
|
322
|
+
get(leaseId) {
|
|
323
|
+
return this.entries.get(leaseId);
|
|
324
|
+
}
|
|
325
|
+
forget(leaseId) {
|
|
326
|
+
this.entries.delete(leaseId);
|
|
327
|
+
}
|
|
328
|
+
leaseIdsForKey(key) {
|
|
329
|
+
const matches = [];
|
|
330
|
+
for (const [leaseId, entry] of this.entries) {
|
|
331
|
+
if (entry.key === key) matches.push(leaseId);
|
|
332
|
+
}
|
|
333
|
+
return matches;
|
|
334
|
+
}
|
|
335
|
+
track(lease, entry) {
|
|
336
|
+
this.entries.set(entry.leaseId, { ...entry, lease });
|
|
337
|
+
lease.on("lost", (err) => {
|
|
338
|
+
this.entries.delete(entry.leaseId);
|
|
339
|
+
this.onLeaseLost?.(err instanceof Error ? err : new CoordinatorError(String(err)));
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async discardSuperseded(key, keepLeaseId) {
|
|
343
|
+
const superseded = [];
|
|
344
|
+
for (const [leaseId, entry] of this.entries) {
|
|
345
|
+
if (entry.key === key && leaseId !== keepLeaseId) {
|
|
346
|
+
this.entries.delete(leaseId);
|
|
347
|
+
superseded.push(entry);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
await Promise.allSettled(superseded.map((entry) => revokeLeaseQuietly(entry.lease)));
|
|
351
|
+
}
|
|
352
|
+
async revokeAll() {
|
|
353
|
+
const revokes = [];
|
|
354
|
+
for (const entry of this.entries.values()) {
|
|
355
|
+
revokes.push(revokeLeaseQuietly(entry.lease));
|
|
356
|
+
}
|
|
357
|
+
this.entries.clear();
|
|
358
|
+
await Promise.allSettled(revokes);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// src/replication/coordinator/etcd-controller-lease.ts
|
|
363
|
+
async function acquireEtcdControllerLease(namespace, leases, input) {
|
|
364
|
+
const key = controllerLeaseKey(input.clusterId);
|
|
365
|
+
const lease = namespace.lease(ttlMsToSeconds(input.ttlMs));
|
|
366
|
+
const leaseId = await lease.grant();
|
|
367
|
+
const grantedAtMs = Date.now();
|
|
368
|
+
const value = serializeLease({
|
|
369
|
+
id: leaseId,
|
|
370
|
+
kind: "controller",
|
|
371
|
+
clusterId: input.clusterId,
|
|
372
|
+
holderId: input.holderId,
|
|
373
|
+
ttlMs: input.ttlMs,
|
|
374
|
+
grantedAtMs,
|
|
375
|
+
expiresAtMs: grantedAtMs + input.ttlMs,
|
|
376
|
+
metadata: cloneMetadata(input.metadata)
|
|
377
|
+
});
|
|
378
|
+
const result = await namespace.if(key, "Create", "==", 0).then(namespace.put(key).value(value).lease(leaseId)).commit();
|
|
379
|
+
if (!result.succeeded) {
|
|
380
|
+
await revokeLeaseQuietly(lease);
|
|
381
|
+
return { acquired: false, lease: await readLeaseAt(namespace, key) };
|
|
382
|
+
}
|
|
383
|
+
leases.track(lease, {
|
|
384
|
+
leaseId,
|
|
385
|
+
key,
|
|
386
|
+
ttlMs: input.ttlMs,
|
|
387
|
+
ttlSeconds: ttlMsToSeconds(input.ttlMs),
|
|
388
|
+
kind: "controller",
|
|
389
|
+
clusterId: input.clusterId,
|
|
390
|
+
holderId: input.holderId,
|
|
391
|
+
metadata: cloneMetadata(input.metadata)
|
|
392
|
+
});
|
|
393
|
+
return { acquired: true, lease: parseLease(value) };
|
|
394
|
+
}
|
|
395
|
+
async function watchEtcdControllerLease(namespace, clusterId, watcher, watchers, onWatcherError) {
|
|
396
|
+
const key = controllerLeaseKey(clusterId);
|
|
397
|
+
let holder = null;
|
|
398
|
+
let reading = true;
|
|
399
|
+
let changedWhileReading = false;
|
|
400
|
+
const report = () => {
|
|
401
|
+
try {
|
|
402
|
+
watcher(holder);
|
|
403
|
+
} catch (err) {
|
|
404
|
+
onWatcherError?.(err instanceof Error ? err : new Error(String(err)));
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const record = (next) => {
|
|
408
|
+
holder = next;
|
|
409
|
+
if (reading) {
|
|
410
|
+
changedWhileReading = true;
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
report();
|
|
414
|
+
};
|
|
415
|
+
const etcdWatcher = await namespace.watch().key(key).create();
|
|
416
|
+
watchers.add(etcdWatcher);
|
|
417
|
+
etcdWatcher.on("put", (kv) => {
|
|
418
|
+
record(parseLease(kv.value.toString("utf8")));
|
|
419
|
+
});
|
|
420
|
+
etcdWatcher.on("delete", () => {
|
|
421
|
+
record(null);
|
|
422
|
+
});
|
|
423
|
+
etcdWatcher.on("error", (err) => {
|
|
424
|
+
onWatcherError?.(err instanceof Error ? err : new Error(String(err)));
|
|
425
|
+
});
|
|
426
|
+
try {
|
|
427
|
+
const value = await namespace.get(key).string();
|
|
428
|
+
if (!changedWhileReading) {
|
|
429
|
+
holder = value ? parseLease(value) : null;
|
|
430
|
+
}
|
|
431
|
+
} catch (err) {
|
|
432
|
+
watchers.delete(etcdWatcher);
|
|
433
|
+
await etcdWatcher.cancel();
|
|
434
|
+
throw err;
|
|
435
|
+
}
|
|
436
|
+
reading = false;
|
|
437
|
+
report();
|
|
438
|
+
return async () => {
|
|
439
|
+
watchers.delete(etcdWatcher);
|
|
440
|
+
await etcdWatcher.cancel();
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
302
444
|
// src/replication/coordinator/etcd-group-store.ts
|
|
303
445
|
var MAX_CAS_ATTEMPTS = 5;
|
|
304
446
|
var MAX_PROMOTION_ATTEMPTS = 3;
|
|
@@ -473,115 +615,104 @@ var EtcdGroupStore = class {
|
|
|
473
615
|
}
|
|
474
616
|
};
|
|
475
617
|
|
|
476
|
-
// src/replication/coordinator/etcd-
|
|
477
|
-
|
|
618
|
+
// src/replication/coordinator/etcd-loader.ts
|
|
619
|
+
var ETCD3_DEPENDENCY = {
|
|
620
|
+
packageName: "etcd3",
|
|
621
|
+
neededBy: "The etcd coordinator",
|
|
622
|
+
installCommand: "pnpm add -E etcd3",
|
|
623
|
+
code: "COORDINATOR_DEPENDENCY_MISSING"
|
|
624
|
+
};
|
|
625
|
+
async function loadEtcd3Module() {
|
|
478
626
|
try {
|
|
479
|
-
await
|
|
480
|
-
} catch {
|
|
481
|
-
|
|
627
|
+
return await import('etcd3');
|
|
628
|
+
} catch (err) {
|
|
629
|
+
throw optionalDependencyError(ETCD3_DEPENDENCY, err);
|
|
482
630
|
}
|
|
483
631
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
632
|
+
|
|
633
|
+
// src/replication/coordinator/etcd-session-watch.ts
|
|
634
|
+
function nodeIdOf(key, prefix) {
|
|
635
|
+
return decodeURIComponent(key.slice(prefix.length));
|
|
636
|
+
}
|
|
637
|
+
async function watchEtcdNodeSessions(namespace, clusterId, watcher, watchers, onWatcherError) {
|
|
638
|
+
const prefix = nodeSessionPrefix(clusterId);
|
|
639
|
+
const live = /* @__PURE__ */ new Set();
|
|
640
|
+
const goneWhileReading = /* @__PURE__ */ new Set();
|
|
641
|
+
let reading = true;
|
|
642
|
+
const report = () => {
|
|
643
|
+
try {
|
|
644
|
+
watcher([...live]);
|
|
645
|
+
} catch (err) {
|
|
646
|
+
onWatcherError?.(err instanceof Error ? err : new Error(String(err)));
|
|
499
647
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this.entries.delete(leaseId);
|
|
514
|
-
superseded.push(entry);
|
|
515
|
-
}
|
|
648
|
+
};
|
|
649
|
+
const etcdWatcher = await namespace.watch().prefix(prefix).create();
|
|
650
|
+
watchers.add(etcdWatcher);
|
|
651
|
+
etcdWatcher.on("put", (kv) => {
|
|
652
|
+
live.add(nodeIdOf(kv.key.toString("utf8"), prefix));
|
|
653
|
+
if (!reading) report();
|
|
654
|
+
});
|
|
655
|
+
etcdWatcher.on("delete", (kv) => {
|
|
656
|
+
const nodeId = nodeIdOf(kv.key.toString("utf8"), prefix);
|
|
657
|
+
live.delete(nodeId);
|
|
658
|
+
if (reading) {
|
|
659
|
+
goneWhileReading.add(nodeId);
|
|
660
|
+
return;
|
|
516
661
|
}
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
662
|
+
report();
|
|
663
|
+
});
|
|
664
|
+
etcdWatcher.on("error", (err) => {
|
|
665
|
+
onWatcherError?.(err instanceof Error ? err : new Error(String(err)));
|
|
666
|
+
});
|
|
667
|
+
try {
|
|
668
|
+
for (const key of await namespace.getAll().prefix(prefix).keys()) {
|
|
669
|
+
const nodeId = nodeIdOf(key, prefix);
|
|
670
|
+
if (!goneWhileReading.has(nodeId)) {
|
|
671
|
+
live.add(nodeId);
|
|
672
|
+
}
|
|
523
673
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
674
|
+
} catch (err) {
|
|
675
|
+
watchers.delete(etcdWatcher);
|
|
676
|
+
await etcdWatcher.cancel();
|
|
677
|
+
throw err;
|
|
678
|
+
}
|
|
679
|
+
reading = false;
|
|
680
|
+
report();
|
|
681
|
+
return async () => {
|
|
682
|
+
watchers.delete(etcdWatcher);
|
|
683
|
+
await etcdWatcher.cancel();
|
|
684
|
+
};
|
|
685
|
+
}
|
|
528
686
|
|
|
529
687
|
// src/replication/coordinator/etcd.ts
|
|
530
688
|
var EtcdClusterCoordinator = class {
|
|
531
|
-
|
|
532
|
-
|
|
689
|
+
options;
|
|
690
|
+
connection = null;
|
|
533
691
|
onWatcherError;
|
|
534
692
|
leases;
|
|
535
693
|
grantedNodeSessionLeaseIds = /* @__PURE__ */ new Map();
|
|
536
694
|
watchers = /* @__PURE__ */ new Set();
|
|
537
|
-
groups;
|
|
538
695
|
constructor(options) {
|
|
539
696
|
assertEtcdOptions(options);
|
|
540
|
-
this.
|
|
541
|
-
this.namespace = this.client.namespace(normaliseKeyPrefix(options.keyPrefix));
|
|
697
|
+
this.options = options;
|
|
542
698
|
this.onWatcherError = options.onWatcherError;
|
|
543
699
|
this.leases = new EtcdLeaseRegistry(this.onWatcherError);
|
|
544
|
-
this.groups = new EtcdGroupStore(this.namespace, this.watchers, this.onWatcherError);
|
|
545
700
|
}
|
|
546
|
-
/**
|
|
701
|
+
/** Tries to acquire the controller lease, and returns whether it succeeded along with the current lease. */
|
|
547
702
|
async tryAcquireControllerLease(input) {
|
|
548
703
|
assertNonEmpty(input.clusterId, "clusterId");
|
|
549
704
|
assertNonEmpty(input.holderId, "holderId");
|
|
550
705
|
assertPositiveTtl(input.ttlMs);
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
holderId: input.holderId,
|
|
560
|
-
ttlMs: input.ttlMs,
|
|
561
|
-
grantedAtMs,
|
|
562
|
-
expiresAtMs: grantedAtMs + input.ttlMs,
|
|
563
|
-
metadata: cloneMetadata(input.metadata)
|
|
564
|
-
});
|
|
565
|
-
const result = await this.namespace.if(key, "Create", "==", 0).then(this.namespace.put(key).value(value).lease(leaseId)).commit();
|
|
566
|
-
if (!result.succeeded) {
|
|
567
|
-
await revokeLeaseQuietly(lease);
|
|
568
|
-
const current = await this.getLeaseFromKey(key);
|
|
569
|
-
return { acquired: false, lease: current };
|
|
570
|
-
}
|
|
571
|
-
this.leases.track(lease, {
|
|
572
|
-
leaseId,
|
|
573
|
-
key,
|
|
574
|
-
ttlMs: input.ttlMs,
|
|
575
|
-
ttlSeconds: ttlMsToSeconds(input.ttlMs),
|
|
576
|
-
kind: "controller",
|
|
577
|
-
clusterId: input.clusterId,
|
|
578
|
-
holderId: input.holderId,
|
|
579
|
-
metadata: cloneMetadata(input.metadata)
|
|
580
|
-
});
|
|
581
|
-
const parsed = parseLease(value);
|
|
582
|
-
return { acquired: true, lease: parsed };
|
|
706
|
+
const { namespace } = await this.connect();
|
|
707
|
+
return acquireEtcdControllerLease(namespace, this.leases, input);
|
|
708
|
+
}
|
|
709
|
+
/** Calls `watcher` with the current controller lease when the watch starts and after each change, and returns a function that stops the watch. */
|
|
710
|
+
async watchControllerLease(clusterId, watcher) {
|
|
711
|
+
assertNonEmpty(clusterId, "clusterId");
|
|
712
|
+
const { namespace } = await this.connect();
|
|
713
|
+
return watchEtcdControllerLease(namespace, clusterId, watcher, this.watchers, this.onWatcherError);
|
|
583
714
|
}
|
|
584
|
-
/** Extends a lease, and
|
|
715
|
+
/** Extends a lease, and returns false for a lease that is expired or unknown to this coordinator. */
|
|
585
716
|
async renewLease(leaseId, ttlMs) {
|
|
586
717
|
assertNonEmpty(leaseId, "leaseId");
|
|
587
718
|
assertPositiveTtl(ttlMs);
|
|
@@ -607,9 +738,10 @@ var EtcdClusterCoordinator = class {
|
|
|
607
738
|
metadata: cloneMetadata(entry.metadata)
|
|
608
739
|
};
|
|
609
740
|
const value = entry.kind === "node-session" && entry.nodeSession ? JSON.stringify({ ...entry.nodeSession, lease: leaseValue }) : serializeLease(leaseValue);
|
|
741
|
+
const { namespace } = await this.connect();
|
|
610
742
|
let refreshed;
|
|
611
743
|
try {
|
|
612
|
-
const result = await
|
|
744
|
+
const result = await namespace.if(entry.key, "Lease", "==", leaseId).then(namespace.put(entry.key).value(value).ignoreLease()).commit();
|
|
613
745
|
refreshed = result.succeeded === true;
|
|
614
746
|
} catch {
|
|
615
747
|
return true;
|
|
@@ -621,7 +753,7 @@ var EtcdClusterCoordinator = class {
|
|
|
621
753
|
entry.ttlMs = ttlMs;
|
|
622
754
|
return true;
|
|
623
755
|
}
|
|
624
|
-
/**
|
|
756
|
+
/** Deletes a lease's key if the key still refers to that lease, then revokes the lease, and returns whether the delete succeeded. */
|
|
625
757
|
async releaseLease(leaseId) {
|
|
626
758
|
assertNonEmpty(leaseId, "leaseId");
|
|
627
759
|
const entry = this.leases.get(leaseId);
|
|
@@ -629,11 +761,12 @@ var EtcdClusterCoordinator = class {
|
|
|
629
761
|
return false;
|
|
630
762
|
}
|
|
631
763
|
this.leases.forget(leaseId);
|
|
632
|
-
const
|
|
764
|
+
const { namespace } = await this.connect();
|
|
765
|
+
const currentValue = await namespace.get(entry.key).string();
|
|
633
766
|
const currentLeaseId = currentValue ? parseLeaseIdForEntry(entry.kind, currentValue) : null;
|
|
634
767
|
let released = false;
|
|
635
768
|
if (currentLeaseId === leaseId && currentValue) {
|
|
636
|
-
const result = await
|
|
769
|
+
const result = await namespace.if(entry.key, "Value", "==", currentValue).then(namespace.delete().key(entry.key)).commit();
|
|
637
770
|
released = result.succeeded === true;
|
|
638
771
|
}
|
|
639
772
|
await revokeLeaseQuietly(entry.lease);
|
|
@@ -644,8 +777,9 @@ var EtcdClusterCoordinator = class {
|
|
|
644
777
|
assertNonEmpty(input.clusterId, "clusterId");
|
|
645
778
|
assertNonEmpty(input.nodeId, "nodeId");
|
|
646
779
|
assertPositiveTtl(input.ttlMs);
|
|
780
|
+
const { namespace } = await this.connect();
|
|
647
781
|
const key = nodeSessionKey(input.clusterId, input.nodeId);
|
|
648
|
-
const existingRawSession = await
|
|
782
|
+
const existingRawSession = await namespace.get(key).string();
|
|
649
783
|
if (existingRawSession) {
|
|
650
784
|
const existingSession = parseNodeSession(existingRawSession);
|
|
651
785
|
const supersedesOwnSession = this.grantedNodeSessionLeaseIds.get(key) === existingSession.lease.id;
|
|
@@ -653,7 +787,7 @@ var EtcdClusterCoordinator = class {
|
|
|
653
787
|
throw new CoordinatorError(`Node session '${input.nodeId}' is already registered`);
|
|
654
788
|
}
|
|
655
789
|
}
|
|
656
|
-
const lease =
|
|
790
|
+
const lease = namespace.lease(ttlMsToSeconds(input.ttlMs));
|
|
657
791
|
const leaseId = await lease.grant();
|
|
658
792
|
const grantedAtMs = Date.now();
|
|
659
793
|
const session = {
|
|
@@ -677,8 +811,8 @@ var EtcdClusterCoordinator = class {
|
|
|
677
811
|
metadata: cloneMetadata(input.metadata)
|
|
678
812
|
};
|
|
679
813
|
const rawSession = JSON.stringify(session);
|
|
680
|
-
const transaction = existingRawSession ?
|
|
681
|
-
const result = await transaction.then(
|
|
814
|
+
const transaction = existingRawSession ? namespace.if(key, "Value", "==", existingRawSession) : namespace.if(key, "Create", "==", 0);
|
|
815
|
+
const result = await transaction.then(namespace.put(key).value(rawSession).lease(leaseId)).commit();
|
|
682
816
|
if (!result.succeeded) {
|
|
683
817
|
await revokeLeaseQuietly(lease);
|
|
684
818
|
throw new CoordinatorError(`Node session '${input.nodeId}' registration conflicted with a concurrent write`);
|
|
@@ -707,14 +841,21 @@ var EtcdClusterCoordinator = class {
|
|
|
707
841
|
});
|
|
708
842
|
return parseNodeSession(rawSession);
|
|
709
843
|
}
|
|
710
|
-
/**
|
|
844
|
+
/** Returns one node's session, or null once its lease expires. */
|
|
711
845
|
async getLiveNodeSession(clusterId, nodeId) {
|
|
712
846
|
assertNonEmpty(clusterId, "clusterId");
|
|
713
847
|
assertNonEmpty(nodeId, "nodeId");
|
|
714
|
-
const
|
|
848
|
+
const { namespace } = await this.connect();
|
|
849
|
+
const value = await namespace.get(nodeSessionKey(clusterId, nodeId)).string();
|
|
715
850
|
return value ? parseNodeSession(value) : null;
|
|
716
851
|
}
|
|
717
|
-
/**
|
|
852
|
+
/** Calls `watcher` with the IDs of every node that holds a live session, and returns a function that stops the watch. */
|
|
853
|
+
async watchNodeSessions(clusterId, watcher) {
|
|
854
|
+
assertNonEmpty(clusterId, "clusterId");
|
|
855
|
+
const { namespace } = await this.connect();
|
|
856
|
+
return watchEtcdNodeSessions(namespace, clusterId, watcher, this.watchers, this.onWatcherError);
|
|
857
|
+
}
|
|
858
|
+
/** Ends one node's session at once by releasing its leases. */
|
|
718
859
|
async deregisterNodeSession(clusterId, nodeId) {
|
|
719
860
|
assertNonEmpty(clusterId, "clusterId");
|
|
720
861
|
assertNonEmpty(nodeId, "nodeId");
|
|
@@ -722,39 +863,47 @@ var EtcdClusterCoordinator = class {
|
|
|
722
863
|
await this.releaseLease(leaseId);
|
|
723
864
|
}
|
|
724
865
|
}
|
|
725
|
-
/** Writes the group's state, which
|
|
726
|
-
setReplicationGroupState(input) {
|
|
727
|
-
|
|
866
|
+
/** Writes the group's state, which creates a new group or replaces an existing one. */
|
|
867
|
+
async setReplicationGroupState(input) {
|
|
868
|
+
const { groups } = await this.connect();
|
|
869
|
+
return groups.setReplicationGroupState(input);
|
|
728
870
|
}
|
|
729
|
-
/**
|
|
730
|
-
getReplicationGroupState(clusterId, groupId) {
|
|
731
|
-
|
|
871
|
+
/** Returns the group's state, or null when etcd has no state for the group. */
|
|
872
|
+
async getReplicationGroupState(clusterId, groupId) {
|
|
873
|
+
const { groups } = await this.connect();
|
|
874
|
+
return groups.getReplicationGroupState(clusterId, groupId);
|
|
732
875
|
}
|
|
733
|
-
/** Calls
|
|
734
|
-
watchReplicationGroup(clusterId, groupId, watcher) {
|
|
735
|
-
|
|
876
|
+
/** Calls `watcher` with the new state after each change to the group, and returns a function that stops the watch. */
|
|
877
|
+
async watchReplicationGroup(clusterId, groupId, watcher) {
|
|
878
|
+
const { groups } = await this.connect();
|
|
879
|
+
return groups.watchReplicationGroup(clusterId, groupId, watcher);
|
|
736
880
|
}
|
|
737
|
-
/**
|
|
738
|
-
compareAndAdvancePrimaryTerm(input) {
|
|
739
|
-
|
|
881
|
+
/** Makes a node primary and advances the term, but only while the group is still at the term that the caller expects. */
|
|
882
|
+
async compareAndAdvancePrimaryTerm(input) {
|
|
883
|
+
const { groups } = await this.connect();
|
|
884
|
+
return groups.compareAndAdvancePrimaryTerm(input);
|
|
740
885
|
}
|
|
741
|
-
/** Replaces the group's in-sync set, and
|
|
742
|
-
updateInSyncSet(input) {
|
|
743
|
-
|
|
886
|
+
/** Replaces the group's in-sync set with a subset of it, and can advance the durability point. */
|
|
887
|
+
async updateInSyncSet(input) {
|
|
888
|
+
const { groups } = await this.connect();
|
|
889
|
+
return groups.updateInSyncSet(input);
|
|
744
890
|
}
|
|
745
891
|
/** Adds one caught-up node to the in-sync set. */
|
|
746
|
-
admitNodeToInSyncSet(input) {
|
|
747
|
-
|
|
892
|
+
async admitNodeToInSyncSet(input) {
|
|
893
|
+
const { groups } = await this.connect();
|
|
894
|
+
return groups.admitNodeToInSyncSet(input);
|
|
748
895
|
}
|
|
749
|
-
/**
|
|
750
|
-
updateNodeMaintenance(input) {
|
|
751
|
-
|
|
896
|
+
/** Sets or clears one node's draining, repairing, and faulted flags. */
|
|
897
|
+
async updateNodeMaintenance(input) {
|
|
898
|
+
const { groups } = await this.connect();
|
|
899
|
+
return groups.updateNodeMaintenance(input);
|
|
752
900
|
}
|
|
753
|
-
/** Promotes
|
|
754
|
-
promoteEligibleReplica(input) {
|
|
755
|
-
|
|
901
|
+
/** Promotes an eligible in-sync replica, and throws a `NoSafePrimaryError` when no replica qualifies. */
|
|
902
|
+
async promoteEligibleReplica(input) {
|
|
903
|
+
const { groups } = await this.connect();
|
|
904
|
+
return groups.promoteEligibleReplica(input);
|
|
756
905
|
}
|
|
757
|
-
/**
|
|
906
|
+
/** Stops every watch, revokes every lease that this coordinator granted, and closes the etcd client. */
|
|
758
907
|
async close() {
|
|
759
908
|
const watcherCancels = [];
|
|
760
909
|
for (const watcher of this.watchers) {
|
|
@@ -764,11 +913,27 @@ var EtcdClusterCoordinator = class {
|
|
|
764
913
|
await Promise.allSettled(watcherCancels);
|
|
765
914
|
this.grantedNodeSessionLeaseIds.clear();
|
|
766
915
|
await this.leases.revokeAll();
|
|
767
|
-
this.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
916
|
+
const connection = this.connection;
|
|
917
|
+
if (!connection) return;
|
|
918
|
+
const opened = await connection.catch(() => null);
|
|
919
|
+
opened?.client.close();
|
|
920
|
+
}
|
|
921
|
+
connect() {
|
|
922
|
+
if (!this.connection) {
|
|
923
|
+
this.connection = loadEtcd3Module().then(
|
|
924
|
+
({ Etcd3 }) => {
|
|
925
|
+
const client = new Etcd3(toEtcdOptions(this.options));
|
|
926
|
+
const namespace = client.namespace(normaliseKeyPrefix(this.options.keyPrefix));
|
|
927
|
+
const groups = new EtcdGroupStore(namespace, this.watchers, this.onWatcherError);
|
|
928
|
+
return { client, namespace, groups };
|
|
929
|
+
},
|
|
930
|
+
(err) => {
|
|
931
|
+
this.connection = null;
|
|
932
|
+
throw err;
|
|
933
|
+
}
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
return this.connection;
|
|
772
937
|
}
|
|
773
938
|
};
|
|
774
939
|
function createEtcdCoordinator(options) {
|