Notes / Observability / 06 Opentelemetry / 8 Context Propagation

8 — Deadline Propagation

How a client deadline must be inherited by every downstream goroutine or service call so that cancelled work stops consuming resources rather than running to completion unobserved.

Updated June 30, 2026 · §202606302351 ·

8 — Deadline Propagation

This is a fundamental concept in Go concurrency, distributed systems, and SRE/system design. Let’s break it down from first principles.


What is a deadline?

A deadline is the maximum amount of time an operation is allowed to run.

For example:

Client

   │ HTTP Request

API Server

The client says:

“I’m willing to wait only 5 seconds.”

That becomes a deadline.

Deadline = Now + 5 seconds

If the server hasn’t responded within 5 seconds, the client disconnects.


What is Context?

In Go, context.Context carries:

  • cancellation signal
  • deadline
  • request-scoped values

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

Now every function receives this context.

Handler(ctx)

     ├── Database(ctx)
     ├── Redis(ctx)
     └── ExternalAPI(ctx)

Notice the same context flows everywhere.


What is deadline propagation?

Imagine this request.

Client


API

   ├── DB Query
   ├── Cache Query
   └── Recommendation Service

The client waits 5 seconds.

If the API starts three goroutines:

go queryDB()
go queryCache()
go queryRecommendation()

Each worker must know that the request expires after 5 seconds.

Instead:

go queryDB(ctx)
go queryCache(ctx)
go queryRecommendation(ctx)

Now each worker has the deadline.

This is deadline propagation.


Why is this important?

Suppose the recommendation service takes 30 seconds.

Timeline:

0s   Request starts

3s   DB finishes

5s   Client disconnects

30s  Recommendation finishes

Without propagation:

Client

   X disconnected

Recommendation goroutine
██████████████████████████████
Still consuming CPU
Still using memory
Still holding connections

The work is useless because nobody is waiting for the result.


With deadline propagation

Every worker periodically checks

select {
case <-ctx.Done():
    return
default:
}

or simply calls APIs that respect the context:

db.QueryContext(ctx, ...)

When the deadline expires:

Client

   X timeout

Context


Cancelled

Database
Redis
HTTP Client
Workers

All stop immediately

Everything exits together.


The statement explained

The parent context deadline must be inherited by every worker goroutine/thread.

Suppose your handler creates 10 workers.

Bad:

go worker()

The worker has no idea when the request ends.

Good:

go worker(ctx)

Every worker inherits

  • cancellation
  • deadline
  • request values

What happens if a worker ignores it?

Imagine this.

HTTP Handler

 ├── Worker A
 ├── Worker B
 └── Worker C

Client leaves after 2 seconds.

Worker B ignores the context.

Worker A
Stopped

Worker B
Still running...

Worker C
Stopped

Worker B may:

  • hold a database connection
  • keep CPU busy
  • occupy memory
  • hold file handles
  • continue making external API calls

All for a request that no longer exists.

This is exactly what the sentence means:

A worker that ignores the deadline will hold resources long after the client has given up.


Real production example

Suppose a product search request.

Search API

├── Elasticsearch
├── Inventory Service
├── Pricing Service
└── Recommendation Engine

Client timeout = 3 seconds

Recommendation Engine usually takes 10 seconds.

Without context propagation:

3s

Client leaves



Recommendation still computes



CPU
Memory
Network



Result discarded

Multiply this by:

5,000 requests/second

Now you have thousands of useless goroutines consuming resources.


In Go

Bad:

func handler(w http.ResponseWriter, r *http.Request) {
    go expensiveWork()
}

Good:

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    go expensiveWork(ctx)
}

Worker:

func expensiveWork(ctx context.Context) {

    for {
        select {

        case <-ctx.Done():
            return

        default:
            doSmallPieceOfWork()
        }
    }
}

Or better yet, use libraries that already support contexts:

db.QueryContext(ctx, query)

http.NewRequestWithContext(ctx, ...)

grpcClient.Call(ctx, ...)

These operations will automatically stop when the context is canceled or its deadline is exceeded.


Why this matters for an Observability Architect

In distributed systems, you often have request chains like:

Client

API Gateway

Service A

Service B

Database

If the client times out after 5 seconds, the cancellation should propagate all the way through the call chain:

Client

API Gateway

Service A

Service B

Database

Each component should stop work as soon as it knows the request can no longer succeed. This reduces wasted CPU, memory, network traffic, and connection pool usage. It also prevents “zombie” work that can increase latency for active requests.

This propagation is one reason tracing systems such as OpenTelemetry often show spans ending early with cancellation or deadline-exceeded errors—the context carrying the deadline flows with the request through the entire distributed trace.

Metadata

DimensionDetail
AuthorAmit Singh
Scopeobservability

Local graph

Full graph →

Linked from 14 notes

Q3 Answer — Context Cancellation Leak

Worked answer to Fan-Out/Fan-In Practice Q3: diagnosing a ghost-request leak where client-visible errors look healthy but infra cost and downstream CPU are elevated.

Q8 Answer — Hierarchical Fan-Out

Worked answer to Fan-Out/Fan-In Practice Q8: budgeting a deadline across two nested fan-out levels and preventing partial failure from silently compounding across hops.

Q9 Answer — Validating Hedging and Deadline Propagation

Worked answer to Fan-Out/Fan-In Practice Q9: fault-injection, load testing, and canary comparison for validating a deadline-propagation and hedging rewrite before it reaches production.

3 — Cross-Signal Correlation

Metrics, logs, and traces are only more useful together than apart if something ties a specific instance of each of them back to the same event. The shared identifier that makes that jump possible — and what breaks when a hop in the call chain doesn't carry it.

1 — OpenTelemetry SDKs & Semantic Conventions

OpenTelemetry is a specification and an API/SDK, not a backend — the pieces that make it up, and the semantic-convention vocabulary that lets two unrelated teams' telemetry be queried the same way.

2 — Tail Latency

Why the p99/p999 request latency matters more than the average in distributed systems — a single slow dependency in a fan-out can dominate the response time even when most calls are fast.

Observability KPIs for the Fan-out / Fan-in Pattern

Observability KPIs for the Fan-out / Fan-in Pattern

10 — Hedged Requests

A tail-latency optimization that issues the same idempotent request to multiple replicas and uses whichever responds first, trading extra compute for dramatically lower P99/P999.

Head vs. Tail Sampling for Distributed Traces

The sampling decision point determines whether a trace pipeline needs to buffer spans in memory — head-based decides at trace start, tail-based decides after the trace completes.

What is Envoy

CNCF-graduated L7 proxy built at Lyft — the de facto data plane for service mesh (Istio, Linkerd's predecessor lineage) — now extending into AI traffic via Envoy AI Gateway, which reached v1.0 with a native MCP Gateway in 2026.

2 — The Signals

Metrics, logs, traces, profiles, and events — what each is built to capture, what it costs, and which question it actually answers vs. which one people mistakenly ask it.

Observability Engineering

A book-shaped table of contents for observability engineering: foundations through architecture, metrics, logging, tracing, profiling, OpenTelemetry, instrumentation, Kubernetes/cloud, data platforms, visualization, alerting, SRE integration, cost, security, platform engineering, AI-driven operations, and MAANG interview preparation — cross-linking existing prometheus/grafana-cloud/kubernetes/sre/platform-engineering notes instead of duplicating them.