TT Lab
Get started
Learn Learning paths Courses

OTCA — OpenTelemetry Certified Associate

Another order’s tag came along

Continue in TT Lab

Goal

Fix seven real Python functions to pass the execution-time context along and to restore the original context after normal, exception, and cancellation outcomes. Then separate the extraction of external baggage from the per-destination propagation policy.

Why it matters

If a request value attaches to the wrong work, you will misinterpret the cause even when the data is collected. This lab runs real asyncio, ThreadPoolExecutor, and the W3C baggage propagator in a dedicated environment with Python 3.12 and OpenTelemetry SDK 1.44.0 installed. The dependencies are preinstalled in the image, so no internet download or API key is needed.

No external HTTP, DNS, Collector, or authentication server is run. warehouse.internal is a logical destination string passed to the function. Passing the assignment does not mean verification of a real network destination, authentication or authorization, loading into an external store, or comprehensive personal data protection. Among the 8 steps, whether baggage is automatically recorded in span attributes is judged on the basis of the SDK experiment in the earlier reading and the official explanation. The remaining code checks are execution observations of Context functions.

Setup and running

The working directory is /root/otca-context. Only 8 starter files that are syntactically correct but behave wrongly are prepared inside starters. Copy the relevant file into the working directory and fix it.

cd /root/otca-context
cp starters/scope.py scope.py
/opt/otel-lab/bin/python /opt/app/otca_sdk/runner.py --track context run 1

If you change the number after run, the observations and checks for that step appear. Compare the current observation with the expected value. This is not an assignment to modify the SDK or the grader, and different code with the same meaning passes if its actual behavior is right. The answer copy is run in a restricted child process and the original file is not changed. Each piece of code is limited to at most 8 seconds and the overall run to a 50-second total budget, and infinite loops and output floods are failures. Do not interpret this execution limit as a separate security isolation against malicious code. Isolation is the job of the lab Pod.

Steps

  1. /root/otca-context/scope.py — Fix with_context(value, callback) in scope.py. While the callback runs once, the baggage request must be value. Pass on the return value and the original exception as they are, and restore the caller's previous context for both normal and exception outcomes.
  2. /root/otca-context/tasks.py — start_task(coro) in tasks.py schedules a coroutine with the current request context and returns an object the caller will await. Even if the caller changes the context later, the work must read the value from the time of scheduling, and the caller's new value must be preserved.
  3. /root/otca-context/threads.py — start_thread(function) in threads.py schedules a synchronous function to run in a thread and returns an object to await. Make the worker read the request value from the moment this function was called, not from the later await moment.
  4. /root/otca-context/executor.py — submit(executor, function) in executor.py submits a function to the given ThreadPoolExecutor and returns a Future. The request value at submission time must be passed along and must not remain for the next ordinary task on the same worker. Also preserve the caller's context.
  5. /root/otca-context/requests.py — async handle(value, callback) in requests.py applies the baggage request as value and awaits an async callback once. Keep the values separate even if two requests overlap, and restore the previous context for normal return, exception, and Task.cancel alike. Pass the return value, exception, and cancellation on to the caller as they are.
  6. /root/otca-context/incoming.py — inbound(carrier) in incoming.py extracts a W3C baggage header dict against an empty OTel Context and returns an OTel Context. Do not mix in the existing local baggage, and preserve the original dict and the caller's context. Extract role=admin too at this step, but do not interpret it as an authentication success.
  7. /root/otca-context/outgoing.py — outbound(source, destination) in outgoing.py allows the strings test-east/test-west for region and web/batch for channel only when destination is exactly equal to warehouse.internal. Select into a new Context and return a W3C baggage header dict. Exclude other keys, values, and destinations and preserve the original. If no value remains, it is an empty dict.
  8. /root/otca-context/report.json — Judge the eight hypotheses in report.json as JSON booleans. The earlier seven pieces of code must also all work. Distinguish the Task creation time, deferred to_thread, the ordinary executor, cancellation recovery, baggage and span attributes, empty inbound, authorization, and the effect of the destination.

Notes

Return to the original request after the callback

/root/otca-context/scope.py: Fix with_context(value, callback) in scope.py. While the callback runs once, the baggage request must be value. Pass on the return value and the original exception as they are, and restore the caller's previous context for both normal and exception outcomes.

The return value of set_baggage and the operation of attaching the current context are different. Check whether the restore after the callback also runs on the exception path.

Preserve the request at Task scheduling time

/root/otca-context/tasks.py: start_task(coro) in tasks.py schedules a coroutine with the current request context and returns an object the caller will await. Even if the caller changes the context later, the work must read the value from the time of scheduling, and the caller's new value must be preserved.

Check the difference between returning a coroutine object and creating a Task that carries the current Context.

Fix the timing of deferred thread work

/root/otca-context/threads.py: start_thread(function) in threads.py schedules a synchronous function to run in a thread and returns an object to await. Make the worker read the request value from the moment this function was called, not from the later await moment.

When does the coroutine returned by to_thread run? Think about the step of scheduling its execution in the current context.

Do not leave context on a reused worker

/root/otca-context/executor.py: submit(executor, function) in executor.py submits a function to the given ThreadPoolExecutor and returns a Future. The request value at submission time must be passed along and must not remain for the next ordinary task on the same worker. Also preserve the caller's context.

If you copy inside the worker, it is already too late. Find a way to make a separate copy for each submission and run inside it.

Clean up overlapping requests and real cancellation

/root/otca-context/requests.py: async handle(value, callback) in requests.py applies the baggage request as value and awaits an async callback once. Keep the values separate even if two requests overlap, and restore the previous context for normal return, exception, and Task.cancel alike. Pass the return value, exception, and cancellation on to the caller as they are.

If detach happens only after a normal return, cancellation skips that line. Distinguish restoring from hiding a cancellation.

Separate the external header from the local request

/root/otca-context/incoming.py: inbound(carrier) in incoming.py extracts a W3C baggage header dict against an empty OTel Context and returns an OTel Context. Do not mix in the existing local baggage, and preserve the original dict and the caller's context. Extract role=admin too at this step, but do not interpret it as an authentication success.

Compare the default extract with an extract that explicitly specifies an empty Context. Distinguish contextvars.Context from the OTel Context.

Restrict the destination, key, and value together

/root/otca-context/outgoing.py: outbound(source, destination) in outgoing.py allows the strings test-east/test-west for region and web/batch for channel only when destination is exactly equal to warehouse.internal. Select into a new Context and return a W3C baggage header dict. Exclude other keys, values, and destinations and preserve the original. If no value remains, it is an empty dict.

If you allow only keys, other information can get into the values. If you compare the destination by prefix, also check that a look-alike host is not allowed.

Verify the behavior and the hypotheses together

/root/otca-context/report.json: Judge the eight hypotheses in report.json as JSON booleans. The earlier seven pieces of code must also all work. Distinguish the Task creation time, deferred to_thread, the ordinary executor, cancellation recovery, baggage and span attributes, empty inbound, authorization, and the effect of the destination.

Having only the report right is not enough. Read the actual observations and the official contract separately, and also fix the cause of each function's failure.