PostgreSQL

PostgreSQL

All Your GUCs in a Row: max_parallel_workers and max_parallel_workers_per_gather

Neither of these parameters means what its name says. max_parallel_workers_per_gather is the most workers the planner is allowed to request for one plan node, and the planner usually requests fewer. max_parallel_workers is documented as a limit for the whole cluster, and it is enforced by comparing a cluster-wide count against a number that every session gets to choose for itself.

All Your GUCs in a Row: max_parallel_maintenance_workers

max_parallel_maintenance_workers is a ceiling, and most of the time something lower gets there first. It caps the number of parallel workers a single utility command may start. The default is 2, the context is user, and the range is 0 to 1024, with 0 turning parallel maintenance off. Raising it is easy. Getting a command to use what

All Your GUCs in a Row: max_notify_queue_pages

Nobody asked for max_notify_queue_pages. It exists because a limit that used to enforce itself stopped doing so, and something had to take its place.

LISTEN/NOTIFY runs on a single queue for the whole cluster, stored as SLRU pages under pg_notify/ in the data directory. Through PostgreSQL 16 that queue topped out at 8GB, and the number

The Call Is Coming From Inside the Session

Every runbook for giving an AI agent a database login contains the same two lines:

1ALTER ROLE agent SET statement_timeout = '2s';
2ALTER ROLE agent SET default_transaction_read_only = on;

Both of those work. Run SELECT pg_sleep(4) as that role and it gets cancelled at two seconds. Run CREATE TABLE and it gets refused. The

All Your GUCs in a Row: max_logical_replication_workers

max_logical_replication_workers is the size of a process pool. The documentation lists the three kinds of worker that draw from it (four in 19) and stops there, which leaves out the part that matters: only one of those kinds is the one you were counting when you picked the number, and when the pool runs dry nothing fails. A subscription just

All Your GUCs in a Row: max_locks_per_transaction

max_locks_per_transaction is neither per transaction nor a maximum. It is the per-process share of one cluster-wide lock table, and any single transaction can take far more than its share as long as nobody else is using theirs. The name has been misleading people since 7.2, and the docs’ description of it as “the average number of object locks used by

19th Nervous Breakdown

PostgreSQL 18 had its first release candidate on September 4, 2025, and went GA three weeks later. As I write this, on September 14, 2026, PostgreSQL 19 has no release candidate date. It has a fourth beta scheduled for September 24, a commit freeze for that beta on September 19, and a stated goal of GA “by the end of

All Your GUCs in a Row: max_index_keys

max_index_keys has been counting things that aren’t keys since PostgreSQL 11. What it reports, 32, is the most columns an index can have, and since INCLUDE arrived a column in an index is not necessarily a key: a 31-key index carrying one INCLUDE column is at the limit, and so is a one-key index carrying 31 of them. The error

All Your GUCs in a Row: max_identifier_length

max_identifier_length reports a number, 63, and the number is the least interesting thing about it. Every relational database caps the length of a name. What is unusual about PostgreSQL is what happens when you go over: MySQL, SQL Server and Oracle reject the statement, while PostgreSQL cuts the name off at 63 bytes, mentions it in a NOTICE, and carries

All Your GUCs in a Row: max_function_args

Nobody looks up max_function_args on purpose. You meet it when PostgreSQL interrupts something unrelated with cannot pass more than 100 arguments to a function, and you go looking for the knob. There isn’t one.

It’s a read-only preset, a sibling of block_size and integer_datetimes. The context is internal; SET, ALTER SYSTEM, and a line