Gather
Appears in EXPLAIN asGather
Collects rows from parallel worker processes into the leader.
What it is
A Gather node launches parallel worker processes that each run the plan beneath it, then collects their rows into the leader process in arrival order (no ordering guarantee).
When the planner picks it
Above a parallel-aware subtree when the planner estimates that splitting the work across workers beats running it serially.
Is it good or bad?
Good when work is large enough to amortize worker startup. Watch 'Workers Launched' vs 'Workers Planned': if fewer launch than planned, the worker pool is exhausted and you're not getting the parallelism you paid to plan.
In depth
How it works under the hood
A Gather node is the boundary between parallel and serial execution. The subtree below it runs in several processes at once — the leader plus one or more background workers — each processing a slice of the data. The Gather collects their output rows back into the single leader process.
Plain Gather returns rows in whatever order they arrive (no ordering). When
order must be preserved, the planner uses Gather Merge instead, which merges
each worker's already-sorted stream.
Planned vs launched workers
The line to read is the worker count:
Gather (actual time=... rows=1 loops=1)
Workers Planned: 4
Workers Launched: 0
-> Partial Aggregate (...)
-> Parallel Seq Scan on big (...)
Workers Planned: 4 but Workers Launched: 0 means the planner budgeted four
workers and the executor got none — so all the "parallel" work actually ran
in the leader, serially. That happens when the global worker pool is exhausted.
Three settings govern this:
max_parallel_workers_per_gather— how many workers one Gather may request.max_parallel_workers— the cluster-wide pool Gathers draw from.max_worker_processes— the hard ceiling (changing it needs a restart).
If launched < planned regularly, the pool is too small for your concurrency, or another heavy parallel query is hogging it.
Per-worker numbers can mislead
Under a Gather, a child node's rows and actual time are usually per-worker
averages, and loops reflects the number of processes. A Parallel Seq Scan
showing rows=50000 loops=3 processed ~150,000 rows in total across three
processes — not 50,000. Keep that in mind when the math looks off.
When parallelism helps (and when it doesn't)
Workers have real startup cost, so parallelism pays off on large scans, joins, and aggregates — not on small, fast queries. For short OLTP statements a serial plan is often correctly chosen; don't force parallelism where the data volume doesn't justify the overhead.
What the analyzer flags here
- Parallel under-use — fewer workers launched than planned
Settings that influence it
max_parallel_workers_per_gathermax_parallel_workersmax_worker_processes
FAQ
- Why did fewer workers launch than were planned?
- The global worker pool (max_parallel_workers / max_worker_processes) was saturated — often by other concurrent parallel queries. The plan still asks for N workers but the executor gets fewer, so part of the work runs in the leader.