Series MapLesson 34 / 112
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Build CoreOrdered learning track

Timeout, Cancellation, and Backpressure

Timeout Cancellation and Backpressure

Kontrol resource pada endpoint enterprise: timeout hierarchy, cancellation, queue saturation, thread exhaustion, and backpressure for JAX-RS production services

6 min read1009 words
PrevNext
Lesson 34112 lesson track22–61 Build Core
#timeout#cancellation#backpressure#resilience+4 more

Part 034 — Timeout, Cancellation, and Backpressure

Fokus part ini: memahami bagaimana service JAX-RS mengontrol waktu, resource, dan overload. Endpoint yang benar secara functional masih bisa merusak production jika tidak punya timeout, cancellation, backpressure, dan queue discipline.

Production failure sering bukan karena logic salah, tetapi karena work yang terlalu lama dibiarkan hidup.

slow dependency -> blocked thread -> queue grows -> memory rises -> latency rises
               -> retries increase -> downstream gets worse -> service collapses

Senior engineer harus mendesain endpoint dengan asumsi:

- dependency bisa lambat
- client bisa disconnect
- request bisa duplicate
- thread pool bisa penuh
- queue bisa menumpuk
- Kubernetes bisa kill pod
- retry bisa memperbesar traffic
- timeout di satu layer bisa bertabrakan dengan timeout layer lain

1. Core Mental Model

Setiap request memakai resource terbatas.

flowchart TD A[HTTP Request] --> B[Connection] B --> C[Container Thread] C --> D[JAX-RS Resource] D --> E[Application Service] E --> F[DB Connection] E --> G[HTTP Client Connection] E --> H[Kafka Producer Buffer] E --> I[Executor Queue] E --> J[Memory / Heap]

Jika request tidak dibatasi oleh timeout dan cancellation, ia bisa menahan resource lebih lama dari nilai bisnisnya.

Principle:

Every unit of work must have a deadline, a cancellation path, and a bounded resource footprint.

2. Timeout Is a Contract, Not Just a Number

Timeout menjawab:

How long is this work still worth doing?

Bukan hanya:

How long before the library throws exception?

Timeout harus konsisten dengan:

- client expectation
- gateway timeout
- server/container timeout
- application deadline
- DB statement timeout
- HTTP client connect/read timeout
- Kafka send timeout
- executor queue wait timeout
- Kubernetes probe and termination settings

Timeout hierarchy:

flowchart TD A[Client Timeout: 10s] --> B[Gateway Timeout: 9s] B --> C[Service Request Deadline: 8s] C --> D[Downstream HTTP Timeout: 2s] C --> E[DB Statement Timeout: 3s] C --> F[Cache Timeout: 200ms]

Good hierarchy:

client timeout > gateway timeout > service deadline > dependency timeout

Bad hierarchy:

client timeout = 5s
service keeps working for 60s
DB query runs for 120s

That creates zombie work.


3. Timeout Types

Timeout typeMeaningCommon failure if missing
connection timeouttime to establish connectionblocked connection attempt
read/socket timeouttime waiting for response datastuck thread
request timeouttotal time for requestrequest consumes resource too long
queue timeouttime waiting in queuestale work starts too late
DB statement timeoutmax query execution timelong-running queries block locks/pool
transaction timeoutmax transaction lifetimelong locks, inconsistent release
Kafka delivery timeoutmax time to send recordproducer buffer pressure
circuit breaker wait/open timeoutrecovery behaviorrepeated calls to failing dependency
Kubernetes termination gracetime to shutdown cleanlykilled mid-request/job

Senior-level question:

Which timeout fires first, and what state is left behind?

4. Request Deadline

A request deadline is stronger than independent timeouts.

Independent timeout model:

DB timeout: 5s
HTTP client timeout: 5s
Kafka timeout: 5s
Total request might take 15s+

Deadline model:

Request deadline: now + 8s
Every dependency call consumes remaining budget.

Conceptual API:

public record Deadline(Instant expiresAt) {
    public Duration remaining(Clock clock) {
        Duration remaining = Duration.between(clock.instant(), expiresAt);
        return remaining.isNegative() ? Duration.ZERO : remaining;
    }

