Process lifecycle
Skill matejformanek/postgres-claude/.claude/skills/process-lifecycle
Turn Claude Code into a long-term collaborator on PostgreSQL internals — cited knowledge corpus, agent skills, slash commands, and task-shaped scenarios for backend hacking.
npx -y skills add matejformanek/postgres-claude --skill process-lifecycleAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
PostgreSQL's per-connection multi-process model — postmaster fork, backend startup / initialization / query loop / clean shutdown, auxiliary processes (checkpointer, bgwriter, walwriter, autovacuum launcher, WAL summarizer, pgarch), background workers (bgworker.c registry + parallel/logical-rep workers), signal handling, and the FATAL/ERROR/PANIC hierarchy. Loads when the user asks about how a connection becomes a backend, what runs before the first query, why a query dies mid-flight, how signals + ProcessInterrupts + CHECK_FOR_INTERRUPTS work together, how autovacuum / bgworker workers get scheduled, or when planning a feature that hooks a startup phase / adds a new auxiliary process / touches shutdown ordering. Skip when the question is about client-side (libpq, drivers) or about the SQL-level session properties (that's `tcop` for query dispatch, `gucs-config` for GUCs).
SKILL.md
11.1 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it
process-lifecycle — postmaster, backends, aux processes, bgworkers
PostgreSQL uses a per-connection process model, not threads. Every client connection gets its own OS process, forked from the postmaster. New backends do NOT inherit query state — every connection starts fresh through InitPostgres. This model shapes almost every feature.
The five process classes
| Class | Files | Lifetime | Example |
|---|---|---|---|
| Postmaster | postmaster/postmaster.c, pmchild.c, launch_backend.c, fork_process.c | Cluster-lifetime | The parent process — accepts connections, forks children, reaps exits, restarts on crash. |
| Regular backend | tcop/postgres.c, tcop/backend_startup.c | Connection-lifetime | The child that runs SQL for one client. |
| Auxiliary process | postmaster/auxprocess.c + one file each: checkpointer.c, bgwriter.c, walwriter.c, walsummarizer.c, startup.c, pgarch.c, syslogger.c, interrupt.c | Cluster-lifetime | Always-on infrastructure processes; not for user queries. |
| Bgworker | postmaster/bgworker.c + registered by extensions | Configurable (per session / for lifetime of cluster / restart on crash) | Autovacuum workers, parallel query workers, logical-rep apply workers, extension workers. |
| Autovacuum launcher/worker | postmaster/autovacuum.c | Cluster-lifetime launcher + short-lived workers | Special case: launcher is aux, workers are bgworkers. |
Backend lifecycle (regular query-serving backend)
Every incoming connection follows this sequence:
- Postmaster accepts —
postmaster.cServerLoopsees a new socket, callsBackendStartupinlaunch_backend.c. - Fork or exec-and-fork — On Unix,
fork()copies the postmaster into a child. On Windows orEXEC_BACKENDbuilds, the child re-execs and re-attaches to shmem viaSubPostmasterMain. - Auth handshake —
backend_startup.cruns the startup message exchange, TLS/GSS, SCRAM/MD5/etc. authentication. Fails here → child exits before any query state exists. InitPostgres— This is the big one (inutils/init/postinit.c). Loads GUC per-role/per-db settings, opens the database's relcache/syscache, checks CONNECT permission, runs the login_PG_inithooks, ends up inPostgresMain.PostgresMainloop — the SQL query loop (intcop/postgres.c). ReadCommand → exec → send RowDescription/DataRow/CommandComplete → ReadyForQuery → next iteration.- Shutdown — Client sends X (Terminate), or postmaster signals shutdown, or fatal error.
proc_exitruns registered callbacks (seeon_shmem_exit/on_proc_exitinstorage/ipc/ipc.c), releases locks + LWLocks, detaches from shmem.
The interrupt / signal system
Because backends can be interrupted between statements (query cancel, admin shutdown, sighup, deadlock), there's a specific pattern:
- Signal handlers must be async-signal-safe. They set flags (
InterruptPending,QueryCancelPending,ProcDiePending,ConfigReloadPending) and set the process latch (SetLatch(MyLatch)). CHECK_FOR_INTERRUPTS()— macros sprinkled through the code that check the flags at safe points and callProcessInterrupts(intcop/postgres.c) if any are set.ProcessInterrupts— the actual interrupt handler. Runs at safe points, may callereport(FATAL/ERROR)to unwind orLATCH_WAIT_TIMEOUThandling.- Safe interruption points — the code base has thousands. Long-running loops need to include
CHECK_FOR_INTERRUPTS()— a missing one → uncancelable query.
Common signals + their flags:
SIGINT(query cancel) →QueryCancelPending.SIGTERM(shutdown) →ProcDiePending.SIGHUP(reload conf) →ConfigReloadPending.SIGUSR1(procsignal — multiplexed) → reason-specific flags viaprocsignal.c.
Auxiliary processes at a glance
Each aux process has its own file with a Main function that the postmaster spawns via SubPostmasterMain (Windows / EXEC_BACKEND) or forks directly:
| File | Function | Purpose |
|---|---|---|
startup.c | StartupProcessMain | Runs crash recovery / WAL replay at startup. Exits once redo completes. |
checkpointer.c | CheckpointerMain | Runs periodic checkpoints; writes the shutdown stats file. |
bgwriter.c | BackgroundWriterMain | Writes dirty buffers to smooth checkpoint I/O. |
walwriter.c | WalWriterMain | Flushes WAL buffers asynchronously. |
walsummarizer.c | WalSummarizerMain | (PG 17+) Summarizes WAL for incremental backup. |
pgarch.c | PgArchiverMain | Archives completed WAL segments (archive_command / archive_library). |
syslogger.c | SysLoggerMain | Rotates + captures postmaster/backend stderr when logging_collector=on. |
interrupt.c | Shared aux-process signal helpers | Not a process itself — the signal handlers shared across aux processes. |
Bgworker registration
Extensions and core code both use RegisterBackgroundWorker (in postmaster/bgworker.c):
- Static — called from
_PG_initat postmaster start. Fixed slot count (max_worker_processesGUC). - Dynamic —
RegisterDynamicBackgroundWorkerat runtime; used by parallel query (from a backend) and by extensions.
Bgworkers have flags controlling: shared-memory access, database connection, restart-on-crash policy, restart interval. See knowledge/idioms/background-worker-startup.md for the flag matrix and lifecycle diagram.
Common patch shapes
Add a startup-lifecycle hook
The scenario add-startup-hook (see knowledge/scenarios/add-startup-hook.md) covers this end-to-end. Short version:
- Hook typically lives in
PostmasterMain(postmaster-wide) orInitPostgres(per-backend). - Decide: cluster-once vs backend-per-connection.
- Existing patterns:
shared_preload_libraries(postmaster startup),local_preload_libraries+session_preload_libraries(per-backend),ClientAuthentication_hook(auth-time),emit_log_hook(per log record).
Add a new auxiliary process
Rare. Requires:
- New file under
src/backend/postmaster/<name>.cwith a<Name>Mainfunction. - Registration in
postmaster/postmaster.c(grep forStartChildProcess/StartAuxiliaryProcess). - Signal handlers (usually delegated to
interrupt.chelpers). - Shmem region if it exchanges data with backends (via
ShmemInit). - Consider whether it should restart on crash (postmaster's
HandleChildCrash).
Note: for most "run something periodically" use cases, a bgworker is preferable — less core code to touch, extensions can add without a core patch.
Add a signal / interrupt reason
- New
PROCSIGNAL_*constant insrc/include/storage/procsignal.h. - Signal-dispatcher
procsignal_sigusr1_handlercase instorage/ipc/procsignal.c. - Flag +
ProcessInterruptscase intcop/postgres.c. - Setter helper if the reason is per-target-backend (e.g.
procsignal_ProcSendSignal(pid, PROCSIG_...))`. - Docs for what "kills" or "cancels" this new interrupt cause.
Pitfalls
- Fork copies memory but not open file descriptors semantically — a
pallocbefore fork is fine (COW), but adsm_attachbefore fork is not (the child would double-detach). This is why aux processes and bgworkers do shmem init in their own Main, not inherited state. - Signal handlers cannot log via ereport — that's
elog(LOG, ...)calls internally allocating inErrorContext, which is not async-signal-safe. Set a flag, return, let the main loop pick it up viaCHECK_FOR_INTERRUPTS. InitPostgresreads pg_authid + pg_database + role/db GUC settings BEFORE the user has issued any SQL — extensions in_PG_initneed to be careful about assuming database context is complete.shared_preload_librariesvssession_preload_librariesvslocal_preload_libraries— different postmaster/backend load points; different capabilities (shmem allocation is only possible fromshared_preload_libraries).- Bgworker signal setup — a bgworker inherits SIG_IGN/SIG_DFL from postmaster. It MUST reset signal handlers in its Main via
pqsignalcalls before doing any interruptible work. proc_exitvs_exitvsabort—proc_exit(0)runs registered callbacks (releases locks, detaches shmem, closes files);_exitskips them;abort(PANIC) triggers a cluster-wide restart. Never call_exitin a place that's holding a lock.EXEC_BACKENDbuilds are Windows-native but also used for testing on Unix — a patch that "works" on your Linux dev box may still fail EXEC_BACKEND CI because the fork-then-exec path is different. Test with-DEXEC_BACKENDlocally when touching startup code.
Related corpus
- Subsystems:
tcop(the query loop side),main(backend entry),libpq-backend(auth handshake),access-transam(WAL / xact IDs, needed at startup). - Idioms:
background-worker-startup,apply-worker-loop,process-utility-hook-chain,abort-transaction-cleanup,crash-recovery-startup. - Data structures:
pgproc-fields(the per-backend PGPROC slot in shmem). - Scenarios:
add-startup-hook,add-new-bgworker. - File docs: 20 files under
knowledge/files/src/backend/postmaster/+src/backend/tcop/.
Corpus-chain shortcut
python3 scripts/corpus-chain.py --scenario add-startup-hook
python3 scripts/corpus-chain.py --file src/backend/tcop/postgres.c
python3 scripts/corpus-chain.py --file src/backend/postmaster/postmaster.c
Third command in particular surfaces the full 20-file neighborhood of the postmaster tree.
Boundary
Use this skill for backend/aux/bgworker/postmaster lifecycle questions.
Don't use for:
- libpq / client-side driver — that's
src/interfaces/libpq/; different codebase, different lifecycle. - Individual SQL commands' handling — that's
tcop(dispatch) +commands/(per-statement). - GUC config loading — use
gucs-configskill; touches lifecycle but focused on the config side. - Extension
_PG_initdetails — usebgworker-and-extensions; this skill covers the invocation timing, not the extension-authoring surface.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.