Technical Analysis: log_postmaster_stats — Periodic Postmaster Activity Reporting
Core Problem
The PostgreSQL postmaster process — the central coordinator responsible for forking new backend connections and parallel workers — lacks adequate runtime observability for retrospective analysis. While other critical subsystems have periodic self-reporting mechanisms (e.g., log_checkpoints for the checkpointer, log_startup_progress_interval for recovery), the postmaster has no equivalent facility to communicate its workload intensity over time.
Why This Matters Architecturally
The postmaster is a single-threaded process that serializes all connection acceptance and fork() calls. Under connection storms, it becomes the bottleneck for the entire cluster. Current observability options are insufficient:
-
pg_stat_database.sessions/pg_stat_database.parallel_workers_launched— These are cumulative counters that show totals but not rates over time intervals. They require external polling infrastructure and cannot tell you what happened during a brief spike between polling intervals. -
log_connections— Produces per-connection log entries, which is prohibitively expensive at high connection rates (thousands/sec). Large deployments cannot keep this enabled continuously due to log volume. -
log_connections=setup_durations(recent addition) — Provides per-connection timing but still doesn't give aggregate rate information or postmaster-level CPU utilization.
The fundamental gap is: no mechanism exists to periodically summarize postmaster throughput and resource consumption, making it difficult to diagnose past overload events, assess fork() efficiency, or detect parallel worker starvation of regular connections.
Proposed Solution
The patch introduces a new GUC log_postmaster_stats (integer, in seconds) that, when enabled, causes the postmaster to emit a periodic summary log line containing:
- Connection rate: average connections/second during the interval
- Disconnection rate: average disconnections/second during the interval
- Parallel worker launch rate: average parallel workers started/second
- CPU usage: user and system time consumed by the postmaster process itself (
getrusage(RUSAGE_SELF)) - Elapsed wall-clock time: actual time since last report
Design Characteristics
The implementation mirrors the pattern established by log_startup_progress_interval, providing consistency in the PostgreSQL logging infrastructure. Key design choices:
-
Interval-based reporting: Rather than per-event logging, this aggregates over configurable time windows, making it viable for production use even under extreme load.
-
RUSAGE_SELFnotRUSAGE_CHILDREN: The patch deliberately reports CPU time of the postmaster process itself, not its children. This isolates the fork/accept overhead from backend processing time. -
Default off (0): Aligned with other diagnostic GUCs, though the author suggests 60 seconds might be a reasonable default for future consideration.
Key Technical Insights
Elapsed Time as an Overload Indicator
One of the most insightful aspects of this proposal is that the elapsed time field itself becomes a diagnostic signal. When log_postmaster_stats = 10 but elapsed time shows 16.25 seconds, it means the postmaster was too busy to even check its reporting timer on schedule. The postmaster event loop was saturated with fork() calls, delaying the stats emission. This is a clever emergent property of the design — the metric's lateness is itself the signal.
CPU Time Discrepancy Analysis
The author notes that during connection storms, user+system CPU (~5s) doesn't account for the full elapsed wall-clock time (~10s). This is because at extreme fork() rates, the postmaster spends significant time in kernel scheduling queues and waiting for copy-on-write page faults in child setup — time that doesn't appear in RUSAGE_SELF but represents real postmaster latency.
Connection vs. Parallel Worker Starvation
By reporting both regular connections/sec and parallel workers/sec in the same log line, operators can diagnose whether parallel query workers are consuming postmaster capacity at the expense of new user connections (or vice versa). This is particularly relevant for mixed OLTP/OLAP workloads.
Open Design Questions
-
Self-diagnosis warnings: The author considered emitting a WARNING when stats couldn't be gathered on time (indicating overload), but deferred this due to community sentiment against self-diagnostic messages.
-
Default value: Should this be enabled by default at a conservative interval (e.g., 60s)? The zero-cost-when-idle nature of the feature might justify a default-on approach, similar to how
log_checkpointsis now on by default. -
Additional metrics: Future iterations might include fork() failure counts, accept() queue depth, or time spent in the accept/fork critical section.
Patch Implications
The patch touches:
- Postmaster main loop (
postmaster.c): Timer check and stats accumulation logic - GUC infrastructure: New integer GUC definition
- Documentation: New parameter documentation
The implementation is lightweight — it adds a timestamp check in the postmaster's main event loop and accumulates counters that are already being tracked. The getrusage() call is inexpensive. The design avoids any shared memory changes or new IPC, keeping the patch self-contained within the postmaster process.