@optimystic/db-core 0.13.5 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cluster/structs.d.ts +13 -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 +9 -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 +34 -2
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +113 -13
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/package.json +2 -2
- package/src/cluster/structs.ts +13 -0
- 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 +9 -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 +133 -15
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — network-size-driven `d_max`.
|
|
3
|
+
*
|
|
4
|
+
* Transcribed from `docs/cohort-topic.md` §Maximum useful depth:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* d_max = max(0, ⌊log_F(n_est)⌋ − 1)
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* If `n_est` confidence falls below `confidence_min` (default 0.3) the computation caps `d_max` at
|
|
11
|
+
* `⌊d_max_cap / 2⌋` (`d_max_cap` default 60) as an upper bound — i.e. `min(formula, ⌊d_max_cap / 2⌋)` —
|
|
12
|
+
* to avoid pathological deep probes from an over-estimated population. Small/sparse populations
|
|
13
|
+
* (where the formula already yields a small value) are unaffected.
|
|
14
|
+
*
|
|
15
|
+
* `d_max` is the start tier of the walk-toward-root and is recomputed **lazily** — participants
|
|
16
|
+
* don't need it precise. The size estimate is read through the injected {@link ISizeEstimator}
|
|
17
|
+
* (db-p2p wraps FRET's `estimateSizeAndConfidence`; db-core never imports FRET).
|
|
18
|
+
*/
|
|
19
|
+
/** Default below which `n_est` confidence triggers the `d_max` cap. */
|
|
20
|
+
export const DEFAULT_CONFIDENCE_MIN = 0.3;
|
|
21
|
+
/** Default hard cap on the walk-toward-root start tier. */
|
|
22
|
+
export const DEFAULT_D_MAX_CAP = 60;
|
|
23
|
+
/**
|
|
24
|
+
* `⌊log_F(n)⌋` computed without floating-point drift near exact powers of `F`. Multiplies back up to
|
|
25
|
+
* confirm the floor (`Math.log` can return e.g. `2.9999999` for `log_16(4096)`), so the result is
|
|
26
|
+
* exact for all `n` representable as a JS integer.
|
|
27
|
+
*/
|
|
28
|
+
function floorLogF(n, F) {
|
|
29
|
+
if (n < 1)
|
|
30
|
+
return 0;
|
|
31
|
+
let d = Math.floor(Math.log(n) / Math.log(F));
|
|
32
|
+
if (d < 0)
|
|
33
|
+
d = 0;
|
|
34
|
+
// Correct floating-point error at the boundary in both directions.
|
|
35
|
+
while (Math.pow(F, d + 1) <= n)
|
|
36
|
+
d++;
|
|
37
|
+
while (d > 0 && Math.pow(F, d) > n)
|
|
38
|
+
d--;
|
|
39
|
+
return d;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a {@link DMaxComputer}. The estimate is read on every {@link DMaxComputer.dMax} call so the
|
|
43
|
+
* value tracks the latest FRET sample; callers cache it themselves if they want stability.
|
|
44
|
+
*/
|
|
45
|
+
export function makeDMaxComputer(config) {
|
|
46
|
+
const { estimator, F } = config;
|
|
47
|
+
if (!Number.isInteger(F) || F < 2) {
|
|
48
|
+
throw new RangeError(`fan-out F must be an integer ≥ 2, got ${F}`);
|
|
49
|
+
}
|
|
50
|
+
const confidenceMin = config.confidenceMin ?? DEFAULT_CONFIDENCE_MIN;
|
|
51
|
+
const dMaxCap = config.dMaxCap ?? DEFAULT_D_MAX_CAP;
|
|
52
|
+
if (!Number.isInteger(dMaxCap) || dMaxCap < 0) {
|
|
53
|
+
throw new RangeError(`d_max_cap must be a non-negative integer, got ${dMaxCap}`);
|
|
54
|
+
}
|
|
55
|
+
const capValue = Math.floor(dMaxCap / 2);
|
|
56
|
+
return {
|
|
57
|
+
dMax() {
|
|
58
|
+
const { nEst, confidence } = estimator.estimate();
|
|
59
|
+
const formula = Math.min(Math.max(0, floorLogF(nEst, F) - 1), dMaxCap);
|
|
60
|
+
// Low confidence caps d_max as an upper bound (not a set-to): an over-estimated
|
|
61
|
+
// population can't push the walk deeper than ⌊d_max_cap / 2⌋, while small
|
|
62
|
+
// populations keep their (smaller) formula value.
|
|
63
|
+
if (confidence < confidenceMin) {
|
|
64
|
+
return Math.min(formula, capValue);
|
|
65
|
+
}
|
|
66
|
+
return formula;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=dmax.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dmax.js","sourceRoot":"","sources":["../../../src/cohort-topic/dmax.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAUH,uEAAuE;AACvE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAC1C,2DAA2D;AAC3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAapC;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACtC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC;IACjB,mEAAmE;IACnE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAAE,CAAC,EAAE,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;QAAE,CAAC,EAAE,CAAC;IACxC,OAAO,CAAC,CAAC;AACV,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IAClD,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,UAAU,CAAC,yCAAyC,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACrE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,iBAAiB,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,UAAU,CAAC,iDAAiD,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAEzC,OAAO;QACN,IAAI;YACH,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACvE,gFAAgF;YAChF,0EAA0E;YAC1E,kDAAkD;YAClD,IAAI,UAAU,GAAG,aAAa,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,OAAO,CAAC;QAChB,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — intra-cohort gossip bus.
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/cohort-topic.md` §Cohort gossip. Each member periodically broadcasts a `CohortGossipV1`
|
|
5
|
+
* carrying its willingness vector, load buckets, exact per-topic summaries, and any registration
|
|
6
|
+
* record deltas (new/touched records + evictions). The bus:
|
|
7
|
+
*
|
|
8
|
+
* - **broadcasts** outbound gossip over the injected {@link ICohortGossipTransport} (FRET cohort
|
|
9
|
+
* gossip underneath — db-core never imports FRET);
|
|
10
|
+
* - **routes by coord**: a delivered frame is fanned to every coord engine's bus on a node, so each
|
|
11
|
+
* bus merges only the gossip whose `coord` names its own cohort (epoch alone never isolates two
|
|
12
|
+
* coords — they can share a member set, hence an epoch);
|
|
13
|
+
* - **merges inbound** gossip — record deltas into the {@link RegistrationStore} (last-writer-wins
|
|
14
|
+
* by `lastPing`; evictions remove), and willingness/load/summaries into the per-member
|
|
15
|
+
* {@link CohortView};
|
|
16
|
+
* - **detects epoch drift**: an inbound `cohortEpoch` that differs from the local epoch fires the
|
|
17
|
+
* drift handlers, and record deltas under a foreign epoch are *not* merged (their slot
|
|
18
|
+
* assignments belong to a different membership snapshot);
|
|
19
|
+
* - **optionally authenticates** inbound transport frames via an injected {@link GossipInboundVerifier}
|
|
20
|
+
* (db-p2p binds peer-sig + cohort membership), dropping forged or non-member gossip before any merge.
|
|
21
|
+
*
|
|
22
|
+
* Convergence target (doc): a single gossip round spreads a touched record / willingness flip to all
|
|
23
|
+
* members — one {@link CohortGossipBus.applyInbound} of that gossip makes it visible locally.
|
|
24
|
+
*/
|
|
25
|
+
import type { RegistrationStore } from "../registration/types.js";
|
|
26
|
+
import type { ICohortGossipTransport, RingCoord } from "../ports.js";
|
|
27
|
+
import type { CohortGossipV1 } from "../wire/types.js";
|
|
28
|
+
import { type CohortView } from "./view.js";
|
|
29
|
+
/** Handler for inbound gossip (after merge). */
|
|
30
|
+
export type GossipHandler = (g: CohortGossipV1) => void;
|
|
31
|
+
/** Handler for a detected cohort-epoch drift on an inbound gossip. */
|
|
32
|
+
export type DriftHandler = (inboundEpoch: Uint8Array, localEpoch: Uint8Array, from: string) => void;
|
|
33
|
+
/** Authenticity gate for an inbound gossip arriving over the transport — `false` drops the frame. */
|
|
34
|
+
export type GossipInboundVerifier = (g: CohortGossipV1) => boolean;
|
|
35
|
+
export interface CohortGossipBusDeps {
|
|
36
|
+
/** FRET-backed cohort gossip transport (broadcast / subscribe). */
|
|
37
|
+
transport: ICohortGossipTransport;
|
|
38
|
+
/** Local registration store record deltas merge into. */
|
|
39
|
+
store: RegistrationStore;
|
|
40
|
+
/** This cohort's coord — the broadcast key **and** the inbound routing key (see {@link CohortGossipV1.coord}). */
|
|
41
|
+
coord: RingCoord;
|
|
42
|
+
/** The local cohort epoch, read fresh each merge so rotation is observed. */
|
|
43
|
+
localEpoch: () => Uint8Array;
|
|
44
|
+
/** Wall clock for inbound merges via the transport path; injectable for tests. */
|
|
45
|
+
now?: () => number;
|
|
46
|
+
/** Frame ceiling for encode/decode (defaults to the codec default). */
|
|
47
|
+
maxMessageBytes?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Optional authenticity gate run on every gossip arriving over the transport (db-p2p binds it to the
|
|
50
|
+
* peer-sig primitive: verify `fromMember`'s signature over the gossip image **and** that `fromMember`
|
|
51
|
+
* is a member of this coord's cohort). A `false` verdict drops the frame — no merge, no handler fire —
|
|
52
|
+
* so a forged or non-member gossip can neither spoof willingness/load nor replicate fake records.
|
|
53
|
+
* Absent → the gate is skipped (unit tests / key-less interim mode); the `coord` routing check below
|
|
54
|
+
* still runs regardless. Not applied to direct {@link CohortGossipBus.applyInbound} calls (the trusted
|
|
55
|
+
* test/internal merge entry point).
|
|
56
|
+
*/
|
|
57
|
+
verifyInbound?: GossipInboundVerifier;
|
|
58
|
+
/**
|
|
59
|
+
* Optional hook fired after a merge that deleted one or more records via gossiped evictions, carrying
|
|
60
|
+
* the **distinct** topic ids touched. db-p2p wires it to re-`touch` the per-cohort topic budget down
|
|
61
|
+
* (`topicBudget.touch(t, store.directParticipants(t))`) so a topic whose participants drain on a
|
|
62
|
+
* *sibling* (the drain arrives here as a gossip eviction, never this member's own TTL sweep) still
|
|
63
|
+
* releases its budget slot — mirroring the engine's own `sweepStale` re-touch. Kept as a decoupled
|
|
64
|
+
* callback so this gossip/replication module carries no anti-DoS dependency. Absent → skipped.
|
|
65
|
+
*/
|
|
66
|
+
onRecordsEvicted?: (topicIds: readonly Uint8Array[]) => void;
|
|
67
|
+
}
|
|
68
|
+
/** Intra-cohort gossip bus (merge logic over an injected transport). */
|
|
69
|
+
export interface CohortGossipBus {
|
|
70
|
+
/** Broadcast `g` to the cohort. */
|
|
71
|
+
broadcast(g: CohortGossipV1): void;
|
|
72
|
+
/** Subscribe to inbound gossip (fired after merge); returns an unsubscribe handle. */
|
|
73
|
+
onGossip(handler: GossipHandler): () => void;
|
|
74
|
+
/** Subscribe to epoch-drift detections; returns an unsubscribe handle. */
|
|
75
|
+
onDrift(handler: DriftHandler): () => void;
|
|
76
|
+
/** Merge one inbound gossip: records/willingness/load, with epoch drift detection. */
|
|
77
|
+
applyInbound(g: CohortGossipV1, now: number): void;
|
|
78
|
+
/** The merged per-member view (willingness / load / summaries). */
|
|
79
|
+
view(): CohortView;
|
|
80
|
+
/** Detach the transport subscription. */
|
|
81
|
+
close(): void;
|
|
82
|
+
}
|
|
83
|
+
/** Build a {@link CohortGossipBus} over the injected transport + store. */
|
|
84
|
+
export declare function createCohortGossipBus(deps: CohortGossipBusDeps): CohortGossipBus;
|
|
85
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAW,SAAS,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAoB,KAAK,UAAU,EAA0B,MAAM,WAAW,CAAC;AAEtF,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;AACxD,sEAAsE;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AACpG,qGAAqG;AACrG,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IACnC,mEAAmE;IACnE,SAAS,EAAE,sBAAsB,CAAC;IAClC,yDAAyD;IACzD,KAAK,EAAE,iBAAiB,CAAC;IACzB,kHAAkH;IAClH,KAAK,EAAE,SAAS,CAAC;IACjB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,UAAU,CAAC;IAC7B,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,UAAU,EAAE,KAAK,IAAI,CAAC;CAC7D;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC/B,mCAAmC;IACnC,SAAS,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAAC;IAC7C,0EAA0E;IAC1E,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI,CAAC;IAC3C,sFAAsF;IACtF,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,mEAAmE;IACnE,IAAI,IAAI,UAAU,CAAC;IACnB,yCAAyC;IACzC,KAAK,IAAI,IAAI,CAAC;CACd;AA6HD,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,eAAe,CAEhF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — intra-cohort gossip bus.
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/cohort-topic.md` §Cohort gossip. Each member periodically broadcasts a `CohortGossipV1`
|
|
5
|
+
* carrying its willingness vector, load buckets, exact per-topic summaries, and any registration
|
|
6
|
+
* record deltas (new/touched records + evictions). The bus:
|
|
7
|
+
*
|
|
8
|
+
* - **broadcasts** outbound gossip over the injected {@link ICohortGossipTransport} (FRET cohort
|
|
9
|
+
* gossip underneath — db-core never imports FRET);
|
|
10
|
+
* - **routes by coord**: a delivered frame is fanned to every coord engine's bus on a node, so each
|
|
11
|
+
* bus merges only the gossip whose `coord` names its own cohort (epoch alone never isolates two
|
|
12
|
+
* coords — they can share a member set, hence an epoch);
|
|
13
|
+
* - **merges inbound** gossip — record deltas into the {@link RegistrationStore} (last-writer-wins
|
|
14
|
+
* by `lastPing`; evictions remove), and willingness/load/summaries into the per-member
|
|
15
|
+
* {@link CohortView};
|
|
16
|
+
* - **detects epoch drift**: an inbound `cohortEpoch` that differs from the local epoch fires the
|
|
17
|
+
* drift handlers, and record deltas under a foreign epoch are *not* merged (their slot
|
|
18
|
+
* assignments belong to a different membership snapshot);
|
|
19
|
+
* - **optionally authenticates** inbound transport frames via an injected {@link GossipInboundVerifier}
|
|
20
|
+
* (db-p2p binds peer-sig + cohort membership), dropping forged or non-member gossip before any merge.
|
|
21
|
+
*
|
|
22
|
+
* Convergence target (doc): a single gossip round spreads a touched record / willingness flip to all
|
|
23
|
+
* members — one {@link CohortGossipBus.applyInbound} of that gossip makes it visible locally.
|
|
24
|
+
*/
|
|
25
|
+
import { bytesEqual, bytesKey } from "../registration/bytes.js";
|
|
26
|
+
import { b64urlToBytes, decodeCohortGossipV1, encodeCohortMessage } from "../wire/codec.js";
|
|
27
|
+
import { fromGossipRecord } from "./records.js";
|
|
28
|
+
import { createCohortView } from "./view.js";
|
|
29
|
+
class TransportCohortGossipBus {
|
|
30
|
+
deps;
|
|
31
|
+
gossipHandlers = new Set();
|
|
32
|
+
driftHandlers = new Set();
|
|
33
|
+
memberView = createCohortView();
|
|
34
|
+
now;
|
|
35
|
+
detach;
|
|
36
|
+
constructor(deps) {
|
|
37
|
+
this.deps = deps;
|
|
38
|
+
this.now = deps.now ?? (() => Date.now());
|
|
39
|
+
this.detach = deps.transport.onMessage((_from, msg) => this.onInbound(msg));
|
|
40
|
+
}
|
|
41
|
+
broadcast(g) {
|
|
42
|
+
this.deps.transport.broadcast(this.deps.coord, encodeCohortMessage(g, this.deps.maxMessageBytes));
|
|
43
|
+
}
|
|
44
|
+
onGossip(handler) {
|
|
45
|
+
this.gossipHandlers.add(handler);
|
|
46
|
+
return () => this.gossipHandlers.delete(handler);
|
|
47
|
+
}
|
|
48
|
+
onDrift(handler) {
|
|
49
|
+
this.driftHandlers.add(handler);
|
|
50
|
+
return () => this.driftHandlers.delete(handler);
|
|
51
|
+
}
|
|
52
|
+
applyInbound(g, now) {
|
|
53
|
+
// Coord routing: a frame is fanned to every coord engine's bus, but it belongs only to the bus
|
|
54
|
+
// whose coord it names. Drop any gossip for another cohort so it cannot merge into this store/view
|
|
55
|
+
// (two coords on one node can even share a member set — and thus an epoch — so epoch alone never
|
|
56
|
+
// isolates them).
|
|
57
|
+
if (!this.isOurCoord(g)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const localEpoch = this.deps.localEpoch();
|
|
61
|
+
const inboundEpoch = b64urlToBytes(g.cohortEpoch);
|
|
62
|
+
const epochMatches = bytesEqual(inboundEpoch, localEpoch);
|
|
63
|
+
if (!epochMatches) {
|
|
64
|
+
for (const handler of this.driftHandlers) {
|
|
65
|
+
handler(inboundEpoch, localEpoch, g.fromMember);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.mergeView(g, inboundEpoch);
|
|
69
|
+
if (epochMatches) {
|
|
70
|
+
this.mergeRecords(g, now);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
view() {
|
|
74
|
+
return this.memberView;
|
|
75
|
+
}
|
|
76
|
+
close() {
|
|
77
|
+
this.detach();
|
|
78
|
+
}
|
|
79
|
+
onInbound(msg) {
|
|
80
|
+
const g = decodeCohortGossipV1(msg, this.deps.maxMessageBytes);
|
|
81
|
+
// Cheap routing check first so a non-matching coord skips the (expensive) signature verify.
|
|
82
|
+
if (!this.isOurCoord(g)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Authenticity gate (db-p2p binds peer-sig + cohort membership); a forged/non-member gossip is
|
|
86
|
+
// dropped before any merge or handler fire. Absent in key-less/unit composition.
|
|
87
|
+
if (this.deps.verifyInbound !== undefined && !this.deps.verifyInbound(g)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.applyInbound(g, this.now());
|
|
91
|
+
for (const handler of this.gossipHandlers) {
|
|
92
|
+
handler(g);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** True iff `g` names this bus's cohort coord (the inbound routing key). */
|
|
96
|
+
isOurCoord(g) {
|
|
97
|
+
return bytesEqual(b64urlToBytes(g.coord), this.deps.coord);
|
|
98
|
+
}
|
|
99
|
+
mergeView(g, inboundEpoch) {
|
|
100
|
+
this.memberView.merge(g.fromMember, {
|
|
101
|
+
cohortEpoch: inboundEpoch,
|
|
102
|
+
willingness: parseInt(g.willingnessBits, 16),
|
|
103
|
+
loadBuckets: g.loadBuckets,
|
|
104
|
+
windowSeconds: g.windowSeconds,
|
|
105
|
+
topicSummaries: g.topicSummaries,
|
|
106
|
+
timestamp: g.timestamp,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
mergeRecords(g, now) {
|
|
110
|
+
for (const gr of g.records ?? []) {
|
|
111
|
+
const incoming = fromGossipRecord(gr);
|
|
112
|
+
// A record already past its TTL is dead; merging it would resurrect a registration the
|
|
113
|
+
// owner has (or soon will have) evicted. Drop it, matching `store.evictStale`'s predicate
|
|
114
|
+
// so replication can never reintroduce what local eviction removes.
|
|
115
|
+
if (now - incoming.lastPing > incoming.ttl) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const held = this.deps.store.getByParticipant(incoming.topicId, incoming.participantId);
|
|
119
|
+
// Last-writer-wins by lastPing: a touch (newer lastPing) replaces an older replica.
|
|
120
|
+
if (held === undefined || incoming.lastPing >= held.lastPing) {
|
|
121
|
+
this.deps.store.put(incoming);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Collect the distinct topics a gossiped eviction drained so the budget can be re-touched *after*
|
|
125
|
+
// the deletes (the count must be post-drain). This is the sibling-drain half of the topic-budget
|
|
126
|
+
// leak fix: a topic whose participants are sharded onto a sibling primary drains into this store as
|
|
127
|
+
// a gossip eviction, never via this member's own TTL sweep, so the `onRecordsEvicted` callback must
|
|
128
|
+
// re-`touch` the budget down here too — otherwise the slot leaks for any multi-member topic.
|
|
129
|
+
let evictedTopics;
|
|
130
|
+
for (const ref of g.evicted ?? []) {
|
|
131
|
+
const topicId = b64urlToBytes(ref.topicId);
|
|
132
|
+
this.deps.store.delete(topicId, b64urlToBytes(ref.participantId));
|
|
133
|
+
if (this.deps.onRecordsEvicted !== undefined) {
|
|
134
|
+
(evictedTopics ??= new Map()).set(bytesKey(topicId), topicId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (evictedTopics !== undefined && evictedTopics.size > 0) {
|
|
138
|
+
this.deps.onRecordsEvicted([...evictedTopics.values()]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Build a {@link CohortGossipBus} over the injected transport + store. */
|
|
143
|
+
export function createCohortGossipBus(deps) {
|
|
144
|
+
return new TransportCohortGossipBus(deps);
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.js","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — translation between the local {@link RegistrationRecord} (raw bytes)
|
|
3
|
+
* and the wire {@link GossipRecordV1} (base64url). The gossip bus carries record deltas so backups
|
|
4
|
+
* hold the record before the primary fails (per `docs/cohort-topic.md` §Registration record).
|
|
5
|
+
*/
|
|
6
|
+
import type { GossipRecordV1 } from "../wire/types.js";
|
|
7
|
+
import type { RegistrationRecord } from "../registration/types.js";
|
|
8
|
+
/** Encode a local registration record for cross-member gossip. */
|
|
9
|
+
export declare function toGossipRecord(rec: RegistrationRecord): GossipRecordV1;
|
|
10
|
+
/** Decode a gossiped record back into the local store shape. */
|
|
11
|
+
export declare function fromGossipRecord(g: GossipRecordV1): RegistrationRecord;
|
|
12
|
+
//# sourceMappingURL=records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/records.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,GAAG,EAAE,kBAAkB,GAAG,cAAc,CAetE;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,GAAG,kBAAkB,CAetE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — translation between the local {@link RegistrationRecord} (raw bytes)
|
|
3
|
+
* and the wire {@link GossipRecordV1} (base64url). The gossip bus carries record deltas so backups
|
|
4
|
+
* hold the record before the primary fails (per `docs/cohort-topic.md` §Registration record).
|
|
5
|
+
*/
|
|
6
|
+
import { b64urlToBytes, bytesToB64url } from "../wire/codec.js";
|
|
7
|
+
/** Encode a local registration record for cross-member gossip. */
|
|
8
|
+
export function toGossipRecord(rec) {
|
|
9
|
+
const out = {
|
|
10
|
+
topicId: bytesToB64url(rec.topicId),
|
|
11
|
+
participantId: bytesToB64url(rec.participantId),
|
|
12
|
+
tier: rec.tier,
|
|
13
|
+
primary: bytesToB64url(rec.primary),
|
|
14
|
+
backups: rec.backups.map(bytesToB64url),
|
|
15
|
+
attachedAt: rec.attachedAt,
|
|
16
|
+
lastPing: rec.lastPing,
|
|
17
|
+
ttl: rec.ttl,
|
|
18
|
+
};
|
|
19
|
+
if (rec.appState !== undefined) {
|
|
20
|
+
out.appState = bytesToB64url(rec.appState);
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
/** Decode a gossiped record back into the local store shape. */
|
|
25
|
+
export function fromGossipRecord(g) {
|
|
26
|
+
const out = {
|
|
27
|
+
topicId: b64urlToBytes(g.topicId),
|
|
28
|
+
participantId: b64urlToBytes(g.participantId),
|
|
29
|
+
tier: g.tier,
|
|
30
|
+
primary: b64urlToBytes(g.primary),
|
|
31
|
+
backups: g.backups.map(b64urlToBytes),
|
|
32
|
+
attachedAt: g.attachedAt,
|
|
33
|
+
lastPing: g.lastPing,
|
|
34
|
+
ttl: g.ttl,
|
|
35
|
+
};
|
|
36
|
+
if (g.appState !== undefined) {
|
|
37
|
+
out.appState = b64urlToBytes(g.appState);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/records.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIhE,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,GAAuB;IACrD,MAAM,GAAG,GAAmB;QAC3B,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;QACnC,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC;QAC/C,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;QACnC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QACvC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,GAAG,EAAE,GAAG,CAAC,GAAG;KACZ,CAAC;IACF,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,GAAG,CAAC,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,CAAiB;IACjD,MAAM,GAAG,GAAuB;QAC/B,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QACjC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;QAC7C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QACjC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QACrC,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,GAAG,EAAE,CAAC,CAAC,GAAG;KACV,CAAC;IACF,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC9B,GAAG,CAAC,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — the merged per-member gossip view.
|
|
3
|
+
*
|
|
4
|
+
* Each cohort member periodically gossips its willingness vector, load barometer buckets, and exact
|
|
5
|
+
* per-topic summaries (`CohortGossipV1`). The bus folds the latest such contribution per member into
|
|
6
|
+
* this view (last-writer-wins by gossip `timestamp`), which the willingness, barometer, and traffic
|
|
7
|
+
* tickets read. Records replicate into the registration store separately (see {@link createCohortGossipBus}).
|
|
8
|
+
*/
|
|
9
|
+
import type { CohortTopicSummary } from "../wire/types.js";
|
|
10
|
+
/** Latest gossip contribution from a single cohort member. */
|
|
11
|
+
export interface MemberContribution {
|
|
12
|
+
/** Cohort epoch this member gossiped under (drift signal). */
|
|
13
|
+
readonly cohortEpoch: Uint8Array;
|
|
14
|
+
/** Willingness vector, 4 bits T0..T3 (0..15) decoded from the hex nibble. */
|
|
15
|
+
readonly willingness: number;
|
|
16
|
+
/** Load barometer buckets, 4 entries 0..7 per tier. */
|
|
17
|
+
readonly loadBuckets: readonly number[];
|
|
18
|
+
/** Observation window for the rate fields in `topicSummaries`. */
|
|
19
|
+
readonly windowSeconds: number;
|
|
20
|
+
/** Exact per-topic summaries this member last reported. */
|
|
21
|
+
readonly topicSummaries: readonly CohortTopicSummary[];
|
|
22
|
+
/** Gossip timestamp (unix ms) — the last-writer-wins key. */
|
|
23
|
+
readonly timestamp: number;
|
|
24
|
+
}
|
|
25
|
+
/** Read view over the merged per-member contributions, keyed by `fromMember`. */
|
|
26
|
+
export interface CohortView {
|
|
27
|
+
/** Latest contribution from `member` (the `fromMember` string), or `undefined`. */
|
|
28
|
+
get(member: string): MemberContribution | undefined;
|
|
29
|
+
/** All current contributions, keyed by `fromMember`. */
|
|
30
|
+
all(): ReadonlyMap<string, MemberContribution>;
|
|
31
|
+
}
|
|
32
|
+
/** Mutable view the bus writes into; exposes the read {@link CohortView} surface. */
|
|
33
|
+
export interface MutableCohortView extends CohortView {
|
|
34
|
+
/** Merge `c` for `member` iff it is at least as recent as the held contribution. Returns true if applied. */
|
|
35
|
+
merge(member: string, c: MemberContribution): boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Construct an empty {@link MutableCohortView}. */
|
|
38
|
+
export declare function createCohortView(): MutableCohortView;
|
|
39
|
+
//# sourceMappingURL=view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvD,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,iFAAiF;AACjF,MAAM,WAAW,UAAU;IAC1B,mFAAmF;IACnF,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IACpD,wDAAwD;IACxD,GAAG,IAAI,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC/C;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACpD,6GAA6G;IAC7G,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC;CACtD;AAuBD,oDAAoD;AACpD,wBAAgB,gBAAgB,IAAI,iBAAiB,CAEpD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — the merged per-member gossip view.
|
|
3
|
+
*
|
|
4
|
+
* Each cohort member periodically gossips its willingness vector, load barometer buckets, and exact
|
|
5
|
+
* per-topic summaries (`CohortGossipV1`). The bus folds the latest such contribution per member into
|
|
6
|
+
* this view (last-writer-wins by gossip `timestamp`), which the willingness, barometer, and traffic
|
|
7
|
+
* tickets read. Records replicate into the registration store separately (see {@link createCohortGossipBus}).
|
|
8
|
+
*/
|
|
9
|
+
class MapCohortView {
|
|
10
|
+
byMember = new Map();
|
|
11
|
+
get(member) {
|
|
12
|
+
return this.byMember.get(member);
|
|
13
|
+
}
|
|
14
|
+
all() {
|
|
15
|
+
return this.byMember;
|
|
16
|
+
}
|
|
17
|
+
merge(member, c) {
|
|
18
|
+
const held = this.byMember.get(member);
|
|
19
|
+
if (held !== undefined && c.timestamp < held.timestamp) {
|
|
20
|
+
return false; // stale; keep the newer contribution
|
|
21
|
+
}
|
|
22
|
+
this.byMember.set(member, c);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Construct an empty {@link MutableCohortView}. */
|
|
27
|
+
export function createCohortView() {
|
|
28
|
+
return new MapCohortView();
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../../src/cohort-topic/gossip/view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkCH,MAAM,aAAa;IACD,QAAQ,GAAG,IAAI,GAAG,EAA8B,CAAC;IAElE,GAAG,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,GAAG;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,CAAqB;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YACxD,OAAO,KAAK,CAAC,CAAC,qCAAqC;QACpD,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAED,oDAAoD;AACpD,MAAM,UAAU,gBAAgB;IAC/B,OAAO,IAAI,aAAa,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export * from "./ports.js";
|
|
2
|
+
export * from "./ring-hash.js";
|
|
3
|
+
export * from "./addressing.js";
|
|
4
|
+
export * from "./tiers.js";
|
|
5
|
+
export * from "./dmax.js";
|
|
6
|
+
export * from "./wire/index.js";
|
|
7
|
+
export * from "./registration/index.js";
|
|
8
|
+
export * from "./gossip/index.js";
|
|
9
|
+
export * from "./sig/index.js";
|
|
10
|
+
export * from "./membership/index.js";
|
|
11
|
+
export * from "./load/index.js";
|
|
12
|
+
export * from "./willingness.js";
|
|
13
|
+
export * from "./traffic.js";
|
|
14
|
+
export * from "./walk.js";
|
|
15
|
+
export * from "./promotion.js";
|
|
16
|
+
export * from "./coldstart.js";
|
|
17
|
+
export * from "./antiflood/index.js";
|
|
18
|
+
export * from "./antidos/index.js";
|
|
19
|
+
export * from "./member-engine.js";
|
|
20
|
+
export * from "./service.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cohort-topic/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export * from "./ports.js";
|
|
2
|
+
export * from "./ring-hash.js";
|
|
3
|
+
export * from "./addressing.js";
|
|
4
|
+
export * from "./tiers.js";
|
|
5
|
+
export * from "./dmax.js";
|
|
6
|
+
export * from "./wire/index.js";
|
|
7
|
+
export * from "./registration/index.js";
|
|
8
|
+
export * from "./gossip/index.js";
|
|
9
|
+
export * from "./sig/index.js";
|
|
10
|
+
export * from "./membership/index.js";
|
|
11
|
+
export * from "./load/index.js";
|
|
12
|
+
export * from "./willingness.js";
|
|
13
|
+
export * from "./traffic.js";
|
|
14
|
+
export * from "./walk.js";
|
|
15
|
+
export * from "./promotion.js";
|
|
16
|
+
export * from "./coldstart.js";
|
|
17
|
+
export * from "./antiflood/index.js";
|
|
18
|
+
export * from "./antidos/index.js";
|
|
19
|
+
export * from "./member-engine.js";
|
|
20
|
+
export * from "./service.js";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cohort-topic/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — capacity barometer.
|
|
3
|
+
*
|
|
4
|
+
* Transcribed from `docs/cohort-topic.md` §Capacity barometer (and folded back from the
|
|
5
|
+
* simulator-validated `packages/substrate-simulator/src/willingness.ts`). A cohort member tracks a
|
|
6
|
+
* coarse **3-bit (0..7) log-bucketed utilization** per tier (T0..T3). The barometer feeds two
|
|
7
|
+
* decisions:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Willingness-bit refresh.** When a tier's bucket reaches `overloadBucket` the member sheds
|
|
10
|
+
* that tier — its load-driven willingness bit flips off until utilization recedes. Siblings see
|
|
11
|
+
* the flip within one gossip round (the bit rides `CohortGossipV1.willingnessBits`).
|
|
12
|
+
* 2. **Early-promote signal.** `bucket ≥ overloadBucket` (default 6, the `bucket_overload` of
|
|
13
|
+
* §Configuration) is the "cohort is hot at this tier" signal the promotion ticket consumes to
|
|
14
|
+
* fire promotion earlier than the strict `cap_promote`. This module only *exposes* the signal;
|
|
15
|
+
* it does not itself promote.
|
|
16
|
+
*
|
|
17
|
+
* The barometer is **not** aggregated across the tree — children promote independently, so a member
|
|
18
|
+
* only ever observes its own per-tier utilization here. The 3-bit buckets (× 4 tiers) plus the
|
|
19
|
+
* willingness bit fit in 16 bits of cohort gossip; the cost is negligible.
|
|
20
|
+
*/
|
|
21
|
+
import { type Tier } from "../tiers.js";
|
|
22
|
+
/** A single tier's coarse load reading, as carried in cohort gossip. */
|
|
23
|
+
export interface LoadBarometer {
|
|
24
|
+
readonly tier: Tier;
|
|
25
|
+
/** 0..7, log-bucketed utilization (see {@link utilizationBucket}). */
|
|
26
|
+
readonly bucket: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load-barometer bucket at/above which a member sheds a tier (willingness flips off) and the cohort
|
|
30
|
+
* is considered hot enough to promote early. `bucket_overload` in `docs/cohort-topic.md`
|
|
31
|
+
* §Configuration; simulator-confirmed at 6.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_OVERLOAD_BUCKET = 6;
|
|
34
|
+
/**
|
|
35
|
+
* Map a utilization ratio (`load / capacity`, where `1.0` = at capacity) to a 0..7 log bucket.
|
|
36
|
+
*
|
|
37
|
+
* Each bucket spans a doubling of utilization, anchored so the top of the range is at-capacity:
|
|
38
|
+
*
|
|
39
|
+
* | bucket | utilization range |
|
|
40
|
+
* |--------|-------------------|
|
|
41
|
+
* | 7 | `u ≥ 1.0` |
|
|
42
|
+
* | 6 | `[0.5, 1.0)` |
|
|
43
|
+
* | 5 | `[0.25, 0.5)` |
|
|
44
|
+
* | … | … |
|
|
45
|
+
* | 1 | `[1/64, 1/32)` |
|
|
46
|
+
* | 0 | `u < 1/64` (incl. 0, negative clamped) |
|
|
47
|
+
*
|
|
48
|
+
* Monotonic non-decreasing in `u`; `bucket = clamp(7 + ⌊log₂ u⌋, 0, 7)`. With the default
|
|
49
|
+
* `overloadBucket = 6`, a member sheds a tier once utilization reaches half capacity.
|
|
50
|
+
*/
|
|
51
|
+
export declare function utilizationBucket(utilization: number): number;
|
|
52
|
+
/** Mutable per-tier capacity barometer for one cohort member. */
|
|
53
|
+
export interface LoadBarometerState {
|
|
54
|
+
/** Record this member's current utilization (`load / capacity`) for `tier`. */
|
|
55
|
+
observe(tier: Tier, utilization: number): void;
|
|
56
|
+
/** Current 0..7 bucket for `tier`. */
|
|
57
|
+
bucket(tier: Tier): number;
|
|
58
|
+
/** `{ tier, bucket }` reading for `tier`. */
|
|
59
|
+
reading(tier: Tier): LoadBarometer;
|
|
60
|
+
/** All four buckets (T0..T3), the array gossiped in `CohortGossipV1.loadBuckets`. */
|
|
61
|
+
loadBuckets(): number[];
|
|
62
|
+
/**
|
|
63
|
+
* Early-promote / shed signal: `bucket(tier) ≥ overloadBucket`. True means the member is hot at
|
|
64
|
+
* `tier` — its load-driven willingness bit is off and the promotion layer may promote early.
|
|
65
|
+
*/
|
|
66
|
+
isOverloaded(tier: Tier): boolean;
|
|
67
|
+
/** Load-driven willingness bit for `tier`: `!isOverloaded(tier)`. (Profile/budget gates live in the willingness check.) */
|
|
68
|
+
loadWilling(tier: Tier): boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface LoadBarometerConfig {
|
|
71
|
+
/** Bucket at/above which a tier is shed and flagged hot. Default {@link DEFAULT_OVERLOAD_BUCKET}. */
|
|
72
|
+
overloadBucket?: number;
|
|
73
|
+
}
|
|
74
|
+
/** Construct an idle (all-bucket-0) {@link LoadBarometerState}. */
|
|
75
|
+
export declare function createLoadBarometer(config?: LoadBarometerConfig): LoadBarometerState;
|
|
76
|
+
/** Iterate the four tiers — convenience for callers folding the barometer into a gossip frame. */
|
|
77
|
+
export declare function eachTier(): readonly Tier[];
|
|
78
|
+
//# sourceMappingURL=barometer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"barometer.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/load/barometer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IAClC,+EAA+E;IAC/E,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,sCAAsC;IACtC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAC3B,6CAA6C;IAC7C,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAC;IACnC,qFAAqF;IACrF,WAAW,IAAI,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;IAClC,2HAA2H;IAC3H,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IACnC,qGAAqG;IACrG,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAuCD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,kBAAkB,CAEpF;AAED,kGAAkG;AAClG,wBAAgB,QAAQ,IAAI,SAAS,IAAI,EAAE,CAE1C"}
|