Free learning / 40-minute concurrency proof lab
Coalesce cache misses without sharing the wrong promise
Make callers share one in-flight build, then challenge leader failure, a real waiter timeout, changed source input and separate workspace keys. Keep each caller's evidence distinct.
No account needed. Use your own copy for writing and keep the original as a reference.
Engineering lab library / Run locally
Choose your next agent lab.
Start with the durable workflow reference, then choose the boundary you want to test. Open its walkthrough for setup, commands and expected observations.
Synthetic records. No provider key or model inference. Runs locally with the standard library.
Start here / Python 3.11+ · Standard library
In-flight sharing, failure cleanup and waiter deadlines
Compare duplicate builds with joined work, preserve a waiter's timeout and clear a failed leader before a later call.
Python 3.11+ · Standard library · Owned threads · No network, model or database
Original LockedIn Labs workshop · 40 minutes. Python 3.11+ standard library. Follow function calls, dictionary keys and exceptions first. The program starts owned threads against synthetic in-memory inputs, holds their builders with explicit events, and joins every worker before reporting completion. No network, model, credentials, files, database or service deployment is involved.
The application caching guide introduces the reuse contract. This workshop executes a different question: what happens when callers ask for the same work before the first build finishes?
1. Name the unfinished work
A cache stampede is overlapping misses causing repeated work for the same candidate. Request coalescing, sometimes called singleflight, lets callers join an existing in-flight computation for a suitable key. The first participant starts the work; a waiter observes its eventual result or failure.
The Go singleflight package documents a practical duplicate-call suppression API. This exercise implements its own small Python test fixture; it does not run that package or claim the same complete API. It shares unfinished work only, retaining no completed-result cache. Registering an in-flight operation, caching its completed output and checking whether that output is valid now are separate jobs.
A Python Future represents a completion that can return a value or raise an exception. A bounded result(timeout=...) wait can raise TimeoutError while the work remains unfinished. Python's Future documentation separates waiting from cancellation. The fixture constructs Futures and manages their state as executor-like teaching machinery. In application code, use a maintained concurrency implementation rather than copying this small manager.
2. Follow one registration and two waits
The registry key is a tuple: supplied workspace, document and renderer version. Default callers both ask for North's guide-7 through renderer-1. These are fixture labels chosen by the driver, not authenticated callers or real tenants.
In FlightGroup.join, one lock covers finding an existing Future and registering a new one. The worker captures the source value under the input lock, then waits on a release event. The driver registers all callers while the build is held. This provides a reproducible overlap without using a sleep to guess which thread ran first. Thread scheduling still varies; do not infer a general ordering or CPU parallelism from this selected interleaving.
The visual explains six independently reset cases. It does not execute Python inside the browser. Predict both the build count and each caller's observation.
UNDERSTAND THE SYSTEM
Share one unfinished build without borrowing its guarantees
These six frames explain owned Python threads from the downloadable lab. Gates hold the build while callers join; a real bounded wait triggers the timeout case. The browser does not run the threads or measure service performance.
Watch one example, step by step.
Read the full worked example
- 011 · Two misses can start the same work twice
The naive case deliberately starts both builders before releasing either. Both capture the same revision and return values; the observed build count is two. Coalescing registers one Future and lets the overlapping caller join, so its first wave starts one build.
ACTUAL THREAD EXECUTIONS: Naive first wave 2 · coalesced first wave 1
- 022 · In-flight sharing is not result caching
After the singleflight wave, the registry is empty. The fixture explicitly makes another same-key call. It starts another build and is not marked shared. Total builds become two. A separate completed-result cache needs its own reuse and publication contract.
LATER INDEPENDENT CALL: Pending keys 0 · later shared false · total builds 2
- 033 · A failed leader must release the key
The held leader raises injected_builder_failure. Both callers observe builder-error, the registry becomes empty, and one explicit healthy later call starts fresh work. That later value is not a successful original attempt or an automatic retry loop.
SHARED FAILURE + CLEANUP: Both caller errors · pending 0 · later value
- 044 · A waiter timeout leaves the builder running
The second caller's actual Future.result wait times out before the driver releases the builder. The shared work is still running. After release, the fixture observer can see a value, while the timed-out caller's recorded outcome remains wait-timeout.
ACTUAL BOUNDED WAIT: Caller 2 wait-timeout · builder still running
- 055 · Sharing does not refresh captured input
The builder captures revision 1. The driver then changes the source to revision 2 before releasing it. Both callers receive the captured revision 1 from one build. Fewer executions have established neither current source validity nor publication safety.
CONTROLLED SOURCE CHANGE: Builds 1 · returned r1 · current source r2
- 066 · Different workspace keys need different work
North and South both request guide-7 with renderer-1. Their full keys differ, so two builders start and each returns its own supplied workspace content. These are fixture labels, not authenticated tenants. Key separation is necessary here but cannot establish authorization.
TWO DISTINCT KEYS: North build 1 · South build 1 · both unshared
Read the connections in plain English
- 01Overlapping callers → In-flight registry
Both callers ask for the same trusted fixture key while the build is unfinished.
- 02In-flight registry → Owned builder
Only the newly registered work starts an owned builder in the coalesced case.
- 03Owned builder → Build completion
Publish one captured value or one explicit builder exception.
- 04Build completion → Overlapping callers
Each observation can receive the value, receive the failure or stop waiting at its own deadline.
- 05Build completion → Captured candidate
Keep source revision and scope with the result instead of treating shared completion as validation.
- 06Captured candidate → Receiving contract
Inspect exact captured input, current source and the intended receiving task.
- 07In-flight registry → Receiving contract
Completed entries disappear; another later call is independent work.
Check your understanding: Does a waiter's timeout prove the shared builder was cancelled?
No. The fixture observes the timeout while the build remains held and running. It then releases the worker and records a separate completion observation. The original caller outcome stays wait-timeout. Dependency cancellation and shared-work ownership need their own explicit policy and evidence.
3. Run six owned-thread cases
Download and read the runner. Keep an unchanged reference. Write predictions before reading its JSON:
python3 --version
python3 fde-cache-coalescing.py > coalescing-two.json
python3 fde-cache-coalescing.py --callers 3 > coalescing-three.jsonThe --callers argument accepts two or three. Separate-keys always uses two workspaces. Each case creates a fresh input set and registry. Gates and waits are bounded; the report is printed only after all owned builder threads join. These are actual function executions, not simulated arrival counts.
| Case | First-wave builds with two callers | Observation to defend |
|---|---|---|
naive | 2 | Both misses execute the builder before either completes |
singleflight | 1 | Second caller joins; a later same-key call starts build 2 |
leader-error | 1 | Both receive the injected failure; key clears and an explicit later call succeeds |
waiter-timeout | 1 | Caller 2 stops waiting while shared work remains running |
source-changed | 1 | Captured r1 is returned although the source has advanced to r2 |
separate-keys | 2 | North and South start separate work despite the same document ID |
joined_existing records which registration reused an existing Future. It is not an authorization verdict or cache-hit rate. before_release describes the held first wave. after_first_wave precedes any separate later call. captured_builds retains the actual captured key and source body for each build. The event log orders registration/join/completion observations under one local lock; the captured inputs supply the separate build evidence.
4. Read the completion without erasing an earlier failure
Keep this ledger beside your predictions:
singleflight:
joined_existing: [false, true]
before_release.builds_started: 1
after_first_wave.pending_keys: []
later_call.shared: false
later_call.builds_started: 2
leader-error:
caller_outcomes: builder-error, builder-error
first error: injected_builder_failure
after_first_wave.pending_keys: []
later_call.result.state: value
waiter-timeout:
caller_outcomes[1].state: wait-timeout
before_release.builder_still_running_after_waiter_timeout: true
fixture_completion_observer[1].state: value
caller_outcomes[1].value: null
source-changed:
captured_builds[0].input.revision: 1
caller_outcomes: revision 1, revision 1
source_after.north.revision: 2The timeout case executes a real short Future.result wait while the builder is held. The driver releases the worker only after observing TimeoutError. The timeout duration is a fixture setting, not a service latency measurement or a universal recommended deadline. The builder does not become cancelled because that wait ended.
After release, the fixture driver can inspect completion independently. It keeps that evidence in fixture_completion_observer; it does not rewrite the timed-out caller's caller_outcomes entry. A late response in a deployed system also needs an explicit receiving and reconciliation contract. Successful background work does not prove that the original caller received or accepted a result.
The failure case shares the authored RuntimeError with all first-wave callers. The registry removes completed work, including failure, before delivering its result. A separate healthy call then starts fresh work. It is deliberately one explicit new attempt; the program has no recursive retry policy. Permanent retention of a failed Future would strand later requests on a poisoned entry.
The fixture copies values when recording caller observations so one report does not mutate another's evidence. It is not a general mutable-object isolation API. Production sharing also needs an agreed result ownership/immutability contract.
5. Change the burst and challenge the boundary
With three callers, naive starts three builders and singleflight starts one first-wave builder. Both extra callers join in the coalesced case. The later independent singleflight call still makes total builds two. Leader-error now produces three first-wave errors. These execution counts establish no throughput, latency, model-token saving or hardware sizing result.
Use focused comparisons when receiving the work:
python3 fde-cache-coalescing.py --case waiter-timeout --callers 3
python3 fde-cache-coalescing.py --case source-changed
python3 fde-cache-coalescing.py --case separate-keysIn the three-caller timeout case, caller 2 keeps its timed-out observation; caller 3 receives the value after release. One caller ending its wait does not make the other waiter's decision for it. Cancellation policy must distinguish ending an individual wait, stopping shared work and cancelling a downstream operation. This exercise induces only the first of those.
Source-changed shares captured revision 1 with both callers after the driver advances the current source to revision 2. Coalescing did its narrow job: one build. It did not refresh input or make publication correct. Continue with the late-publication proof to inspect an authoritative publication fence. Current resource authorization still requires a separate trusted check before disclosure.
Separate-keys keeps North and South work apart. A document ID alone would not define this fixture's result. In a real application, derive scope in trusted code and identify every input that changes the result. Matching labels do not authenticate a workspace, and coalescing is not a tenant-isolation test.
The registry lives in one process. A second process or replica has its own registry; this lab supplies no cross-process suppression, durable lease, crash recovery, distributed lock or restart evidence. It also supplies no production admission bound on distinct keys, dependency execution timeout or resource authorization. The driver limits its own workload to two or three callers, and releases/joins its workers. Do not deploy this teaching manager.
6. Receive it as a delivery pod
Rotate three roles across another run:
- Builder: preserve runtime, exact command, captured input and the event log. Identify the registration lock, cleanup boundary and one intentional failure.
- Receiver: run the three-caller burst without author takeover. Explain one joined call, one failed or timed-out caller and one later independent attempt.
- Application/platform owner: name trusted key derivation, current validity, wait ownership, dependency cancellation, distinct-key admission and the actual process/replica boundary. Choose one bounded receiving-environment experiment.
Keep individual observations and assistance separate from the team's explanation:
Learner / run / runtime / exact command:
Prediction before the run:
Requested key / joined existing work:
Actual first-wave builds / later builds:
My caller outcome / independent completion evidence:
Captured input revision / current source revision:
My contribution / assistance received:
Changed caller count / prediction / observed result:
Receiving action actually attempted / assistance:
Unresolved production boundary / accountable owner / next bounded experiment:A receiver might next test an approved adapter with representative synthetic requests, one unavailable dependency and a separately defined wait policy. Document who owns each unresolved request after its caller stops waiting. Do not test real customer work merely because this local exercise completes.
7. Defend the mechanism in an interview
Explain: Why is singleflight not a cache? Use the empty post-wave registry and the later independent build as evidence.
Debug: A waiter times out, but another caller succeeds. Identify their shared work, separate observations and actual stop boundary before recommending a retry.
Design: Specify a trusted result key, current source/access checks, bounded waits, failure cleanup, mutable-result ownership and distinct-key admission. Explain the additional evidence needed across replicas and process failure.
Primary references checked 28 October 2026: Go singleflight and Python concurrent futures. The Python fixture, comparisons, visual frames and receiving exercise are original instruction. No Go package or production cache is activated.