When a request ends, what must a framework's container do with the components it created in that request's scope?
answer
- the scope closes, not the object
- reverse order release hooks
- borrowed resources handed back
- close after the response is written
- leak shows as pool exhaustion
basics
~20 sClose the scope: run each created instance's release hook, normally in reverse construction order, hand pooled resources back, and drop the scope's instance map. Skipping it leaks connections, which surfaces as pool exhaustion under load rather than as an error.
solid answer
~50 sEnding a request means **closing its scope**, and closing a scope is a defined sequence: the container walks the instances it created there, invokes the release hook on each one that has one, and discards the scope's instance map so nothing is retained. Release normally runs in reverse construction order, because a component built later may depend on one built earlier. It must run on the failure path too, and it must run **after the response is fully written**, which matters for streamed bodies whose data still comes from scoped components. A failing release hook should be recorded while the remaining ones still run, otherwise one bad component leaks everything created after it. When this goes wrong the first production symptom is usually pool exhaustion: the service behaves normally for a predictable number of requests and then stalls acquiring connections.
go deeper
Know that per-request components are released when the request finishes, and that this is an explicit step the container performs rather than something memory reclamation does for you.
Explain the sequence: stop resolving, run release hooks in reverse construction order, drop the instance map. Say why the error path needs the same treatment as the success path.
Tie it to production symptoms — pool exhaustion, held locks, growing memory — and know the streamed-response case where cleanup that looks correct runs too early.
Treat deterministic release as a platform property: one place that owns scope closure for every response shape, monitored pool counters, and a rule that release hooks borrow nothing and decide nothing.
## What "closing a scope" means A request scope is an instance map plus a record of what was created in it. Closing it is not garbage collection and does not depend on it: it is an explicit, ordered sequence the container performs. 1. Stop serving resolutions from that scope — nothing new may be created in a scope that is closing. 2. Walk the instances created in it that expose a release hook, and invoke each one. 3. Discard the map, so the scope retains no references and whatever it held becomes reclaimable. Only the container-created instances are the container's responsibility. Anything a component constructed by hand inside its own logic is that component's job to release, in its own release hook. ## Ordering and errors - **Reverse construction order** is the norm. Construction order encodes dependency order, so a component built later may hold one built earlier; releasing newest first guarantees nothing is torn down while something still using it is alive. It is the same discipline as unwinding a stack. - **One failure must not abort the sweep.** If a release hook throws, the container should record it and continue with the rest. The alternative leaks every instance after the failing one, and the leak grows with every request that hits the same failure. - **Release should be quick and local.** A hook that makes a network call or blocks on a lock stretches the tail of every request, because it runs on the request's own path. ## When the scope must close | Situation | Correct moment to close | What breaks if you close earlier | |---|---|---| | Ordinary buffered response | After the response is fully written | Nothing — body is already produced | | Streamed or chunked body | After the last chunk is written | The stream reads from components that were already released | | Handler threw, error response sent | After the error response is written | The error path leaks exactly when the system is already unhealthy | | Client disconnected mid-response | As soon as the framework abandons the exchange | Resources stay borrowed for a request nobody is waiting for | The common mistake is closing the scope when the handler **returns**. For a streamed body the handler returns long before the body is finished, so the scope must be tied to the completion of the exchange, not to the handler call. ## The transient-disposal trap A transient component built inside a request scope is released with that scope only if the container tracks disposables **in the scope that created them**. Some containers track them in the longest-lived scope instead, so short-lived objects accumulate for the life of the process even though everything about their registration says "short-lived". It is worth knowing which behaviour your container has before registering anything transient that owns a resource. ## What breaks when disposal is skipped 1. **Pool exhaustion first.** Connections, buffers or permits borrowed per request and never returned run the pool dry after a predictable number of requests. The service looks healthy, then every request blocks on acquisition. This is usually the earliest and clearest symptom. 2. **Locks and open units of work.** A unit of work left open holds its locks, so unrelated requests start timing out on rows they never touched. 3. **Memory growth.** Retained scope maps keep whole object graphs alive. Slower to show than pool exhaustion, and easy to misdiagnose as a memory problem in the application code rather than a lifetime problem in the wiring. 4. **Cross-request leakage.** If a scope's map is retained and later reused, one request can observe another's state, which is a correctness and privacy failure rather than a capacity one. ## What disposal is not for Release hooks are for **giving resources back**, not for doing business work. Committing a unit of work from a release hook is a common and poor design: by the time the scope closes the response is typically committed — the status line and headers are already on the wire — so a failure has no way to be reported to the client. Decide the outcome inside the request, and let the hook only release what was borrowed. ## How to verify it - Drive steady traffic and watch the pool's in-use count return to its baseline between bursts; a monotonic climb is a leak. - Include the failure paths in that traffic. Leaks live disproportionately on the error path, because that is the path with the least test coverage. - Exercise a streamed endpoint specifically, since it is the one case where correct-looking cleanup runs too early.
- Why release a scope's instances in reverse construction order?Because construction order encodes dependency order: a component built later may hold one built earlier. Releasing newest first guarantees nothing is torn down while a user of it is still alive. It is why containers record creation order per scope rather than iterating an unordered map.
- Which failure does a leaked request scope usually produce first?Pool exhaustion. A borrowed connection or buffer that is never returned drains a bounded pool after a predictable number of requests, so the service serves traffic normally and then stalls with acquisition timeouts. Memory growth follows, but the pool is the smaller bounded resource and gives way first.
- Should a release hook be allowed to fail the request?Generally no — by the time it runs the response is usually committed, so there is nothing left to tell the client. Record the failure, emit it to monitoring, continue releasing the rest, and treat a repeatedly failing hook as an operational alert rather than a per-request error.
saying these in an interview costs you the question
- Assumes automatic memory reclamation returns pooled connections without any release hook
- Closes the request scope when the handler returns, cutting a streamed response off from its resources
- Lets one failing release hook abandon the rest of the scope's cleanup
- Thinks cleanup runs only on the success path and not when the handler throws
- Uses a release hook to commit a unit of work and report failures to the client