pgconfigurator
pgconfigurator

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

Paste a plan into the analyzer →

Settings that influence it

max_parallel_workers_per_gathermax_parallel_workersmax_worker_processes

How we tune these →

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.

See also