···1111cid = { version = "0.11", default-features = false, features = ["alloc"] }
1212clap = { version = "4.5.60", features = ["derive", "env"] }
1313dashmap = "5"
1414+fastrand = "2.3.0"
1415fjall = "3.0.3"
1516futures = "0.3"
1617governor = "0.6"
+4-3
hacking.md
···121121- [x] repo-stream: drop record block contents with processor fn
122122 - [x] in getRecord before describeRepo
123123 - [x] in commit handling
124124+- [x] meta/metrics keyspace for general stats
125125+ - [x] total repos (hyperloglog estimate?)
126126+ - [x] resync queue size
127127+- [x] jitter http throttle
124128- [x] commit CAR handling: generate a list of keys with gaps noted, to reliably detect missing adjacent keys
125129 - [ ] watch logs for errors now that we're strict
126126-- [ ] meta/metrics keyspace for general stats
127127- - [ ] total repos (hyperloglog estimate?)
128128- - [ ] resync queue size
129130130131very much still todo but i'm getting tired
131132- [x] config: add a `--heavy` mode that always uses `getRepo` and never `describeRepo`
+16-9
src/http.rs
···1818use jacquard_common::http_client::{HttpClient, HttpClientExt};
1919use jacquard_common::stream::{ByteStream, StreamError};
20202121+const THROTTLE_JITTER_MS: f32 = 16.;
2222+2123/// State shared across all clones of a [`ThrottledClient`].
2224struct Shared {
2325 /// Duration of one token at the configured rate (= 1s / rate).
···3739 .or_insert_with(|| Arc::new(RateLimiter::direct(self.quota)))
3840 .value(),
3941 )
4242+ }
4343+4444+ fn jittered_interval(&self) -> Duration {
4545+ let secs = fastrand::f32() * THROTTLE_JITTER_MS / 1000.0;
4646+ self.token_interval + Duration::from_secs_f32(secs)
4047 }
4148}
4249···9198 if let Some(host) = parts.uri.host() {
9299 let limiter = self.shared.get_or_create_limiter(host);
93100 while limiter.check().is_err() {
9494- metrics::gauge!("lightrail_http_host_thorottling").increment(1);
9595- tokio::time::sleep(self.shared.token_interval).await;
9696- metrics::gauge!("lightrail_http_host_thorottling").decrement(1);
101101+ metrics::gauge!("lightrail_http_host_throttling").increment(1);
102102+ tokio::time::sleep(self.shared.jittered_interval()).await;
103103+ metrics::gauge!("lightrail_http_host_throttling").decrement(1);
97104 }
98105 }
99106···125132 if let Some(host) = parts.uri.host() {
126133 let limiter = self.shared.get_or_create_limiter(host);
127134 while limiter.check().is_err() {
128128- metrics::gauge!("lightrail_http_host_thorottling").increment(1);
129129- tokio::time::sleep(self.shared.token_interval).await;
130130- metrics::gauge!("lightrail_http_host_thorottling").decrement(1);
135135+ metrics::gauge!("lightrail_http_host_throttling").increment(1);
136136+ tokio::time::sleep(self.shared.jittered_interval()).await;
137137+ metrics::gauge!("lightrail_http_host_throttling").decrement(1);
131138 }
132139 }
133140 self.inner
···147154 if let Some(host) = parts.uri.host() {
148155 let limiter = self.shared.get_or_create_limiter(host);
149156 while limiter.check().is_err() {
150150- metrics::gauge!("lightrail_http_host_thorottling").increment(1);
151151- tokio::time::sleep(self.shared.token_interval).await;
152152- metrics::gauge!("lightrail_http_host_thorottling").decrement(1);
157157+ metrics::gauge!("lightrail_http_host_throttling").increment(1);
158158+ tokio::time::sleep(self.shared.jittered_interval()).await;
159159+ metrics::gauge!("lightrail_http_host_throttling").decrement(1);
153160 }
154161 }
155162 self.inner.send_http_bidirectional(parts, body).await