PGSimCity

title
PGSimCity
type
toolbox
summary
Browser 3D city where PostgreSQL's buffer pool, WAL, checkpointer and vacuum are walkable buildings
tags
postgresql, visualization, typescript, education, watchlist
language
TypeScript
license
Apache-2.0
created
2026-07-29
updated
2026-07-29

A PostgreSQL cluster rendered as a city you can orbit, fly through, or walk around at eye level, with the engine's subsystems as districts. Nikolay Samokhvalov's stated audience is engineers who are competent at their own job and have never operated a database β€” people who need to know why a checkpoint spikes latency, why one forgotten transaction bloats a table forever, and what synchronous_commit is charging them. It runs in the browser with no install.

The mapping is literal enough to be useful. The postmaster forks one backend per connection and never touches data. Sixteen backend processes stand in a row and their lighting is their state, idle in transaction included. shared_buffers is a plaza of 1,024 representative frames next to wal_buffers, the ProcArray, the lock table, CLOG and the buffer mapping table. Below that is an excavation β€” the data directory, where memory ends and storage begins β€” holding heap files as fields of 8 KiB pages, B-trees drawn as actual trees, TOAST, the FSM and visibility map, the OS page cache, and the disks. The WAL district runs east (walwriter, pg_wal segments, archiver, walsender), the maintenance yard west (checkpointer, background writer, autovacuum launcher and workers), and the standby sits south with its walreceiver, startup process replaying WAL, and the lag between them. An outer continuity quarter covers WAL archive, base backups, PITR, a second delayed standby, and leader lease and rejoin machinery.

Colour is semantic and never decorative: WAL amber, dirty pages red, clean pages blue, vacuum violet, checkpoints pink, background writer teal, replication orange, storage green, indexes aqua, locks red.

Things it can actually show you

The scenarios are where the model earns its keep, because they make normally invisible interactions watchable at human speed.

  • Cache thrash sets shared_buffers to 16 MiB, below the manual control's own 128 MiB floor, so the clock sweep races and backends end up writing their own dirty victims before they can read another page.
  • Long-running transaction sinks the xmin horizon blade and turns it red. Autovacuum still walks to the tables and still reports zero removable rows while the sessions table keeps bloating. Release the transaction and cleanup resumes.
  • Checkpoint storm spins up the checkpointer's flywheel, shudders through the fsync phase, and floods the WAL district with full-page writes after each checkpoint begins.
  • Slow replay pulls sent_lsn, write_lsn, flush_lsn and replay_lsn apart on the standby.
  • Setting synchronous_commit to off stops backends waiting in commit_wait, and then the page tells you what you traded.

Enter traces a single statement; picking Non-HOT UPDATE with slow playback shows where it enters the buffer pool, creates WAL, and waits to commit. T runs a 14-chapter guided tour following one connection from client through planning, caching, WAL, checkpoints, vacuum and replication. G drops you to 1.7 m and lets you walk, at which point a buffer frame that read as a single tile from the establishing shot becomes a structure overhead.

Build

three.js r185, TypeScript, Vite. three.js is the only bundled runtime dependency of the 3D app, there is no framework, and Plausible is the sole external service. npm install && npm run dev; the build output is a static bundle with no application server.

Three rules hold the source together, and they're the interesting part:

  1. world/layout.ts is the single source of truth for geography β€” anchors, table definitions, route network. No district hard-codes a coordinate another district needs.
  2. The simulation never imports three.js, and the world never mutates the simulation. They meet at SimState.
  3. Rendering carries meaning differently per theme: at night structure is matte and meaning is neon; in daylight, hue and value carry meaning without leaning on bloom.

window.PGSIMCITY exposes sim, registry, bus, rig, gfx and flows if you'd rather drive the city from the console. The separate 2D Query flow can lazy-load PGlite after an explicit click, at which point real PostgreSQL supplies parsing, plans, catalogs, buffer counters, errors and results while its plan drives the closest modelled interior path β€” the page labels the two sources separately, because PostgreSQL exposes the former and not the latter.

How much to believe

It is a model, not an emulator. No PostgreSQL source runs, and the numbers are scaled so a human can watch them. The README claims a real verification effort: three specialist review rounds checking correctness against postgresql.org/docs and the source rather than memory, every finding independently re-checked by a reviewer tasked with refuting it, and a separate audit treating buildings, adjacencies and animations as claims. A 234-test suite fails CI on red, pinning the WAL trigger point as max_wal_size / (1 + checkpoint_completion_target) at every call site, cache hit ratio as blks_hit / (blks_hit + blks_read), and the clock-sweep usage_count cap at 5.

The live site is blunter than the README. Its banner reads "Early, unreviewed prototype. It almost certainly contains inaccuracies in both the model and explanations." Two self-assessments that far apart on the same 0.x project is the thing to keep an eye on, and it's why this sits on watchlist rather than being recommended outright. Known limitation the author names: touch controls have only been verified in Chrome's mobile emulation.

postgresql is the entity page for the engine being modelled. linux-7-postgres-regression is a good pairing β€” it turns on the buffer-pool machinery this visualises, where StrategyGetBuffer picking a clock-sweep victim under a single global spinlock stops being an implementation detail and halves throughput on a 96-vCPU box. PGSimCity draws that selection loop and pins the usage_count cap in a test; the regression is what happens when the loop's critical-section assumption breaks.

Repo: NikolayS/PGSimCity, Apache-2.0, copyright 2026 Nikolay Samokhvalov. Live at nikolays.github.io/PGSimCity. Roadmap, including what is known to be wrong and what is deliberately not being done, in ROADMAP.md. Star count not recorded at ingest.