Open-Source Wikis

/

PostgreSQL

/

PostgreSQL

/

Architecture

postgres/postgres

Architecture

PostgreSQL is a process-per-connection relational database server. A single supervisor process — the postmaster — listens on a TCP socket (and a Unix-domain socket) and forks a dedicated backend for each client connection. All backends and auxiliary processes share a region of System V / POSIX shared memory that holds the buffer pool, the lock tables, the proc array, and other coordination structures.

This page sketches the system at three layers: the process model, the in-memory layout, and the request pipeline.

Process model

The postmaster's only job is supervision: it forks children, restarts them after crashes, and routes signals. It never touches shared memory itself, which keeps it isolated from buffer-pool corruption when a backend crashes.

graph TD
    Postmaster["postmaster<br/>(src/backend/postmaster/postmaster.c)"]
    Postmaster -->|fork| Backend1["backend<br/>(per client connection)"]
    Postmaster -->|fork| Backend2[backend]
    Postmaster -->|fork| WALWriter["WAL writer"]
    Postmaster -->|fork| BgWriter["background writer"]
    Postmaster -->|fork| Checkpointer["checkpointer"]
    Postmaster -->|fork| Autovacuum["autovacuum launcher<br/>+ workers"]
    Postmaster -->|fork| WalSender["wal senders<br/>(replication)"]
    Postmaster -->|fork| LogicalRep["logical replication<br/>launcher + workers"]
    Postmaster -->|fork| Logger["syslogger"]

    Backend1 -.->|reads/writes| Shmem[(Shared memory<br/>buffer pool, locks, proc array, WAL buffers)]
    Backend2 -.-> Shmem
    WALWriter -.-> Shmem
    BgWriter -.-> Shmem
    Checkpointer -.-> Shmem
    Autovacuum -.-> Shmem
    WalSender -.-> Shmem

The auxiliary processes are launched and respawned by the postmaster but do specialized work:

Process Source Role
Backend src/backend/tcop/postgres.c One per connection. Runs the SQL main loop.
WAL writer src/backend/postmaster/walwriter.c Asynchronously flushes WAL buffers.
Checkpointer src/backend/postmaster/checkpointer.c Periodically writes a consistency snapshot to disk.
Background writer src/backend/postmaster/bgwriter.c Writes dirty buffers ahead of evictions.
Autovacuum src/backend/postmaster/autovacuum.c Launches vacuum/analyze workers based on table activity.
Stats collector src/backend/utils/activity/ Gathers cumulative statistics (now in shared memory, no separate process since v15).
WAL sender / receiver src/backend/replication/walsender.c, walreceiver.c Streaming replication endpoints.
Logical rep launcher src/backend/replication/logical/launcher.c Spawns per-subscription apply workers.

Shared memory layout

All cooperative state lives in a single shared-memory segment created by the postmaster at startup. Major regions:

  • Buffer pool (src/backend/storage/buffer/) — fixed-size pages (default 8 KB) cached from disk. Replacement is a clock-sweep algorithm.
  • Lock tables (src/backend/storage/lmgr/) — heavyweight (relation/row) locks plus lightweight latches used inside the engine.
  • ProcArray (src/backend/storage/ipc/procarray.c) — list of all live backends with their current XID and snapshot horizons. The visibility checker scans this on every tuple test.
  • WAL buffers (src/backend/access/transam/xlog.c) — ring of pages flushed to pg_wal/.
  • CLOG / commit log (src/backend/access/transam/clog.c) — per-XID commit/abort status, paged into shared SLRU caches.
  • Shared catalog cache invalidation (src/backend/utils/cache/inval.c) — propagates DDL invalidations to peer backends.

A backend allocates from its own MemoryContext tree (see src/backend/utils/mmgr/). The top context is TopMemoryContext; queries run inside ExecutorContext and PortalContext, which are reset between statements.

Request pipeline

A client query travels through a fixed sequence of subsystems:

graph LR
    Client -->|libpq frontend protocol| Backend
    Backend --> Parser["Parser<br/>(src/backend/parser)"]
    Parser --> Analyzer["Parse analysis<br/>analyze.c"]
    Analyzer --> Rewriter["Rewriter<br/>(src/backend/rewrite)"]
    Rewriter --> Planner["Planner / Optimizer<br/>(src/backend/optimizer)"]
    Planner --> Executor["Executor<br/>(src/backend/executor)"]
    Executor --> AM["Access methods<br/>heap, btree, gin, gist, brin, ..."]
    AM --> Buffer["Buffer manager<br/>+ smgr"]
    Buffer --> Disk[(disk / WAL)]
    Executor -->|tuples| Backend
    Backend -->|results| Client

The stages:

  1. Parse. src/backend/parser/gram.y (a bison grammar) turns SQL text into a raw parse tree of Node structs (see src/backend/nodes/). parse_analyze resolves names against the catalog and produces a Query tree.
  2. Rewrite. Views and rules transform the Query (src/backend/rewrite/rewriteHandler.c).
  3. Plan. The planner enumerates access paths and join orders, costs them, and emits a PlannedStmt (a tree of Plan nodes). Top-level entry: standard_planner in src/backend/optimizer/plan/planner.c. The geometric/genetic optimizer (GEQO) handles large joins.
  4. Execute. The executor walks the plan tree pulling tuples on demand. Each plan node has matching exec code (e.g., nodeSeqscan.c, nodeHashjoin.c). Tuples come from the access methods, which read pages through the buffer manager.
  5. Storage. Pages are 8 KB blocks. Heap tuples store row data; index access methods (B-tree, GIN, GiST, SP-GiST, BRIN, hash) maintain their own page formats. All page modifications are logged to WAL before they are flushed.

Top-level dispatch lives in src/backend/tcop/postgres.c (PostgresMain, exec_simple_query, exec_parse_message, etc.). This file is the front door from the wire protocol to the rest of the engine.

MVCC and concurrency

Reads do not block writes and writes do not block reads. PostgreSQL uses multi-version concurrency control: each row version carries xmin/xmax transaction IDs, and a transaction's snapshot is the set of XIDs visible to it. Visibility checks live in src/backend/utils/time/ (e.g., tqual.c is now heapam_visibility.c) and src/backend/access/heap/heapam_visibility.c. Old row versions are reclaimed by VACUUM.

Heavyweight locks (relation, row, advisory) are managed by the lock manager (src/backend/storage/lmgr/lock.c); deadlocks are detected via a wait-for graph (deadlock.c). Lightweight locks (LWLocks) protect shared-memory data structures; src/backend/storage/lmgr/lwlock.c.

See MVCC and transactions for the full treatment.

Durability and recovery

Every change to a buffer page is preceded by a WAL record (src/backend/access/transam/xlog.c). WAL is written sequentially to pg_wal/ and fsync'd at commit (or grouped, depending on synchronous_commit). On crash recovery, the startup process replays WAL from the last checkpoint forward to rebuild the consistent state. WAL is also the substrate for streaming replication: a WAL sender on the primary ships records to a WAL receiver on the replica, which replays them.

See WAL and recovery and Replication for details.

Where to look next

You want to know about... Page
How a query is parsed and planned Parser, Planner
How the executor pulls tuples Executor
Heap, index AMs, table AM API Access methods
Buffer pool, smgr, file layer Storage
WAL records and crash recovery WAL and recovery
System catalogs (pg_class, pg_attribute, ...) Catalog
Streaming and logical replication Replication
The postmaster's startup/fork dance Postmaster and process model

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – PostgreSQL wiki | Factory