# Chapter 4 wait-capture environment Chapter 4 wait diagrams must come from `pg_wait_tracer`'s exact capture data, not `EXPLAIN ANALYZE` timing. The capture image supplies the Linux BPF toolchain; the traced PostgreSQL instances remain the disposable plan-audit containers. Build the PostgreSQL-version-matched image from the manuscript repository: ```sh docker build -t learnyouapg-pgwt-capture -f scripts/wait_capture/Dockerfile . ``` For Citus, pass the audited Citus image explicitly: ```sh docker build --build-arg BASE_IMAGE=citusdata/citus:14.1.0 \ -t learnyouapg-pgwt-capture-citus \ -f scripts/wait_capture/Dockerfile . ``` The capture runner is intentionally not enabled by ordinary manuscript builds. It needs a Linux host with BPF and tracefs support. Docker Desktop's Linux VM can expose a target process but does not expose the scheduler tracepoints that the full-fidelity tracer needs, so it is not a valid capture host. ## The operation-card capture contract Generate the exact-capture matrix before touching the database: ```sh make capture-matrix ``` It currently enumerates 34 transcluded cards plus two published Operations reference cards: 34 single-postmaster stock PostgreSQL/pgvector captures, plus `Broadcast` and `Redistribute`, which need separate coordinator-and-worker Citus archives. Every row contains the real query (with its `EXPLAIN` wrapper removed), a unique statement marker, the required target, its raw-trace directory, and its final OTLP destination. On a prepared Linux capture host, run the stock sweep explicitly: ```sh make capture-waits-stock \ AUDIT_DATABASE=postgresql://[email protected]/plan_audit \ POSTMASTER_PID="$(head -1 "$PGDATA/postmaster.pid")" \ PGWT_ROOT=/opt/pg_wait_tracer ``` The runner starts `pg_wait_tracer --mode full` for each card, runs the marked query in a transaction that rolls back setup and data changes, retains the raw archive under `captures/chapter4/raw/<operation>/`, and exports only that marked execution into `captures/chapter4/<operation>.otlp.json`. It rejects a missing query marker, a non-exact export, or an ambiguous selection. It never rewrites a manuscript card or attaches a diagram by itself. `GatherMerge` also has an explicit capture-only scale overlay. The 10k-row plan fixture launches workers but finishes before their PID metadata can be retained. Run that one capture with `--prepare-capture-overlay`; the overlay extends `animals` to five million rows while leaving the card's literal query and operator unchanged. The five `Operations/Other` cards use tracked operator-preserving capture SQL under `scripts/wait_capture/fixtures/other_*.sql`. Their tiny manuscript queries remain the literal plan examples; the capture-only workloads make the same node observable without changing those plans. CTE Scan crosses `work_mem`, Function and Values Scan execute literal sleeping expressions, Subquery Scan retains its `OFFSET 0` boundary around measured inner work, and Foreign Scan uses a loopback `postgres_fdw` socket because `file_fdw` does not publish its child-pipe block as a PostgreSQL wait event. Recapture just this batch with: ```sh python3 scripts/wait_capture/capture_chapter4_stock.py \ --section Other \ --database "$AUDIT_DATABASE" \ --postmaster-pid "$POSTMASTER_PID" \ --pgwt-root "$PGWT_ROOT" \ --sudo ``` `Broadcast` and `Redistribute` are intentionally not sent through that command. Their capture requires an isolated Citus cluster plus one exact tracer archive for the coordinator and for each worker. Attach a Citus graph only after the coordinator's marked execution, every participating worker PID, and their overlapping execution window have been retained and correlated. A coordinator- only export is not complete distributed-operation evidence. For each completed capture: 1. Run the exact tracer (`pg_wait_tracer --mode full`) around the deterministic operation fixture on the matching PostgreSQL, pgvector, or Citus target. 2. Export the selected execution with the tracer project's `tools/export_otel.py`. Its `execution_detail` request is exact-fidelity only and preserves leader and parallel-worker lanes. 3. Commit the resulting OTLP JSON under `captures/chapter4/`, named after the operation (for example, `captures/chapter4/nested_loop.otlp.json`). This is the auditable primary evidence, not an intermediate build product. 4. Change that manifest entry to `runtime_status: wait-capture-attached`, `trace_kind: captured-wait-trace`, and add both the OTLP `capture_asset` and generated SVG `trace_asset` paths. 5. Add the SVG image line immediately after the card's literal `EXPLAIN` output. `make chapter-4` will render it and refuse missing source evidence. The renderer only accepts `capture-required` or `captured-wait-trace`; it cannot turn `EXPLAIN ANALYZE` timing, a sampled trace, or a teaching fixture into a Chapter 4 wait diagram. After the manifest points to the accepted OTLP capture, regenerate only the affected card instead of the complete ledger: ```sh make operation-trace OP=NestedLoop make operation-assets OP=NestedLoop # plan and trace together ``` Use `make list-operations` when the Make key is not obvious from the Markdown filename. Rendering is deliberately separate from this capture procedure, so rerunning either command cannot silently execute a query or replace evidence.