feat(fabric): add the RoutingFabric seam for binding-cache invalidation (RIG-3108) - #955
Open
rigel-mintaka wants to merge 1 commit into
Conversation
This was referenced Sep 7, 2026
|
Compass engineering docs preview: https://compass-managed-rig-3108-rou.compass-eng-docs.pages.dev Deployed from Changed pages: |
rigel-mintaka
force-pushed
the
compass-managed/rig-3108-routing-fabric
branch
from
September 7, 2026 17:12
c1a9ae4 to
f522d21
Compare
rigel-mintaka
marked this pull request as ready for review
September 7, 2026 17:30
rigel-mintaka
force-pushed
the
compass-managed/rig-3108-routing-fabric
branch
from
September 7, 2026 19:39
f522d21 to
21efbea
Compare
This was referenced Sep 7, 2026
…on (RIG-3108)
Session bindings are moving from one hub's RAM to Postgres, with the hub's maps
demoted to per-instance caches. Those caches need an invalidation signal, and
neither existing seam can carry one.
`EventFabric` cannot: both its read paths are durable queue-group consumers, so
each event is claimed by exactly one instance. An invalidation delivered that
way reaches one hub and leaves every other serving a stale binding, with no
error and no missing ack to reveal it. Its interface also promises durable
at-least-once fan-out, while an invalidation is deliberately droppable — a lost
one degrades to a cache-miss re-read against Postgres, which is the arbiter. Two
opposite delivery contracts behind one interface would leave a caller unable to
tell from the type whether a message can be dropped, so this is a third seam
rather than a method on an existing one.
The subject grammar is `compass.routing.binding.<tenant>`, with the literal
`binding` pinned to token 3. That ordering is a correctness requirement: the
comms stream captures `compass.*.comms.*`, which matches any four-token subject
whose third token is `comms`, so the other ordering
(`compass.routing.<tenant>.binding`) would see a tenant literally named `comms`
publish into the JetStream comms stream. Pinning the literal makes the collision
unrepresentable for every tenant value instead of merely unlikely.
Publish cross-checks the payload's tenant against the subject's, because the read
side subscribes tenant-wildcard and its callback never sees the subject — the
payload's tenant is the receiver's only scope.
The record decided the mechanism ("core NATS carrying invalidation/update
events... core NATS at-most-once suffices because a dropped invalidation degrades
to a cache-miss re-read") but not the seam; §T4's "over the EventFabric" phrasing
is a paraphrase of that section, not a separate ruling.
The store migration and the hub's cache demotion follow in later commits; this
adds only the seam.
Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka
force-pushed
the
compass-managed/rig-3108-routing-fabric
branch
from
September 7, 2026 22:27
21efbea to
90c232e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is part of a stack containing 6 PRs:
mainSession bindings are moving from one hub's RAM to Postgres, with the hub's maps
demoted to per-instance caches. Those caches need an invalidation signal, and
neither existing seam can carry one.
EventFabriccannot: both its read paths are durable queue-group consumers, soeach event is claimed by exactly one instance. An invalidation delivered that
way reaches one hub and leaves every other serving a stale binding, with no
error and no missing ack to reveal it. Its interface also promises durable
at-least-once fan-out, while an invalidation is deliberately droppable — a lost
one degrades to a cache-miss re-read against Postgres, which is the arbiter. Two
opposite delivery contracts behind one interface would leave a caller unable to
tell from the type whether a message can be dropped, so this is a third seam
rather than a method on an existing one.
The subject grammar is
compass.routing.binding.<tenant>, with the literalbindingpinned to token 3. That ordering is a correctness requirement: thecomms stream captures
compass.*.comms.*, which matches any four-token subjectwhose third token is
comms, so the other ordering(
compass.routing.<tenant>.binding) would see a tenant literally namedcommspublish into the JetStream comms stream. Pinning the literal makes the collision
unrepresentable for every tenant value instead of merely unlikely.
Publish cross-checks the payload's tenant against the subject's, because the read
side subscribes tenant-wildcard and its callback never sees the subject — the
payload's tenant is the receiver's only scope.
The record decided the mechanism ("core NATS carrying invalidation/update
events... core NATS at-most-once suffices because a dropped invalidation degrades
to a cache-miss re-read") but not the seam; §T4's "over the EventFabric" phrasing
is a paraphrase of that section, not a separate ruling.
The store migration and the hub's cache demotion follow in later commits; this
adds only the seam.
Co-authored-by: Matt Wilkinson matt@rigel.build