Skip to content

Pin PostgreSQL 19 parallel autovacuum on a columnar table (#398) - #412

Open
jdatcmd wants to merge 1 commit into
mainfrom
feat/398-pg19-vacuum-options
Open

Pin PostgreSQL 19 parallel autovacuum on a columnar table (#398)#412
jdatcmd wants to merge 1 commit into
mainfrom
feat/398-pg19-vacuum-options

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Closes #398. @ChronicallyJD for review. No benchmark needed.

The answer

You asked the simple question first: does a table access method participate in 19's
parallel autovacuum, or is it heap-specific? Measured on 19beta2:

columnar heap
ALTER TABLE ... SET (autovacuum_parallel_workers = 3) accepted accepted
lands in pg_class.reloptions yes yes
has any effect no yes
VACUUM (VERBOSE) reports nothing finished vacuuming ...

So it is your state (2), the one you called out as its own defect: the parameter is
settable and silently ignored. pgcolumnar_relation_vacuum marks the visibility map
and retires fully-deleted row groups. It does no parallel work and never reads the
parameter.

The option I preferred is not available

On the issue I said I would rather reject it than ignore it, because our vacuum is not
the heap's and a parameter that cannot mean what it says should not be settable.

We cannot reject it. Storage-parameter validation belongs to PostgreSQL and is
driven by relkind, not by the access method, and pgColumnar has no reloptions hook to
refuse from. Rejecting would need a core change. So the available honest option is to
document it, which is what this does.

Worth noting autovacuum_max_parallel_workers defaults to 0 on 19beta2, so
parallel autovacuum is opt-in at the cluster level too. Nobody gets this by accident
today.

The control is the part I would not drop

Without a heap arm, "the parameter does nothing here" cannot be told apart from
"parallel autovacuum does nothing on this cluster", and with the GUC defaulting to 0
the second reading would be true for everyone. The suite asserts VACUUM VERBOSE
reports on heap while the columnar path is silent, because the AM path is ours.

If a future release makes table AMs participate, that check flips, and it should
then be rewritten to assert we report workers too. That is the intended signal, same
shape as the REPACK suite in #409.

Tests

test/pg19_vacuum_options.sh, registered, 19-gated with a visible skip below it
(verified on PG18: SKIP parallel autovacuum requires PostgreSQL 19, exit 0). Twelve
checks: acceptance on both access methods, the reloptions record, VACUUM,
VACUUM (PARALLEL 2) and pgcolumnar.vacuum all succeeding with it set, and rows and
content hash unchanged by any of them.

Gate

Five-major matrix, ALL VERSIONS PASSED, pg19_vacuum_options=PASS on all five.

docs/limitations.md says the parameter is accepted, does nothing, and cannot be
refused.

🤖 Generated with Claude Code

The question was whether a table access method participates in 19's parallel
autovacuum at all. Measured: the per-table storage parameter
autovacuum_parallel_workers is ACCEPTED on a columnar table and lands in
pg_class.reloptions exactly as it does on heap, and it has no effect.

pgcolumnar_relation_vacuum marks the visibility map and retires fully-deleted row
groups. It does no parallel work and never reads the parameter, so a user can set
it and nothing happens.

It cannot simply be rejected, which was my stated preference on the issue.
Storage-parameter validation belongs to PostgreSQL and is driven by relkind rather
than by the access method, and pgColumnar has no reloptions hook to refuse it
from. Documenting it is the honest option that is actually available.

test/pg19_vacuum_options.sh, registered, gated to 19 with a visible skip below it,
asserts all of that: the parameter is accepted on both access methods and recorded
in reloptions, VACUUM and VACUUM (PARALLEL 2) and pgcolumnar.vacuum all succeed
with it set, and rows and content hash are unchanged by any of them.

The heap arm is a control rather than decoration. autovacuum_max_parallel_workers
defaults to 0 on 19beta2, so without a table where parallel vacuum demonstrably
does something, "the parameter does nothing here" cannot be told apart from
"parallel autovacuum does nothing on this cluster". The control asserts VACUUM
VERBOSE reports on heap while the columnar path is silent, because the AM path is
ours. If a future release makes table AMs participate, that check flips and should
be rewritten to assert we report workers too.

docs/limitations.md says plainly that the parameter is accepted, does nothing, and
cannot be refused.

Gate: five-major matrix, ALL VERSIONS PASSED, pg19_vacuum_options PASS on all five.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@ChronicallyJD ChronicallyJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. I checked both of the things you added that I had not established.

The answer matches what I measured, so I spent the review on the two claims that were
yours rather than re-running mine.

"We cannot reject it" holds

$ grep -nE "reloptions|amoptions" access/tableam.h
  (nothing)

$ grep -nE "^extern bytea \*.*reloptions" access/reloptions.h
  heap_reloptions(char relkind, ...)
  view_reloptions(...)
  partitioned_table_reloptions(...)
  index_reloptions(amoptions_function amoptions, ...)

Index access methods get an amoptions function threaded into validation. Table access
methods get nothing: tableam.h has no reloptions entry point at all, and validation
goes through heap_reloptions(relkind, ...), keyed on relkind as you said.

So rejection needs a core change. Documenting is the honest option available, and I
withdraw the preference I stated on the issue.

The default is 0, and it changes the severity

 name                            | setting | boot_val | source
---------------------------------+---------+----------+---------
 autovacuum_max_parallel_workers | 0       | 0        | default

Checked on a cluster where I deliberately did not set it. I missed this, and it
matters: my issue framed a user setting the storage parameter and silently getting
nothing, which reads as a live footgun. With the cluster GUC at 0, parallel autovacuum is
off entirely unless someone opts in, so a user has to take two steps to reach the
confusing state rather than one.

That does not make it a non-issue, since the two-step user is exactly the one who has read
the 19 release notes and is trying to use the feature. But it is a smaller thing than I
implied, and the PR is right to say so.

I set the GUC to 8 in my own run without noting that this was not the default. Worth
recording, because a measurement that quietly configures away the safety margin can
overstate a problem.

The rest

The heap control is the part I would also not drop, for the reason we have both now hit
twice this week: without an arm where the mechanism demonstrably works, a negative is
unreadable. My first attempt at this measurement had one index per table and could not
have launched a worker in either arm, and it looked like a clean result.

docs/limitations.md gains the line, which is where a user will look.

Approving.

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.

Does a table access method participate in PostgreSQL 19 parallel autovacuum, and what do we do if the storage parameter is set?

2 participants