fix(github): give multi-keyspace Strata applies the keyspace-grouped shard layout - #1189
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes the GitHub sharded-apply comment UX for Strata applies that span multiple keyspaces within a single deployment by extending the sharded layout to render one section per keyspace (while preserving existing single-keyspace rendering).
Changes:
- Refactors sharded-apply template data from a single
Keyspace+ flat shard list toKeyspaces []ShardedKeyspace, enabling keyspace-grouped rendering. - Updates sharded-apply detection/build logic to treat multi-keyspace, single-deployment shard fan-out as “sharded”, and qualifies ordering-label identities with
keyspace/shardwhen multiple keyspaces are present. - Adds/updates previews and tests to cover multi-keyspace rendering, ordering, and label qualification.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| TEMPLATES.md | Adds rendered template example for “Apply Across Multiple Keyspaces”. |
| pkg/webhook/templates/sharded_apply.go | Refactors template input model to keyspace-grouped layout; updates rendering to iterate keyspace sections and flatten for cross-keyspace rollups. |
| pkg/webhook/templates/sharded_apply_test.go | Updates existing tests for new data shape; adds coverage for multi-keyspace sections and failure lifting across keyspaces. |
| pkg/webhook/templates/sharded_apply_tenant_test.go | Updates tenant-hint test to new Keyspaces shape. |
| pkg/webhook/templates/preview_sharded.go | Updates previews to use Keyspaces; adds a new multi-keyspace sharded preview. |
| pkg/webhook/sharded_apply.go | Allows multi-keyspace sharded applies; builds grouped keyspace data and derives cross-keyspace shard ordering labels. |
| pkg/webhook/sharded_apply_test.go | Updates sharded-apply behavior tests; adds multi-keyspace rendering and ordering-label qualification tests. |
| pkg/webhook/multi_apply.go | Updates sharded-apply layout comment to reflect multi-keyspace support. |
| pkg/cmd/internal/templates/preview_comment.go | Registers the new multi-keyspace sharded preview in the CLI preview output. |
Suppressed comments (1)
pkg/webhook/templates/sharded_apply.go:345
- In multi-keyspace sharded applies, the "First failure" callout currently prints only the shard name (e.g. "shard -"), which is ambiguous because shard names can repeat across keyspaces (every unsharded keyspace uses "-"). Consider keyspace-qualifying the shard identifier in this callout when more than one keyspace is present.
for _, s := range allShardStatuses(data.Keyspaces) {
if !isShardFailureState(s.State) {
continue
}
shard := html.EscapeString(s.Shard)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…shard layout A Strata apply spanning several keyspaces of one deployment previously fell through to the multi-deployment layout, rendering one identical bullet per operation with the deployment name as its label. The sharded layout now accepts any number of keyspaces: one section per keyspace in resolved order, with the shard histogram, first-failure callout, and rollout ordering spanning all of them. Ordering labels keyspace-qualify their blocker when the apply is multi-keyspace, since every unsharded keyspace's shard is named "-". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A divergence group heading on the apply comment enumerated every shard
name inline, growing unbounded with group width even though the group's
status table right below lists the same names. Wide groups now state
their coverage against the keyspace's shard count ("12 of 32 shards")
via the shared plan-side helper; passing the real total means a subset
can never read as covering all shards, since a divergent keyspace always
has at least two groups. Shard names in group headings and status table
rows are normalized through markdownInlineCode so a name from the
topology cannot break the span or the table.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
63d5c1a to
e74aa6d
Compare
morgo
left a comment
There was a problem hiding this comment.
🤖 Approved on Morgan's behalf at e74aa6d0. CI green (37/37). Rendering-only, nothing blocking — findings below are all non-blocking.
Blast radius checked first. isShardedApply has exactly one non-test call site, formatApplyStatusComment, so admitting multi-keyspace applies changes layout selection and nothing else. No apply path, no DDL execution, no gating decision moves.
The one thing I'd like changed, eventually. shardStatusesByKeyspace now pairs derived[i] with groups[i] positionally, where the old shardStatuses read the name back out of d.Deployment and was order-independent. That's a new dependency on presentation.Derive returning Deployments index-parallel to its input.
It holds today — Derive does deployments := make([]Deployment, len(ops)) then deployments[i] = deriveDeployment(ops, i), with no sort or filter — so the code is correct as written. But that invariant is not documented: Derive's comment specifies the input must be in resolved order and says nothing about the output's correspondence. And firstFailure in the same file explicitly anticipates "a caller that later re-slices or sorts Deployments," so reordering is already contemplated in this package. If anyone ever sorts inside Derive — grouping failures first, say — this breaks silently: shard rows land under the wrong keyspace heading with the wrong names, on an operator-facing status comment, with no test necessarily catching it.
Cheapest fix is a sentence on Derive stating that Deployments is index-parallel to ops. Keying by identity would be more robust still, and the qualified identity is unambiguous since shard names contain no /.
Two smaller notes.
The body says "Single-keyspace applies render byte-for-byte as before." That's true of the layout change — I diffed it, the old writeShardKeyspaceSection emitted the same #### Keyspace heading, so a one-element loop is genuinely identical output. But the second commit's divergence-heading coverage change does alter single-keyspace output whenever a group exceeds the inline cap. Both paragraphs are individually accurate; read together the blanket phrasing in the first slightly oversells it. Worth tightening so a reader doesn't take it as a full compatibility guarantee.
On size: GitHubIssueCommentMaxChars documents the logs fold as the elastic section that "spends whatever room the rest of the comment leaves," and this PR grows the non-elastic part for exactly the widest applies that previously fell back to the smaller per-deployment layout. It's much milder than I first assumed, because the apply comment is status-only — no DDL cells — so growth is bounded by shard-row count, and the coverage collapse already caps heading enumeration. But there's no clamp on the shard tables themselves, and the failure mode past the cap is a rejected comment, i.e. the operator sees nothing rather than a degraded view. Not reachable at the fleet widths in the example; worth a bound before it is. The coverage-collapse pattern this PR introduces is the obvious model for it.
Verified clean:
- No map-iteration nondeterminism.
keyspaceOrderandgroupOrderare slices built in operation order;cellsByKeyspace,groupIndex, andshardsByKeyspaceare only ever read through those slices. tables[ks.Keyspace+"\x00"+c.Table]uses a NUL separator rather than a bare concat, so the distinct-table count can't collide across keyspaces.- Keyspace-qualifying the presentation identity only when the apply spans more than one keyspace is the right call, and the reasoning in the comment is correct: every unsharded keyspace's shard is
-, so bare names in ordering labels would genuinely be ambiguous. Status rows keeping the plain name under their heading is the right complement. - The coverage denominator is the keyspace's shard count within the apply, which is the correct denominator for an apply status comment and does prevent a divergent subset reading as "all".
- Three new tests cover multi-keyspace sections, ordering, lifted first-failure, and wide-group coverage; no existing golden assertion was edited, so the single-keyspace regression surface stayed under test.
The sharded apply comment paired each derived deployment with its work group by slice position, quietly depending on Derive returning results index-parallel to its input. Key the pairing on the presentation identity instead — unique across groups by construction — and state the index-parallel contract on Derive for callers that do rely on position. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 Thanks — all three addressed:
— Claude (Fable 5) via Claude Code |
…t-dialect-classify * origin/main: chore(postgres): remove unreachable blocked apply guard (#1203) feat(postgres): enforce the table size ceiling at plan time (#1199) fix(github): give multi-keyspace Strata applies the keyspace-grouped shard layout (#1189) feat(cli): name each remote handle in the deployment-filtered status list (#1062) fix(github): lead wide shard groups with coverage instead of walling the plan comment (#1188) feat(engine): cap how many drivers one apply may occupy (#1183) # Conflicts: # pkg/webhook/templates/plan.go # pkg/webhook/templates/sharded_plan_test.go
…ogress-dialect * origin/main: fix(github): redact paths and connection identities in comments (#1200) fix(github): classify and format plan DDL under its own dialect (#1194) chore(postgres): remove unreachable blocked apply guard (#1203) feat(postgres): enforce the table size ceiling at plan time (#1199) fix(github): give multi-keyspace Strata applies the keyspace-grouped shard layout (#1189) feat(cli): name each remote handle in the deployment-filtered status list (#1062) fix(github): lead wide shard groups with coverage instead of walling the plan comment (#1188) feat(engine): cap how many drivers one apply may occupy (#1183) # Conflicts: # pkg/ddl/format_test.go # pkg/webhook/templates/plan.go # pkg/webhook/templates/sharded_plan_test.go
Why this matters
The sharded comment layout required a single namespace, so an apply spanning several keyspaces fell to the per-deployment layout — whose unit is one operation. A wide apply rendered dozens of identical per-operation bullets and empty details blocks, and the operator could not see keyspaces, shards, or ordering at all. This PR gives multi-keyspace applies the same keyspace-grouped shard view a single-keyspace apply already gets.
What it does
isShardedApplyadmits multi-keyspace applies, and the sharded layout groups by keyspace:One presentation pass derives ordering across all keyspaces. Ordering labels qualify shards with their keyspace (
cdb_resolute_lookup/-) only when the apply spans more than one — unsharded keyspaces all use the-shard, so bare names would be ambiguous — while shard names inside each section stay plain.The layout change itself leaves single-keyspace applies rendering byte-for-byte as before — a one-element keyspace loop emits the same section the old code did.
Divergence group headings adopt the plan comment's shared shard-list helpers: beyond a small inline cap the heading states coverage against the keyspace's shard count (
15 of 16 shards) instead of enumerating names — which stay reachable in the group's status table — and shard names in headings and status rows are normalized as inline code. Passing the real keyspace total means a divergent subset can never read as "all" shards. This part does change single-keyspace output: any divergence group wider than the inline cap now states coverage instead of listing names.Apply Across Multiple Keyspaces
Schema Change Status — Production
Database:
cdb_resolute| Type:Strata| Apply ID:apply-a1b2c3d4e5f6Applied by @jackjackbits at 2026-01-01 00:00:00 UTC
Shards: 1 completed, 1 running table copy, 4 waiting for cdb_resolute_lookup/-
Keyspace
cdb_resolute-Keyspace
cdb_resolute_lookup-Keyspace
cdb_resolute_sharded-4040-8080-c0c0-VSchema
cdb_resolute_sharded: PendingLast updated:2026-01-01 00:00:00 UTC (2026-01-01 00:00:00 UTC)
How it moves us toward the northstar
Real sharded fleets are multi-keyspace — a change routinely touches a sharded keyspace plus its lookup keyspace. Fanning out per shard is only first-class if the PR comment presents that whole graph as one coherent view; this is the base the table-altitude rendering (#1190) and live copy progress (#1191) build on.
Opened by Claude (Fable 5).