pgconfigurator
pgconfigurator

Parallel-aware nodes (the “Parallel …” prefix)

Appears in EXPLAIN asParallel Seq Scan, Parallel Index Scan, Parallel Hash, …

Worker-cooperating variants of scans, hashes, and appends.

What it is

A 'Parallel ' prefix on a node (Parallel Seq Scan, Parallel Index Scan, Parallel Bitmap Heap Scan, Parallel Hash, Parallel Append, …) means the node cooperates across worker processes — for example, workers divide a table's blocks between them rather than each reading the whole table.

When the planner picks it

Beneath a Gather / Gather Merge, when the planner builds a parallel plan.

Is it good or bad?

These behave like their serial namesakes — the same tuning advice applies. Note that per-node row and timing figures are per-worker averages, which can be confusing when reading the plan. The catalog maps each parallel variant to its base node's page.

In depth

A flag, not a node type

"Parallel-aware" isn't a separate node category — it's a property a node carries. When a Gather (or Gather Merge) launches workers, the nodes beneath it run in each worker, and those that are parallel-aware cooperate across workers so the work is actually divided. The EXPLAIN label is prefixed:

  • Parallel Seq Scan — workers split the table's blocks between them.
  • Parallel Index Scan / Parallel Index Only Scan — similar, by index range.
  • Parallel Bitmap Heap Scan — workers consume the bitmap together.
  • Parallel Hash — workers cooperatively build one shared hash table for the join.
  • Parallel Append — workers divide the Append's children.
  • Parallel Foreign Scan — only if the FDW implements it.

Nodes inside a parallel subtree that aren't parallel-aware (e.g. a Sort, WindowAgg) still run once per worker, with each worker processing its slice independently.

The two big numerical traps

  1. Per-loop figures — under a Gather, a child node's rows and actual time are typically per-worker averages, and loops is the number of participating processes. rows=50000 loops=3 processed roughly 150,000 rows in total.
  2. work_mem is per worker — a parallel Sort or Hash gets work_mem for each participant, so total memory is work_mem × processes. Raising it without thinking about that multiplies real memory.

When the planner doesn't choose parallelism

Even when a parallel plan exists, the planner may stick with serial because:

  • the query's estimated cost is below the parallel-cost thresholds (parallel_setup_cost, parallel_tuple_cost);
  • the table or index portion to scan is below min_parallel_table_scan_size / min_parallel_index_scan_size;
  • max_parallel_workers_per_gather = 0 for the session;
  • the rest of the plan (a LIMIT 1, an order it can't preserve) prevents the optimization.

A query that "should be parallel" but isn't usually fails one of those checks, not because parallel mode is disabled.

See also