Skip to content

feat: RoutingIndex -- metric-bucketed Tier-2 structural match - #435

Merged
zzylol merged 1 commit into
mainfrom
feat/routing-index
Jul 30, 2026
Merged

zzylol merged 1 commit into
mainfrom
feat/routing-index

Conversation

@zzylol

@zzylol zzylol commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the Tier-2 half of control_plane/docs/design-backend-plan-wire-format.md (#433) §4's RoutingIndex -- items #2/#3 from that design (structural index + wiring it into the live serving path), scoped to run in parallel with #434 (the BackendPlan wire types, #1), which it has no code dependency on.

Sourced from PolicyRegistry (StreamingConfig's content-addressed view) rather than the not-yet-built BackendPlan message -- PolicyRegistry is already genuinely "what control_plane planned" (pushed via StreamingConfig), not something reconstructed from ingest-time side effects, so this is a faithful (if not yet wire-format-complete) implementation of the design's intent, not a stand-in for it.

  • Tier 1 (exact PolicyFingerprint lookup) is already PolicyRegistry::get -- O(1), untouched.
  • Tier 2 (new): RoutingIndex buckets policies by metric at construction time. find_matching_policies (control_plane::asap_tier_analysis) used to scan every policy in the registry for every candidate, checking each one's metric name first -- paying for every other metric's policies on every lookup. It now only touches the bucket for the candidate's own metric.
  • engine.rs's two candidate-resolution loops (range-query, instant-query) build a RoutingIndex once per query snapshot instead of a raw PolicyRegistry, matching the design doc's caller pattern.

Not done here (documented in the module doc as explicit follow-up): building RoutingIndex once per StreamingConfig hot-reload swap instead of once per query -- StreamingConfig::policy_registry's own doc comment already flags this as a further optimization "if it shows up in profiles." That requires threading a cached derived value through HotReloadStreamingConfig's swap path, a separate, larger change than this PR's scope.

Behavior is unchanged: find_matching_policies's match predicate is untouched, and all ~18 of its existing tests pass with no assertion changes (only their PolicyRegistry construction is now wrapped in RoutingIndex::build).

Test plan

  • cargo build --release --workspace -- clean
  • cargo test --release -p asap_types -- 42/42 (5 new RoutingIndex tests)
  • cargo test --release --lib -p control_plane -- 712/712 (1 pre-existing, unrelated skip as before)
  • cargo test --release --lib -p data_plane -- 906/906
  • cargo test --release -p data_plane --test e2e_controller_plans_and_backend_serves -- 10/14 (2 pre-existing unrelated failures + 2 known #[ignore]d, unchanged from before this PR)

🤖 Generated with Claude Code

Implements the Tier-2 half of control_plane/docs/design-backend-plan-wire-format.md
(#433) §4's RoutingIndex, sourced from PolicyRegistry (StreamingConfig's
content-addressed view -- genuinely "what control_plane planned" today,
not reconstructed from ingest side effects) rather than the not-yet-built
BackendPlan wire format. Tier 1 (exact PolicyFingerprint lookup) is
already PolicyRegistry::get, unchanged.

find_matching_policies (control_plane::asap_tier_analysis) used to scan
every policy in the registry for every candidate, checking each one's
metric name first -- paying for every OTHER metric's policies on every
lookup. RoutingIndex buckets by metric once at construction, so a lookup
only touches policies for the relevant metric. Same match predicate,
same observable behavior (verified: all ~18 existing find_matching_policies
tests pass unchanged, only their PolicyRegistry construction is now
wrapped in RoutingIndex::build).

engine.rs's two candidate-resolution loops (range-query, instant-query)
now build a RoutingIndex once per query snapshot instead of a raw
PolicyRegistry, matching the design doc's stated caller pattern.

Building RoutingIndex once per StreamingConfig hot-reload swap instead of
once per query (StreamingConfig::policy_registry's own doc comment
already flags this as a further, larger optimization "if it shows up in
profiles") is not done here -- would require threading a cached derived
value through HotReloadStreamingConfig's swap path, a separate, larger
change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@zzylol
zzylol merged commit f34d997 into main Jul 30, 2026
@zzylol
zzylol deleted the feat/routing-index branch July 30, 2026 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant