Skip to content

docs(r09): complete Part 10-11 observability + low-latency (ch 36-40) - #9

Merged
patterncatalyst merged 21 commits into
mainfrom
docs/r09-observability-perf
Jul 22, 2026
Merged

patterncatalyst merged 21 commits into
mainfrom
docs/r09-observability-perf

Conversation

@patterncatalyst

Copy link
Copy Markdown
Owner

Completes every chapter and example before the capstone (41).

Chapters authored

  • 36 /proc, /sys, and the agent (sysagent) — Observability
  • 37 the USE method in 60 seconds (loadmix) + Figure 37.1
  • 38 OpenTelemetry to LGTM (lsp-otel) + Figure 38.1
  • (39, 40 already present; 40 authored earlier this branch + Figure 40.1)

Examples completed (Go + Rust + C++ ports, verify.lua, gated on real environments)

  • 37 loadmix — Go + Rust ports (C++ was reference); PASS 50/50 ×3 on systems-target (vm mode).
  • 38 lsp-otel — real verify.lua (queries Prometheus + Tempo through Grafana), C++ + Rust ports (Go was reference); PASS 15/15 ×3 on the live LGTM stack (local mode). Metrics land in Prometheus, traces in Tempo, per-language service.name.
  • 40 chatterd-fastpath — verify.lua + Go + Rust ports; PASS 46/46 ×3 on systems-target.

Verification

Each example gated this session with the runner (lab up / LGTM up): 36 PASS×3, 37 PASS×3, 38 PASS×3, 40 PASS×3. scripts/validate.py: OK.

Notable findings recorded in-tree

  • Go's runtime.NumCPU() returns the affinity-mask size — a taskset-driven "count lies about its container" bug (ex 40); fixed with a /sys read.
  • Shutdown diverges by runtime: non-SA_RESTART EINTR (C++/Rust) vs netpoller close-the-fd (Go) in ex 40; SIG_IGN inheritance for background jobs in ex 38's C++ port.
  • OTLP/HTTP exporters need the full /v1/{traces,metrics} URLs; each SDK's default logging must be silenced to hold the output contract (ex 38).

Examples 36-41 scaffolded and registered in manifest.yaml.
Complete: 36 (tri-language), 39 (tri-language + chapter).
Partial: 37, 38, 40, 41 — stub languages and verify.lua remain.
Diagrams present for 36 and 39 only.
Behavioral contracts derived from the C++ reference in each example,
9 sequenced steps (VM steps serial, no concurrent cargo/cmake builds),
13 acceptance criteria, 8 risks.

Checkpointed before execution so an interrupted session does not lose
the plan.
Bans lie/lies/lying when the subject is the work, the method, or the
reader; keeps it when the subject is a machine reporting a wrong value
(ch07 mtimes, ch34 hardware_concurrency), which is accurate idiom.

Adds the identifier guidance: name a deliberately-bad variant after its
defect, never --lie, since flag names propagate into all three language
sources, verify.lua, recorded chapter output, and the example slug.
S1/S2 of batch 1. Captures exact stdout/stderr/exit for every
invocation the verify.lua files will assert against, on systems-target
(2 vCPU, kernel 6.19.10-300.fc44).

Calibration: ex40 fastpath beats naive by 1.42-1.49x across three
independent bench runs (stable, never inverted); ex37 --resource mem
exits 0 without OOM; analyze samplers confirmed concurrent (10.37s
wall for --seconds 10, not ~50s).

Records a suspected saturator bug in the ex37 C++ reference, pending
verification.
…ntly

std::jthread's destructor calls request_stop() *before* join(). Each
sat_* function spawned its workers into a vector whose scope closed
immediately after the spawn loop, so every worker saw stop_requested()
on its first iteration and bailed in milliseconds -- the deadline check
in each loop was dead code and --seconds N had no effect.

Adds sleep_until(deadline) inside each pool scope so the destructor's
stop request becomes the intended post-deadline signal. Affects all
four saturators (cpu, mem, io, net).

Verified on host: --resource cpu|io|net --seconds 3 now takes 3.00s
wall (was milliseconds), with ops rising from near-zero to 2.9e10
iterations / 86k writes / 7.6M packets respectively.

Also drops a banned word from the file header comment.
Supersedes the pre-f2b2dba ex37 captures, which recorded the broken
instant-exit behavior. ex40 section untouched.

Timings now honor --seconds: cpu 5.002s, io 5.012s, net 5.207s for
--seconds 5 (was 0.007s/0.058s/0.220s). Load attribution is stable
across 3 attempts -- busy_pct 1.34 idle to 100.00 under load,
run_queue 3.00 to 7.00-8.00, Utilization fired=true, verdict=cpu with
ratio 1.75-2.00, well clear of the proposed gate.

mem re-tested under real sustained pressure: no OOM, guest stayed
responsive, swap recovered within ~1s. New nuance -- mem can overrun
its requested duration (8s -> 12.6s) because the inner touch loop only
checks stop between passes, so verify.lua needs a generous upper bound
for mem specifically.
Sets published: false on the two r09 batch-1 working files (execution
plan and raw golden-capture dumps) so they stay in git for
resumability without rendering as public pages.

Does NOT exclude the _plans collection wholesale: iteration-plan.md is
reader-facing and linked as "Roadmap" from _includes/footer.html:14
and _layouts/part_index.html:40, so dropping the collection would 404
both. Records that constraint in CLAUDE.md along with the
published: false convention for future working plans.
Replaces the 17-line scaffold with 379 lines / 28 assertion call sites
(50 runtime checks): error shapes, saturate mode for cpu/io/net/mem
with worker-formula and ops and elapsed-duration bounds, the full
25-line analyze skeleton in order, 3-attempt load attribution, and
sampler-concurrency wall-clock sanity.

Gated on the C++ reference: PASS 50 / FAIL 0 on systems-target.
Negative control -- staging /bin/true as app -- correctly fails
PASS 9 / FAIL 41, so the suite is not satisfiable by exit 0 alone.

The duration assertion is the one that would have caught the saturator
bug fixed in f2b2dba.
Replaces the hello-syscall template check with behavioral assertions
against systems-target: error shapes (exit 2 + usage), out-of-range
--pin and measure connect/IPv4 errors (exit 1), the real
run-latency-bench.sh, and SIGINT shutdown per server.

Pinning is proven at the kernel level by reading Cpus_allowed_list from
/proc/<pid>/status in the driver rather than trusting the program's own
printed line. The perf gate compares median-of-three p50 per variant
with a conservative 0.90 factor.

Verified on systems-target: cpp PASS 46 / FAIL 0, with
median(naive p50)=11287 ns vs median(fastpath p50)=7580 ns (1.489x),
inside the 1.425-1.488x golden-capture band the threshold was
calibrated against.

Go and Rust remain the new-example.sh scaffold and fail this verify
until ported.
Ports the 577-line C++ reference to Go with byte-identical observable
behavior: same usage text, same "app: ..." lines, same percentiles_ns
format. Verified on systems-target, PASS 28 / FAIL 0, median(naive
p50)=14818 ns vs median(fastpath p50)=9748 ns (1.52x).

Two deliberate divergences from the C++ port, both documented in the
file header:

  - Shutdown: the C++ port uses a non-SA_RESTART SIGINT handler to break
    a blocking read with EINTR. Go's netpoller parks the goroutine off
    the syscall, so EINTR never arrives; instead a signal.Notify
    goroutine sets the stop flag and closes the listener/conn to unblock
    the parked read (the shutdown(2)-driven wakeup, Go-idiomatic). The
    accept loop carries a 200ms deadline mirroring the C++ poll timeout.

  - Range check: runtime.NumCPU() returns the affinity-MASK size, so
    under the bench driver's `taskset -c 1` it reports 1 and rejected a
    pin to CPU 1 as out of range. Replaced with onlineCPUs(), which
    reads /sys/devices/system/cpu/online to match the C++ port's
    sysconf(_SC_NPROCESSORS_ONLN) system-wide count. A count-lies-about-
    -its-container bug hit via taskset rather than a cgroup.

Rust still the scaffold; ported next.
Ports the C++ reference to Rust with byte-identical observable behavior:
same usage text, same "app: ..." lines, same percentiles_ns format.
Verified on systems-target, PASS with median(naive p50)=12063 ns vs
median(fastpath p50)=8578 ns (1.41x).

This is the port that follows the C++ original most literally, because
it can: Rust's std makes real blocking syscalls (no netpoller parking
work off the syscall), so the C++ technique -- a non-SA_RESTART signal
handler that lets a blocking read/poll return EINTR so the loop notices
the stop flag -- works verbatim, where Go needed a close-the-fd wakeup.
The servers are built on raw fds via nix (OwnedFd as the RAII Socket)
rather than std::net, because std's accept and read_exact swallow EINTR
internally, which would defeat the signal this example is about.

Pinning is a single sched_setaffinity(0, ...) -- the process is
single-threaded (tid == pid), so it both pins the hot loop and shows up
in /proc/<pid>/status. Uses sysconf(_SC_NPROCESSORS_ONLN) for the range
check to match C++ and avoid the taskset-masked count the Go port had
to work around.

Deps: dropped the hello-syscall scaffold's rustix/anyhow/core_affinity;
nix now carries socket/net/poll/sched/signal/fs. All three languages of
example 40 now pass the vm-mode verify gate.
Adds a Port findings section to the stub-completion plan capturing the
three Go divergences and the Rust verbatim-port note from S7/S8:

  1. Go SA_RESTART/netpoller shutdown (predicted, Risk 3) -> close-fd
     wakeup instead of EINTR.
  2. Go /proc thread-group-leader affinity proof (predicted, Risk 2) ->
     dual SchedSetaffinity(0) + SchedSetaffinity(pid).
  3. Go runtime.NumCPU() masked by taskset (NOT predicted) -> the range
     check rejected a pin to CPU 1 under `taskset -c 1`; fixed with
     onlineCPUs() reading /sys to match C++ sysconf semantics. This is
     the hardware_concurrency-lies-about-its-container theme reached via
     an affinity mask rather than a cgroup, and is the finding worth
     carrying into chapter 40's prose.
  4. Rust follows C++ verbatim (real blocking syscalls); raw-fd nix
     because std's accept/read_exact swallow EINTR.

Progress table: S4 verify.lua 40, S7 Go port 40, S8 Rust port 40 done;
S9 recorded as 40-complete, 37 pending. S1/S2 marked done per the
golden-capture companion plan.
Writes _docs/40-low-latency-fastpath.md to the chapter spine (hook →
Figure 40.1 → concept → how-the-code-works → errors → concurrency lens →
build/run/observe → cross-check → what-you-learned → verified footer),
with the three cross-language port findings woven into the prose:

  - The taskset/runtime.NumCPU() range-check divergence anchors a
    "count that lies about its container" section beside the pin_to_cpu
    codetab — the affinity-mask door to the same bug hardware_concurrency
    hits via a cgroup.
  - The SA_RESTART/netpoller shutdown split drives the Concurrency lens:
    C++/Rust unblock with a non-SA_RESTART EINTR handler, Go closes the
    fd because no syscall is in flight to interrupt (codetab shows the
    two handlers beside Go's trigger).
  - Rust's verbatim-C++ raw-fd rationale (std's accept/read_exact
    swallow EINTR) in the same lens.

The busy-poll thesis is proven from the kernel, not the program:
voluntary_ctxt_switches from /proc/<pid>/status moved 2->20500 (naive,
one sleep/wake per message) versus 2->3 (busy-poll) over a 20k-message
run this session — the chapter's centerpiece cross-check, captured on
systems-target. Adds Figure 40.1 (40-fastpath-datapath, paired
SVG+Excalidraw) and its catalogue entry. Status footer verified against
this session's runner PASS x3 and the real captures. validate.py OK.
Ports the 659-line C++ loadmix reference to Go with byte-identical
observable behavior: same usage text, same loadmix:/analyze: line shapes,
same worker-count formulas, same percentile/verdict output. Verified on
systems-target, PASS 50 / FAIL 0 against the unchanged verify.lua.

