Skip to content

docs(traces,logs): bound the Retry-After window and record the OTLP status deviation - #61

Draft
turnipdabeets wants to merge 1 commit into
mainfrom
docs/otlp-retry-window-and-status-deviation
Draft

docs(traces,logs): bound the Retry-After window and record the OTLP status deviation#61
turnipdabeets wants to merge 1 commit into
mainfrom
docs/otlp-retry-window-and-status-deviation

Conversation

@turnipdabeets

Copy link
Copy Markdown
Collaborator

Two gaps @jonmcwest raised against OTLP while reviewing posthog-js#4726. Both are in the shared retry policy, so both land in logs and traces in identical words.

What the spec says today

Repeated refusals are undefined. The policy gives the wait for a refusal — max(ownBackoff, min(parsedRetryAfter, documentedMaximum)) — and says nothing about a second one arriving while the first window is still open. Read literally each refusal installs a fresh wait, so a host refused faster than the window is long slides the deadline forever. logs gates its size and reconnect triggers on the window being closed and metrics re-arms its timer from it, so an unbounded slide suppresses all three for as long as that host keeps flushing — reachable in normal operation by a React Native app taking flush() on every app-state transition.

The 5xx retry set diverges from OTLP without saying so. The policy requires 408/429/5xx retriable; OTLP permits only 429/502/503/504. The PostHog rule is deliberate, but nothing records that, so each reviewer rediscovers it as a possible defect — which is exactly what happened here.

What changes

  • A refusal naming a later deadline extends the window; a shorter one never pulls it in. The extension is bounded by the documented maximum measured from where the window was first installed. At the ceiling the window closes, one attempt goes out, and a further refusal installs a new window.
  • The 408/5xx set is recorded as a deliberate OTLP divergence, with its reason and the condition for revisiting it.

Three scenarios per capability cover extend, don't-shrink, and the ceiling.

The trade-off, stated in the spec

Bounding the window serves a longer wait short, so the SDK may retry before a newer Retry-After expires — which OTLP does not sanction. Honouring every fresh header instead makes the suppression above unbounded, which costs records once the queue fills. One risks a request every few minutes against a rate-limited endpoint; the other risks data. The ceiling fails toward keeping records.

This is currently dormant either way: capture-logs sends no Retry-After and no 429 (grepped rust/capture-logs/src/), so every header an SDK sees comes from a customer's proxy or CDN. posthog/posthog#75090 would have changed that and was closed unmerged. The spec names that as the revisit trigger.

Impact

  • posthog-js already implements the ceiling as of #4726 — no follow-up code change there.
  • No ingestion change. Narrowing the status set would need the ingestion team to say which 5xx are transient, and would be its own proposal.

openspec validate --specs --strict → 62 passed, 0 failed. openspec validate clarify-otlp-retry-window-and-status-deviation --strict → valid.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BjT26chjUzuPSyJZqh4AWQ

…tatus deviation

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjT26chjUzuPSyJZqh4AWQ
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