@optimystic/db-core 0.14.1 → 0.16.3
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/src/blocks/block-store.d.ts +18 -1
- package/dist/src/blocks/block-store.d.ts.map +1 -1
- package/dist/src/blocks/helpers.d.ts +2 -2
- package/dist/src/blocks/helpers.d.ts.map +1 -1
- package/dist/src/blocks/helpers.js +2 -2
- package/dist/src/blocks/helpers.js.map +1 -1
- package/dist/src/btree/btree.d.ts +27 -6
- package/dist/src/btree/btree.d.ts.map +1 -1
- package/dist/src/btree/btree.js +104 -61
- package/dist/src/btree/btree.js.map +1 -1
- package/dist/src/chain/chain.d.ts.map +1 -1
- package/dist/src/chain/chain.js +11 -1
- package/dist/src/chain/chain.js.map +1 -1
- package/dist/src/cluster/index.d.ts +1 -0
- package/dist/src/cluster/index.d.ts.map +1 -1
- package/dist/src/cluster/index.js +1 -0
- package/dist/src/cluster/index.js.map +1 -1
- package/dist/src/cluster/membership.d.ts +49 -0
- package/dist/src/cluster/membership.d.ts.map +1 -0
- package/dist/src/cluster/membership.js +75 -0
- package/dist/src/cluster/membership.js.map +1 -0
- package/dist/src/cluster/structs.d.ts +50 -1
- package/dist/src/cluster/structs.d.ts.map +1 -1
- package/dist/src/cluster/structs.js +9 -1
- package/dist/src/cluster/structs.js.map +1 -1
- package/dist/src/cohort-topic/addressing.d.ts +8 -2
- package/dist/src/cohort-topic/addressing.d.ts.map +1 -1
- package/dist/src/cohort-topic/addressing.js +11 -3
- package/dist/src/cohort-topic/addressing.js.map +1 -1
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +13 -7
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +17 -6
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -1
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts +17 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/replay-guard.js +33 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -1
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts +7 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/topic-budget.js +8 -2
- package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -1
- package/dist/src/cohort-topic/coldstart.d.ts +34 -14
- package/dist/src/cohort-topic/coldstart.d.ts.map +1 -1
- package/dist/src/cohort-topic/coldstart.js +25 -11
- package/dist/src/cohort-topic/coldstart.js.map +1 -1
- package/dist/src/cohort-topic/gossip/bus.d.ts +12 -1
- package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -1
- package/dist/src/cohort-topic/gossip/bus.js +27 -1
- package/dist/src/cohort-topic/gossip/bus.js.map +1 -1
- package/dist/src/cohort-topic/gossip/view.d.ts +8 -0
- package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -1
- package/dist/src/cohort-topic/gossip/view.js +17 -1
- package/dist/src/cohort-topic/gossip/view.js.map +1 -1
- package/dist/src/cohort-topic/member-engine.d.ts +10 -3
- package/dist/src/cohort-topic/member-engine.d.ts.map +1 -1
- package/dist/src/cohort-topic/member-engine.js +29 -14
- package/dist/src/cohort-topic/member-engine.js.map +1 -1
- package/dist/src/cohort-topic/membership/publisher.d.ts +14 -7
- package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -1
- package/dist/src/cohort-topic/membership/publisher.js +15 -27
- package/dist/src/cohort-topic/membership/publisher.js.map +1 -1
- package/dist/src/cohort-topic/membership/verifier.d.ts +35 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -1
- package/dist/src/cohort-topic/membership/verifier.js +88 -3
- package/dist/src/cohort-topic/membership/verifier.js.map +1 -1
- package/dist/src/cohort-topic/promotion.d.ts +7 -0
- package/dist/src/cohort-topic/promotion.d.ts.map +1 -1
- package/dist/src/cohort-topic/promotion.js +16 -2
- package/dist/src/cohort-topic/promotion.js.map +1 -1
- package/dist/src/cohort-topic/registration/renewal.d.ts +18 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -1
- package/dist/src/cohort-topic/registration/renewal.js +54 -0
- package/dist/src/cohort-topic/registration/renewal.js.map +1 -1
- package/dist/src/cohort-topic/registration/types.d.ts +12 -0
- package/dist/src/cohort-topic/registration/types.d.ts.map +1 -1
- package/dist/src/cohort-topic/registration/types.js +14 -0
- package/dist/src/cohort-topic/registration/types.js.map +1 -1
- package/dist/src/cohort-topic/service.d.ts +4 -3
- package/dist/src/cohort-topic/service.d.ts.map +1 -1
- package/dist/src/cohort-topic/service.js +38 -12
- package/dist/src/cohort-topic/service.js.map +1 -1
- package/dist/src/cohort-topic/sig/payloads.d.ts +23 -7
- package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -1
- package/dist/src/cohort-topic/sig/payloads.js +22 -6
- package/dist/src/cohort-topic/sig/payloads.js.map +1 -1
- package/dist/src/cohort-topic/traffic.d.ts +6 -0
- package/dist/src/cohort-topic/traffic.d.ts.map +1 -1
- package/dist/src/cohort-topic/traffic.js +16 -1
- package/dist/src/cohort-topic/traffic.js.map +1 -1
- package/dist/src/cohort-topic/walk.d.ts +22 -7
- package/dist/src/cohort-topic/walk.d.ts.map +1 -1
- package/dist/src/cohort-topic/walk.js +78 -18
- package/dist/src/cohort-topic/walk.js.map +1 -1
- package/dist/src/cohort-topic/wire/codec.d.ts +3 -1
- package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/codec.js +7 -1
- package/dist/src/cohort-topic/wire/codec.js.map +1 -1
- package/dist/src/cohort-topic/wire/index.d.ts +1 -1
- package/dist/src/cohort-topic/wire/index.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/index.js +1 -1
- package/dist/src/cohort-topic/wire/index.js.map +1 -1
- package/dist/src/cohort-topic/wire/payloads.d.ts +5 -4
- package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/payloads.js +14 -5
- package/dist/src/cohort-topic/wire/payloads.js.map +1 -1
- package/dist/src/cohort-topic/wire/primitives.d.ts +65 -0
- package/dist/src/cohort-topic/wire/primitives.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/primitives.js +173 -0
- package/dist/src/cohort-topic/wire/primitives.js.map +1 -0
- package/dist/src/cohort-topic/wire/types.d.ts +132 -5
- package/dist/src/cohort-topic/wire/types.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.d.ts +15 -5
- package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.js +146 -123
- package/dist/src/cohort-topic/wire/validate.js.map +1 -1
- package/dist/src/collection/collection.d.ts +23 -12
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +111 -35
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collection/struct.d.ts +37 -2
- package/dist/src/collection/struct.d.ts.map +1 -1
- package/dist/src/collection/struct.js +16 -1
- package/dist/src/collection/struct.js.map +1 -1
- package/dist/src/collections/tree/tree.d.ts +18 -1
- package/dist/src/collections/tree/tree.d.ts.map +1 -1
- package/dist/src/collections/tree/tree.js +36 -6
- package/dist/src/collections/tree/tree.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/log/log.d.ts +26 -3
- package/dist/src/log/log.d.ts.map +1 -1
- package/dist/src/log/log.js +34 -7
- package/dist/src/log/log.js.map +1 -1
- package/dist/src/log/struct.d.ts +5 -4
- package/dist/src/log/struct.d.ts.map +1 -1
- package/dist/src/log/struct.js.map +1 -1
- package/dist/src/matchmaking/wire.d.ts +2 -1
- package/dist/src/matchmaking/wire.d.ts.map +1 -1
- package/dist/src/matchmaking/wire.js +13 -92
- package/dist/src/matchmaking/wire.js.map +1 -1
- package/dist/src/network/i-key-network.d.ts +9 -0
- package/dist/src/network/i-key-network.d.ts.map +1 -1
- package/dist/src/network/i-repo.d.ts +8 -0
- package/dist/src/network/i-repo.d.ts.map +1 -1
- package/dist/src/network/struct.d.ts +10 -0
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/reactivity/push-state.d.ts.map +1 -1
- package/dist/src/reactivity/push-state.js +4 -33
- package/dist/src/reactivity/push-state.js.map +1 -1
- package/dist/src/reactivity/wire-validate.d.ts +7 -22
- package/dist/src/reactivity/wire-validate.d.ts.map +1 -1
- package/dist/src/reactivity/wire-validate.js +7 -73
- package/dist/src/reactivity/wire-validate.js.map +1 -1
- package/dist/src/reactivity/wire.js +3 -3
- package/dist/src/reactivity/wire.js.map +1 -1
- package/dist/src/testing/async-wait.d.ts +23 -0
- package/dist/src/testing/async-wait.d.ts.map +1 -0
- package/dist/src/testing/async-wait.js +42 -0
- package/dist/src/testing/async-wait.js.map +1 -0
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +3 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/test-transactor.d.ts +44 -0
- package/dist/src/testing/test-transactor.d.ts.map +1 -0
- package/dist/src/testing/test-transactor.js +423 -0
- package/dist/src/testing/test-transactor.js.map +1 -0
- package/dist/src/transaction/actions-engine.d.ts +6 -3
- package/dist/src/transaction/actions-engine.d.ts.map +1 -1
- package/dist/src/transaction/actions-engine.js +8 -7
- package/dist/src/transaction/actions-engine.js.map +1 -1
- package/dist/src/transaction/coordinator.d.ts +43 -22
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +443 -178
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transaction/errors.d.ts +82 -0
- package/dist/src/transaction/errors.d.ts.map +1 -0
- package/dist/src/transaction/errors.js +93 -0
- package/dist/src/transaction/errors.js.map +1 -0
- package/dist/src/transaction/index.d.ts +6 -3
- package/dist/src/transaction/index.d.ts.map +1 -1
- package/dist/src/transaction/index.js +4 -2
- package/dist/src/transaction/index.js.map +1 -1
- package/dist/src/transaction/operations-hash.d.ts +119 -0
- package/dist/src/transaction/operations-hash.d.ts.map +1 -0
- package/dist/src/transaction/operations-hash.js +180 -0
- package/dist/src/transaction/operations-hash.js.map +1 -0
- package/dist/src/transaction/read-dependency-collector.d.ts +43 -0
- package/dist/src/transaction/read-dependency-collector.d.ts.map +1 -0
- package/dist/src/transaction/read-dependency-collector.js +69 -0
- package/dist/src/transaction/read-dependency-collector.js.map +1 -0
- package/dist/src/transaction/session.d.ts +12 -3
- package/dist/src/transaction/session.d.ts.map +1 -1
- package/dist/src/transaction/session.js +35 -9
- package/dist/src/transaction/session.js.map +1 -1
- package/dist/src/transaction/transaction.d.ts +162 -1
- package/dist/src/transaction/transaction.d.ts.map +1 -1
- package/dist/src/transaction/transaction.js +85 -3
- package/dist/src/transaction/transaction.js.map +1 -1
- package/dist/src/transaction/validator.d.ts +3 -11
- package/dist/src/transaction/validator.d.ts.map +1 -1
- package/dist/src/transaction/validator.js +69 -28
- package/dist/src/transaction/validator.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +35 -0
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +146 -40
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/dist/src/transactor/transactor-source.d.ts +19 -5
- package/dist/src/transactor/transactor-source.d.ts.map +1 -1
- package/dist/src/transactor/transactor-source.js +43 -11
- package/dist/src/transactor/transactor-source.js.map +1 -1
- package/dist/src/transform/atomic-proxy.d.ts +35 -5
- package/dist/src/transform/atomic-proxy.d.ts.map +1 -1
- package/dist/src/transform/atomic-proxy.js +39 -7
- package/dist/src/transform/atomic-proxy.js.map +1 -1
- package/dist/src/transform/cache-source.d.ts +36 -5
- package/dist/src/transform/cache-source.d.ts.map +1 -1
- package/dist/src/transform/cache-source.js +84 -8
- package/dist/src/transform/cache-source.js.map +1 -1
- package/dist/src/transform/helpers.d.ts.map +1 -1
- package/dist/src/transform/helpers.js +8 -2
- package/dist/src/transform/helpers.js.map +1 -1
- package/dist/src/transform/tracker.d.ts +17 -2
- package/dist/src/transform/tracker.d.ts.map +1 -1
- package/dist/src/transform/tracker.js +66 -11
- package/dist/src/transform/tracker.js.map +1 -1
- package/dist/src/utility/backoff.d.ts +48 -0
- package/dist/src/utility/backoff.d.ts.map +1 -0
- package/dist/src/utility/backoff.js +60 -0
- package/dist/src/utility/backoff.js.map +1 -0
- package/dist/src/utility/pending.d.ts +1 -0
- package/dist/src/utility/pending.d.ts.map +1 -1
- package/dist/src/utility/pending.js +3 -1
- package/dist/src/utility/pending.js.map +1 -1
- package/package.json +4 -4
- package/src/blocks/block-store.ts +19 -1
- package/src/blocks/helpers.ts +3 -3
- package/src/btree/btree.ts +108 -63
- package/src/chain/chain.ts +11 -1
- package/src/cluster/index.ts +1 -0
- package/src/cluster/membership.ts +85 -0
- package/src/cluster/structs.ts +51 -1
- package/src/cohort-topic/addressing.ts +12 -4
- package/src/cohort-topic/antidos/bootstrap-evidence.ts +21 -9
- package/src/cohort-topic/antidos/replay-guard.ts +37 -0
- package/src/cohort-topic/antidos/topic-budget.ts +17 -2
- package/src/cohort-topic/coldstart.ts +48 -15
- package/src/cohort-topic/gossip/bus.ts +39 -2
- package/src/cohort-topic/gossip/view.ts +26 -1
- package/src/cohort-topic/member-engine.ts +36 -15
- package/src/cohort-topic/membership/publisher.ts +25 -33
- package/src/cohort-topic/membership/verifier.ts +119 -3
- package/src/cohort-topic/promotion.ts +23 -2
- package/src/cohort-topic/registration/renewal.ts +73 -1
- package/src/cohort-topic/registration/types.ts +15 -0
- package/src/cohort-topic/service.ts +41 -15
- package/src/cohort-topic/sig/payloads.ts +29 -9
- package/src/cohort-topic/traffic.ts +23 -1
- package/src/cohort-topic/walk.ts +95 -24
- package/src/cohort-topic/wire/codec.ts +12 -0
- package/src/cohort-topic/wire/index.ts +2 -0
- package/src/cohort-topic/wire/payloads.ts +14 -5
- package/src/cohort-topic/wire/primitives.ts +188 -0
- package/src/cohort-topic/wire/types.ts +136 -4
- package/src/cohort-topic/wire/validate.ts +168 -134
- package/src/collection/collection.ts +123 -40
- package/src/collection/struct.ts +44 -2
- package/src/collections/tree/tree.ts +42 -5
- package/src/index.ts +1 -0
- package/src/log/log.ts +37 -9
- package/src/log/struct.ts +5 -4
- package/src/matchmaking/wire.ts +25 -102
- package/src/network/i-key-network.ts +10 -0
- package/src/network/i-repo.ts +8 -0
- package/src/network/struct.ts +10 -0
- package/src/reactivity/push-state.ts +4 -37
- package/src/reactivity/wire-validate.ts +7 -81
- package/src/reactivity/wire.ts +3 -3
- package/src/testing/async-wait.ts +65 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/test-transactor.ts +468 -0
- package/src/transaction/actions-engine.ts +8 -6
- package/src/transaction/coordinator.ts +498 -229
- package/src/transaction/errors.ts +91 -0
- package/src/transaction/index.ts +23 -3
- package/src/transaction/operations-hash.ts +196 -0
- package/src/transaction/read-dependency-collector.ts +78 -0
- package/src/transaction/session.ts +37 -9
- package/src/transaction/transaction.ts +211 -4
- package/src/transaction/validator.ts +73 -48
- package/src/transactor/network-transactor.ts +164 -49
- package/src/transactor/transactor-source.ts +47 -12
- package/src/transform/atomic-proxy.ts +92 -49
- package/src/transform/cache-source.ts +87 -9
- package/src/transform/helpers.ts +8 -2
- package/src/transform/tracker.ts +69 -11
- package/src/utility/backoff.ts +95 -0
- package/src/utility/pending.ts +3 -1
- package/src/transaction/context.ts +0 -103
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../../src/transform/tracker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../../src/transform/tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jittered exponential backoff shared by the retry loops in db-core
|
|
3
|
+
* ({@link Collection.sync} and {@link TransactionCoordinator.commit}).
|
|
4
|
+
*
|
|
5
|
+
* The point of the jitter is fairness under contention: when many clients lose the SAME
|
|
6
|
+
* optimistic-concurrency race at t=0, a bare exponential curve makes them all re-attempt at the
|
|
7
|
+
* identical next tick — a thundering herd that just re-collides. Multiplying the exponential value
|
|
8
|
+
* by a random factor spreads those re-attempts across a window so offered load per tick sheds
|
|
9
|
+
* instead of cascading. This is the same shape as `cohort-topic`'s `backoffRetryMs`, with the
|
|
10
|
+
* proportional-jitter term added.
|
|
11
|
+
*/
|
|
12
|
+
/** A source of uniform randomness in [0, 1). Injected in tests so a deterministic sequence can be
|
|
13
|
+
* asserted; production uses {@link cryptoRand}. */
|
|
14
|
+
export type RandFn = () => number;
|
|
15
|
+
/** Uniform value in [0, 1) drawn from the same CSPRNG (`@noble/hashes` `randomBytes`) the rest of
|
|
16
|
+
* db-core uses — deliberately NOT `Math.random`, so backoff jitter shares the package's randomness
|
|
17
|
+
* source rather than introducing a second, weaker one. */
|
|
18
|
+
export declare function cryptoRand(): number;
|
|
19
|
+
export interface JitteredBackoffConfig {
|
|
20
|
+
/** Base delay (ms) for attempt 0, before jitter. */
|
|
21
|
+
baseMs: number;
|
|
22
|
+
/** Hard ceiling (ms) on the pre-jitter exponential value. */
|
|
23
|
+
capMs: number;
|
|
24
|
+
/** Geometric growth per attempt. Default 2 (doubling). */
|
|
25
|
+
factor?: number;
|
|
26
|
+
/** Fraction of the exponential value that jitter may subtract, in [0, 1]. Default 0.5, so the
|
|
27
|
+
* returned delay lands uniformly in `((1 - jitterFraction) · exp, exp]` — i.e. `(0.5·exp, exp]`. */
|
|
28
|
+
jitterFraction?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Delay (ms) for retry `attempt` (0-based):
|
|
32
|
+
*
|
|
33
|
+
* exp = min(baseMs · factor^attempt, capMs)
|
|
34
|
+
* delay = exp · (1 - jitterFraction · rand()) // rand() ∈ [0, 1)
|
|
35
|
+
*
|
|
36
|
+
* With the defaults (factor 2, jitterFraction 0.5) the delay is uniform in `(0.5·exp, exp]`: never
|
|
37
|
+
* zero (as long as `baseMs > 0`) and never above `capMs`. `attempt` must be a non-negative integer
|
|
38
|
+
* — pass `consecutiveFailures - 1` so the first retry backs off by ~`baseMs`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function jitteredBackoffMs(attempt: number, config: JitteredBackoffConfig, rand?: RandFn): number;
|
|
41
|
+
/** Build an AbortError for a cooperatively-aborted wait. Prefers the signal's own reason when it is
|
|
42
|
+
* an Error (so callers who passed a custom abort reason see it), otherwise a `name='AbortError'`
|
|
43
|
+
* Error. */
|
|
44
|
+
export declare function makeAbortError(signal?: AbortSignal): Error;
|
|
45
|
+
/** Sleep for `ms`, resolving early (rejecting with an AbortError) if `signal` aborts — so a retry
|
|
46
|
+
* loop waiting out a backoff rejects promptly instead of finishing the sleep. */
|
|
47
|
+
export declare function abortableDelay(ms: number, signal?: AbortSignal): Promise<void>;
|
|
48
|
+
//# sourceMappingURL=backoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../../src/utility/backoff.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH;mDACmD;AACnD,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;AAElC;;0DAE0D;AAC1D,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAED,MAAM,WAAW,qBAAqB;IACrC,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;wGACoG;IACpG,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,EAC7B,IAAI,GAAE,MAAmB,GACvB,MAAM,CAQR;AAED;;YAEY;AACZ,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,KAAK,CAO1D;AAED;iFACiF;AACjF,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB9E"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomBytes } from '@noble/hashes/utils.js';
|
|
2
|
+
/** Uniform value in [0, 1) drawn from the same CSPRNG (`@noble/hashes` `randomBytes`) the rest of
|
|
3
|
+
* db-core uses — deliberately NOT `Math.random`, so backoff jitter shares the package's randomness
|
|
4
|
+
* source rather than introducing a second, weaker one. */
|
|
5
|
+
export function cryptoRand() {
|
|
6
|
+
const b = randomBytes(4);
|
|
7
|
+
// Assemble a uint32 (>>> 0 clears the sign bit the shifts would otherwise set), then normalise.
|
|
8
|
+
const u = (((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]) >>> 0);
|
|
9
|
+
return u / 0x1_0000_0000;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Delay (ms) for retry `attempt` (0-based):
|
|
13
|
+
*
|
|
14
|
+
* exp = min(baseMs · factor^attempt, capMs)
|
|
15
|
+
* delay = exp · (1 - jitterFraction · rand()) // rand() ∈ [0, 1)
|
|
16
|
+
*
|
|
17
|
+
* With the defaults (factor 2, jitterFraction 0.5) the delay is uniform in `(0.5·exp, exp]`: never
|
|
18
|
+
* zero (as long as `baseMs > 0`) and never above `capMs`. `attempt` must be a non-negative integer
|
|
19
|
+
* — pass `consecutiveFailures - 1` so the first retry backs off by ~`baseMs`.
|
|
20
|
+
*/
|
|
21
|
+
export function jitteredBackoffMs(attempt, config, rand = cryptoRand) {
|
|
22
|
+
if (!Number.isInteger(attempt) || attempt < 0) {
|
|
23
|
+
throw new RangeError(`attempt must be a non-negative integer, got ${attempt}`);
|
|
24
|
+
}
|
|
25
|
+
const factor = config.factor ?? 2;
|
|
26
|
+
const jitterFraction = config.jitterFraction ?? 0.5;
|
|
27
|
+
const exp = Math.min(config.baseMs * factor ** attempt, config.capMs);
|
|
28
|
+
return exp * (1 - jitterFraction * rand());
|
|
29
|
+
}
|
|
30
|
+
/** Build an AbortError for a cooperatively-aborted wait. Prefers the signal's own reason when it is
|
|
31
|
+
* an Error (so callers who passed a custom abort reason see it), otherwise a `name='AbortError'`
|
|
32
|
+
* Error. */
|
|
33
|
+
export function makeAbortError(signal) {
|
|
34
|
+
if (signal && signal.reason instanceof Error) {
|
|
35
|
+
return signal.reason;
|
|
36
|
+
}
|
|
37
|
+
const err = new Error('The operation was aborted');
|
|
38
|
+
err.name = 'AbortError';
|
|
39
|
+
return err;
|
|
40
|
+
}
|
|
41
|
+
/** Sleep for `ms`, resolving early (rejecting with an AbortError) if `signal` aborts — so a retry
|
|
42
|
+
* loop waiting out a backoff rejects promptly instead of finishing the sleep. */
|
|
43
|
+
export function abortableDelay(ms, signal) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
if (signal?.aborted) {
|
|
46
|
+
reject(makeAbortError(signal));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const onAbort = () => {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
reject(makeAbortError(signal));
|
|
52
|
+
};
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
signal?.removeEventListener('abort', onAbort);
|
|
55
|
+
resolve();
|
|
56
|
+
}, ms);
|
|
57
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=backoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../../src/utility/backoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAkBrD;;0DAE0D;AAC1D,MAAM,UAAU,UAAU;IACzB,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IACzB,gGAAgG;IAChG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,aAAa,CAAC;AAC1B,CAAC;AAcD;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAChC,OAAe,EACf,MAA6B,EAC7B,OAAe,UAAU;IAEzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,UAAU,CAAC,+CAA+C,OAAO,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,GAAG,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACtE,OAAO,GAAG,GAAG,CAAC,CAAC,GAAG,cAAc,GAAG,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;YAEY;AACZ,MAAM,UAAU,cAAc,CAAC,MAAoB;IAClD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,YAAY,KAAK,EAAE,CAAC;QAC9C,OAAO,MAAM,CAAC,MAAM,CAAC;IACtB,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnD,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;IACxB,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;iFACiF;AACjF,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,MAAoB;IAC9D,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC5C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/B,OAAO;QACR,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,cAAc,CAAC,MAAO,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACX,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending.d.ts","sourceRoot":"","sources":["../../../src/utility/pending.ts"],"names":[],"mappings":"AAAA,qBAAa,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"pending.d.ts","sourceRoot":"","sources":["../../../src/utility/pending.ts"],"names":[],"mappings":"AAAA,qBAAa,OAAO,CAAC,CAAC;IA8Bb,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IA7B3B,QAAQ,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,UAAU,CAAS;IAC3B,EAAE,SAAc;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC;gBAWlB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;CAY3B"}
|
|
@@ -2,10 +2,11 @@ export class Pending {
|
|
|
2
2
|
promise;
|
|
3
3
|
response;
|
|
4
4
|
error;
|
|
5
|
+
_responded = false;
|
|
5
6
|
t1 = Date.now();
|
|
6
7
|
duration;
|
|
7
8
|
get isResponse() {
|
|
8
|
-
return this.
|
|
9
|
+
return this._responded;
|
|
9
10
|
}
|
|
10
11
|
get isError() {
|
|
11
12
|
return this.error !== undefined;
|
|
@@ -27,6 +28,7 @@ export class Pending {
|
|
|
27
28
|
promise.then(response => {
|
|
28
29
|
this.duration = Date.now() - this.t1;
|
|
29
30
|
this.response = response;
|
|
31
|
+
this._responded = true;
|
|
30
32
|
return response;
|
|
31
33
|
}, error => {
|
|
32
34
|
this.duration = Date.now() - this.t1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../../src/utility/pending.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,OAAO;
|
|
1
|
+
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../../src/utility/pending.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,OAAO;IA8BX;IA7BR,QAAQ,CAAK;IACb,KAAK,CAAW;IACR,UAAU,GAAG,KAAK,CAAC;IAC3B,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAChB,QAAQ,CAAU;IAElB,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IAED,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACjC,CAAC;IAED,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,MAAM;QACX,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,QAAS,CAAC;QACvB,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,CAAC,KAAM,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC;IAC3B,CAAC;IAED,YACQ,OAAmB;QAAnB,YAAO,GAAP,OAAO,CAAY;QAE1B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YACvB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,OAAO,QAAQ,CAAC;QACjB,CAAC,EAAE,KAAK,CAAC,EAAE;YACV,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACpB,CAAC,CAAC,CAAC;IACJ,CAAC;CACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optimystic/db-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core database functionality for Optimystic",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"import": "./dist/src/index.js"
|
|
18
18
|
},
|
|
19
19
|
"./test": {
|
|
20
|
-
"types": "./dist/
|
|
21
|
-
"import": "./dist/
|
|
20
|
+
"types": "./dist/src/testing/index.d.ts",
|
|
21
|
+
"import": "./dist/src/testing/index.js"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
@@ -68,6 +68,6 @@
|
|
|
68
68
|
"@noble/hashes": "^2.0.1",
|
|
69
69
|
"debug": "^4.4.3",
|
|
70
70
|
"multiformats": "^13.4.2",
|
|
71
|
-
"uint8arrays": "^
|
|
71
|
+
"uint8arrays": "^6.1.1"
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import type { BlockType, IBlock, BlockId, BlockHeader, BlockOperation } from "./index.js";
|
|
2
2
|
|
|
3
|
+
/** Why a block was read, used to filter the optimistic-concurrency conflict (read) set.
|
|
4
|
+
*
|
|
5
|
+
* - `value` — the transaction's result depends on this block's CONTENT (a leaf/entry, a log
|
|
6
|
+
* entry, or an interior node whose keys/counts were used as data). Always retained.
|
|
7
|
+
* - `navigation` — an interior structural block (a B-tree branch) walked through ONLY to reach
|
|
8
|
+
* a separately-captured `value` read further down the same descent. May be dropped from the
|
|
9
|
+
* conflict set, because any concurrent restructuring that would change the queried result also
|
|
10
|
+
* bumps the revision of a `value` block that stays in the set (the target leaf). See
|
|
11
|
+
* docs/correctness.md Theorem 5.
|
|
12
|
+
*
|
|
13
|
+
* Defaults to `value` EVERYWHERE a read is captured, so an unclassified read is always retained
|
|
14
|
+
* (fail-safe): a forgotten tag can only cause an extra false-positive rejection, never a missed
|
|
15
|
+
* conflict. Only the B-tree point-lookup descent opts specific interior reads into `navigation`. */
|
|
16
|
+
export type ReadPurpose = 'value' | 'navigation';
|
|
17
|
+
|
|
3
18
|
export type BlockSource<T extends IBlock> = {
|
|
4
19
|
createBlockHeader(type: BlockType, newId?: BlockId): BlockHeader;
|
|
5
|
-
|
|
20
|
+
/** Fetch a block by id. `purpose` classifies WHY the block is being read for OCC read-set
|
|
21
|
+
* filtering (see {@link ReadPurpose}); it defaults to `value`, so existing callers that omit
|
|
22
|
+
* it capture a retained read exactly as before. */
|
|
23
|
+
tryGet(id: BlockId, purpose?: ReadPurpose): Promise<T | undefined>;
|
|
6
24
|
generateId(): BlockId;
|
|
7
25
|
};
|
|
8
26
|
|
package/src/blocks/helpers.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { BlockOperation, IBlock, BlockId, BlockStore } from "../index.js";
|
|
1
|
+
import type { BlockOperation, IBlock, BlockId, BlockStore, ReadPurpose } from "../index.js";
|
|
2
2
|
import { applyOperation } from "../transform/helpers.js";
|
|
3
3
|
|
|
4
|
-
export async function get<T extends IBlock>(store: BlockStore<T>, id: BlockId): Promise<T> {
|
|
5
|
-
const block = await store.tryGet(id);
|
|
4
|
+
export async function get<T extends IBlock>(store: BlockStore<T>, id: BlockId, purpose?: ReadPurpose): Promise<T> {
|
|
5
|
+
const block = await store.tryGet(id, purpose);
|
|
6
6
|
if (!block) throw Error(`Missing block (${id})`);
|
|
7
7
|
return block;
|
|
8
8
|
}
|
package/src/btree/btree.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { apply, get } from "../blocks/index.js";
|
|
|
4
4
|
import { TreeLeafBlockType, TreeBranchBlockType, entries$, nodes$, partitions$ } from "./nodes.js";
|
|
5
5
|
import type { BranchNode, ITreeNode, LeafNode } from "./nodes.js";
|
|
6
6
|
import type { TreeBlock } from "./tree-block.js";
|
|
7
|
-
import { AtomicProxy } from "../transform/atomic-proxy.js";
|
|
7
|
+
import { AtomicProxy, type AtomicScope } from "../transform/atomic-proxy.js";
|
|
8
8
|
|
|
9
9
|
export const NodeCapacity = 64;
|
|
10
10
|
|
|
@@ -27,11 +27,12 @@ export class BTree<TKey, TEntry> {
|
|
|
27
27
|
public readonly trunk: ITreeTrunk,
|
|
28
28
|
public readonly keyFromEntry = (entry: TEntry) => entry as unknown as TKey,
|
|
29
29
|
public readonly compare = (a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0 as number,
|
|
30
|
+
public readonly nodeCapacity = NodeCapacity,
|
|
30
31
|
) {
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
private atomic<T>(fn: () => Promise<T
|
|
34
|
-
return this._proxy ? this._proxy.atomic(fn) : fn();
|
|
34
|
+
private atomic<T>(fn: (scope?: AtomicScope) => Promise<T>, parent?: AtomicScope): Promise<T> {
|
|
35
|
+
return this._proxy ? this._proxy.atomic(fn, parent) : fn(parent);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
static createRoot(
|
|
@@ -45,13 +46,14 @@ export class BTree<TKey, TEntry> {
|
|
|
45
46
|
createTrunk: getTrunkFunc,
|
|
46
47
|
keyFromEntry = (entry: TEntry) => entry as unknown as TKey,
|
|
47
48
|
compare = (a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0,
|
|
49
|
+
nodeCapacity = NodeCapacity,
|
|
48
50
|
newId?: BlockId,
|
|
49
51
|
) {
|
|
50
52
|
const proxy = new AtomicProxy(store);
|
|
51
53
|
const root = BTree.createRoot(proxy as BlockStore<TreeBlock>);
|
|
52
54
|
proxy.insert(root);
|
|
53
55
|
const trunk = createTrunk(proxy as BlockStore<TreeBlock>, root.header.id, newId);
|
|
54
|
-
const tree = new BTree(proxy, trunk, keyFromEntry, compare);
|
|
56
|
+
const tree = new BTree(proxy, trunk, keyFromEntry, compare, nodeCapacity);
|
|
55
57
|
tree._proxy = proxy;
|
|
56
58
|
return tree;
|
|
57
59
|
}
|
|
@@ -68,16 +70,24 @@ export class BTree<TKey, TEntry> {
|
|
|
68
70
|
|
|
69
71
|
/** Attempts to find the given key
|
|
70
72
|
* @returns Path to the key or the "crack" before it. If `on` is true on the resulting path, the key was found.
|
|
71
|
-
* If `on` is false, next() and prior() can attempt to move to the nearest match.
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
* If `on` is false, next() and prior() can attempt to move to the nearest match.
|
|
74
|
+
* @param navigate when true, the interior descent nodes are captured as droppable `navigation`
|
|
75
|
+
* reads (see {@link getPath}). DEFAULT false: `find` returns a Path a caller may use to seed a
|
|
76
|
+
* range scan (`ascending`/`next`), whose result depends on tree shape, so its structural reads
|
|
77
|
+
* must stay retained. Only {@link get} — a point lookup with no such escape — opts in. */
|
|
78
|
+
async find(key: TKey, navigate = false): Promise<Path<TKey, TEntry>> {
|
|
79
|
+
return await this.getPath(await this.trunk.get(), key, navigate);
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
/** Retrieves the entry for the given key.
|
|
77
83
|
* Use find instead for a path to the key, the nearest match, or as a basis for navigation.
|
|
78
84
|
* @returns the entry for the given key if found; undefined otherwise. */
|
|
79
85
|
async get(key: TKey): Promise<TEntry | undefined> {
|
|
80
|
-
|
|
86
|
+
// Value-of-key point lookup: the result depends ONLY on the terminal leaf's content, so the
|
|
87
|
+
// interior descent nodes are pure navigation and are dropped from the OCC conflict set. The
|
|
88
|
+
// returned Path is consumed by `at` and discarded — it never seeds a scan — so navigate=true
|
|
89
|
+
// is safe here (unlike the public `find`).
|
|
90
|
+
return this.at(await this.find(key, /* navigate */ true));
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
/** @returns the entry for the given path if on an entry; undefined otherwise. */
|
|
@@ -122,6 +132,14 @@ export class BTree<TKey, TEntry> {
|
|
|
122
132
|
return path.version === this._version;
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
/** Bump the version so every Path handed out before now reports invalid (see {@link isValid}).
|
|
136
|
+
* Needed when the tree's data was mutated through a DIFFERENT store binding than this instance —
|
|
137
|
+
* e.g. the tree collection's action handler writes through a per-action Atomic store, so this
|
|
138
|
+
* read-side btree never sees the mutation and must be told to invalidate outstanding paths. */
|
|
139
|
+
invalidatePaths() {
|
|
140
|
+
++this._version;
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
/**
|
|
126
144
|
* Adds a value to the tree. Be sure to check the result, as the tree does not allow duplicate keys.
|
|
127
145
|
* Added entries are frozen to ensure immutability
|
|
@@ -145,7 +163,7 @@ export class BTree<TKey, TEntry> {
|
|
|
145
163
|
* * on = false if update/insert failed.
|
|
146
164
|
* * wasUpdate = true, given path is not on an entry
|
|
147
165
|
* * else newEntry's new key already present; returned path is "near" existing entry */
|
|
148
|
-
async updateAt(path: Path<TKey, TEntry>, newEntry: TEntry): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]> {
|
|
166
|
+
async updateAt(path: Path<TKey, TEntry>, newEntry: TEntry, parent?: AtomicScope): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]> {
|
|
149
167
|
return this.atomic(async () => {
|
|
150
168
|
this.validatePath(path);
|
|
151
169
|
if (path.on) {
|
|
@@ -156,7 +174,7 @@ export class BTree<TKey, TEntry> {
|
|
|
156
174
|
result[0].version = ++this._version;
|
|
157
175
|
}
|
|
158
176
|
return result;
|
|
159
|
-
});
|
|
177
|
+
}, parent);
|
|
160
178
|
}
|
|
161
179
|
|
|
162
180
|
/** Inserts the entry if it doesn't exist, or updates it if it does.
|
|
@@ -182,11 +200,13 @@ export class BTree<TKey, TEntry> {
|
|
|
182
200
|
* @returns path to new entry and whether an update or insert attempted.
|
|
183
201
|
* If getUpdated callback returns a row that is already present, the resulting path will not be on. */
|
|
184
202
|
async merge(newEntry: TEntry, getUpdated: (existing: TEntry) => TEntry): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]> {
|
|
185
|
-
return this.atomic(async () => {
|
|
186
|
-
const newKey =
|
|
203
|
+
return this.atomic(async (scope) => {
|
|
204
|
+
const newKey = this.keyFromEntry(newEntry);
|
|
187
205
|
const path = await this.find(newKey);
|
|
188
206
|
if (path.on) {
|
|
189
|
-
|
|
207
|
+
// Thread this scope into updateAt so the nested call joins this atomic batch
|
|
208
|
+
// (reuses the tracker) rather than opening/committing a second scope.
|
|
209
|
+
const result = await this.updateAt(path, getUpdated(this.getEntry(path)), scope); // Don't use internalUpdate - need to freeze and check for mutation
|
|
190
210
|
// Note: updateAt already increments version, so don't double-increment here
|
|
191
211
|
return result;
|
|
192
212
|
} else {
|
|
@@ -277,7 +297,7 @@ export class BTree<TKey, TEntry> {
|
|
|
277
297
|
/** @returns a path one step backward. on will be true if the path hasn't hit the end. */
|
|
278
298
|
async prior(path: Path<TKey, TEntry>): Promise<Path<TKey, TEntry>> {
|
|
279
299
|
const newPath = path.clone();
|
|
280
|
-
this.movePrior(newPath);
|
|
300
|
+
await this.movePrior(newPath);
|
|
281
301
|
return newPath;
|
|
282
302
|
}
|
|
283
303
|
|
|
@@ -332,20 +352,40 @@ export class BTree<TKey, TEntry> {
|
|
|
332
352
|
return endPath;
|
|
333
353
|
}
|
|
334
354
|
|
|
335
|
-
|
|
355
|
+
/** Descend from `node` toward `key`, building the path. When `navigate` is true this is a point
|
|
356
|
+
* lookup (from {@link get}): each interior branch read below the root is captured as a droppable
|
|
357
|
+
* `navigation` read, and the terminal leaf is upgraded back to a retained `value` read — the
|
|
358
|
+
* result depends only on the leaf, and any concurrent restructuring that would change the result
|
|
359
|
+
* also bumps the leaf's revision (Theorem 5), so the interior reads are redundant. When false
|
|
360
|
+
* (find/range/scan/write descents) every read stays `value` (retained). */
|
|
361
|
+
protected async getPath(node: ITreeNode, key: TKey, navigate = false): Promise<Path<TKey, TEntry>> {
|
|
336
362
|
if (node.header.type === TreeLeafBlockType) {
|
|
337
363
|
const leaf = node as LeafNode<TEntry>;
|
|
364
|
+
if (navigate) {
|
|
365
|
+
// The leaf ends a point-lookup descent: its content is the load-bearing VALUE read.
|
|
366
|
+
// Its parent fetched it as `navigation` (a child's type is unknown before reading);
|
|
367
|
+
// upgrade it (value-wins) so it is retained while the interior nodes above are dropped.
|
|
368
|
+
this.markReadValue(leaf.header.id);
|
|
369
|
+
}
|
|
338
370
|
const [on, index] = this.indexOfEntry(leaf.entries, key);
|
|
339
371
|
return new Path<TKey, TEntry>([], leaf, index, on, this._version);
|
|
340
372
|
} else {
|
|
341
373
|
const branch = node as BranchNode<TKey>;
|
|
342
374
|
const index = this.indexOfKey(branch.partitions, key);
|
|
343
|
-
const
|
|
375
|
+
const child = await get(this.store, branch.nodes[index]!, navigate ? 'navigation' : 'value');
|
|
376
|
+
const path = await this.getPath(child, key, navigate);
|
|
344
377
|
path.branches.unshift(new PathBranch(branch, index));
|
|
345
378
|
return path;
|
|
346
379
|
}
|
|
347
380
|
}
|
|
348
381
|
|
|
382
|
+
/** Upgrade a captured read of `id` to a retained `value` read via the store chain's read
|
|
383
|
+
* collector (duck-typed — a no-op for stores without one, e.g. test doubles or log-walk
|
|
384
|
+
* caches). Used by a point lookup to pin its terminal leaf. */
|
|
385
|
+
private markReadValue(id: BlockId) {
|
|
386
|
+
(this.store as { markReadValue?: (id: BlockId) => void }).markReadValue?.(id);
|
|
387
|
+
}
|
|
388
|
+
|
|
349
389
|
private indexOfEntry(entries: TEntry[], key: TKey): [on: boolean, index: number] {
|
|
350
390
|
let lo = 0;
|
|
351
391
|
let hi = entries.length - 1;
|
|
@@ -392,34 +432,35 @@ export class BTree<TKey, TEntry> {
|
|
|
392
432
|
if (!path.on) { // Attempt to move off of crack
|
|
393
433
|
path.on = path.branches.every(branch => branch.index >= 0 && branch.index < branch.node.nodes.length)
|
|
394
434
|
&& path.leafIndex >= 0 && path.leafIndex < path.leafNode.entries.length;
|
|
395
|
-
if (path.on) {
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
} else if (path.leafIndex >= path.leafNode.entries.length - 1) {
|
|
399
|
-
let popCount = 0;
|
|
400
|
-
let found = false;
|
|
401
|
-
const last = path.branches.length - 1;
|
|
402
|
-
while (popCount <= last && !found) {
|
|
403
|
-
const branch = path.branches[last - popCount]!;
|
|
404
|
-
if (branch.index === branch.node.partitions.length) // last node in branch
|
|
405
|
-
++popCount;
|
|
406
|
-
else
|
|
407
|
-
found = true;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (!found) {
|
|
411
|
-
path.leafIndex = path.leafNode.entries.length; // after last row = end crack
|
|
412
|
-
path.on = false;
|
|
413
|
-
} else {
|
|
414
|
-
path.branches.splice(-popCount, popCount);
|
|
415
|
-
const branch = path.branches.at(-1)!;
|
|
416
|
-
++branch.index;
|
|
417
|
-
this.moveToFirst(await get(this.store, branch.node.nodes[branch.index]!), path);
|
|
435
|
+
if (path.on || path.leafIndex < path.leafNode.entries.length) {
|
|
436
|
+
return; // got on an entry, or an interior crack — nothing to advance into
|
|
418
437
|
}
|
|
419
|
-
|
|
420
|
-
else {
|
|
438
|
+
// end-of-leaf crack (leafIndex === entries.length): fall through to branch-popping leaf-advance
|
|
439
|
+
} else if (path.leafIndex < path.leafNode.entries.length - 1) {
|
|
421
440
|
++path.leafIndex;
|
|
422
441
|
path.on = true;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
// on the last entry of the leaf, or off an end-of-leaf crack: advance into the next leaf
|
|
445
|
+
let popCount = 0;
|
|
446
|
+
let found = false;
|
|
447
|
+
const last = path.branches.length - 1;
|
|
448
|
+
while (popCount <= last && !found) {
|
|
449
|
+
const branch = path.branches[last - popCount]!;
|
|
450
|
+
if (branch.index === branch.node.partitions.length) // last node in branch
|
|
451
|
+
++popCount;
|
|
452
|
+
else
|
|
453
|
+
found = true;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!found) {
|
|
457
|
+
path.leafIndex = path.leafNode.entries.length; // after last row = end crack
|
|
458
|
+
path.on = false;
|
|
459
|
+
} else {
|
|
460
|
+
path.branches.splice(-popCount, popCount);
|
|
461
|
+
const branch = path.branches.at(-1)!;
|
|
462
|
+
++branch.index;
|
|
463
|
+
await this.moveToFirst(await get(this.store, branch.node.nodes[branch.index]!), path);
|
|
423
464
|
}
|
|
424
465
|
}
|
|
425
466
|
|
|
@@ -460,7 +501,7 @@ export class BTree<TKey, TEntry> {
|
|
|
460
501
|
if (this.compare(oldKey, newKey) !== 0) { // if key changed, delete and re-insert
|
|
461
502
|
let newPath = await this.internalInsert(newEntry)
|
|
462
503
|
if (newPath.on) { // insert succeeded
|
|
463
|
-
this.internalDelete(await this.find(oldKey)); // Re-find - the prior insert invalidated the path
|
|
504
|
+
await this.internalDelete(await this.find(oldKey)); // Re-find - the prior insert invalidated the path
|
|
464
505
|
newPath = await this.find(newKey); // Re-find- delete invalidated path
|
|
465
506
|
}
|
|
466
507
|
return [newPath, false];
|
|
@@ -512,7 +553,7 @@ export class BTree<TKey, TEntry> {
|
|
|
512
553
|
}
|
|
513
554
|
if (split) {
|
|
514
555
|
const newBranch = newBranchNode(this.store, [split.key], [await this.trunk.getId(), split.right.header.id]);
|
|
515
|
-
|
|
556
|
+
this.store.insert(newBranch);
|
|
516
557
|
await this.trunk.set(newBranch);
|
|
517
558
|
path.branches.unshift(new PathBranch(newBranch, split.indexDelta));
|
|
518
559
|
}
|
|
@@ -577,7 +618,7 @@ export class BTree<TKey, TEntry> {
|
|
|
577
618
|
|
|
578
619
|
private leafInsert(path: Path<TKey, TEntry>, entry: TEntry): Split<TKey> | undefined {
|
|
579
620
|
const { leafNode: leaf, leafIndex: index } = path;
|
|
580
|
-
if (leaf.entries.length <
|
|
621
|
+
if (leaf.entries.length < this.nodeCapacity) { // No split needed
|
|
581
622
|
apply(this.store, leaf, [entries$, index, 0, [entry]]);
|
|
582
623
|
return undefined;
|
|
583
624
|
}
|
|
@@ -610,7 +651,7 @@ export class BTree<TKey, TEntry> {
|
|
|
610
651
|
const pathBranch = path.branches[branchIndex]!;
|
|
611
652
|
const { index: splitIndex, node } = pathBranch;
|
|
612
653
|
pathBranch.index += split.indexDelta;
|
|
613
|
-
if (node.nodes.length <
|
|
654
|
+
if (node.nodes.length < this.nodeCapacity) { // no split needed
|
|
614
655
|
apply(this.store, node, [partitions$, splitIndex, 0, [split.key]]);
|
|
615
656
|
apply(this.store, node, [nodes$, splitIndex + 1, 0, [split.right.header.id]]);
|
|
616
657
|
return undefined;
|
|
@@ -629,6 +670,7 @@ export class BTree<TKey, TEntry> {
|
|
|
629
670
|
newNodes.splice(pathBranch.index + 1, 0, split.right.header.id);
|
|
630
671
|
}
|
|
631
672
|
const newBranch = newBranchNode(this.store, newPartitions, newNodes);
|
|
673
|
+
this.store.insert(newBranch);
|
|
632
674
|
|
|
633
675
|
// Delete partitions and nodes
|
|
634
676
|
const newPartition = node.partitions[midIndex - 1]!;
|
|
@@ -644,7 +686,7 @@ export class BTree<TKey, TEntry> {
|
|
|
644
686
|
}
|
|
645
687
|
|
|
646
688
|
protected async rebalanceLeaf(path: Path<TKey, TEntry>, depth: number): Promise<ITreeNode | undefined> {
|
|
647
|
-
if (depth === 0 || path.leafNode.entries.length >= (
|
|
689
|
+
if (depth === 0 || path.leafNode.entries.length >= (this.nodeCapacity >>> 1)) {
|
|
648
690
|
return undefined;
|
|
649
691
|
}
|
|
650
692
|
|
|
@@ -653,19 +695,19 @@ export class BTree<TKey, TEntry> {
|
|
|
653
695
|
const pIndex = parent.index;
|
|
654
696
|
const pNode = parent.node;
|
|
655
697
|
|
|
656
|
-
const rightSibId = pNode.nodes[pIndex + 1]
|
|
698
|
+
const rightSibId = pNode.nodes[pIndex + 1];
|
|
657
699
|
const rightSib = rightSibId ? (await get(this.store, rightSibId)) as LeafNode<TEntry> : undefined;
|
|
658
|
-
if (rightSib && rightSib.entries.length > (
|
|
700
|
+
if (rightSib && rightSib.entries.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from right sibling
|
|
659
701
|
const entry = rightSib.entries[0]!;
|
|
660
702
|
apply(this.store, rightSib, [entries$, 0, 1, []]);
|
|
661
703
|
apply(this.store, leaf, [entries$, leaf.entries.length, 0, [entry]]);
|
|
662
|
-
this.updatePartition(pIndex + 1, path, depth - 1, this.keyFromEntry(
|
|
704
|
+
this.updatePartition(pIndex + 1, path, depth - 1, this.keyFromEntry(rightSib.entries[0]!));
|
|
663
705
|
return undefined;
|
|
664
706
|
}
|
|
665
707
|
|
|
666
|
-
const leftSibId = pNode.nodes[pIndex - 1]
|
|
708
|
+
const leftSibId = pNode.nodes[pIndex - 1];
|
|
667
709
|
const leftSib = leftSibId ? (await get(this.store, leftSibId)) as LeafNode<TEntry> : undefined;
|
|
668
|
-
if (leftSib && leftSib.entries.length > (
|
|
710
|
+
if (leftSib && leftSib.entries.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from left sibling
|
|
669
711
|
const entry = leftSib.entries[leftSib.entries.length - 1]!;
|
|
670
712
|
apply(this.store, leftSib, [entries$, leftSib.entries.length - 1, 1, []]);
|
|
671
713
|
apply(this.store, leaf, [entries$, 0, 0, [entry]]);
|
|
@@ -674,7 +716,7 @@ export class BTree<TKey, TEntry> {
|
|
|
674
716
|
return undefined;
|
|
675
717
|
}
|
|
676
718
|
|
|
677
|
-
if (rightSib && rightSib.entries.length + leaf.entries.length <=
|
|
719
|
+
if (rightSib && rightSib.entries.length + leaf.entries.length <= this.nodeCapacity) { // Attempt to merge right sibling into leaf (right sib deleted)
|
|
678
720
|
apply(this.store, leaf, [entries$, leaf.entries.length, 0, rightSib.entries]);
|
|
679
721
|
this.deletePartition(pNode, pIndex);
|
|
680
722
|
if (pIndex === 0) { // 0th node of parent, update parent key
|
|
@@ -684,7 +726,12 @@ export class BTree<TKey, TEntry> {
|
|
|
684
726
|
return await this.rebalanceBranch(path, depth - 1);
|
|
685
727
|
}
|
|
686
728
|
|
|
687
|
-
if (leftSib && leftSib.entries.length + leaf.entries.length <=
|
|
729
|
+
if (leftSib && leftSib.entries.length + leaf.entries.length <= this.nodeCapacity) { // Attempt to merge into left sibling (leaf deleted)
|
|
730
|
+
// NOTE: this fixes leafIndex, but the parent-branch index up the path is left pointing at
|
|
731
|
+
// the now-deleted leaf's old slot (same tolerated-staleness class as rebalanceBranch's
|
|
732
|
+
// left-merge, part c). Harmless today because deleteAt version-bumps and abandons the
|
|
733
|
+
// returned path. A future path-reuse consumer must audit BOTH left-merge sites, not just
|
|
734
|
+
// rebalanceBranch.
|
|
688
735
|
path.leafNode = leftSib;
|
|
689
736
|
path.leafIndex += leftSib.entries.length;
|
|
690
737
|
apply(this.store, leftSib, [entries$, leftSib.entries.length, 0, leaf.entries]);
|
|
@@ -701,7 +748,7 @@ export class BTree<TKey, TEntry> {
|
|
|
701
748
|
return path.branches[depth + 1]?.node ?? path.leafNode;
|
|
702
749
|
}
|
|
703
750
|
|
|
704
|
-
if (depth === 0 || (branch.nodes.length >=
|
|
751
|
+
if (depth === 0 || (branch.nodes.length >= this.nodeCapacity >>> 1)) {
|
|
705
752
|
return undefined;
|
|
706
753
|
}
|
|
707
754
|
|
|
@@ -709,9 +756,9 @@ export class BTree<TKey, TEntry> {
|
|
|
709
756
|
const pIndex = parent.index;
|
|
710
757
|
const pNode = parent.node;
|
|
711
758
|
|
|
712
|
-
const rightSibId = pNode.nodes[pIndex + 1]
|
|
759
|
+
const rightSibId = pNode.nodes[pIndex + 1];
|
|
713
760
|
const rightSib = rightSibId ? (await get(this.store, rightSibId)) as BranchNode<TKey> : undefined;
|
|
714
|
-
if (rightSib && rightSib.nodes.length > (
|
|
761
|
+
if (rightSib && rightSib.nodes.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from right sibling
|
|
715
762
|
const node = rightSib.nodes[0]!;
|
|
716
763
|
const rightKey = rightSib.partitions[0]!;
|
|
717
764
|
this.insertPartition(branch, branch.partitions.length, pNode.partitions[pIndex]!, node);
|
|
@@ -722,7 +769,7 @@ export class BTree<TKey, TEntry> {
|
|
|
722
769
|
|
|
723
770
|
const leftSibId = pNode.nodes[pIndex - 1];
|
|
724
771
|
const leftSib = leftSibId ? (await get(this.store, leftSibId)) as BranchNode<TKey> : undefined;
|
|
725
|
-
if (leftSib && leftSib.nodes.length > (
|
|
772
|
+
if (leftSib && leftSib.nodes.length > (this.nodeCapacity >>> 1)) { // Attempt to borrow from left sibling
|
|
726
773
|
const node = leftSib.nodes[leftSib.nodes.length - 1]!;
|
|
727
774
|
const pKey = leftSib.partitions[leftSib.partitions.length - 1]!;
|
|
728
775
|
this.insertPartition(branch, 0, pNode.partitions[pIndex - 1]!, node, 0);
|
|
@@ -732,27 +779,25 @@ export class BTree<TKey, TEntry> {
|
|
|
732
779
|
return undefined;
|
|
733
780
|
}
|
|
734
781
|
|
|
735
|
-
if (rightSib && rightSib.nodes.length + branch.nodes.length <=
|
|
782
|
+
if (rightSib && rightSib.nodes.length + branch.nodes.length <= this.nodeCapacity) { // Attempt to merge right sibling into self
|
|
736
783
|
const pKey = pNode.partitions[pIndex]!;
|
|
737
784
|
this.deletePartition(pNode, pIndex);
|
|
738
785
|
apply(this.store, branch, [partitions$, branch.partitions.length, 0, [pKey]]);
|
|
739
786
|
apply(this.store, branch, [partitions$, branch.partitions.length, 0, rightSib.partitions]);
|
|
740
787
|
apply(this.store, branch, [nodes$, branch.nodes.length, 0, rightSib.nodes]);
|
|
741
|
-
if (pIndex === 0 && pNode.partitions.length > 0) { // if parent is left edge, new right sibling is now the first partition
|
|
742
|
-
this.updatePartition(pIndex, path, depth - 1, pNode.partitions[0]!);
|
|
743
|
-
}
|
|
744
788
|
this.store.delete(rightSib.header.id);
|
|
745
789
|
return this.rebalanceBranch(path, depth - 1);
|
|
746
790
|
}
|
|
747
791
|
|
|
748
|
-
if (leftSib && leftSib.nodes.length + branch.nodes.length <=
|
|
792
|
+
if (leftSib && leftSib.nodes.length + branch.nodes.length <= this.nodeCapacity) { // Attempt to merge self into left sibling
|
|
793
|
+
const leftLen = leftSib.nodes.length; // capture BEFORE the appends below shift it (see leaf path at rebalanceLeaf)
|
|
749
794
|
const pKey = pNode.partitions[pIndex - 1]!;
|
|
750
795
|
this.deletePartition(pNode, pIndex - 1);
|
|
751
796
|
apply(this.store, leftSib, [partitions$, leftSib.partitions.length, 0, [pKey]]);
|
|
752
797
|
apply(this.store, leftSib, [partitions$, leftSib.partitions.length, 0, branch.partitions]);
|
|
753
798
|
apply(this.store, leftSib, [nodes$, leftSib.nodes.length, 0, branch.nodes]);
|
|
754
799
|
pathBranch.node = leftSib;
|
|
755
|
-
pathBranch.index += leftSib
|
|
800
|
+
pathBranch.index += leftLen; // branch's children now sit after leftSib's original children
|
|
756
801
|
this.store.delete(branch.header.id);
|
|
757
802
|
return this.rebalanceBranch(path, depth - 1);
|
|
758
803
|
}
|