Saturators use goroutines + sync.WaitGroup + sync/atomic counters in
place of C++ jthread/stop_token (each worker observes the deadline
itself, so wg.Wait holds for the full --seconds with no sleep-before-join
workaround). satIO uses x/sys/unix Open+Pwrite with O_DSYNC; satNet a
net.UDPConn with SetReadDeadline. analyze shells out via
`sh -c "( env LC_ALL=C LANG=C <cmd> ) 2>&1"` (the popen subshell trick),
runs the five interval samplers concurrently, and parses every table by
column name from its own header row. go.mod/go.sum unchanged.
Ports the C++ loadmix reference to Rust with byte-identical observable
behavior. Verified on systems-target, PASS 50 / FAIL 0 against the
unchanged verify.lua.

Saturators use std::thread::scope as the jthread analogue (workers loop
on their own deadline check, so no sleep-before-join hack is needed).
sat_io uses OpenOptionsExt::custom_flags(O_DSYNC) + FileExt::write_at;
sat_net nests two scopes so the UdpSocket receiver outlives the sender
pool, mirroring the C++ join order. analyze shells out via
`sh -c "( env LC_ALL=C LANG=C <cmd> ) 2>&1"`, runs the five samplers
concurrently, and parses every table by column name.

Cargo.toml: package renamed template-hello -> loadmix, rustix replaced
with libc = "0.2" (cached; builds --offline), edition 2024 and
[[bin]] name = "app" kept; Cargo.lock regenerated.
Replaces the hello-syscall template check with behavioral assertions for
lsp-otel (local mode, requires the lsp-lgtm stack). Builds, runs serve in
the background, drives N traced requests, SIGINTs serve to flush the SDK,
then proves the telemetry LANDED in the stack -- not the program's own
claim -- by querying through Grafana's datasource proxy:

  - requests_total{service_name=lsp-otel-<lang>} >= N in Prometheus,
  - request_duration_milliseconds_count{service_name=...} >= N,
  - at least one trace with that service.name in Tempo.

Each language tags a distinct service.name so the three never collide in
the shared stack. Metrics/traces are polled with a bounded retry (SDK
batches ~2s + backend ingest lag). SKIPs when the stack is unreachable.
Verified against the Go reference: PASS 15 / FAIL 0.
Writes _docs/37-use-method-60s-analysis.md to the chapter spine (hook →
Figure 37.1 → two-modes → parse-by-column-name → how-the-code-works →
errors → concurrency lens → build/run/observe → cross-check →
what-you-learned → verified footer). Part "Observability".

Throughline: the USE method as a runnable checklist; parsing every tool's
table by column name from its own header row (robust to sysstat/procps
column reordering); running the five interval samplers concurrently so an
N-second checklist costs ~N seconds not 5N; and the payoff — a concurrent
analyze attributes a background --resource cpu saturator (busy_pct
1.34→100.00, run_queue 3→7-8, verdict cpu). Two codetabs (Row/make_row
and parse_mpstat) verbatim across cpp/go/rust; the Concurrency lens
explains the jthread request_stop-before-join fix and why Go/Rust (each
worker checks its own deadline) never needed it.

All numbers from the golden capture and this session's gate (PASS 50 /
FAIL 0 per language on systems-target). Adds Figure 37.1
(37-use-method-checklist, paired SVG+Excalidraw) and its catalogue entry.
validate.py OK.
Writes _docs/36-proc-sys-and-the-agent.md to the chapter spine. Part
"Observability". sysagent v0 is a USE-method collector that reads /proc +
/sys/fs/cgroup directly (no root, no shelled-out tools) — the deliberate
contrast with chapter 37, which shells out to the real checklist tools.

Throughline: the /proc/stat DELTA (two cumulative reads --interval-ms
apart -> one utilization %), the six sources gathered concurrently with
the sampling sleep, graceful degradation when PSI is absent
(psi_available:false, not an error), and a byte-for-byte identical JSON
schema across cpp/go/rust exposed by both `sample --json` and
`serve /metrics`. Two verbatim codetabs (the /proc/stat delta and the
concurrent-gather orchestration). Reuses the existing
36-proc-datasource-map figure. All numbers real (PASS 32 / FAIL 0 per
language, local mode; the captured sample --json/serve output).
validate.py OK.
Ports the Go OTLP reference to Rust with opentelemetry-otlp 0.32
(http-proto + reqwest-blocking-client, no tokio). Same serve/drive CLI,
same output lines, same trace structure (request → work + respond), the
requests_total counter and request_duration histogram, service.name
lsp-otel-rust. Verified on the live LGTM stack: PASS 15 / FAIL 0 —
metrics reach Prometheus and traces reach Tempo.

Two fixes over the initial draft: the per-signal OTLP HTTP exporters need
the full /v1/traces and /v1/metrics URLs (opentelemetry-otlp 0.32 does
NOT append the signal path to an explicit with_endpoint, so posting to
the bare base 404'd and the export silently failed); and dropped a
TRACE-level tracing_subscriber that flooded stderr and diverged from the
quiet Go/C++ ports (with the now-unused tracing/tracing-subscriber deps).
Builds offline from the cached crates.
Ports the Go OTLP reference to C++23 with opentelemetry-cpp 1.24.0 (OTLP
HTTP exporter), completing a crashed first attempt. Same serve/drive CLI,
output lines, trace structure (request → work + respond), requests_total
counter and request_duration histogram, service.name lsp-otel-cpp.
Verified on the live LGTM stack: PASS 15 / FAIL 0 — metrics in Prometheus,
traces in Tempo.

Signal handling mirrors Go/Rust: a sigwait() thread feeds an EventChannel
the main loop selects over alongside accepted connections. Fixes worth
recording:
  - full /v1/traces and /v1/metrics exporter URLs (the base 404s);
  - a NoopLogHandler + LogLevel::None so the SDK's default stdout/stderr
    logging doesn't break the two-line output contract;
  - SIGINT was inherited as SIG_IGN from bash's background-job disposition
    (cmd &), which pthread_sigmask+sigwait can't override — install a real
    (no-op) sigaction before blocking, before telemetry::init so the SDK
    export threads inherit the blocked mask too;
  - Histogram::Record needs the (value, Context{}) overload under this
    build's ABI v1;
  - the otlp_http static archives have a genuine link cycle — bracket the
    resolved library list with -Wl,--start-group/--end-group via a
    CMAKE_CXX_LINK_EXECUTABLE override.
Builds offline from the conan cache.
Writes _docs/38-opentelemetry-to-lgtm.md to the chapter spine. Part
"Observability". lsp-otel is a traced request/response server exporting
OTLP traces + metrics to the LGTM stack: a parent `request` span wrapping
`work` + `respond` children, a requests_total counter, a request_duration
histogram, driven by an untraced `drive` client.

Throughline: the three OTel signal types from one request; parent/child
span nesting carried by context (Go/Rust explicit context value, C++ Scope
guard); batched export with a flush-on-shutdown; and proving the export
LANDED by querying Tempo (traces) and Prometheus (metrics) through
Grafana's proxy rather than trusting the program. Two verbatim codetabs
(the handle_request span pipeline and the signal+accept event-channel
select). The Concurrency lens records the three real portability lessons
from the ports: full /v1/{traces,metrics} URLs, silencing each SDK's
default logging, and the C++ SIG_IGN-inheritance signal fix.

All numbers real (PASS 15 / FAIL 0 per language; requests_total=10 and
request_duration_milliseconds_count=10 for all three; Tempo trace tree
request→{work,respond}). Adds Figure 38.1 (38-otlp-pipeline) and its
catalogue entry. validate.py OK.
@patterncatalyst
patterncatalyst merged commit 86d050c into main Jul 22, 2026
8 checks passed
@patterncatalyst
patterncatalyst deleted the docs/r09-observability-perf branch July 22, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant