Skip to main content

espresso_telemetry/
rate_limit.rs

1//! Single-shot ERROR on the first HTTP 429 from the telemetry proxy, which caps
2//! each node's hourly byte budget. Only the metrics push observes 429 (logs
3//! retry via opentelemetry-otlp's `experimental-http-retry`); `compare_exchange`
4//! on the shared latch keeps it to one ERROR per process.
5
6use std::sync::atomic::{AtomicBool, Ordering};
7
8/// Log a single ERROR on the first 429, deduped via `flag`. `env_filter` is the
9/// active `ESPRESSO_NODE_TELEMETRY_LOG`; `retry_after_secs` is the parsed
10/// `Retry-After` header, if numeric.
11pub(crate) fn log_rate_limit_once(
12    flag: &AtomicBool,
13    env_filter: &str,
14    retry_after_secs: Option<u64>,
15) {
16    if flag
17        .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
18        .is_ok()
19    {
20        let retry = retry_after_secs
21            .map(|s| format!("{s}s"))
22            .unwrap_or_else(|| "unknown".to_string());
23        tracing::error!(
24            telemetry_log = env_filter,
25            retry_after = %retry,
26            "telemetry rate limit hit (HTTP 429). The proxy capped this node's hourly byte budget. \
27             Your current ESPRESSO_NODE_TELEMETRY_LOG is \"{env_filter}\": narrow it (e.g. \
28             \"warn\", or \"warn,hotshot=info\"). Retry-After: {retry}. This message is logged \
29             once per process."
30        );
31    }
32}