Every “optimization” we shipped seemed to win in micro-benchmarks and then do nothing in production. We were tuning the wrong things — and worse, we had no way to tell which things were wrong. The fix was not another clever trick; it was a discipline, and the discipline earned its name the hard way.
The moment it clicked was a 10 TB data migration. The content-addressed write path was allocating 2.4 KB per object just to hash the object’s contents and render that hash as hexadecimal text. At 50k objects per second that is 120 MB/s of garbage, and the garbage collector’s pauses reached 40 ms — an eternity next to a latency target in the low milliseconds. The “fast” hashing was not fast once the collector got involved.
The mental model is one sentence: in a hot path, allocation is latency. A garbage-collection pause is part of your p99 whether or not you wrote it down.
A few terms, because the rest of the post leans on them. An allocation is memory taken from the heap, the shared pool the runtime manages. A heap escape is when a value that could have stayed on the function’s stack is pushed onto the heap because something outlives the call. Garbage collection (GC) is the runtime reclaiming that heap memory, and its pauses are time your request spends waiting. A hot path is code that runs on nearly every request. A syscall is a call from the program into the operating-system kernel; each one pays a context-switch cost.
docs/CORE/STANDARDS.md codifies the two engineering mindsets; this post is the
⚡ Bolt side — measured, profiled, allocation-light work on hot paths.
Why the Obvious Solutions Failed
Pool the hash buffers. Reusing a buffer through a pool sounds free, but the pool’s mutex becomes a contention point at 50k operations per second — more expensive than the allocation it removes. Worse, pooled objects survive the young generation of the collector and get promoted to the old generation, which produces longer pauses, not shorter ones.
| |
Cache the hex strings. Content-addressed storage means every object’s hash is effectively unique, so a cache would have a near-zero hit rate while growing without bound.
| |
Call the standard encoder and accept its destination buffer. The standard hex encoder writes into a destination slice you have to allocate first, so the allocation just moves rather than disappearing.
| |
The Solution — Three Patterns With Annotated Code
1. Zero-Escape SHA-256 and Hex Encoding
A stack array lives and dies with the function call. If we hand the hashing and encoding routines a slice backed by that array, their output never touches the heap.
| |
hashBuf[:0] and hexBuf[:0] are the trick: each creates a slice with the
array’s capacity but length zero, backed by stack memory. Sum and Encode
append into that capacity, and the resulting slices point into the stack array —
so the compiler can prove there is no heap escape.
2. Deadline Amortization — Cut SetDeadline ~98%
A deadline is the point at which the network layer gives up on a connection. Resetting it before every read and write is the safe, obvious thing to do — and it costs two kernel syscalls per chunk. We amortized the cost by setting one absolute deadline per phase of the connection instead.
| |
Result: from 200 SetDeadline calls per second per connection to 3. The
syscall overhead all but vanishes, without weakening the Slowloris defenses —
the phases still bound how long a stalled client can hold a slot.
3. Combined Metadata Reads
Our embedded key-value store exposes a view: a read-only, snapshot-consistent transaction. Each view takes a lock. We were opening three separate views per write to read three buckets — three locks, three snapshots — when one view can read all three.
| |
Result: three times fewer transactions and lock acquisitions, and a third of the contention.
The Measurement Contract (Non-Negotiable)
No “it’s probably faster” claims. Ever.
| |
benchstat is the standard Go tool that compares two sets of benchmark runs and
tells you whether a difference is statistically real or just noise. A
regression gate turns that comparison into a build condition: if a branch
allocates more or runs measurably slower than the base, CI fails and the merge
stops. A profile baseline is a saved CPU or memory profile that every later
change is judged against.
| Requirement | Tool | Gate |
|---|---|---|
| No allocation regression | benchstat | CI fails if allocation delta > 0% |
| No latency regression | benchstat | CI fails if ns/op delta > 5% |
| Profile baseline | go test -cpuprofile | file-based profiles only |
| History tracking | checked-in benchmark history | per-benchmark deltas over time |
The “file-based profiles only” rule is a security decision, not a convenience one: a live profiling endpoint on a server with no authentication is a remote-code-execution-class surface, so we never expose one on the data path. The exact rule number and the paths live in the References section.
⚡↔🛡 Trade-off Posture
Bolt optimizes inside hot paths but never at the expense of invariants:
| Invariant | Never Compromised |
|---|---|
| Verify-on-read | stays ON (see 007) |
| Bounds checking | all inputs validated, no unsafe casts |
| Integrity / crypto / placement core | compiled-in, auditable |
Principle: speed comes from removing waste, not skipping checks.
When NOT to Apply Bolt Patterns
| Pattern | Don’t Use When |
|---|---|
| Stack-buffer formatting | low-frequency paths (startup, config, admin APIs) |
| Deadline amortization | per-request deadlines are required (multi-tenant isolation) |
| Combined reads | the buckets live on different storage backends |
| Zero-escape hashing | non-hot paths, or when the hash must escape (e.g. returned to a caller) |
References / Dig deeper
- The two mindsets: docs/CORE/STANDARDS.md — ⚡ Bolt and 🛡 Sentinel.
- Measurement gate: 025: The Benchstat Gauntlet.
- Profiling baseline: 042: Perf Profiling Baseline.
- Integrity and verify-on-read: 007: At-Rest Integrity and GC.
- Transport: 003: TCP → QUIC.
- Applied patterns: 047, 048, 049, 045, 036.
- Benchmark harness and history: the CI benchmark-compare workflow,
docs/REFERENCE/PERFORMANCE.md, and the checked-in benchmark history data file. - The networked-profiler prohibition is recorded as Rule 75 in the steering
rules; the profile-baseline spec is
openspec/changes/perf-profiling-baseline.