Historical HTTP retries, backpressured foreach_record, start_dt/end_dt rename#25
Conversation
…t rename HTTP layer: - Retry transient failures (HTTP 429/5xx + connection/timeout errors) with full-jitter exponential backoff, honoring Retry-After (capped). Configurable via the new max_retries keyword on Historical(...) (default 3); retry budget, backoff, and sleep hook are injectable for testing. Once the budget is spent, the final status still maps to BentoClientError/BentoServerError as before. - Rewrite foreach_record/open_stream to read the response body synchronously on the calling task: real backpressure (no unbounded Base.BufferStream buffering the whole payload) and deterministic teardown on every exit path, including an early return or an exception out of the callback (no leaked producer task). Adds an injectable stream_opener seam so the streaming path is unit-testable. API rename (breaking): - Rename the time-range keyword arguments start/end_ -> start_dt/end_dt across get_range, foreach_record, submit_job, get_record_count, get_billable_size, get_cost, and the Live subscribe! / live_session subscription / stream_to_file / stream_multi_to_files start keyword. The trailing underscore only existed because `end` is a Julia reserved word; the pair is now symmetric. Databento wire parameters (start/end) are unchanged. Adds retry + streaming test coverage; updates docs, README, benchmarks, CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
DatabentoAPI.jl/src/live/streaming.jl
Line 899 in a1a66ef
Every stream_multi_to_files call starts this @async block, but start is no longer a keyword or local variable after the rename to start_dt. This raises UndefVarError(:start) before any subscription is opened; the surrounding catch only logs the crash, so callers can get file paths back even though no live capture ran. Use the renamed start_dt value here, as stream_to_file does above.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- streaming.jl: `stream_multi_to_files` still passed `start_initial = start` (the 12-space-indented copy my earlier replace_all missed — it only matched the 16-space copy in `stream_to_file`). On the capture path this threw `UndefVarError: start not defined`, crashing the session mid-capture and causing the live-streaming round-trip test to fail/hang. Now `start_dt`. - http.jl (open_stream): only retry transient transport errors in the pre-body phase. Once the consumer callback has started reading the body it may already have observed records, so a mid-body socket error must surface rather than replay the partial stream. Addresses Codex review (P2). Verified: full offline suite passes locally (all 19 files, including test_live_streaming, which previously hung/failed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed
Verified the full offline suite passes locally (all 19 test files, including |
Summary
Three changes, from the review/polish pass:
1. Automatic HTTP retries (Historical API)
request()now retries transient failures — HTTP 429 and 5xx, plus connection/timeout errors — with full-jitter exponential backoff, honoring aRetry-Afterheader (capped). Configurable via a newmax_retrieskeyword onHistorical(...)(default 3); the retry budget, backoff, and sleep hook are injectable for testing. Once the budget is exhausted the final status still maps toBentoClientError/BentoServerErroras before. This was the most visible gap vs. the official Databento clients.2.
foreach_recordbackpressure + deterministic teardownThe streaming path previously drained the connection into an unbounded
Base.BufferStreamon a background task — which could buffer the entire compressed payload in memory, and left the producer task running (blocking until server EOF/timeout) when the consumer aborted or finished early. It now reads the response body synchronously on the calling task, so records are pulled from the socket only as fast as the consumer processes them (real backpressure), and the connection is torn down on every exit path — including an early return or an exception out of the callback. Adds an injectablestream_openerseam so the streaming path is unit-testable (it had no HTTP-level test before).3. Breaking:
start/end_→start_dt/end_dtThe old pair was asymmetric — the trailing underscore only existed because
endis a Julia reserved word. Renamed acrossget_range,foreach_record,submit_job,get_record_count,get_billable_size,get_cost, and the Livesubscribe!/live_sessionsubscription /stream_to_file/stream_multi_to_filesstartkeyword. Databento wire parameters (start/end) are unchanged.Docs, README, benchmarks, and CHANGELOG updated. Adds retry + streaming test coverage.
Verification note (please read)
Local test verification was not completed cleanly: my machine runs Julia 1.13.0-beta3, and the async-TCP
test_live_streaming.jlmock-gateway tests fail/hang there. Those tests run through the Live capture path, which none of these changes touch (the retry/open_streamwork is Historical-only; the rename is keyword names). The package targetsjulia = "1.12"and CI runs 1.12, so CI on this PR is the source of truth. Flagging it rather than claiming a green run I didn't get.🤖 Generated with Claude Code