Skip to content

fix: preserve reader grants across ash.rebuild_partitions() (#213) - #218

Draft
NikolayS wants to merge 1 commit into
mainfrom
release/fix-rebuild-grants-213
Draft

fix: preserve reader grants across ash.rebuild_partitions() (#213)#218
NikolayS wants to merge 1 commit into
mainfrom
release/fix-rebuild-grants-213

Conversation

@NikolayS

@NikolayS NikolayS commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Fixes #213ash.rebuild_partitions() silently strips every reader's grants.

Found by the pre-tag audit (#160), and independently rediscovered during the PG 17 gate run.
Draft on purpose: needs REV and your explicit approval.

The bug

rebuild_partitions() drops ash.query_map_all, recreates sample_N / query_map_N, and rebuilds
the view — replaying none of the grants. Afterwards, as a reader:

select * from ash.status();
ERROR:  permission denied for view query_map_all
CONTEXT:  SQL statement "select count(*) from ash.query_map_all"
          PL/pgSQL function status() line 31 at SQL statement

aas(), summary(), samples(), top(), chart(), timeline() all fail the same way. A plain
pg_monitor member is hit too, since 2.0 grants it the reader bundle by default.

Onset is delayed, which is what makes it nasty: right after the rebuild, raw storage is empty
and every reader returns fine. The outage lands minutes later when the first samples arrive — so
the obvious smoke test passes. The regression test here therefore rebuilds, then samples, then
reads as the reader role
.

The fix

Snapshots explicit SELECT ACLs for every recreated relation, then restores them:

  • Complete reader bundles → re-run ash.grant_reader(), so newly numbered partitions are covered
  • Partial / hand-narrowed grants → restored by exact relation name, including grant options,
    never widened to the full bundle
  • Roles with no grants → get nothing

Also fixes README:376-378, which attributed the loss to "new partitions" when the recreated view
is the first blocker, and never warned that a rebuild destroys the default pg_monitor grant.

Verification

RED against unpatched SQL, after take_sample() inserted exactly one row — the real error above,
reproduced through the delayed-onset path rather than an ACL-only check.

GREEN on PG 18.3 and PG 17, through a 3→4→3 rebuild cycle:

  • named reader and pg_monitor both call ash.status() and ash.samples() successfully after sampling
  • both receive all 14 reader relations after 3→4
  • partial role stays exactly query_map_0:true, sample_1:false — not widened
  • no-access role stays {}
  • decoded sample is exactly one Timeout:PgSleep row; samples_total = 1, num_partitions = 4

Fresh install, double re-apply, and the full 1.0→2.0 chain all clean with zero errors or warnings.

Review note

No shared helper was factored out with the installer's equivalent #107 block (:6283-6360),
deliberately: that path spans a script-wide function-recreation cycle while this one is
transaction-local and relation-specific, and unifying them would drag install-order and hardening
dependencies into an already-verified path. Reasonable, but it does mean two places now implement
grant preservation — worth a look if you'd rather they converge.

🤖 Generated with Claude Code

https://claude.ai/code/session_014HzBGzjFyN8dXZHbdWmYBj

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.

Release blocker: rebuild_partitions() strips every reader grant, including the default pg_monitor grant

1 participant