    public boolean expired(Clock clock) {
        return !clock.instant().isBefore(expiresAt);
    }
}

Usage:

Duration remaining = ctx.deadline().remaining(clock);
if (remaining.compareTo(Duration.ofMillis(200)) < 0) {
    throw new RequestDeadlineExceededException();
}

Not every codebase has explicit deadline objects. But senior engineers should look for the concept.


5. Cancellation

Timeout only detects that work took too long. Cancellation stops work that no longer matters.

Cancellation sources:

- client disconnect
- gateway timeout
- service request deadline exceeded
- parent task cancelled
- pod shutdown
- circuit breaker open
- queue rejection

Cancellation is hard because not all operations are cancellable.

OperationCancellation behavior
CPU loopmust check interruption/deadline manually
blocking JDBC querymay need statement timeout or cancel support
HTTP callclient library may close socket/request
Kafka sendmay be difficult after record accepted to buffer
file upload/downloadmust close stream and cleanup partial file
database transactionmust rollback
async taskmust respect Future cancellation/interruption

Important distinction:

Client no longer waiting does not automatically mean server stopped working.

6. Client Disconnect

When a client disconnects:

- response write may fail
- server may still run business logic
- downstream calls may continue
- database transaction may continue
- logs may show success even though client saw failure

For non-mutating read endpoints, abandoned work should usually be stopped.

For mutating command endpoints, cancellation is more nuanced:

- Did mutation already commit?
- Did event already publish?
- Can the command be safely retried?
- Is idempotency key used?
- Should server finish command even if client disconnects?

Senior-level rule:

Cancellation policy must be operation-specific, especially for mutating commands.

7. Backpressure

Backpressure means the system refuses, slows, or sheds work when downstream capacity is insufficient.

Without backpressure:

incoming rate > processing capacity
queue grows
latency grows
timeouts grow
retries grow
system collapses

With backpressure:

incoming rate > capacity
system rejects early or slows intake
latency remains bounded
failure is explicit

Backpressure mechanisms:

- bounded thread pools
- bounded queues
- connection pool limits
- rate limiting
- semaphore bulkhead
- circuit breaker
- load shedding
- max request body size
- streaming flow control
- Kafka consumer pause/resume
- DB statement timeout

Backpressure is not failure avoidance. It is controlled failure.


8. Queue Discipline

Queues hide overload until it is too late.

Bad pattern:

Executors.newFixedThreadPool(50); // often backed by unbounded queue in common patterns

Danger:

- memory grows
- stale tasks run long after caller timed out
- latency becomes unbounded
- shutdown takes too long

Better pattern:

- bounded pool
- bounded queue
- named threads
- rejection policy
- metrics
- timeout/deadline before enqueue
- context propagation wrapper

Conceptual executor config:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    20,
    20,
    0L,
    TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<>(500),
    namedThreadFactory("quote-worker"),
    new ThreadPoolExecutor.AbortPolicy()
);

Rejection should be handled deliberately:

try {
    executor.execute(task);
} catch (RejectedExecutionException e) {
    throw new ServiceOverloadedException("quote-worker queue is full", e);
}

Map overload to a controlled HTTP response, commonly 503 Service Unavailable or 429 Too Many Requests, depending on ownership and semantics.


9. Thread Pool Exhaustion

Symptoms:

- request latency spikes
- CPU not necessarily high
- threads stuck in WAITING/BLOCKED/TIMED_WAITING
- health checks fail
- queue length increases
- container still alive but unresponsive

Common causes:

- slow DB query
- missing HTTP client timeout
- connection pool starvation
- blocking I/O on request threads
- large file buffering
- unbounded async queue
- lock contention
- retry storm

Debugging artifacts:

- thread dump
- pool metrics
- DB pool metrics
- HTTP client pool metrics
- request latency histogram
- dependency latency
- garbage collection logs
- Kubernetes CPU throttling metrics

Thread dump questions:

[ ] What are most request threads doing?
[ ] Are they waiting on DB connection?
[ ] Are they blocked on socket read?
[ ] Are they waiting on lock?
[ ] Are they doing CPU work?
[ ] Are they writing large response?

10. Connection Pool Backpressure

Database and HTTP connection pools are natural bulkheads.

If pool size is too large:

- downstream is overloaded
- DB has too many active sessions
- latency increases globally

If pool size is too small:

- service queues internally
- throughput limited
- request threads wait for connections

Pool acquisition timeout matters.

No acquisition timeout -> thread can wait too long
Short acquisition timeout -> fail fast during saturation

PR review:

[ ] Is connection pool size justified?
[ ] Is acquisition timeout configured?
[ ] Are pool metrics exported?
[ ] Are slow queries visible?
[ ] Does service fail fast when pool is exhausted?

11. Timeout and Retry Interaction

Retry without timeout is dangerous.

attempt 1 hangs
attempt 2 never starts
request deadline exceeded
resources remain held

Retry with too-long timeout is also dangerous.

3 attempts x 5s each = 15s
but caller timeout = 8s

Retry budget should fit within request deadline.

Request deadline: 8s
Attempt 1: 1.5s
Backoff: 100ms
Attempt 2: 1.5s
Backoff: 200ms
Attempt 3: 1s
Remaining time for response handling

Retry rules:

- retry only retryable failures
- retry only idempotent or idempotency-protected operations
- retry with bounded attempts
- retry with jittered backoff
- retry within deadline
- do not retry overload blindly

Part 035 will go deeper into retry budget, circuit breaker, and bulkhead.


12. HTTP Status for Timeout and Overload

Common mappings:

SituationPossible statusNotes
client sent invalid timeout/deadline400if client deadline format invalid
request exceeded service deadline504 or 503depends whether service acts as gateway/dependency caller
downstream gateway timeout504when acting as gateway/proxy-like service
service overloaded503include Retry-After if useful
caller exceeded rate limit429rate limiting decision
client disconnectedoften not visible to clientlog as client abort/cancel
async command accepted202if work continues after request

Do not expose internal dependency names in error body.

Good error shape should include:

- stable error code
- human-readable message safe for client
- correlation ID
- retryability hint, if part of API standard
- no stack trace
- no secret/internal topology

13. Streaming Backpressure

Streaming endpoints must avoid hidden buffering.

Risks:

- server reads faster than downstream can write
- client reads slowly
- proxy buffers full response
- heap fills with byte arrays
- timeout kills long stream
- partial file not cleaned up

Streaming checklist:

[ ] Is data streamed or fully buffered?
[ ] Is response flush behavior understood?
[ ] Does proxy/gateway buffer response?
[ ] Is max duration configured?
[ ] Is client disconnect detected?
[ ] Are partial files cleaned up?
[ ] Are byte counts logged/metricized?

For file upload:

[ ] max body size
[ ] streaming parser
[ ] temp file location
[ ] disk quota
[ ] virus/security scanning boundary
[ ] checksum
[ ] cleanup on failure

14. Database Timeout and Cancellation

Database-backed endpoints need multiple guards:

- connection acquisition timeout
- statement timeout
- transaction timeout
- lock timeout
- idle-in-transaction timeout
- query plan/index review

Common production failure:

HTTP request timeout fires at gateway after 30s.
DB query continues for 5 minutes.
Connection remains occupied.
Other requests wait for pool.
Service collapses gradually.

Mitigation:

- configure DB statement timeout below request deadline
- avoid long transaction around remote calls
- log slow query with correlation ID
- export pool metrics
- use lock timeout for lock-heavy operations

15. Kafka Timeout and Backpressure

Kafka producer can apply backpressure through:

- buffer memory
- max block time
- delivery timeout
- linger/batch settings
- acks
- retry config

Failure example:

Kafka broker unavailable
producer buffer fills
send blocks
request threads block
HTTP latency spikes

Design questions:

[ ] Is event publish part of request critical path?
[ ] Is outbox pattern used instead?
[ ] What happens if Kafka is down?
[ ] Does request fail, degrade, or persist command for later publish?
[ ] Are producer send failures observable?

For consumers:

- pause/resume can apply backpressure
- max poll records controls batch size
- processing time must fit poll interval or use correct pattern
- DLQ/retry must not create infinite poison loop

16. Kubernetes and Timeout Alignment

Kubernetes adds another lifecycle layer.

Relevant settings:

- readiness probe
- liveness probe
- startup probe
- terminationGracePeriodSeconds
- preStop hook
- resource requests/limits
- HPA signals
- ingress timeout annotations

