Middle · Explain · Hosted worker scopes

Why must an application-owned hosted worker create a scope per operation, and when does the completion signal become trustworthy in this fixture?

Think through your answer before opening the explanation.

Production engineering

Reveal answer and reasoning

A hosted worker outlives an individual request, so retaining a request-scoped dependency inside it can cross the wrong lifetime boundary. The actual worker in this project receives IServiceScopeFactory and creates a new async scope for each accepted item. Its result becomes successful only after that scope has been disposed. This makes disposal an observable part of the completion contract rather than an assumption based on a handler returning.

The bounded queue is still ordinary process memory, and the worker's lifetime is still tied to its host. Correct scope ownership does not create persistent jobs or make every dependency safe for arbitrary concurrent use.

WorkQueue contains a bounded Channel with capacity four, and /worker admits values from one through ten using TryWrite. ScopedWorker reads accepted items, links host and request cancellation and creates an async scope through CreateAsyncScope. It resolves ScopedOperation inside that scope, awaits the processing result, exits the await-using scope and only then completes the item's TaskCompletionSource. ScopeEvidence tracks created, disposed and live instances independently.

Two sequential requests containing three and four therefore return six and eight with distinct scope identifiers, while the settled evidence reports two created, two disposed and zero live. The actual processing delay is an implementation detail, not a timing promise.

What a strong answer covers

  • A hosted worker outlives an individual request, so retaining a request-scoped dependency inside it can cross the wrong lifetime boundary.
  • WorkQueue contains a bounded Channel with capacity four, and /worker admits values from one through ten using TryWrite.
  • A full queue can reject admission with429, and a canceled request has a different result from a completed operation.

Common pitfall

A full queue can reject admission with429, and a canceled request has a different result from a completed operation. Completing a channel during shutdown gives accepted work an opportunity to finish within the host's stop budget, but a crash can bypass that opportunity entirely. The example's processing function is tightly bounded and its scopes are not shared between items. This does not justify using a context or another scoped dependency concurrently merely because it came from DI. Queue bounds, request cancellation, per-item failure handling and durable acknowledgment must each have their own owner when extending the worker beyond its classroom behavior.

Follow-up questions

  1. Why would replacing a scoped dependency with a singleton merely to avoid disposal change correctness rather than just allocation cost?
  2. What race would appear if the TaskCompletionSource were signaled before the operation scope was disposed?

Self-review

Choose what to revisit. These marks are your own assessment and are separate from lesson completion.

Primary references

Open lesson and code ↗

← All interview questions