Back to parallel query, and a parameter easy to confuse with the family’s opener. enable_async_append was about running foreign scans concurrently across remote servers. This one is about running local Append children concurrently across worker processes. Different mechanism, different problem, similar-sounding name. Default on, context user, same family framing: a diagnostic instrument, not a tuning knob.
Two kinds of parallel Append
An Append node concatenates rows from several children — the partitions of a partitioned table, the arms of a UNION ALL. When a query goes parallel, there are two ways that Append can involve the workers, and the difference is the whole point of this parameter.
A regular Append inside a parallel plan parallelizes within each child, one child at a time. All the workers gang up on the first child, cooperatively scanning it to completion, then all move on to the second child together, and so on. The parallelism is happening inside each partition’s scan; the Append itself just processes its children in order.
A Parallel Append, added in PostgreSQL 11, parallelizes across children. Instead of making every worker cooperate on one child at a time, the executor spreads the workers out over the children, so multiple children are being scanned simultaneously — worker 1 on partition A, worker 2 on partition B, worker 3 on partition C, all at once. In EXPLAIN the node is labelled Parallel Append rather than plain Append under a Gather.
Why have both? Because they win in different situations, and Parallel Append’s real value is a case the regular one can’t handle at all. A regular parallel Append requires every child to be a partial (parallel-aware) plan — if even one partition can only be scanned non-parallel, you don’t get a partial Append and the whole thing falls back to serial. Parallel Append lifts that restriction: it can mix partial and non-partial children. A non-partial child is scanned start-to-finish by a single worker (scanning it with several would duplicate rows), but different non-partial children run in different workers concurrently. The canonical case is a partitioned table queried through an index that doesn’t support parallel scans: each partition’s index scan must run to completion in one process, but Parallel Append runs several of those single-process index scans at the same time, one per worker. You get coarse-grained parallelism — parallelism between partitions — precisely where fine-grained parallelism within a partition wasn’t available. That is the feature’s reason to exist.
Symptoms that warrant flipping it
Parallel Append helps most on queries that fan out across many partitions or many UNION ALL arms, especially when the per-partition scans can’t themselves be parallelized. It helps least — and can actively hurt — on small queries, where spinning up workers to run a handful of trivial child scans costs more than it saves.
The reason you’d flip the switch is usually the second case. If a UNION ALL or partitioned-table query over small inputs got slower, and EXPLAIN (ANALYZE) shows a Parallel Append whose per-worker actual times reveal each worker doing very little real work, the parallel setup overhead is likely exceeding the benefit. SET enable_parallel_append = off for the session, re-run, and compare against the serial Append; if the non-parallel plan is faster, you’ve confirmed the overhead diagnosis. The deeper fix is often to stop the query going parallel on inputs too small to deserve it — check the per-relation parallel_workers setting and the min_parallel_table_scan_size / min_parallel_index_scan_size thresholds, and confirm statistics are current so the planner sizes the work correctly.
The opposite diagnostic is also useful: you expected Parallel Append to kick in on a big multi-partition query and it didn’t. Flipping the switch off won’t help there — instead, the absence is the clue. The planner may have costed the parallel version as too expensive (stale statistics, or parallel_setup_cost set high), or the query may not have qualified. This mirrors the two-switch lesson from the async-append post: some parallel features have preconditions beyond the GUC, and a missing Parallel Append in the plan is more often a costing or eligibility problem than something this parameter controls.
As always, enable_parallel_append = off is a probe. Parallel Append is the correct plan for large fan-out queries — genuinely so for partitioned tables scanned via non-parallel indexes, where nothing else provides parallelism — and there’s a defensible operational pattern, if your workload is mostly small queries with a few big analytical ones, of leaving it off globally and enabling it per-session for the queries that benefit, exactly as you might with other parallel knobs. But that’s a measured decision, not a default to distrust. Diagnose with the switch, fix the statistics or the parallel-cost thresholds, and set it back.