If readiness probe stays green during overload, traffic continues.

Better readiness signal may include:

- application started
- critical dependency optional/required status
- worker queue saturation
- shutdown/draining mode

But be careful: readiness should not flap due to minor dependency blips unless the service truly cannot serve.

Shutdown sequence:

sequenceDiagram autonumber participant K as Kubernetes participant Pod participant Service participant Req as In-flight Requests K->>Pod: SIGTERM Pod->>Service: enter draining mode Service->>K: readiness false Service->>Req: finish or cancel by deadline Service->>Service: stop accepting new work Service->>Service: shutdown pools/clients Pod-->>K: exits before grace period

17. Graceful Degradation

Graceful degradation means the system provides reduced functionality instead of total failure.

Examples:

- return cached catalog when pricing service is slow, if allowed
- disable non-critical enrichment
- skip optional recommendation lookup
- queue background notification instead of synchronous send
- use read-only mode during partial outage

But degradation must be safe.

Unsafe degradation:

- using stale pricing when quote requires exact current price
- skipping authorization check
- hiding failed order submission as success
- silently dropping event

Rule:

Never degrade correctness, security, or auditability silently.

18. Load Shedding

Load shedding rejects work early to protect the system.

Possible triggers:

- request queue too deep
- CPU throttling severe
- DB pool exhausted
- executor saturated
- memory pressure
- circuit breaker open
- dependency latency above threshold

Response should be explicit:

503 Service Unavailable
Retry-After: <optional>
X-Correlation-ID: <id>

Load shedding should be measured:

- shed request count
- reason
- endpoint
- tenant/class of traffic, if allowed
- dependency causing pressure

Avoid high-cardinality labels.


19. Practical Endpoint Design Patterns

Pattern A — Fast read endpoint

- short timeout
- no retry unless dependency call is safe
- cache allowed if data semantics permit
- fail fast on pool exhaustion

Pattern B — Mutating command endpoint

- idempotency key recommended
- transaction boundary explicit
- downstream call avoided inside transaction
- event publish via outbox if consistency needed
- response semantics clear: 200/201/202/409

Pattern C — Long-running operation

- avoid holding HTTP request open
- return 202 Accepted
- provide operation status endpoint
- persist operation state
- support idempotency and reconciliation

Pattern D — File upload/download

- stream payload
- apply size limits
- cleanup partial data
- monitor byte count and duration
- avoid request-thread starvation

20. Observability Requirements

Timeout/backpressure behavior must be visible.

Metrics:

- request duration histogram
- dependency duration histogram
- timeout count by operation/dependency
- cancellation count
- rejected task count
- executor active/queued/completed
- DB pool active/idle/pending
- HTTP client pool metrics
- Kafka producer buffer/latency/error
- load shedding count

Logs:

- timeout event with operation and remaining deadline
- cancellation reason
- rejected execution reason
- dependency failure classification
- correlation ID and trace ID

Traces:

- span status/error for timeout
- dependency span duration
- queue wait span, if available
- cancellation attribute, if standard internally

21. Debugging Workflow

For latency/timeout incident:

1. Check whether latency is endpoint-wide or dependency-specific.
2. Compare p50/p95/p99 latency.
3. Check timeout count and where timeout fires.
4. Check thread pool and request thread state.
5. Check DB/HTTP/Kafka pool saturation.
6. Check retry volume.
7. Check queue length and rejection count.
8. Check CPU throttling and memory pressure.
9. Check recent deployment/config change.
10. Identify whether work continues after caller timeout.

Useful commands/artifacts:

- thread dump
- heap/native memory metrics
- application logs by correlation ID
- distributed trace
- DB slow query log
- connection pool metrics
- Kubernetes pod events
- ingress/gateway logs

22. Common Anti-Patterns

Anti-pattern: no timeout on outbound HTTP client
Impact: stuck request threads

Anti-pattern: DB query timeout longer than gateway timeout
Impact: zombie DB work after client failure

Anti-pattern: unbounded executor queue
Impact: memory growth and stale work

Anti-pattern: retrying non-idempotent command
Impact: duplicate mutation

Anti-pattern: retrying overload response aggressively
Impact: retry storm

