Skip to content

backend-plan: wire push + data_plane consume endpoint (Phase 2+3) - #440

Merged
zzylol merged 3 commits into
mainfrom
feat/backend-plan-wire-push
Jul 30, 2026
Merged

zzylol merged 3 commits into
mainfrom
feat/backend-plan-wire-push

Conversation

@zzylol

@zzylol zzylol commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Recreated after #438 was auto-closed when its stacked base branch (#437) was deleted on merge — same content, now based directly on main (which already has #437's BackendPlan::from_stage_config).

Summary

Phases 2+3 of the BackendPlan cutover (control_plane/docs/design-backend-plan-wire-format.md): the control plane now actually pushes an encoded BackendPlan to a new POST /api/v1/backend-plan endpoint, alongside (not instead of) the legacy streaming-config/storage_routing documents.

  • BackendClient::post_backend_plan_typed — new typed POST method, same transient/permanent classification as the existing typed methods, hitting a sibling endpoint derived from the configured streaming-config URL.
  • emit::backend_push::push_cumulative_entries (the one real call site that builds BackendStageConfig for a live push) now also builds a BackendPlan via backend_plan::from_stage_config from the same cumulative_be snapshot, and fires a best-effort, single-attempt push. Its outcome never affects PushOutcome — a dropped push just falls back to SketchStore reconstruction at serving time until the next replan cycle.
  • data_plane: HotReloadBackendPlan (same ArcSwap shape as HotReloadStreamingConfig) + GET/POST /api/v1/backend-plan handlers, wired into main.rs with an empty bootstrap handle (mirrors the existing storage-routing bootstrap pattern).

Test plan

  • cargo test --release -p control_plane --lib backend_client:: backend_push:: — including a dedicated test proving the plan push fires alongside the coupled push, and a test proving a plan-push failure does NOT flip PushOutcome.
  • cargo test --release -p data_plane --lib — 915/915 pass (2 pre-existing ignores), including new HotReloadBackendPlan round-trip + 503 + bad-bytes tests.
  • cargo build --release --workspace — clean.

zzylol and others added 3 commits July 29, 2026 22:09
Phase 1 of the BackendPlan end-to-end wiring (control_plane emit ->
data_plane serving). Pure, additive: a new from_stage_config() function
builds a typed BackendPlan from the same BackendStageConfig input
emit_backend_streaming_config_json already turns into the legacy JSON
wire format. No wire/push change yet -- that's Phase 2/3.

Fingerprint parity with the legacy StreamingConfig path is the critical
invariant: Materialization.fingerprint is derived by round-tripping
through build_backend_aggregation_json + AggregationConfig::from_yaml_data
(the exact JSON shape and parser data_plane's real
POST /api/v1/streaming-config handler uses), not a re-derived field
mapping, so the two identity spaces can never drift apart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Combines the wire push and endpoint pieces of the BackendPlan cutover
(control_plane/docs/design-backend-plan-wire-format.md):

control_plane side:
- BackendClient::post_backend_plan_typed posts encoded BackendPlan bytes
  to POST /api/v1/backend-plan (sibling endpoint derived from the
  configured streaming-config URL, same convention as storage_routing).
- emit::backend_push::push_cumulative_entries now also builds a
  BackendPlan from the same cumulative_be snapshot used for the legacy
  streaming-config/storage-routing documents (via
  backend_plan::from_stage_config from the prior PR) and fires a
  best-effort, single-attempt push alongside the existing coupled push.
  This is the real plan-time call site (Phase 3) -- the only place that
  builds BackendStageConfig for a live push.
- The plan push's outcome never affects PushOutcome, which existing
  callers key real (legacy-path) behavior on -- nothing consumes
  BackendPlan yet, so its failure must stay invisible to them. No
  in-function retry loop (unlike the coupled documents): the next
  replan cycle is the retry backstop, matching push_or_log's existing
  fire-and-forget contract for the legacy YAML path.

data_plane side:
- HotReloadBackendPlan (storage_engines/types/hot_reload_config.rs):
  same ArcSwap-snapshot/swap shape as HotReloadStreamingConfig, applied
  to control_plane::backend_plan::BackendPlan.
- GET/POST /api/v1/backend-plan handlers, registered alongside (not
  replacing) /api/v1/streaming-config. POST decodes protobuf bytes and
  atomically swaps; does not touch the sid catalog or SketchStore
  reconciliation.
- main.rs installs an empty handle at startup so the endpoints don't
  503 before the control plane's first push lands, mirroring the
  existing backend-storage-routing bootstrap pattern.

Nothing consumes the installed BackendPlan yet -- that's Phase 4
(serving-time cutover), which also retires ObservedFamilyCostModel's
SketchStore-reconstruction to a fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace forward references to "Phase 4"/"nothing consumes this yet"
(stale now that the serving-time consumer exists) with descriptions of
the actual current architecture: BackendPlan is read by ASAPQueryEngine's
serving-time lookup, sits alongside (not in place of) the legacy
streaming-config path, and a dropped push just falls back to SketchStore
reconstruction until the next replan cycle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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