feat(store): cascade-safe partial rebuild + control rebuild-projections CLI (spec 21)#512
Conversation
…kipped counter (F-03, F-14, #506)
…dTargetResult helper
…ts surface in counters (F-14, #505)
…wipe on TRUNCATE (spec 21 AC 6)
…drop separate target (spec 21 AC 6, #497)
…ser_roles test assert
…ns CLI (#505, #506) Spec 21: RebuildAll operability & cascade-safety — prerequisite for spec 19's snapshot-restore, which replays subsets by definition. - F-01 (#505): new `control rebuild-projections [target…]` subcommand (doctor pattern) — prints the resolved plan before running, reports applied/skipped per target, re-runs auth.ReconcileSystemRoles after a successful rebuild, and is deliberately CLI-only (no remote RPC; authz = host access). Documented in deploy/QUICKSTART.md with docref anchors. - F-03 (#506): partial rebuilds are cascade-safe. RebuildAll expands a partial selection to a fixpoint over the LIVE FK graph: every table a TRUNCATE ... CASCADE reaches pulls in its owning target, and unowned cascade children pull in their FK parents' targets (the second-order case: user_group_members_projection hangs off users_projection but is replayed by user_groups). A closure table nobody can replay refuses the rebuild. ResolveTargets exposes the same expansion for the CLI plan preview. Regression pins: TOTP enrollment, identity link, AND group membership survive `rebuild-projections users`. - F-14: TargetResult gains a separate Skipped counter — applied and skipped events are no longer conflated. - F-04 (AC 6): new full-fidelity round-trip test — rich fixture, full ordered row::text dump over every AllRebuildTargets table, no-arg rebuild, byte-identical re-dump; self-discovering over the registry. The round-trip test immediately found three real replay-infidelity bugs, fixed here: - migration-seeded rows (system Admin/User roles, the server_settings 'global' row) exist only as rows, not events — a rebuild destroyed them. rebuildTarget gains SeedSQL, re-applied post-TRUNCATE; the seeds mirror the post-migration state (008 seeds + 009's deliberate permission blanking) with FIXED timestamps (008 changed accordingly), and the round-trip test permanently guards the two against drift. - user_roles_projection had two writers (ApplyUser inserts creation-time role_ids, ApplyUserRole applies grants) split across two rebuild targets — whichever truncated second wiped the other's replay. The user_roles target is folded into users (both tables, both streams, ApplyUserWithRoles dispatcher), replaying in true sequence order.
|
Warning Review limit reached
Next review available in: 10 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Closes #505. Closes #506. Implements spec 21 (RebuildAll operability & cascade-safety) — the second spec-19 prerequisite (after #507/#511); spec 19's snapshot-restore replays subsets by definition and rides on this machinery.
What
control rebuild-projections [target…]subcommand (doctor pattern): resolves and prints the plan before any destructive statement, reportsapplied/skipped/duration per target, exits 0/1/2 (success / rolled-back failure / could-not-run), and re-runsauth.ReconcileSystemRolesafter success so the rebuilt DB is immediately RBAC-correct without a control restart. Deliberately CLI-only — authz is host access, no remote RPC (AC 3). Documented indeploy/QUICKSTART.md(docref-anchored).RebuildAllexpands a partial selection to a fixpoint over the live FK graph (pg_constraint, not a hand-list): every table aTRUNCATE … CASCADEreaches pulls in its owning target; an unowned cascade child pulls in its FK parents' targets — that second-order rule is what savesuser_group_members_projection, which hangs offusers_projectionbut is replayed byuser_groups. A closure table nobody can replay refuses the rebuild (fail-closed).ResolveTargetsexposes the same expansion for the CLI preview. Regression pin: TOTP enrollment, identity link, AND group membership surviverebuild-projections users.Skippedcounter onTargetResult;ErrSkipEventno longer counts as applied.row::textdump of everyAllRebuildTargetstable → no-arg rebuild → byte-identical re-dump. Self-discovering over the registry.Real bugs the round-trip test caught (fixed here)
server_settings'global'row exist only as migration seeds, not events — a rebuild TRUNCATEd them away permanently (settings reads then fail; RBAC loses its system roles).rebuildTargetgainsSeedSQL, re-applied post-TRUNCATE. The seeds mirror the post-migration state — 009 deliberately blanks system-role permissions (reconciler-owned) — with fixed timestamps (008 updated; beta Path A). The round-trip test is the standing drift guard between the migration seed and the rebuild seed.user_roles_projectionhad two writers split across two targets (ApplyUser writes creation-timerole_ids, ApplyUserRole writes grants) — whichever TRUNCATEd second wiped the other's replay. Theuser_rolestarget is folded intousers(both tables, both streams,ApplyUserWithRolesdispatcher), replaying in true sequence order.res.Targetshas exactly one entry — cascade expansion legitimately widens the run set (arolesrebuild expands to 6 targets via ugr → user_groups → users), so they now locate their target by name.Verification
go vet,staticcheck,gofmt,docref checkclean; fullgo test ./... -count=1green (33 packages).coderabbit review: no findings.