Anti-pattern: holding DB transaction while calling remote service
Impact: long locks and pool exhaustion

Anti-pattern: async work without cancellation/error handling
Impact: orphaned tasks and invisible failures

Anti-pattern: fallback that hides correctness failure
Impact: data corruption or false success

23. PR Review Checklist

[ ] Does every outbound HTTP client have connect/read/request timeout?
[ ] Does DB access have statement/lock/acquisition timeout where appropriate?
[ ] Is request deadline defined or implied clearly?
[ ] Are dependency timeouts shorter than caller/gateway timeout?
[ ] Is retry bounded and within deadline?
[ ] Is retry limited to idempotent or idempotency-protected operations?
[ ] Is async queue bounded?
[ ] Is rejection handled explicitly?
[ ] Are thread pools named and metricized?
[ ] Does code handle cancellation/interruption where relevant?
[ ] Does mutating endpoint define behavior after client disconnect?
[ ] Does endpoint fail fast under saturation?
[ ] Are overload responses mapped consistently?
[ ] Are timeout and rejection metrics/logs available?
[ ] Is fallback safe and documented?

24. Internal Verification Checklist

For CSG Quote & Order or any enterprise codebase, verify from actual artifacts:

HTTP/server runtime
[ ] What is inbound request timeout?
[ ] What timeout is configured at gateway/load balancer/ingress?
[ ] What timeout is configured in Servlet/Jersey/embedded runtime?
[ ] Are client disconnects detected/logged?

Outbound HTTP
[ ] Which HTTP client library is used?
[ ] Are connect/read/request timeouts configured centrally?
[ ] Are retries configured centrally?
[ ] Is there a standard resilience wrapper?

Resilience library/platform
[ ] Is Resilience4j or equivalent used internally?
[ ] Are circuit breaker, retry, bulkhead, rate limiter standards documented?
[ ] Are configs per dependency or global?
[ ] Are retry budgets enforced?

Thread pools/executors
[ ] Which executors exist?
[ ] Are queues bounded?
[ ] Are rejection policies explicit?
[ ] Are executor metrics exported?
[ ] Is context propagation integrated?

Database
[ ] What connection pool is used?
[ ] What are pool size and acquisition timeout?
[ ] Are statement timeout and lock timeout configured?
[ ] Are slow queries logged with correlation ID?

Kafka/eventing
[ ] Is event publishing synchronous or outbox-based?
[ ] What producer timeout/buffer settings exist?
[ ] What happens when Kafka is unavailable?
[ ] Are consumer retry/DLQ policies bounded?

Kubernetes/platform
[ ] What are pod CPU/memory limits?
[ ] Are pods CPU-throttled during load?
[ ] Are readiness/liveness/startup probes aligned with startup and overload?
[ ] Is termination graceful?
[ ] Are ingress timeout annotations configured?

Observability
[ ] Are timeout metrics emitted?
[ ] Are rejection/load-shedding metrics emitted?
[ ] Are dependency latency histograms available?
[ ] Are traces showing dependency duration and errors?

25. Minimal Production Standard

A production-grade JAX-RS service should have:

[ ] inbound timeout understood
[ ] gateway timeout understood
[ ] outbound HTTP timeouts configured
[ ] DB statement/acquisition timeout configured
[ ] bounded executor queues
[ ] explicit rejection handling
[ ] retry budget and idempotency rules
[ ] cancellation behavior for long work
[ ] backpressure/load shedding strategy
[ ] graceful shutdown and draining
[ ] timeout/rejection/dependency metrics
[ ] runbook for latency and saturation incidents

26. Key Takeaways

- Timeout is a business and operational contract.
- Cancellation stops work that no longer matters, but mutating commands need explicit policy.
- Backpressure is controlled failure that prevents collapse.
- Queues must be bounded and observable.
- Retry must fit inside deadline and idempotency rules.
- Client timeout does not automatically stop server-side work.
- DB, HTTP, Kafka, executor, gateway, and Kubernetes timeouts must be aligned.
- Senior engineers review resource control, not only functional correctness.

The next part continues resilience engineering with retry budget, circuit breaker, and bulkhead patterns.

Lesson Recap

You just completed lesson 34 in build core. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.