Skip to content

feat(ingest): HTTP OTLP backpressure parity (429 + Retry-After)#54

Merged
aksOps merged 1 commit into
mainfrom
feat/http-otlp-backpressure
Apr 27, 2026
Merged

feat(ingest): HTTP OTLP backpressure parity (429 + Retry-After)#54
aksOps merged 1 commit into
mainfrom
feat/http-otlp-backpressure

Conversation

@aksOps

@aksOps aksOps commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OTLP HTTP receiver now responds with 429 Too Many Requests + Retry-After: 1 (mirroring the gRPC RESOURCE_EXHAUSTED path introduced in Phase 1) when the async ingest pipeline queue is at capacity. Body remains OTLP-shaped (Status protobuf), so spec-compliant clients back off.
  • isQueueFull helper recognizes both ErrQueueFull and gRPC codes.ResourceExhausted so the mapping survives future error-plumbing changes inside ingest.
  • New metric otelcontext_http_otlp_throttled_total{signal} wired via SetThrottleCallback from main.go gives operators a unified throttling signal across both transports.

Why

Phase 4 of the 7-day-retention robustness initiative. Without this, healthy clients hitting the HTTP path during a queue spike would see a confusing 500 instead of the standard 429 + Retry-After flow that OTLP HTTP exporters know how to handle.

Test plan

  • go test ./... -race -count=1 — full unit suite green
  • TestHTTPBackpressure_TracesReturns429WithRetryAfter — verifies status, header, and content type
  • TestHTTPBackpressure_LogsReturns429 — same for logs
  • TestHTTPBackpressure_ThrottleCallbackInvoked — verifies signal-labeled callback fires once per 429
  • TestHTTPBackpressure_NotInvokedOnSuccess — verifies callback does NOT fire on 200
  • TestIsQueueFull_ClassifiesCorrectly — verifies sentinel and gRPC status detection
  • golangci-lint run --new-from-rev=origin/main — clean

🤖 Generated with Claude Code

The OTLP HTTP receiver now mirrors the gRPC RESOURCE_EXHAUSTED path
introduced in Phase 1: when the async ingest pipeline rejects a batch
because the queue is at capacity, the handler responds with HTTP 429
Too Many Requests, a Retry-After: 1 header, and an OTLP-shaped Status
protobuf body so spec-compliant clients back off.

`isQueueFull` recognizes both the local `ErrQueueFull` sentinel and any
gRPC RESOURCE_EXHAUSTED status returned by the underlying TraceServer /
LogsServer / MetricsServer Export methods, so a future change in error
plumbing inside ingest does not silently break the 429 mapping.

New metric `otelcontext_http_otlp_throttled_total{signal}` is wired in
main.go via SetThrottleCallback so operators see a unified backpressure
signal across both transports. Throttle counts are not incremented on
successful requests — verified by test.

Tests cover: 429 + Retry-After on traces and logs, callback invocation
with the right signal label, callback NOT firing on success, and
isQueueFull classification of both error shapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
4.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@aksOps aksOps merged commit fdf2433 into main Apr 27, 2026
16 of 17 checks passed
@aksOps aksOps deleted the feat/http-otlp-backpressure branch April 27, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant