Repository navigation
Convert JsonLinesSink to async reqwest (migrate from ureq) #1000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4be96cc
5e3d1e8
838a3ab
f25186a
dd9680e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::future::Future; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::io; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::sync::Arc; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -6,10 +7,9 @@ use arrow::record_batch::RecordBatch; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use logfwd_types::diagnostics::ComponentStats; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[allow(deprecated)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use super::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BatchMetadata, Compression, HTTP_MAX_RETRIES, HTTP_RETRY_INITIAL_DELAY_MS, OutputSink, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| build_col_infos, is_transient_error, str_value, write_row_json, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BatchMetadata, Compression, HTTP_MAX_RETRIES, HTTP_RETRY_INITIAL_DELAY_MS, build_col_infos, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| str_value, write_row_json, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -24,30 +24,31 @@ pub struct JsonLinesSink { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub(crate) batch_buf: Vec<u8>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compress_buf: Vec<u8>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compression: Compression, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| http_agent: ureq::Agent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client: reqwest::Client, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stats: Arc<ComponentStats>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl JsonLinesSink { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Create a new JSON-lines sink. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The caller supplies a shared `reqwest::Client` (typically cloned from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// the factory) so all workers reuse the same connection pool. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn new( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: Vec<(String, String)>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| compression: Compression, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
40
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The caller supplies a shared `reqwest::Client` (typically cloned from | |
| /// the factory) so all workers reuse the same connection pool. | |
| pub fn new( | |
| name: String, | |
| url: String, | |
| headers: Vec<(String, String)>, | |
| compression: Compression, | |
| /// This convenience constructor preserves the historical API by creating | |
| /// a default `reqwest::Client` for the sink. | |
| pub fn new( | |
| name: String, | |
| url: String, | |
| headers: Vec<(String, String)>, | |
| compression: Compression, | |
| stats: Arc<ComponentStats>, | |
| ) -> Self { | |
| Self::with_client( | |
| name, | |
| url, | |
| headers, | |
| compression, | |
| reqwest::Client::new(), | |
| stats, | |
| ) | |
| } | |
| /// Create a new JSON-lines sink using a shared `reqwest::Client`. | |
| /// | |
| /// Callers that manage sink creation centrally should prefer this so all | |
| /// workers reuse the same connection pool. | |
| pub fn with_client( | |
| name: String, | |
| url: String, | |
| headers: Vec<(String, String)>, | |
| compression: Compression, |
Copilot
AI
Apr 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR introduces substantial new HTTP behavior (reqwest transport, retry/backoff, 429 → RetryAfter, 4xx → Rejected). The current unit tests in this module cover serialization but not the send/retry/status-handling logic; adding async tests (e.g., with a lightweight local HTTP server) would help prevent regressions around status classification and Retry-After parsing.
Copilot
AI
Apr 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let body = payload.to_vec(); plus body.clone() on every attempt copies the full (potentially large) payload per retry and adds an extra allocation even for the first attempt. To avoid this regression vs the old slice-based resend, consider using a cheap-clone buffer type (e.g., bytes::Bytes) or otherwise sharing the underlying payload across retries without re-copying.
Copilot
AI
Apr 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send_batch_inner treats any status that is neither 4xx nor 5xx as success. That means 3xx redirects (and other non-success codes) will incorrectly be counted as delivered. Consider explicitly checking status.is_success() and handling non-success/non-4xx/non-5xx statuses as an error or rejection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟢 Low
https://github.com/strawgate/memagent/blob/9ad2a6acfdfa68425bdac8cd6f3f8664a573570e/crates/logfwd-output/src/json_lines.rs#L103-L118
In
send_batch_inner,self.stats.inc_lines(batch.num_rows() as u64)counts all rows regardless of whether they were actually written. In raw passthrough mode, rows where_rawis null are skipped during serialization (lines 106-112) but still counted in the stats, inflating the line count when_rawcontains null values. Consider counting only rows that were actually written to the output buffer.🚀 Reply "fix it for me" or copy this AI Prompt for your agent: