fix(vitess): gate stored-plan applies on recorded VSchema deletions and mutations - #1084
Conversation
There was a problem hiding this comment.
Pull request overview
This PR closes a safety gap for Vitess-family engines by persisting gate-relevant VSchema change metadata (the vschema_changed flag plus recorded vschema_deletions) into stored plans, and extending the stored-plan apply gate to block applies on unsafe VSchema removals unless the operator explicitly opts in.
Changes:
- Persist VSchema gate metadata into
NamespacePlanData.Metadatavia a shared helper (storage.VSchemaPlanMetadata) from both local-plan and gRPC plan persistence paths. - Add stored-plan inspection for unsafe VSchema changes (
Plan.UnsafeVSchemaChanges) and extend the stored-plan gate to reject on unsafe DDL or unsafe VSchema changes whenallow_unsafeis not set. - Add targeted unit/integration-style tests covering persistence and gate behavior, including fail-closed behavior on missing/corrupt VSchema metadata.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/tern/local_client.go | Refactors namespace plan-data construction and persists VSchema plan metadata during local plan storage. |
| pkg/tern/local_client_plan_namespaces_test.go | Tests that local plan persistence stores VSchema metadata and doesn’t let sibling shard changes wipe it. |
| pkg/storage/types.go | Extends stored namespace plan data with a persisted Metadata map for gate-facing plan metadata. |
| pkg/storage/plan_vschema.go | Introduces persisted VSchema metadata keys, extraction helper, and stored-plan unsafe VSchema detection. |
| pkg/storage/plan_vschema_test.go | Unit tests for metadata extraction and stored-plan unsafe VSchema detection (including fail-closed cases). |
| pkg/api/vschema_stored_plan_gate_test.go | Verifies ExecuteApply blocks/permits stored-plan applies based on persisted VSchema deletions and opt-in. |
| pkg/api/proto_helpers.go | Persists VSchema metadata in the gRPC plan persistence path using the shared helper. |
| pkg/api/proto_helpers_test.go | Tests that proto→storage namespace conversion persists gate metadata and omits it for DDL-only changes. |
| pkg/api/plan_handlers.go | Extends stored-plan unsafe gate to reject on unsafe VSchema changes (in addition to unsafe DDL). |
| pkg/api/handlers_test.go | Updates stored-plan fixtures to include vschema_changed metadata where VSchema artifacts exist. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
b2c87d0 to
c536069
Compare
…nd mutations Persist the gate-facing VSchema change-metadata (the changed flag plus the recorded structural deletions and in-place vindex mutations) into stored plans at both persistence sites via a single storage helper, and extend the stored-plan unsafe gate to read it: a recorded deletion or mutation requires the same explicit opt-in as destructive DDL, and a VSchema change whose metadata is missing or undecodable fails closed with guidance to re-plan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c536069 to
0bb53ef
Compare
|
🤖 Review findings - created by Kiran's code review agent - for schemabot/pull/1084, 0bb53ef. Verdict: 4 findings — 2 blocking (missing apply-time VSchema gate, fail-open on artifact/metadata divergence), 2 non-blocking (test coverage gaps). Blocking
Non-blocking
General suggestions
The one thing that could have broken, verifiedThe riskiest mechanism is the Verified correct
This review was generated by Claude Code (claude-sonnet-5). |
The data plane's dispatch admission only re-checked table DDL, so a VSchema-only operation - whose scope carries no DDL - was admitted without re-checking the stored plan's recorded VSchema deletions and mutations. Admission now runs a VSchema counterpart of the DDL gate at both the create and attach paths. For deployments that materialize the plan from the dispatch request, the dispatch's VSchema changes now carry the namespace's persisted VSchema change-metadata (new engine-agnostic metadata field on the TableChange proto), so the materialized plan runs the same gate as a locally stored plan instead of failing closed on every additive change. UnsafeVSchemaChanges now inspects the union of namespaces carrying a VSchema document and namespaces carrying VSchema change-metadata: a divergent record - metadata without a document - fails closed instead of being skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 All four addressed in c05f24a:
This reply was generated by Claude Code (Fable 5). |
…r fixture Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ew-drift-rollup * origin/main: (357 commits) fix(github): render each lint violation as its own bullet in unsafe-change comments (#1105) feat(engine): disclose at plan time whether an apply continues or discards a copy (#1087) fix(operator): choose the drive mode from the generation manifest, not the attached row count (#1101) feat(tern): one deployment correlates to exactly one remote apply (#1060) fix(github): record the passing check when an apply plan finds no changes (#1099) feat(spirit): detect an unfinished row copy and log what the apply will do to it (#1048) docs: reserve metrics for signals worth alerting on (#1089) feat(cli): browse stored plan history with the list-plans command (#1083) feat(cli): render status sources as OSC 8 hyperlinks on interactive terminals (#1097) feat(github): show VSchema changes in sharded apply comments (#1096) test(webhook): PostgreSQL failure-matrix row — declined stop is terminal, apply completes (#1098) feat(observability): log the delivery GUID when a goroutine panics (#1092) test(webhook): pin apply-confirm lock-path dispositions (#1091) fix(api): type terminal rollback validation errors (#1090) build(deps): pin pg-sprite to released v0.1.0 (#1093) feat(cli): show apply provenance as a clickable source in status output (#1086) fix(github): give sharded applies a real terminal summary comment (#1085) fix(vitess): gate stored-plan applies on recorded VSchema deletions and mutations (#1084) webhook: PostgreSQL failure-matrix rows — restart survival and permanent privilege refusal (#1079) fix(tern): complete a deployment-keyed apply only when its generation manifest is satisfied (#1076) ... # Conflicts: # pkg/webhook/plan.go # pkg/webhook/templates/plan.go
Why this matters
Plan time already treats a VSchema removal (a deleted vindex or routing entry) and an in-place vindex mutation (same name, different routing behavior) as unsafe changes requiring the same operator opt-in as destructive DDL, because either changes Vitess query routing the moment the VSchema is applied. But that verdict lived only in the live plan response: persisting a plan kept just the changed flag for artifact attachment and dropped the deletion and mutation records, so the stored-plan apply gate checked DDL alone. An apply created from a stored plan could remove or rewire vindexes and routing entries without anyone acknowledging the risk, on every Vitess-family engine.
What it does
Persists the gate-facing VSchema change-metadata into stored plans and teaches the stored-plan unsafe gates — at queueing and at dispatch admission — to read it.
VSchemaPlanMetadata): thevschema_changedflag plus the recordedvschema_deletionsandvschema_mutations. Display-only metadata such as the rendered diff is deliberately not persisted.UnsafeVSchemaChanges()alongsideUnsafeDDLChanges(), and the queueing gate now rejects on either, with the rejection naming the namespace and the operator-facing reason for each removal or mutation.TableChangeproto), so a deployment that materializes the plan from the dispatch request runs the same gate as one reading its own stored plan.Two safety properties worth calling out:
How it moves us toward the northstar
The northstar is that a schema change is equally safe no matter which surface executes it: every destructive action requires an explicit, auditable operator opt-in whether the apply comes from a live plan or a stored one. This closes the remaining path where a VSchema removal or mutation could apply unacknowledged, making the stored plan a faithful carrier of the plan-time safety verdict instead of a lossy copy.
Opened by Claude (Fable 5).