@optimystic/db-core 0.13.4 → 0.14.0
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/cluster/structs.d.ts +29 -0
- package/dist/src/cluster/structs.d.ts.map +1 -1
- package/dist/src/cohort-topic/addressing.d.ts +53 -0
- package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
- package/dist/src/cohort-topic/addressing.js +95 -0
- package/dist/src/cohort-topic/addressing.js.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
- package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
- package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/index.js +6 -0
- package/dist/src/cohort-topic/antidos/index.js.map +1 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
- package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
- package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/index.js +3 -0
- package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
- package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
- package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
- package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
- package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
- package/dist/src/cohort-topic/coldstart.d.ts +117 -0
- package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
- package/dist/src/cohort-topic/coldstart.js +127 -0
- package/dist/src/cohort-topic/coldstart.js.map +1 -0
- package/dist/src/cohort-topic/dmax.d.ts +44 -0
- package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
- package/dist/src/cohort-topic/dmax.js +70 -0
- package/dist/src/cohort-topic/dmax.js.map +1 -0
- package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
- package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/bus.js +146 -0
- package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
- package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
- package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/index.js +4 -0
- package/dist/src/cohort-topic/gossip/index.js.map +1 -0
- package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
- package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/records.js +41 -0
- package/dist/src/cohort-topic/gossip/records.js.map +1 -0
- package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
- package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/view.js +30 -0
- package/dist/src/cohort-topic/gossip/view.js.map +1 -0
- package/dist/src/cohort-topic/index.d.ts +21 -0
- package/dist/src/cohort-topic/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/index.js +21 -0
- package/dist/src/cohort-topic/index.js.map +1 -0
- package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
- package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
- package/dist/src/cohort-topic/load/barometer.js +93 -0
- package/dist/src/cohort-topic/load/barometer.js.map +1 -0
- package/dist/src/cohort-topic/load/index.d.ts +2 -0
- package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/load/index.js +2 -0
- package/dist/src/cohort-topic/load/index.js.map +1 -0
- package/dist/src/cohort-topic/member-engine.d.ts +134 -0
- package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
- package/dist/src/cohort-topic/member-engine.js +264 -0
- package/dist/src/cohort-topic/member-engine.js.map +1 -0
- package/dist/src/cohort-topic/membership/index.d.ts +4 -0
- package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/index.js +4 -0
- package/dist/src/cohort-topic/membership/index.js.map +1 -0
- package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
- package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/publisher.js +107 -0
- package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
- package/dist/src/cohort-topic/membership/source.d.ts +31 -0
- package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/source.js +32 -0
- package/dist/src/cohort-topic/membership/source.js.map +1 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/verifier.js +250 -0
- package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
- package/dist/src/cohort-topic/ports.d.ts +149 -0
- package/dist/src/cohort-topic/ports.d.ts.map +1 -0
- package/dist/src/cohort-topic/ports.js +25 -0
- package/dist/src/cohort-topic/ports.js.map +1 -0
- package/dist/src/cohort-topic/promotion.d.ts +135 -0
- package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
- package/dist/src/cohort-topic/promotion.js +269 -0
- package/dist/src/cohort-topic/promotion.js.map +1 -0
- package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
- package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/bytes.js +32 -0
- package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
- package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
- package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/handoff.js +98 -0
- package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
- package/dist/src/cohort-topic/registration/index.d.ts +7 -0
- package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/index.js +7 -0
- package/dist/src/cohort-topic/registration/index.js.map +1 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/renewal.js +306 -0
- package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
- package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
- package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/sharding.js +48 -0
- package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
- package/dist/src/cohort-topic/registration/store.d.ts +13 -0
- package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/store.js +72 -0
- package/dist/src/cohort-topic/registration/store.js.map +1 -0
- package/dist/src/cohort-topic/registration/types.d.ts +71 -0
- package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/types.js +27 -0
- package/dist/src/cohort-topic/registration/types.js.map +1 -0
- package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
- package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
- package/dist/src/cohort-topic/ring-hash.js +45 -0
- package/dist/src/cohort-topic/ring-hash.js.map +1 -0
- package/dist/src/cohort-topic/service.d.ts +157 -0
- package/dist/src/cohort-topic/service.d.ts.map +1 -0
- package/dist/src/cohort-topic/service.js +237 -0
- package/dist/src/cohort-topic/service.js.map +1 -0
- package/dist/src/cohort-topic/sig/index.d.ts +3 -0
- package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/index.js +3 -0
- package/dist/src/cohort-topic/sig/index.js.map +1 -0
- package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
- package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/payloads.js +25 -0
- package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
- package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
- package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/threshold.js +51 -0
- package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
- package/dist/src/cohort-topic/tiers.d.ts +58 -0
- package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
- package/dist/src/cohort-topic/tiers.js +49 -0
- package/dist/src/cohort-topic/tiers.js.map +1 -0
- package/dist/src/cohort-topic/traffic.d.ts +90 -0
- package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
- package/dist/src/cohort-topic/traffic.js +146 -0
- package/dist/src/cohort-topic/traffic.js.map +1 -0
- package/dist/src/cohort-topic/walk.d.ts +123 -0
- package/dist/src/cohort-topic/walk.d.ts.map +1 -0
- package/dist/src/cohort-topic/walk.js +150 -0
- package/dist/src/cohort-topic/walk.js.map +1 -0
- package/dist/src/cohort-topic/willingness.d.ts +123 -0
- package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
- package/dist/src/cohort-topic/willingness.js +143 -0
- package/dist/src/cohort-topic/willingness.js.map +1 -0
- package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
- package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/codec.js +163 -0
- package/dist/src/cohort-topic/wire/codec.js.map +1 -0
- package/dist/src/cohort-topic/wire/index.d.ts +6 -0
- package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/index.js +6 -0
- package/dist/src/cohort-topic/wire/index.js.map +1 -0
- package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
- package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/payloads.js +102 -0
- package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
- package/dist/src/cohort-topic/wire/types.d.ts +304 -0
- package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/types.js +16 -0
- package/dist/src/cohort-topic/wire/types.js.map +1 -0
- package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
- package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/validate.js +427 -0
- package/dist/src/cohort-topic/wire/validate.js.map +1 -0
- package/dist/src/collection/collection.d.ts +58 -1
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +76 -0
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collections/tree/tree.d.ts +58 -2
- package/dist/src/collections/tree/tree.d.ts.map +1 -1
- package/dist/src/collections/tree/tree.js +59 -2
- package/dist/src/collections/tree/tree.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/log/log.d.ts +42 -4
- package/dist/src/log/log.d.ts.map +1 -1
- package/dist/src/log/log.js +71 -3
- package/dist/src/log/log.js.map +1 -1
- package/dist/src/log/struct.d.ts +122 -2
- package/dist/src/log/struct.d.ts.map +1 -1
- package/dist/src/log/struct.js.map +1 -1
- package/dist/src/matchmaking/capability-filter.d.ts +24 -0
- package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
- package/dist/src/matchmaking/capability-filter.js +37 -0
- package/dist/src/matchmaking/capability-filter.js.map +1 -0
- package/dist/src/matchmaking/config.d.ts +77 -0
- package/dist/src/matchmaking/config.d.ts.map +1 -0
- package/dist/src/matchmaking/config.js +69 -0
- package/dist/src/matchmaking/config.js.map +1 -0
- package/dist/src/matchmaking/index.d.ts +21 -0
- package/dist/src/matchmaking/index.d.ts.map +1 -0
- package/dist/src/matchmaking/index.js +21 -0
- package/dist/src/matchmaking/index.js.map +1 -0
- package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
- package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
- package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
- package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
- package/dist/src/matchmaking/provider.d.ts +70 -0
- package/dist/src/matchmaking/provider.d.ts.map +1 -0
- package/dist/src/matchmaking/provider.js +93 -0
- package/dist/src/matchmaking/provider.js.map +1 -0
- package/dist/src/matchmaking/query-eval.d.ts +53 -0
- package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
- package/dist/src/matchmaking/query-eval.js +69 -0
- package/dist/src/matchmaking/query-eval.js.map +1 -0
- package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
- package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker-walk.js +75 -0
- package/dist/src/matchmaking/seeker-walk.js.map +1 -0
- package/dist/src/matchmaking/seeker.d.ts +51 -0
- package/dist/src/matchmaking/seeker.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker.js +62 -0
- package/dist/src/matchmaking/seeker.js.map +1 -0
- package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
- package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
- package/dist/src/matchmaking/topic-anchor.js +67 -0
- package/dist/src/matchmaking/topic-anchor.js.map +1 -0
- package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
- package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
- package/dist/src/matchmaking/voting-quorum.js +238 -0
- package/dist/src/matchmaking/voting-quorum.js.map +1 -0
- package/dist/src/matchmaking/wire.d.ts +259 -0
- package/dist/src/matchmaking/wire.d.ts.map +1 -0
- package/dist/src/matchmaking/wire.js +484 -0
- package/dist/src/matchmaking/wire.js.map +1 -0
- package/dist/src/network/i-repo.d.ts +19 -0
- package/dist/src/network/i-repo.d.ts.map +1 -1
- package/dist/src/network/repo-protocol.d.ts +3 -1
- package/dist/src/network/repo-protocol.d.ts.map +1 -1
- package/dist/src/network/struct.d.ts +23 -1
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/reactivity/backfill.d.ts +118 -0
- package/dist/src/reactivity/backfill.d.ts.map +1 -0
- package/dist/src/reactivity/backfill.js +137 -0
- package/dist/src/reactivity/backfill.js.map +1 -0
- package/dist/src/reactivity/backpressure.d.ts +99 -0
- package/dist/src/reactivity/backpressure.d.ts.map +1 -0
- package/dist/src/reactivity/backpressure.js +153 -0
- package/dist/src/reactivity/backpressure.js.map +1 -0
- package/dist/src/reactivity/checkpoint.d.ts +165 -0
- package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
- package/dist/src/reactivity/checkpoint.js +232 -0
- package/dist/src/reactivity/checkpoint.js.map +1 -0
- package/dist/src/reactivity/config.d.ts +131 -0
- package/dist/src/reactivity/config.d.ts.map +1 -0
- package/dist/src/reactivity/config.js +135 -0
- package/dist/src/reactivity/config.js.map +1 -0
- package/dist/src/reactivity/dedupe.d.ts +51 -0
- package/dist/src/reactivity/dedupe.d.ts.map +1 -0
- package/dist/src/reactivity/dedupe.js +90 -0
- package/dist/src/reactivity/dedupe.js.map +1 -0
- package/dist/src/reactivity/forwarder.d.ts +50 -0
- package/dist/src/reactivity/forwarder.d.ts.map +1 -0
- package/dist/src/reactivity/forwarder.js +52 -0
- package/dist/src/reactivity/forwarder.js.map +1 -0
- package/dist/src/reactivity/index.d.ts +34 -0
- package/dist/src/reactivity/index.d.ts.map +1 -0
- package/dist/src/reactivity/index.js +34 -0
- package/dist/src/reactivity/index.js.map +1 -0
- package/dist/src/reactivity/notification.d.ts +72 -0
- package/dist/src/reactivity/notification.d.ts.map +1 -0
- package/dist/src/reactivity/notification.js +92 -0
- package/dist/src/reactivity/notification.js.map +1 -0
- package/dist/src/reactivity/policy.d.ts +59 -0
- package/dist/src/reactivity/policy.d.ts.map +1 -0
- package/dist/src/reactivity/policy.js +64 -0
- package/dist/src/reactivity/policy.js.map +1 -0
- package/dist/src/reactivity/push-state.d.ts +140 -0
- package/dist/src/reactivity/push-state.d.ts.map +1 -0
- package/dist/src/reactivity/push-state.js +270 -0
- package/dist/src/reactivity/push-state.js.map +1 -0
- package/dist/src/reactivity/recover.d.ts +75 -0
- package/dist/src/reactivity/recover.d.ts.map +1 -0
- package/dist/src/reactivity/recover.js +105 -0
- package/dist/src/reactivity/recover.js.map +1 -0
- package/dist/src/reactivity/replay-buffer.d.ts +57 -0
- package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
- package/dist/src/reactivity/replay-buffer.js +88 -0
- package/dist/src/reactivity/replay-buffer.js.map +1 -0
- package/dist/src/reactivity/resume.d.ts +214 -0
- package/dist/src/reactivity/resume.d.ts.map +1 -0
- package/dist/src/reactivity/resume.js +358 -0
- package/dist/src/reactivity/resume.js.map +1 -0
- package/dist/src/reactivity/rotation.d.ts +283 -0
- package/dist/src/reactivity/rotation.d.ts.map +1 -0
- package/dist/src/reactivity/rotation.js +265 -0
- package/dist/src/reactivity/rotation.js.map +1 -0
- package/dist/src/reactivity/subscriber.d.ts +66 -0
- package/dist/src/reactivity/subscriber.d.ts.map +1 -0
- package/dist/src/reactivity/subscriber.js +75 -0
- package/dist/src/reactivity/subscriber.js.map +1 -0
- package/dist/src/reactivity/subscription.d.ts +52 -0
- package/dist/src/reactivity/subscription.d.ts.map +1 -0
- package/dist/src/reactivity/subscription.js +28 -0
- package/dist/src/reactivity/subscription.js.map +1 -0
- package/dist/src/reactivity/topic-anchor.d.ts +46 -0
- package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
- package/dist/src/reactivity/topic-anchor.js +61 -0
- package/dist/src/reactivity/topic-anchor.js.map +1 -0
- package/dist/src/reactivity/verify.d.ts +41 -0
- package/dist/src/reactivity/verify.d.ts.map +1 -0
- package/dist/src/reactivity/verify.js +48 -0
- package/dist/src/reactivity/verify.js.map +1 -0
- package/dist/src/reactivity/wire-validate.d.ts +28 -0
- package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
- package/dist/src/reactivity/wire-validate.js +79 -0
- package/dist/src/reactivity/wire-validate.js.map +1 -0
- package/dist/src/reactivity/wire.d.ts +102 -0
- package/dist/src/reactivity/wire.d.ts.map +1 -0
- package/dist/src/reactivity/wire.js +129 -0
- package/dist/src/reactivity/wire.js.map +1 -0
- package/dist/src/transaction/coordinator.d.ts +9 -5
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +48 -25
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transactor/change-notifier.d.ts +74 -0
- package/dist/src/transactor/change-notifier.d.ts.map +1 -0
- package/dist/src/transactor/change-notifier.js +4 -0
- package/dist/src/transactor/change-notifier.js.map +1 -0
- package/dist/src/transactor/index.d.ts +1 -0
- package/dist/src/transactor/index.d.ts.map +1 -1
- package/dist/src/transactor/index.js +1 -0
- package/dist/src/transactor/index.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +44 -2
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +132 -17
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/package.json +3 -2
- package/src/cluster/structs.ts +86 -57
- package/src/cohort-topic/addressing.ts +112 -0
- package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
- package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
- package/src/cohort-topic/antidos/index.ts +5 -0
- package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
- package/src/cohort-topic/antidos/replay-guard.ts +109 -0
- package/src/cohort-topic/antidos/topic-budget.ts +145 -0
- package/src/cohort-topic/antiflood/index.ts +2 -0
- package/src/cohort-topic/antiflood/invariants.ts +108 -0
- package/src/cohort-topic/antiflood/jitter.ts +117 -0
- package/src/cohort-topic/coldstart.ts +204 -0
- package/src/cohort-topic/dmax.ts +88 -0
- package/src/cohort-topic/gossip/bus.ts +217 -0
- package/src/cohort-topic/gossip/index.ts +3 -0
- package/src/cohort-topic/gossip/records.ts +45 -0
- package/src/cohort-topic/gossip/view.ts +66 -0
- package/src/cohort-topic/index.ts +20 -0
- package/src/cohort-topic/load/barometer.ts +134 -0
- package/src/cohort-topic/load/index.ts +1 -0
- package/src/cohort-topic/member-engine.ts +409 -0
- package/src/cohort-topic/membership/index.ts +3 -0
- package/src/cohort-topic/membership/publisher.ts +171 -0
- package/src/cohort-topic/membership/source.ts +41 -0
- package/src/cohort-topic/membership/verifier.ts +345 -0
- package/src/cohort-topic/ports.ts +157 -0
- package/src/cohort-topic/promotion.ts +384 -0
- package/src/cohort-topic/registration/bytes.ts +37 -0
- package/src/cohort-topic/registration/handoff.ts +154 -0
- package/src/cohort-topic/registration/index.ts +6 -0
- package/src/cohort-topic/registration/renewal.ts +423 -0
- package/src/cohort-topic/registration/sharding.ts +61 -0
- package/src/cohort-topic/registration/store.ts +81 -0
- package/src/cohort-topic/registration/types.ts +76 -0
- package/src/cohort-topic/ring-hash.ts +50 -0
- package/src/cohort-topic/service.ts +390 -0
- package/src/cohort-topic/sig/index.ts +2 -0
- package/src/cohort-topic/sig/payloads.ts +39 -0
- package/src/cohort-topic/sig/threshold.ts +64 -0
- package/src/cohort-topic/tiers.ts +74 -0
- package/src/cohort-topic/traffic.ts +211 -0
- package/src/cohort-topic/walk.ts +255 -0
- package/src/cohort-topic/willingness.ts +237 -0
- package/src/cohort-topic/wire/codec.ts +204 -0
- package/src/cohort-topic/wire/index.ts +16 -0
- package/src/cohort-topic/wire/payloads.ts +117 -0
- package/src/cohort-topic/wire/types.ts +343 -0
- package/src/cohort-topic/wire/validate.ts +478 -0
- package/src/collection/collection.ts +93 -1
- package/src/collections/tree/tree.ts +84 -3
- package/src/index.ts +3 -0
- package/src/log/log.ts +86 -7
- package/src/log/struct.ts +126 -2
- package/src/matchmaking/capability-filter.ts +45 -0
- package/src/matchmaking/config.ts +98 -0
- package/src/matchmaking/index.ts +21 -0
- package/src/matchmaking/multi-cohort-seeker.ts +234 -0
- package/src/matchmaking/provider.ts +123 -0
- package/src/matchmaking/query-eval.ts +105 -0
- package/src/matchmaking/seeker-walk.ts +127 -0
- package/src/matchmaking/seeker.ts +86 -0
- package/src/matchmaking/topic-anchor.ts +90 -0
- package/src/matchmaking/voting-quorum.ts +394 -0
- package/src/matchmaking/wire.ts +680 -0
- package/src/network/i-repo.ts +19 -0
- package/src/network/repo-protocol.ts +3 -2
- package/src/network/struct.ts +24 -1
- package/src/reactivity/backfill.ts +220 -0
- package/src/reactivity/backpressure.ts +191 -0
- package/src/reactivity/checkpoint.ts +308 -0
- package/src/reactivity/config.ts +172 -0
- package/src/reactivity/dedupe.ts +132 -0
- package/src/reactivity/forwarder.ts +87 -0
- package/src/reactivity/index.ts +34 -0
- package/src/reactivity/notification.ts +123 -0
- package/src/reactivity/policy.ts +79 -0
- package/src/reactivity/push-state.ts +343 -0
- package/src/reactivity/recover.ts +153 -0
- package/src/reactivity/replay-buffer.ts +141 -0
- package/src/reactivity/resume.ts +549 -0
- package/src/reactivity/rotation.ts +415 -0
- package/src/reactivity/subscriber.ts +132 -0
- package/src/reactivity/subscription.ts +66 -0
- package/src/reactivity/topic-anchor.ts +71 -0
- package/src/reactivity/verify.ts +73 -0
- package/src/reactivity/wire-validate.ts +87 -0
- package/src/reactivity/wire.ts +224 -0
- package/src/transaction/coordinator.ts +53 -27
- package/src/transactor/change-notifier.ts +80 -0
- package/src/transactor/index.ts +1 -0
- package/src/transactor/network-transactor.ts +162 -19
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactivity — Backfill RPC (`docs/reactivity.md` §Backfill RPC, §Wire formats).
|
|
3
|
+
*
|
|
4
|
+
* A subscriber that detects a revision gap (slow-subscriber drop, brief sleep) requests the missing
|
|
5
|
+
* range from its serving cohort; the cohort answers from its {@link ReplayBuffer} ring. The reply
|
|
6
|
+
* carries the **intersection** of the requested range with the buffer plus the `available` window the
|
|
7
|
+
* cohort actually holds, so a subscriber whose lag fell past the ring's low edge learns it must fall
|
|
8
|
+
* back further (to a checkpoint resume or a chain read) rather than silently receiving a short answer.
|
|
9
|
+
*
|
|
10
|
+
* Entries retain the original threshold signature (the buffer stores full {@link NotificationV1}s), so a
|
|
11
|
+
* backfill reply is verifiable **end-to-end** — the subscriber re-runs the same {@link NotificationVerifier}
|
|
12
|
+
* over each backfilled entry it runs over a live notification. Any cohort member can serve, because the
|
|
13
|
+
* replay buffer is gossiped across the cohort (origination ticket).
|
|
14
|
+
*
|
|
15
|
+
* Wire conventions match the rest of reactivity: JSON, byte fields base64url, unix-ms timestamps, `v: 1`,
|
|
16
|
+
* per-message structural validation on decode. `BackfillV1` is signed by the subscriber's peer key
|
|
17
|
+
* (`signature`); replay protection rides the request envelope as elsewhere in the substrate.
|
|
18
|
+
*/
|
|
19
|
+
import type { ReplayBuffer } from "./replay-buffer.js";
|
|
20
|
+
import type { ReactivitySubscriber } from "./subscriber.js";
|
|
21
|
+
import { type NotificationV1 } from "./wire.js";
|
|
22
|
+
/** A subscriber's request to replay a contiguous revision range from a cohort's buffer. */
|
|
23
|
+
export interface BackfillV1 {
|
|
24
|
+
v: 1;
|
|
25
|
+
/** Collection id, base64url. */
|
|
26
|
+
collectionId: string;
|
|
27
|
+
/** Inclusive low edge of the requested range. */
|
|
28
|
+
fromRevision: number;
|
|
29
|
+
/** Inclusive high edge of the requested range. */
|
|
30
|
+
toRevision: number;
|
|
31
|
+
/**
|
|
32
|
+
* Unix ms — bound into {@link backfillSigningPayload} so the request's freshness is authenticated.
|
|
33
|
+
* The serving handler's replay guard keys on the signature + this timestamp, so a captured request
|
|
34
|
+
* cannot be replayed with a forged-fresh timestamp (the forged value invalidates the signature).
|
|
35
|
+
*/
|
|
36
|
+
timestamp: number;
|
|
37
|
+
/** Subscriber peer-key signature over the request. */
|
|
38
|
+
signature: string;
|
|
39
|
+
}
|
|
40
|
+
/** The cohort's reply: the held intersection of the request, plus the window it actually has. */
|
|
41
|
+
export interface BackfillReplyV1 {
|
|
42
|
+
v: 1;
|
|
43
|
+
/** The signed notifications in `[from, to] ∩ buffer`, ascending by revision. */
|
|
44
|
+
entries: NotificationV1[];
|
|
45
|
+
/** The revision window the serving cohort actually holds (so the subscriber can fall back further). */
|
|
46
|
+
available: {
|
|
47
|
+
fromRevision: number;
|
|
48
|
+
toRevision: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Narrow an already-parsed value to {@link BackfillV1}, throwing on any defect. */
|
|
52
|
+
export declare function validateBackfillV1(value: unknown): BackfillV1;
|
|
53
|
+
/** A {@link BackfillV1} minus its `signature` — the canonical bytes the subscriber peer-key-signs. */
|
|
54
|
+
export type BackfillSignable = Omit<BackfillV1, "signature">;
|
|
55
|
+
/**
|
|
56
|
+
* Canonical signed byte image of a {@link BackfillV1} (every field except `signature`). Mirrors the
|
|
57
|
+
* cohort-topic `registerSigningPayload` pattern: determinism comes from encoding an explicitly-ordered,
|
|
58
|
+
* type-tagged JSON array (array order is stable; object key order is not) as UTF-8. The leading
|
|
59
|
+
* `"BackfillV1"` tag means a backfill image can never collide with the `"ResumeV1"` image even when the
|
|
60
|
+
* shared fields are identical. Signer and verifier must agree byte-for-byte — the array order is the
|
|
61
|
+
* contract (pinned by the determinism test).
|
|
62
|
+
*/
|
|
63
|
+
export declare function backfillSigningPayload(body: BackfillSignable): Uint8Array;
|
|
64
|
+
/** Narrow an already-parsed value to {@link BackfillReplyV1}, throwing on any defect. */
|
|
65
|
+
export declare function validateBackfillReplyV1(value: unknown): BackfillReplyV1;
|
|
66
|
+
/** Encode a {@link BackfillV1} as a length-prefixed UTF-8 JSON frame. */
|
|
67
|
+
export declare function encodeBackfillV1(msg: BackfillV1, maxMessageBytes?: number): Uint8Array;
|
|
68
|
+
/** Decode a length-prefixed {@link BackfillV1} frame. */
|
|
69
|
+
export declare function decodeBackfillV1(bytes: Uint8Array, maxMessageBytes?: number): BackfillV1;
|
|
70
|
+
/** Encode a {@link BackfillReplyV1} as a length-prefixed UTF-8 JSON frame. */
|
|
71
|
+
export declare function encodeBackfillReplyV1(msg: BackfillReplyV1, maxMessageBytes?: number): Uint8Array;
|
|
72
|
+
/** Decode a length-prefixed {@link BackfillReplyV1} frame. */
|
|
73
|
+
export declare function decodeBackfillReplyV1(bytes: Uint8Array, maxMessageBytes?: number): BackfillReplyV1;
|
|
74
|
+
/**
|
|
75
|
+
* Serve a {@link BackfillV1} from a cohort's replay buffer: return the intersection of the requested
|
|
76
|
+
* range with the buffer and report the buffer's actual `available` window. A request whose `collectionId`
|
|
77
|
+
* does not match `expectedCollectionId` is rejected (a backfill is collection-scoped). When the buffer is
|
|
78
|
+
* empty, `available` collapses to the request's `fromRevision` (an empty window) and no entries are returned.
|
|
79
|
+
*/
|
|
80
|
+
export declare function serveBackfill(buffer: ReplayBuffer, req: BackfillV1, expectedCollectionId: string): BackfillReplyV1;
|
|
81
|
+
/** Sends a signed {@link BackfillV1} to the serving cohort and awaits its {@link BackfillReplyV1}. */
|
|
82
|
+
export type BackfillTransport = (req: BackfillV1) => Promise<BackfillReplyV1>;
|
|
83
|
+
/** Construction inputs for {@link createBackfillRequester}. */
|
|
84
|
+
export interface BackfillRequesterDeps {
|
|
85
|
+
/** Collection this subscription tracks, base64url. */
|
|
86
|
+
readonly collectionId: string;
|
|
87
|
+
/** Sign the request over its unsigned image (the subscriber's peer key); returns the base64url sig. */
|
|
88
|
+
readonly sign: (req: Omit<BackfillV1, "signature">) => string;
|
|
89
|
+
/** Send the request and await the reply (the db-p2p reactivity application protocol supplies this). */
|
|
90
|
+
readonly transport: BackfillTransport;
|
|
91
|
+
/** Re-enter each backfilled entry through the delivery path (verify → contiguity → deliver → dedupe). */
|
|
92
|
+
readonly subscriber: ReactivitySubscriber;
|
|
93
|
+
/** Unix-ms clock stamped into the signed `timestamp` (injected — db-core proper never reads an ambient clock). */
|
|
94
|
+
readonly clock: () => number;
|
|
95
|
+
/**
|
|
96
|
+
* Called when the cohort's `available` window does not reach the gap's low edge (`available.fromRevision
|
|
97
|
+
* > from`): the buffer no longer covers the gap, so the subscriber must escalate to a checkpoint resume
|
|
98
|
+
* or a chain read (`docs/reactivity.md` §Backfill RPC, "fall back further").
|
|
99
|
+
*/
|
|
100
|
+
readonly onUnderflow?: (requested: {
|
|
101
|
+
from: number;
|
|
102
|
+
to: number;
|
|
103
|
+
}, available: {
|
|
104
|
+
fromRevision: number;
|
|
105
|
+
toRevision: number;
|
|
106
|
+
}) => void;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Build the `requestBackfill(from, to)` driver that connects the subscriber delivery path's backfill seam
|
|
110
|
+
* ([reactivity-origination-replay-delivery]) to the {@link BackfillV1} RPC. It signs and sends the request,
|
|
111
|
+
* replays the returned entries through {@link ReactivitySubscriber.onNotification} (so they close the gap,
|
|
112
|
+
* verified and deduped), and reports an underflow when the cohort's `available` window fell past the gap's
|
|
113
|
+
* low edge so the caller can fall back to a checkpoint resume or a chain read.
|
|
114
|
+
*
|
|
115
|
+
* Returns a `Promise`; the synchronous `(from, to) => void` seam wraps a call to this with `void`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function createBackfillRequester(deps: BackfillRequesterDeps): (from: number, to: number) => Promise<BackfillReplyV1>;
|
|
118
|
+
//# sourceMappingURL=backfill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../../../src/reactivity/backfill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactivity — Backfill RPC (`docs/reactivity.md` §Backfill RPC, §Wire formats).
|
|
3
|
+
*
|
|
4
|
+
* A subscriber that detects a revision gap (slow-subscriber drop, brief sleep) requests the missing
|
|
5
|
+
* range from its serving cohort; the cohort answers from its {@link ReplayBuffer} ring. The reply
|
|
6
|
+
* carries the **intersection** of the requested range with the buffer plus the `available` window the
|
|
7
|
+
* cohort actually holds, so a subscriber whose lag fell past the ring's low edge learns it must fall
|
|
8
|
+
* back further (to a checkpoint resume or a chain read) rather than silently receiving a short answer.
|
|
9
|
+
*
|
|
10
|
+
* Entries retain the original threshold signature (the buffer stores full {@link NotificationV1}s), so a
|
|
11
|
+
* backfill reply is verifiable **end-to-end** — the subscriber re-runs the same {@link NotificationVerifier}
|
|
12
|
+
* over each backfilled entry it runs over a live notification. Any cohort member can serve, because the
|
|
13
|
+
* replay buffer is gossiped across the cohort (origination ticket).
|
|
14
|
+
*
|
|
15
|
+
* Wire conventions match the rest of reactivity: JSON, byte fields base64url, unix-ms timestamps, `v: 1`,
|
|
16
|
+
* per-message structural validation on decode. `BackfillV1` is signed by the subscriber's peer key
|
|
17
|
+
* (`signature`); replay protection rides the request envelope as elsewhere in the substrate.
|
|
18
|
+
*/
|
|
19
|
+
import { decodeCohortMessage, encodeCohortMessage, DEFAULT_MAX_MESSAGE_BYTES, } from "../cohort-topic/wire/codec.js";
|
|
20
|
+
import { asObject, b64urlField, failWire, reqFiniteNumber, reqIntInRange, reqString, requireV1, } from "./wire-validate.js";
|
|
21
|
+
import { validateNotificationArray, validateNotificationV1 } from "./wire.js";
|
|
22
|
+
/** Narrow an already-parsed value to {@link BackfillV1}, throwing on any defect. */
|
|
23
|
+
export function validateBackfillV1(value) {
|
|
24
|
+
const what = "BackfillV1";
|
|
25
|
+
const obj = asObject(value, what);
|
|
26
|
+
requireV1(obj, what);
|
|
27
|
+
const fromRevision = reqIntInRange(obj, "fromRevision", what, 0);
|
|
28
|
+
const toRevision = reqIntInRange(obj, "toRevision", what, fromRevision);
|
|
29
|
+
return {
|
|
30
|
+
v: 1,
|
|
31
|
+
collectionId: b64urlField(reqString(obj, "collectionId", what), "collectionId", what),
|
|
32
|
+
fromRevision,
|
|
33
|
+
toRevision,
|
|
34
|
+
timestamp: reqFiniteNumber(obj, "timestamp", what),
|
|
35
|
+
signature: b64urlField(reqString(obj, "signature", what), "signature", what),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const utf8 = new TextEncoder();
|
|
39
|
+
/**
|
|
40
|
+
* Canonical signed byte image of a {@link BackfillV1} (every field except `signature`). Mirrors the
|
|
41
|
+
* cohort-topic `registerSigningPayload` pattern: determinism comes from encoding an explicitly-ordered,
|
|
42
|
+
* type-tagged JSON array (array order is stable; object key order is not) as UTF-8. The leading
|
|
43
|
+
* `"BackfillV1"` tag means a backfill image can never collide with the `"ResumeV1"` image even when the
|
|
44
|
+
* shared fields are identical. Signer and verifier must agree byte-for-byte — the array order is the
|
|
45
|
+
* contract (pinned by the determinism test).
|
|
46
|
+
*/
|
|
47
|
+
export function backfillSigningPayload(body) {
|
|
48
|
+
return utf8.encode(JSON.stringify([
|
|
49
|
+
"BackfillV1",
|
|
50
|
+
body.v,
|
|
51
|
+
body.collectionId,
|
|
52
|
+
body.fromRevision,
|
|
53
|
+
body.toRevision,
|
|
54
|
+
body.timestamp,
|
|
55
|
+
]));
|
|
56
|
+
}
|
|
57
|
+
/** Narrow an already-parsed value to {@link BackfillReplyV1}, throwing on any defect. */
|
|
58
|
+
export function validateBackfillReplyV1(value) {
|
|
59
|
+
const what = "BackfillReplyV1";
|
|
60
|
+
const obj = asObject(value, what);
|
|
61
|
+
requireV1(obj, what);
|
|
62
|
+
const available = asObject(obj["available"], `${what}.available`);
|
|
63
|
+
const availFrom = reqIntInRange(available, "fromRevision", `${what}.available`, 0);
|
|
64
|
+
return {
|
|
65
|
+
v: 1,
|
|
66
|
+
entries: validateNotificationArray(obj["entries"], what),
|
|
67
|
+
available: {
|
|
68
|
+
fromRevision: availFrom,
|
|
69
|
+
toRevision: reqIntInRange(available, "toRevision", `${what}.available`, availFrom),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Encode a {@link BackfillV1} as a length-prefixed UTF-8 JSON frame. */
|
|
74
|
+
export function encodeBackfillV1(msg, maxMessageBytes = DEFAULT_MAX_MESSAGE_BYTES) {
|
|
75
|
+
return encodeCohortMessage(validateBackfillV1(msg), maxMessageBytes);
|
|
76
|
+
}
|
|
77
|
+
/** Decode a length-prefixed {@link BackfillV1} frame. */
|
|
78
|
+
export function decodeBackfillV1(bytes, maxMessageBytes) {
|
|
79
|
+
return validateBackfillV1(decodeCohortMessage(bytes, maxMessageBytes));
|
|
80
|
+
}
|
|
81
|
+
/** Encode a {@link BackfillReplyV1} as a length-prefixed UTF-8 JSON frame. */
|
|
82
|
+
export function encodeBackfillReplyV1(msg, maxMessageBytes = DEFAULT_MAX_MESSAGE_BYTES) {
|
|
83
|
+
return encodeCohortMessage(validateBackfillReplyV1(msg), maxMessageBytes);
|
|
84
|
+
}
|
|
85
|
+
/** Decode a length-prefixed {@link BackfillReplyV1} frame. */
|
|
86
|
+
export function decodeBackfillReplyV1(bytes, maxMessageBytes) {
|
|
87
|
+
return validateBackfillReplyV1(decodeCohortMessage(bytes, maxMessageBytes));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Serve a {@link BackfillV1} from a cohort's replay buffer: return the intersection of the requested
|
|
91
|
+
* range with the buffer and report the buffer's actual `available` window. A request whose `collectionId`
|
|
92
|
+
* does not match `expectedCollectionId` is rejected (a backfill is collection-scoped). When the buffer is
|
|
93
|
+
* empty, `available` collapses to the request's `fromRevision` (an empty window) and no entries are returned.
|
|
94
|
+
*/
|
|
95
|
+
export function serveBackfill(buffer, req, expectedCollectionId) {
|
|
96
|
+
if (req.collectionId !== expectedCollectionId) {
|
|
97
|
+
failWire(`serveBackfill: request collectionId does not match this cohort's collection`);
|
|
98
|
+
}
|
|
99
|
+
const entries = buffer.range(req.fromRevision, req.toRevision).map((e) => validateNotificationV1(e.payload));
|
|
100
|
+
const low = buffer.lowRevision;
|
|
101
|
+
const high = buffer.highRevision;
|
|
102
|
+
const available = low === undefined || high === undefined
|
|
103
|
+
? { fromRevision: req.fromRevision, toRevision: req.fromRevision }
|
|
104
|
+
: { fromRevision: low, toRevision: high };
|
|
105
|
+
return { v: 1, entries, available };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Build the `requestBackfill(from, to)` driver that connects the subscriber delivery path's backfill seam
|
|
109
|
+
* ([reactivity-origination-replay-delivery]) to the {@link BackfillV1} RPC. It signs and sends the request,
|
|
110
|
+
* replays the returned entries through {@link ReactivitySubscriber.onNotification} (so they close the gap,
|
|
111
|
+
* verified and deduped), and reports an underflow when the cohort's `available` window fell past the gap's
|
|
112
|
+
* low edge so the caller can fall back to a checkpoint resume or a chain read.
|
|
113
|
+
*
|
|
114
|
+
* Returns a `Promise`; the synchronous `(from, to) => void` seam wraps a call to this with `void`.
|
|
115
|
+
*/
|
|
116
|
+
export function createBackfillRequester(deps) {
|
|
117
|
+
return async (from, to) => {
|
|
118
|
+
const unsigned = { v: 1, collectionId: deps.collectionId, fromRevision: from, toRevision: to, timestamp: deps.clock() };
|
|
119
|
+
const req = { ...unsigned, signature: deps.sign(unsigned) };
|
|
120
|
+
const reply = await deps.transport(req);
|
|
121
|
+
// Underflow: the cohort's held window does not reach the gap's low edge (`docs/reactivity.md`
|
|
122
|
+
// §Backfill RPC, "fall back further"). The returned entries all sit *above* the still-unfillable
|
|
123
|
+
// low range, so none can apply contiguously from `from`. Replaying them would only re-fire the
|
|
124
|
+
// subscriber's gap → `requestBackfill` seam (which, when wired back to this driver, recurses
|
|
125
|
+
// without bound). Escalate to a checkpoint resume / chain read instead and leave the replay to the
|
|
126
|
+
// recovery path that can actually close the low range.
|
|
127
|
+
if (reply.available.fromRevision > from) {
|
|
128
|
+
deps.onUnderflow?.({ from, to }, reply.available);
|
|
129
|
+
return reply;
|
|
130
|
+
}
|
|
131
|
+
for (const entry of reply.entries) {
|
|
132
|
+
await deps.subscriber.onNotification(entry);
|
|
133
|
+
}
|
|
134
|
+
return reply;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=backfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfill.js","sourceRoot":"","sources":["../../../src/reactivity/backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,yBAAyB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,eAAe,EACf,aAAa,EACb,SAAS,EACT,SAAS,GACT,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAuB,MAAM,WAAW,CAAC;AAiCnG,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAChD,MAAM,IAAI,GAAG,YAAY,CAAC;IAC1B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IACxE,OAAO;QACN,CAAC,EAAE,CAAC;QACJ,YAAY,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC;QACrF,YAAY;QACZ,UAAU;QACV,SAAS,EAAE,eAAe,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC;QAClD,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC;KAC5E,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAK/B;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAsB;IAC5D,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACjC,YAAY;QACZ,IAAI,CAAC,CAAC;QACN,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,UAAU;QACf,IAAI,CAAC,SAAS;KACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,MAAM,IAAI,GAAG,iBAAiB,CAAC;IAC/B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC;IACnF,OAAO;QACN,CAAC,EAAE,CAAC;QACJ,OAAO,EAAE,yBAAyB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;QACxD,SAAS,EAAE;YACV,YAAY,EAAE,SAAS;YACvB,UAAU,EAAE,aAAa,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,YAAY,EAAE,SAAS,CAAC;SAClF;KACD,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,GAAe,EAAE,kBAA0B,yBAAyB;IACpG,OAAO,mBAAmB,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC;AACtE,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,KAAiB,EAAE,eAAwB;IAC3E,OAAO,kBAAkB,CAAC,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,qBAAqB,CAAC,GAAoB,EAAE,kBAA0B,yBAAyB;IAC9G,OAAO,mBAAmB,CAAC,uBAAuB,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC;AAC3E,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,qBAAqB,CAAC,KAAiB,EAAE,eAAwB;IAChF,OAAO,uBAAuB,CAAC,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,MAAoB,EAAE,GAAe,EAAE,oBAA4B;IAChG,IAAI,GAAG,CAAC,YAAY,KAAK,oBAAoB,EAAE,CAAC;QAC/C,QAAQ,CAAC,6EAA6E,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7G,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC;IACjC,MAAM,SAAS,GAAG,GAAG,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS;QACxD,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE;QAClE,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAC3C,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC;AA2BD;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAA2B;IAClE,OAAO,KAAK,EAAE,IAAY,EAAE,EAAU,EAA4B,EAAE;QACnE,MAAM,QAAQ,GAAkC,EAAE,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QACvJ,MAAM,GAAG,GAAe,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACxC,8FAA8F;QAC9F,iGAAiG;QACjG,+FAA+F;QAC/F,6FAA6F;QAC7F,mGAAmG;QACnG,uDAAuD;QACvD,IAAI,KAAK,CAAC,SAAS,CAAC,YAAY,GAAG,IAAI,EAAE,CAAC;YACzC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAClD,OAAO,KAAK,CAAC;QACd,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactivity — slow-subscriber backpressure (`docs/reactivity.md` §Slow-subscriber backpressure).
|
|
3
|
+
*
|
|
4
|
+
* A forwarder's primary maintains a **per-subscriber** bounded queue with **drop-oldest** semantics. When
|
|
5
|
+
* a subscriber's queue is full and a new notification arrives, the *oldest* queued entry is dropped and a
|
|
6
|
+
* monotone `dropped` counter increments. The subscriber detects the resulting revision jump on its next
|
|
7
|
+
* delivery and issues a `BackfillV1` against the replay buffer (the backfill RPC is owned by
|
|
8
|
+
* [reactivity-backfill-resume-checkpoints]; this module only produces the gap).
|
|
9
|
+
*
|
|
10
|
+
* The point is **isolation**: each subscriber has its own queue, so one phone on a flaky link fills and
|
|
11
|
+
* drops *its own* queue without stalling fan-out to the rest of the cohort's attached subscribers. Memory
|
|
12
|
+
* is bounded by `cohort_subscribers × queue_max × notification_size` — the per-subscriber queue depth is
|
|
13
|
+
* the small `queue_max` (default 32), never the unbounded backlog of the slowest receiver.
|
|
14
|
+
*
|
|
15
|
+
* This is the primary-local fan-out buffer, **not** cohort soft state: it is never gossiped (only the
|
|
16
|
+
* primary drives delivery), so it is absent from {@link import("./push-state.js").PushStateGossipV1}. A
|
|
17
|
+
* `cohortEpoch` handoff rebuilds it empty at the new primary — a few dropped notifications at handoff are
|
|
18
|
+
* exactly what the replay buffer + backfill path recover.
|
|
19
|
+
*/
|
|
20
|
+
import type { NotificationV1 } from "./wire.js";
|
|
21
|
+
/** The result of enqueuing one notification onto a subscriber's bounded queue. */
|
|
22
|
+
export interface EnqueueResult {
|
|
23
|
+
/** `true` iff the queue was full and its oldest entry was evicted to make room (drop-oldest). */
|
|
24
|
+
readonly droppedOldest: boolean;
|
|
25
|
+
/** The evicted notification, when `droppedOldest`; the subscriber will detect the gap and backfill. */
|
|
26
|
+
readonly evicted?: NotificationV1;
|
|
27
|
+
/** Queue depth after the enqueue (`<= capacity`). */
|
|
28
|
+
readonly depth: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A bounded, drop-oldest per-subscriber delivery queue (`docs/reactivity.md` §Slow-subscriber
|
|
32
|
+
* backpressure). Holds at most `capacity` (= `queue_max`) pending notifications; a full queue evicts its
|
|
33
|
+
* oldest on the next enqueue and increments {@link dropped}.
|
|
34
|
+
*/
|
|
35
|
+
export declare class BoundedQueue {
|
|
36
|
+
readonly capacity: number;
|
|
37
|
+
private readonly entries;
|
|
38
|
+
private droppedCount;
|
|
39
|
+
constructor(capacity?: number);
|
|
40
|
+
/** Pending depth (`0..capacity`). */
|
|
41
|
+
get size(): number;
|
|
42
|
+
/** Monotone count of notifications dropped to drop-oldest pressure over this queue's lifetime. */
|
|
43
|
+
get dropped(): number;
|
|
44
|
+
/** True iff a further enqueue would evict the oldest entry. */
|
|
45
|
+
get full(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Enqueue `n` for delivery. On overflow the **oldest** entry is dropped (not the incoming one — a slow
|
|
48
|
+
* subscriber wants the freshest revisions, and the dropped span is recovered via backfill) and
|
|
49
|
+
* {@link dropped} increments. Returns whether an eviction occurred and the resulting depth.
|
|
50
|
+
*/
|
|
51
|
+
enqueue(n: NotificationV1): EnqueueResult;
|
|
52
|
+
/** The oldest pending notification, or `undefined` when empty (does not dequeue). */
|
|
53
|
+
peek(): NotificationV1 | undefined;
|
|
54
|
+
/** Remove and return the oldest pending notification (FIFO delivery), or `undefined` when empty. */
|
|
55
|
+
dequeue(): NotificationV1 | undefined;
|
|
56
|
+
/** Drain and return all pending notifications in FIFO order, leaving the queue empty. */
|
|
57
|
+
drain(): NotificationV1[];
|
|
58
|
+
/** A read-only snapshot of the pending notifications, oldest first (does not mutate). */
|
|
59
|
+
pending(): readonly NotificationV1[];
|
|
60
|
+
}
|
|
61
|
+
/** The per-subscriber drop record surfaced by {@link SubscriberBackpressure.enqueue}. */
|
|
62
|
+
export interface SubscriberEnqueueResult extends EnqueueResult {
|
|
63
|
+
/** The subscriber this enqueue targeted, base64url peer id. */
|
|
64
|
+
readonly subscriberId: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The forwarder primary's per-subscriber backpressure map (the `PushState.perSubscriberQueue` field). One
|
|
68
|
+
* {@link BoundedQueue} per attached subscriber, created lazily on first delivery. `enqueue` routes a
|
|
69
|
+
* notification to exactly one subscriber's queue, so a slow subscriber's drops never touch another
|
|
70
|
+
* subscriber's queue — the isolation the §Slow-subscriber backpressure section requires.
|
|
71
|
+
*/
|
|
72
|
+
export declare class SubscriberBackpressure {
|
|
73
|
+
readonly capacity: number;
|
|
74
|
+
private readonly queues;
|
|
75
|
+
constructor(capacity?: number);
|
|
76
|
+
/** Number of subscribers with a live queue. */
|
|
77
|
+
get subscriberCount(): number;
|
|
78
|
+
/** The live queue for `subscriberId`, creating an empty one on first use. */
|
|
79
|
+
queue(subscriberId: string): BoundedQueue;
|
|
80
|
+
/** The live queue for `subscriberId`, or `undefined` if none exists yet (no lazy creation). */
|
|
81
|
+
peekQueue(subscriberId: string): BoundedQueue | undefined;
|
|
82
|
+
/** Enqueue `n` for one subscriber. A drop on that subscriber's queue isolates it from the others. */
|
|
83
|
+
enqueue(subscriberId: string, n: NotificationV1): SubscriberEnqueueResult;
|
|
84
|
+
/**
|
|
85
|
+
* Fan one notification out to every named subscriber, returning only the subscribers whose queue
|
|
86
|
+
* dropped its oldest under pressure. A slow subscriber that drops is the *only* one affected — fast
|
|
87
|
+
* subscribers' queues accept the same notification contiguously (the isolation property).
|
|
88
|
+
*/
|
|
89
|
+
fanOut(subscriberIds: Iterable<string>, n: NotificationV1): SubscriberEnqueueResult[];
|
|
90
|
+
/** Drop a departed subscriber's queue (TTL eviction / withdrawal), reclaiming its memory. */
|
|
91
|
+
remove(subscriberId: string): void;
|
|
92
|
+
/** The total notifications dropped across all subscribers (diagnostics). */
|
|
93
|
+
totalDropped(): number;
|
|
94
|
+
/** The subscriber ids currently holding a queue (for iteration / diagnostics). */
|
|
95
|
+
subscribers(): IterableIterator<string>;
|
|
96
|
+
}
|
|
97
|
+
/** Build a {@link SubscriberBackpressure} map with the configured per-subscriber depth (default `queue_max`). */
|
|
98
|
+
export declare function createSubscriberBackpressure(capacity?: number): SubscriberBackpressure;
|
|
99
|
+
//# sourceMappingURL=backpressure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backpressure.d.ts","sourceRoot":"","sources":["../../../src/reactivity/backpressure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC7B,iGAAiG;IACjG,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,uGAAuG;IACvG,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,YAAY,CAAK;gBAEb,QAAQ,GAAE,MAA0B;IAOhD,qCAAqC;IACrC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,kGAAkG;IAClG,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+DAA+D;IAC/D,IAAI,IAAI,IAAI,OAAO,CAElB;IAED;;;;OAIG;IACH,OAAO,CAAC,CAAC,EAAE,cAAc,GAAG,aAAa;IAYzC,qFAAqF;IACrF,IAAI,IAAI,cAAc,GAAG,SAAS;IAIlC,oGAAoG;IACpG,OAAO,IAAI,cAAc,GAAG,SAAS;IAIrC,yFAAyF;IACzF,KAAK,IAAI,cAAc,EAAE;IAIzB,yFAAyF;IACzF,OAAO,IAAI,SAAS,cAAc,EAAE;CAGpC;AAED,yFAAyF;AACzF,MAAM,WAAW,uBAAwB,SAAQ,aAAa;IAC7D,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,sBAAsB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;gBAE9C,QAAQ,GAAE,MAA0B;IAOhD,+CAA+C;IAC/C,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,6EAA6E;IAC7E,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY;IASzC,+FAA+F;IAC/F,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIzD,qGAAqG;IACrG,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,uBAAuB;IAIzE;;;;OAIG;IACH,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,cAAc,GAAG,uBAAuB,EAAE;IAWrF,6FAA6F;IAC7F,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAIlC,4EAA4E;IAC5E,YAAY,IAAI,MAAM;IAQtB,kFAAkF;IAClF,WAAW,IAAI,gBAAgB,CAAC,MAAM,CAAC;CAGvC;AAED,iHAAiH;AACjH,wBAAgB,4BAA4B,CAAC,QAAQ,GAAE,MAA0B,GAAG,sBAAsB,CAEzG"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactivity — slow-subscriber backpressure (`docs/reactivity.md` §Slow-subscriber backpressure).
|
|
3
|
+
*
|
|
4
|
+
* A forwarder's primary maintains a **per-subscriber** bounded queue with **drop-oldest** semantics. When
|
|
5
|
+
* a subscriber's queue is full and a new notification arrives, the *oldest* queued entry is dropped and a
|
|
6
|
+
* monotone `dropped` counter increments. The subscriber detects the resulting revision jump on its next
|
|
7
|
+
* delivery and issues a `BackfillV1` against the replay buffer (the backfill RPC is owned by
|
|
8
|
+
* [reactivity-backfill-resume-checkpoints]; this module only produces the gap).
|
|
9
|
+
*
|
|
10
|
+
* The point is **isolation**: each subscriber has its own queue, so one phone on a flaky link fills and
|
|
11
|
+
* drops *its own* queue without stalling fan-out to the rest of the cohort's attached subscribers. Memory
|
|
12
|
+
* is bounded by `cohort_subscribers × queue_max × notification_size` — the per-subscriber queue depth is
|
|
13
|
+
* the small `queue_max` (default 32), never the unbounded backlog of the slowest receiver.
|
|
14
|
+
*
|
|
15
|
+
* This is the primary-local fan-out buffer, **not** cohort soft state: it is never gossiped (only the
|
|
16
|
+
* primary drives delivery), so it is absent from {@link import("./push-state.js").PushStateGossipV1}. A
|
|
17
|
+
* `cohortEpoch` handoff rebuilds it empty at the new primary — a few dropped notifications at handoff are
|
|
18
|
+
* exactly what the replay buffer + backfill path recover.
|
|
19
|
+
*/
|
|
20
|
+
import { QUEUE_MAX_DEFAULT } from "./config.js";
|
|
21
|
+
/**
|
|
22
|
+
* A bounded, drop-oldest per-subscriber delivery queue (`docs/reactivity.md` §Slow-subscriber
|
|
23
|
+
* backpressure). Holds at most `capacity` (= `queue_max`) pending notifications; a full queue evicts its
|
|
24
|
+
* oldest on the next enqueue and increments {@link dropped}.
|
|
25
|
+
*/
|
|
26
|
+
export class BoundedQueue {
|
|
27
|
+
capacity;
|
|
28
|
+
entries = [];
|
|
29
|
+
droppedCount = 0;
|
|
30
|
+
constructor(capacity = QUEUE_MAX_DEFAULT) {
|
|
31
|
+
if (!Number.isInteger(capacity) || capacity < 1) {
|
|
32
|
+
throw new RangeError(`reactivity bounded queue: capacity must be an integer >= 1, got ${capacity}`);
|
|
33
|
+
}
|
|
34
|
+
this.capacity = capacity;
|
|
35
|
+
}
|
|
36
|
+
/** Pending depth (`0..capacity`). */
|
|
37
|
+
get size() {
|
|
38
|
+
return this.entries.length;
|
|
39
|
+
}
|
|
40
|
+
/** Monotone count of notifications dropped to drop-oldest pressure over this queue's lifetime. */
|
|
41
|
+
get dropped() {
|
|
42
|
+
return this.droppedCount;
|
|
43
|
+
}
|
|
44
|
+
/** True iff a further enqueue would evict the oldest entry. */
|
|
45
|
+
get full() {
|
|
46
|
+
return this.entries.length >= this.capacity;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enqueue `n` for delivery. On overflow the **oldest** entry is dropped (not the incoming one — a slow
|
|
50
|
+
* subscriber wants the freshest revisions, and the dropped span is recovered via backfill) and
|
|
51
|
+
* {@link dropped} increments. Returns whether an eviction occurred and the resulting depth.
|
|
52
|
+
*/
|
|
53
|
+
enqueue(n) {
|
|
54
|
+
let evicted;
|
|
55
|
+
if (this.entries.length >= this.capacity) {
|
|
56
|
+
evicted = this.entries.shift();
|
|
57
|
+
this.droppedCount += 1;
|
|
58
|
+
}
|
|
59
|
+
this.entries.push(n);
|
|
60
|
+
return evicted !== undefined
|
|
61
|
+
? { droppedOldest: true, evicted, depth: this.entries.length }
|
|
62
|
+
: { droppedOldest: false, depth: this.entries.length };
|
|
63
|
+
}
|
|
64
|
+
/** The oldest pending notification, or `undefined` when empty (does not dequeue). */
|
|
65
|
+
peek() {
|
|
66
|
+
return this.entries[0];
|
|
67
|
+
}
|
|
68
|
+
/** Remove and return the oldest pending notification (FIFO delivery), or `undefined` when empty. */
|
|
69
|
+
dequeue() {
|
|
70
|
+
return this.entries.shift();
|
|
71
|
+
}
|
|
72
|
+
/** Drain and return all pending notifications in FIFO order, leaving the queue empty. */
|
|
73
|
+
drain() {
|
|
74
|
+
return this.entries.splice(0, this.entries.length);
|
|
75
|
+
}
|
|
76
|
+
/** A read-only snapshot of the pending notifications, oldest first (does not mutate). */
|
|
77
|
+
pending() {
|
|
78
|
+
return [...this.entries];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The forwarder primary's per-subscriber backpressure map (the `PushState.perSubscriberQueue` field). One
|
|
83
|
+
* {@link BoundedQueue} per attached subscriber, created lazily on first delivery. `enqueue` routes a
|
|
84
|
+
* notification to exactly one subscriber's queue, so a slow subscriber's drops never touch another
|
|
85
|
+
* subscriber's queue — the isolation the §Slow-subscriber backpressure section requires.
|
|
86
|
+
*/
|
|
87
|
+
export class SubscriberBackpressure {
|
|
88
|
+
capacity;
|
|
89
|
+
queues = new Map();
|
|
90
|
+
constructor(capacity = QUEUE_MAX_DEFAULT) {
|
|
91
|
+
if (!Number.isInteger(capacity) || capacity < 1) {
|
|
92
|
+
throw new RangeError(`reactivity backpressure: capacity must be an integer >= 1, got ${capacity}`);
|
|
93
|
+
}
|
|
94
|
+
this.capacity = capacity;
|
|
95
|
+
}
|
|
96
|
+
/** Number of subscribers with a live queue. */
|
|
97
|
+
get subscriberCount() {
|
|
98
|
+
return this.queues.size;
|
|
99
|
+
}
|
|
100
|
+
/** The live queue for `subscriberId`, creating an empty one on first use. */
|
|
101
|
+
queue(subscriberId) {
|
|
102
|
+
let q = this.queues.get(subscriberId);
|
|
103
|
+
if (q === undefined) {
|
|
104
|
+
q = new BoundedQueue(this.capacity);
|
|
105
|
+
this.queues.set(subscriberId, q);
|
|
106
|
+
}
|
|
107
|
+
return q;
|
|
108
|
+
}
|
|
109
|
+
/** The live queue for `subscriberId`, or `undefined` if none exists yet (no lazy creation). */
|
|
110
|
+
peekQueue(subscriberId) {
|
|
111
|
+
return this.queues.get(subscriberId);
|
|
112
|
+
}
|
|
113
|
+
/** Enqueue `n` for one subscriber. A drop on that subscriber's queue isolates it from the others. */
|
|
114
|
+
enqueue(subscriberId, n) {
|
|
115
|
+
return { subscriberId, ...this.queue(subscriberId).enqueue(n) };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fan one notification out to every named subscriber, returning only the subscribers whose queue
|
|
119
|
+
* dropped its oldest under pressure. A slow subscriber that drops is the *only* one affected — fast
|
|
120
|
+
* subscribers' queues accept the same notification contiguously (the isolation property).
|
|
121
|
+
*/
|
|
122
|
+
fanOut(subscriberIds, n) {
|
|
123
|
+
const drops = [];
|
|
124
|
+
for (const id of subscriberIds) {
|
|
125
|
+
const result = this.enqueue(id, n);
|
|
126
|
+
if (result.droppedOldest) {
|
|
127
|
+
drops.push(result);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return drops;
|
|
131
|
+
}
|
|
132
|
+
/** Drop a departed subscriber's queue (TTL eviction / withdrawal), reclaiming its memory. */
|
|
133
|
+
remove(subscriberId) {
|
|
134
|
+
this.queues.delete(subscriberId);
|
|
135
|
+
}
|
|
136
|
+
/** The total notifications dropped across all subscribers (diagnostics). */
|
|
137
|
+
totalDropped() {
|
|
138
|
+
let total = 0;
|
|
139
|
+
for (const q of this.queues.values()) {
|
|
140
|
+
total += q.dropped;
|
|
141
|
+
}
|
|
142
|
+
return total;
|
|
143
|
+
}
|
|
144
|
+
/** The subscriber ids currently holding a queue (for iteration / diagnostics). */
|
|
145
|
+
subscribers() {
|
|
146
|
+
return this.queues.keys();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Build a {@link SubscriberBackpressure} map with the configured per-subscriber depth (default `queue_max`). */
|
|
150
|
+
export function createSubscriberBackpressure(capacity = QUEUE_MAX_DEFAULT) {
|
|
151
|
+
return new SubscriberBackpressure(capacity);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=backpressure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backpressure.js","sourceRoot":"","sources":["../../../src/reactivity/backpressure.ts"],"names":[],"mappings":"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"}
|