Course contents
On this page
Find out where a slow commit waits
Connect a request timeline to a failure test instead of guessing from one graph.
The shop’s purchases become slow. A dashboard shows low CPU use. That observation rules out neither lock waits nor storage waits. We need evidence tied to the request’s actual dependencies.
Metrics summarize events or resource state over time. A trace follows one operation across its steps. Profiling investigates where execution resources are spent. Verification checks whether outcomes satisfy the promised behavior, including under failures. They answer different questions.
Trace one purchase
Assume the measured request spends 2 ms executing, 18 ms waiting for a durable-log acknowledgement, and 1 ms returning the result, with no overlap in this example.
Execution
2 ms
Reads, checks, and tentative changes
Durability wait
18 ms
Waiting for the required log persistence
Reply
1 ms
Return the committed outcome
The trace suggests where to investigate, not a proven root cause. Check storage queueing, flush duration, and competing work. A controlled experiment that changes one factor can test the explanation. Optimizing a filter based only on low overall throughput would be guesswork.
Define success before counting it
A service-level indicator is a measured property such as successful purchase rate or response time. A service-level objective sets a target over a stated window. Count failures and timeouts according to the user-facing operation, rather than reporting only the fast successes.
For correctness, retain histories containing operation IDs, invocation times, responses, and relevant observed values. A timed-out write has an uncertain outcome until evidence resolves it; it should not automatically be classified as an aborted write.
Test the persistence boundary
The logging lesson says a confirmed commit must be recoverable under its stated failure assumptions. A useful test interrupts execution at different points around log persistence, data installation, and reply delivery. After restart, compare the recovered state with the allowed outcomes.
Also test failure during recovery, malformed input, exhausted memory, and I/O errors. A test harness that merely starts the process again misses whether the right records survived. Keep a simple reference model where possible, and compare the implementation’s observable behavior with it.
Change the symptom
The next incident has 18 ms of lock waiting and only 1 ms of log waiting. Should the previous storage fix be applied automatically?
Follow the new evidence
No. The same total-latency symptom can have a different cause. Inspect the lock holder, transaction duration, and competing work. The earlier explanation was conditional on the observed path, not a permanent property of all slow commits.
A dependable engine needs evidence for both speed and correctness. The measurements tell us where work goes; adversarial tests tell us whether failures preserve the contract.
SQLite’s testing account describes concrete failure-testing categories. Google’s monitoring workbook discusses operational evidence and its use.
Extended reference
The extended chapter preserves the original technical coverage, additional examples, and citations. The explanation above supplies the concepts needed for this page; the reference develops the specialist cases.
Find a section from the original chapter
- Learning outcomes
- A latency graph is an observation, not an explanation
- Define user-facing objectives
- Metrics and saturation
- Tracing the critical path
- Profiling and controlled experiments
- Correctness histories
- Crash and fault injection
- Workload verification
- Incident interpretation
- Define the measured operation before collecting metrics
- Histograms preserve more than averages
- Trace one durable write to the constrained resource
- Metrics should expose occupancy and progress
- Profiling distinguishes computation from waiting
- Load generation can hide overload
- Benchmark the envelope, including skew
- Crash testing targets persistence boundaries
- Distributed faults need histories, not only uptime
- Deterministic simulation changes debugging economics
- Alerting should protect the user objective
- Evidence and interpretation remain separate
- A fault history preserves uncertainty
- Design conclusion
- Sources and further reading