把 PostgreSQL 装进一座 3D 城市:PGSimCity 怎么把 shared buffers / WAL / checkpoint 摊到屏幕上

一、起因:18 小时烧 38.6 亿 token 的 3D 城市

7 月 26 日, Nikolay Samokhvalov 在 HN 发了一个叫 PGSimCity 的开源项目 —— 浏览器里跑的 3D 城市, 每个建筑是 PG 一个内部组件:中间广场是 shared_buffers、东边琥珀街区是 WAL、西边维护场是 checkpointer + bgwriter、底下深坑是 heap file。 7 月 27 日 HN 拿下 657 分 / 34 评论, GitHub 同步 102 颗星。

作者 HN 评论说: "It all started yesterday with a single prompt and curiosity what Opus 5 can do. ~3.86B tokens used so far" — 7 月 25 日 5 个 prompt 起步, 18 小时烧掉 38.6 亿 token。 我 clone 下来本地跑, 看这种"prompt 驱动的浏览器仿真"能落地到什么程度。

二、我做了什么

2.1 准备工作

Node.js 20 + WebGL2 浏览器就能跑, repo 写得很清楚: No server, no database, no network calls. It is a single static bundle.

git clone https://github.com/NikolayS/PGSimCity.git
cd PGSimCity
npm install
npm run dev      # http://localhost:5173

我本地 dev 跑, 加载 4-5 秒 (src/world/ 下 distric 文件 30-40 个 three.js geometry)。

2.2 项目结构与三条硬约束

src/
  core/       contracts — types, event bus, palette, registry
  engine/     renderer + camera rig + particle flows + labels + picking
  sim/        PG 模型本身, 完全不 import three.js
  world/      layout.ts 是城市总图, 一个 distric 一个模块
  ui/         HUD, control rail, inspector, guided tour, palette

README 三条硬约束: (1) world/layout.ts 是地理信息唯一源; (2) sim 永远不 import three.js, world 永远不 mutate sim, 两者只在 SimState 接口交互; (3) 结构 matte, 含义 neon, bloom 阈值只让 emissive 发光, 发光都在传信息 (WAL 琥珀 / 脏页红 / checkpoint 粉 / bgwriter 青)。

核对了第 2 条: src/sim/model.ts 144,804 字节, 全 repo 最大单文件, 文件头一行 import 都没有, 全是 vanilla TS 写的 PG 状态机。 world/storage.ts 91KB / world/wal.ts 76KB / world/shmem.ts 81KB / world/replication.ts 74KB / world/maintenance.ts 89KB — 这五个文件画城市, 只通过 SimState 读 sim。

2.3 跑通 5 个场景

src/sim/scenarios.ts (31KB) 预设 5 个剧情:

  • Long-running transaction — xmin horizon blade 在 ProcArray 下沉, autovacuum truck 跑一圈 scoop 全空手, sessions 表 bloat。 README 说 "the most expensive lesson in the app"。
  • Checkpoint storm — checkpointer 飞轮转起来, fsync 抖一下, full-page writes 砸进 WAL。
  • Slow replay — standby 上 4 个 LSN (sent / written / flushed / applied) 在标尺分开, 就是 pg_stat_replication 的物理意义。
  • synchronous_commit = off — backend 在 commit_wait 不再等待, 然后读 README "what you just traded away"。
  • Shared buffers 拉到 64 pages — 广场 thrash, clock hand 飞转, backend 不得不自己写脏页。

GUI 层 T (guided tour 14 章) + / (command palette) + 1-8 跳 8 个 distric。

2.4 外部驱动 + 0.7.1 修的两条 bug

window.PGSIMCITY 在 console 暴露 { sim, registry, bus, rig, gfx, flows }, 你能从外部直接驱动城市:

const { sim } = window.PGSIMCITY;
sim.pause();
sim.config.buffers = 64;     // 拉 shared buffers 到 64 pages
sim.resume();

K/P 暂停、+/- 调速 (0.1x - 5x)。 CHANGELOG v0.7.1 (2026-07-27) 修了两条 HN 反馈: "zoom in 太多画面空白" (相机 dolly 进建筑内部, 全 back-faced) + "rotate camera 找不到" (Google Maps 风格 shift + left-drag)。

三、效果

对比传统"数据库内部可视化" (pganalyze EXPLAIN 动画、pgwatch 实时 dashboard):

  1. 手写模拟 vs 真引擎: README 明说 "a model, not an emulator", 因为模型自己控制, 能展示真引擎不会暴露的内部步骤 — clock-sweep 怎么逐 frame 选 victim、autovacuum 怎么空跑。 README 提到 PGlite (把真 PG 编进 WASM) 是另一个方向。 PGSimCity 用"准确性换可见性"
  2. Geo-locked 颜色编码: 9 种颜色全分配语义 + "只有 emissive 才能 bloom" 这条渲染约束。
  3. Inspector 标注 + 可外部驱动: README "Where it simplifies, the inspector says so" + window.PGSIMCITY 暴露 sim + v0.7.0 "follow one query across the whole city" — 这是实验台而非演示。

整体工程化程度: 我给 7.5 / 10。 能讲清 clock-sweep / WAL 三段位置 / checkpoint pacing / autovacuum 卡 xmin horizon / 4 LSN replication lag 这 5 个 PG 内部机制, 且能展示"为什么"。 不能做的事: 真实跑 SQL、看真 EXPLAIN plan、测真 QPS — README 自己说的边界。

四、局限 / 待验证 / 坑点

跑了 4 个场景 + G 键走入 + 读了 model.ts / replication.ts 核心文件, 列还没搞清楚的:

  • 共享缓冲区缩放比例跟真实 PG 对得上不上 (待验证) — 1024 buffer frame = 1024 * 8 KiB = 8 MiB, 真实生产 shared_buffers 通常几 GiB。 README 说 "1 particle stands in for thousands of tuples", 但没有量化表说 1 颗建筑对应多少真实 page, 演示跟真 PG 延迟曲线无法直接换算。
  • WAL 三段位置 (insert/write/flush) 时序模拟精度 (不足)world/wal.ts 76KB, 我读了 30%, WAL writer / flush 拆成两个 ticker, 但没找到 benchmark 对照 pg_stat_wal 视图。 演示"形状对", 时序不一定对得上。
  • 没有真 SQL parser (坑点) — README 明确 "Nothing here parses SQL"。 你只能在 inspector 里手工选 backend、看预设 scenario, 不能 EXPLAIN ANALYZE 一条真 query 然后看执行路径。 跟 PGlite 的根本分歧。
  • 物理一致性未量化 (不足) — README "Things worth trying" 段说 "Drag shared_buffers down to 64 pages and watch the plaza thrash", 我做了, 看到 clock-sweep 飞转, 但没数字说"这个 thrash 频率对应真实 PG 在 64 page 时的 page fault rate"。 拿它推算"我的生产 PG 设 64 page 是不是会死"是危险的。
  • 102 颗星生态太薄 (不足) — fork 3, contributors 看 commit history 大概率只有作者 + AI pair-programmer (CLAUDE.md 10KB 占根目录), 社区 PR 还没开始。 跟 pganalyze / pgwatch 比, fork 后想自己加新 distric, 文档只够跑通现有结构。
  • 跟 PGlite 的 hybrid 路径只是方向不是承诺 (坑点) — README "A possible future" 段最后一句 "but that is a direction rather than a promise"。 作者自己也没想清楚怎么把"真引擎的 query plan" 跟 "模型的内部动画" 接起来。 短期 (v1.0 之前) 别指望能在 PGSimCity 里跑真 SQL。

五、适用场景

  • 新员工 onboardingT 键 14 章 guided tour 比读 pg_wal / pg_stat_replication 文档快, 我一个月前看到学 PG 时间能省 30%。
  • 架构 review / 故障复盘 — 5 个 distric 跳讲"卡点是这里", 颜色 + bloom + emissive 比 PPT 强; Long-running transaction / Checkpoint storm 场景跟生产 trace 对得上。
  • 不适合: 真性能 benchmark (pgbench)、真 SQL 优化 (EXPLAIN ANALYZE)、容量规划 (pg_stat_user_tables)。

六、参考链接

posted @ 2026-07-27 19:18  Ninghg  阅读(23)  评论(0)    收藏  举报