1use std::collections::BTreeMap;
6use std::os::unix::net::UnixStream;
7use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
8use std::sync::{Arc, Mutex, MutexGuard};
9use std::time::{Duration, Instant};
10
11use serde::de::{Deserializer, IgnoredAny, MapAccess, Visitor};
12use serde_json::{Value, json};
13
14use crate::protocol::MAX_LINE;
15
16pub const SHORT_LINE: usize = 64 * 1024;
18const WANTED_FILES: u64 = 4096;
20
21#[derive(Clone, Debug)]
22pub struct Limits {
23 pub connections: usize,
25 pub per_uid: usize,
27 pub idle: Duration,
29 pub line: Duration,
31 pub write: Duration,
33 pub short_line: usize,
35 pub long_line: usize,
36 pub buffered: usize,
38}
39
40impl Default for Limits {
41 fn default() -> Self {
42 Self {
43 connections: 1024,
44 per_uid: 512,
45 idle: Duration::from_secs(15 * 60),
46 line: Duration::from_secs(60),
47 write: Duration::from_secs(60),
48 short_line: SHORT_LINE,
49 long_line: MAX_LINE,
50 buffered: 2 * MAX_LINE,
51 }
52 }
53}
54
55impl Limits {
56 pub fn for_files(files: u64) -> Self {
59 let d = Self::default();
60 let connections = d.connections.min(files as usize / 2);
61 Self {
62 connections,
63 per_uid: d.per_uid.min(connections),
64 ..d
65 }
66 }
67}
68
69pub fn open_files() -> u64 {
72 crate::sys::raise_open_files(WANTED_FILES)
73}
74
75#[derive(Debug, Default)]
78pub struct Counters {
79 pub busy: AtomicU64,
81 pub evicted: AtomicU64,
83 pub idle: AtomicU64,
85 pub slow: AtomicU64,
87 pub too_large: AtomicU64,
88 pub rate_limited: AtomicU64,
89}
90
91impl Counters {
92 pub fn add(counter: &AtomicU64) {
93 counter.fetch_add(1, Ordering::Relaxed);
94 }
95
96 pub fn to_json(&self) -> Value {
97 let n = |c: &AtomicU64| c.load(Ordering::Relaxed);
98 json!({
99 "busy": n(&self.busy),
100 "evicted": n(&self.evicted),
101 "idle": n(&self.idle),
102 "slow": n(&self.slow),
103 "too_large": n(&self.too_large),
104 "rate_limited": n(&self.rate_limited),
105 })
106 }
107}
108
109struct Conn {
111 uid: u32,
112 stream: Arc<UnixStream>,
113 working: bool,
115 quiet_since: Instant,
116}
117
118pub struct Registry {
120 pub limits: Limits,
121 conns: Mutex<BTreeMap<u64, Conn>>,
122 next: AtomicU64,
123 buffered: AtomicUsize,
124}
125
126impl Registry {
127 pub fn new(limits: Limits) -> Arc<Self> {
128 Arc::new(Self {
129 limits,
130 conns: Mutex::default(),
131 next: AtomicU64::new(0),
132 buffered: AtomicUsize::new(0),
133 })
134 }
135
136 fn lock(&self) -> MutexGuard<'_, BTreeMap<u64, Conn>> {
137 self.conns.lock().unwrap_or_else(|e| e.into_inner())
138 }
139
140 pub fn admit(self: &Arc<Self>, uid: u32, stream: Arc<UnixStream>) -> Option<(Slot, bool)> {
145 let mut conns = self.lock();
146 let of_uid = conns.values().filter(|c| c.uid == uid).count();
147 let crowded = of_uid >= self.limits.per_uid;
148 let full = crowded || conns.len() >= self.limits.connections;
149 if full {
150 let quiet = conns.iter().filter(|(_, c)| !c.working);
151 let fair = quiet.filter(|(_, c)| !crowded || c.uid == uid);
152 let (&id, _) = fair.min_by_key(|(_, c)| c.quiet_since)?;
153 let gone = conns.remove(&id)?;
154 let _ = gone.stream.shutdown(std::net::Shutdown::Read);
156 }
157 let id = self.next.fetch_add(1, Ordering::Relaxed);
158 let conn = Conn {
159 uid,
160 stream,
161 working: false,
162 quiet_since: Instant::now(),
163 };
164 conns.insert(id, conn);
165 let slot = Slot {
166 registry: self.clone(),
167 id,
168 };
169 Some((slot, full))
170 }
171}
172
173pub struct Slot {
175 registry: Arc<Registry>,
176 id: u64,
177}
178
179impl Slot {
180 pub fn limits(&self) -> &Limits {
181 &self.registry.limits
182 }
183
184 pub fn uid(&self) -> Option<u32> {
187 self.registry.lock().get(&self.id).map(|c| c.uid)
188 }
189
190 pub fn begin(&self) -> bool {
194 let mut conns = self.registry.lock();
195 let conn = conns.get_mut(&self.id);
196 conn.map(|c| c.working = true).is_some()
197 }
198
199 pub fn end(&self) {
201 if let Some(c) = self.registry.lock().get_mut(&self.id) {
202 c.working = false;
203 c.quiet_since = Instant::now();
204 }
205 }
206
207 pub fn evicted(&self) -> bool {
208 !self.registry.lock().contains_key(&self.id)
209 }
210
211 pub fn hold(&self) -> Held<'_> {
212 Held {
213 registry: &self.registry,
214 bytes: 0,
215 }
216 }
217}
218
219impl Drop for Slot {
220 fn drop(&mut self) {
221 self.registry.lock().remove(&self.id);
222 }
223}
224
225pub struct Held<'a> {
228 registry: &'a Registry,
229 bytes: usize,
230}
231
232impl Held<'_> {
233 pub fn grow(&mut self, total: usize) -> bool {
235 let more = total.saturating_sub(self.bytes);
236 let was = self.registry.buffered.fetch_add(more, Ordering::Relaxed);
237 if was + more > self.registry.limits.buffered {
238 self.registry.buffered.fetch_sub(more, Ordering::Relaxed);
239 return false;
240 }
241 self.bytes += more;
242 true
243 }
244}
245
246impl Drop for Held<'_> {
247 fn drop(&mut self) {
248 let bytes = self.bytes;
249 self.registry.buffered.fetch_sub(bytes, Ordering::Relaxed);
250 }
251}
252
253#[derive(Debug, Default, PartialEq, Eq)]
258pub struct Head {
259 pub id: u64,
260 pub method: Option<String>,
261 pub repo: Option<String>,
262 pub token: Option<String>,
263}
264
265impl<'de> Visitor<'de> for &mut Head {
266 type Value = ();
267
268 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 f.write_str("a request")
270 }
271
272 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<(), A::Error> {
273 while let Some(key) = map.next_key::<String>()? {
274 match key.as_str() {
275 "id" => self.id = map.next_value()?,
276 "method" => self.method = map.next_value()?,
277 "repo" => self.repo = map.next_value()?,
278 "token" => self.token = map.next_value()?,
279 _ => {
280 map.next_value::<IgnoredAny>()?;
281 }
282 }
283 }
284 Ok(())
285 }
286}
287
288pub fn head_of(bytes: &[u8]) -> Head {
291 let mut head = Head::default();
292 let mut de = serde_json::Deserializer::from_slice(bytes);
293 let _ = (&mut de).deserialize_map(&mut head);
294 head
295}
296
297const OP: u64 = 60_000;
300
301#[derive(Clone, Copy, Debug)]
303pub struct Bucket {
304 level: u64,
305 at_ms: i64,
306}
307
308impl Bucket {
309 pub fn full(per_minute: u64, now_ms: i64) -> Self {
310 Self {
311 level: per_minute * OP,
312 at_ms: now_ms,
313 }
314 }
315
316 pub fn take(&mut self, per_minute: u64, now_ms: i64) -> Result<(), u64> {
318 let passed = now_ms.saturating_sub(self.at_ms).max(0) as u64;
319 let grown = self.level.saturating_add(passed.saturating_mul(per_minute));
320 self.level = grown.min(per_minute * OP);
321 self.at_ms = now_ms;
322 match self.level.checked_sub(OP) {
323 Some(left) => {
324 self.level = left;
325 Ok(())
326 }
327 None => Err((OP - self.level).div_ceil(per_minute)),
328 }
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 fn stream() -> Arc<UnixStream> {
337 Arc::new(UnixStream::pair().unwrap().0)
338 }
339
340 fn limits(connections: usize, per_uid: usize) -> Limits {
341 Limits {
342 connections,
343 per_uid,
344 ..Limits::default()
345 }
346 }
347
348 #[test]
349 fn file_descriptors_decide_the_connections() {
350 let few = Limits::for_files(256);
351 assert_eq!((few.connections, few.per_uid), (128, 128));
352 let some = Limits::for_files(1600);
353 assert_eq!((some.connections, some.per_uid), (800, 512));
354 let many = Limits::for_files(1 << 20);
355 assert_eq!((many.connections, many.per_uid), (1024, 512));
356 }
357
358 #[test]
359 fn the_daemon_asks_for_descriptors() {
360 let files = open_files();
361 assert!(files >= 256, "{files}");
362 }
363
364 #[test]
365 fn the_quietest_connection_makes_room() {
366 let r = Registry::new(limits(2, 2));
367 let a = stream();
368 let (first, cut) = r.admit(1, a.clone()).unwrap();
369 assert!(!cut);
370 let (second, cut) = r.admit(1, stream()).unwrap();
371 assert!(!cut);
372 std::thread::sleep(Duration::from_millis(2));
373 first.begin();
374 first.end(); let (_third, cut) = r.admit(1, stream()).unwrap();
376 assert!(cut);
377 assert!(!first.evicted());
378 assert!(second.evicted());
379 assert!(!second.begin(), "evicted: its request must not run");
380 }
381
382 #[test]
383 fn a_connection_at_work_is_never_cut() {
384 let r = Registry::new(limits(1, 1));
385 let (slot, _) = r.admit(1, stream()).unwrap();
386 assert!(slot.begin());
387 assert!(r.admit(1, stream()).is_none());
388 slot.end();
389 assert!(r.admit(1, stream()).is_some());
390 }
391
392 #[test]
393 fn a_uid_at_its_cap_gives_up_its_own() {
394 let r = Registry::new(limits(8, 1));
395 let (other, _) = r.admit(2, stream()).unwrap();
396 std::thread::sleep(Duration::from_millis(2));
397 let (mine, cut) = r.admit(1, stream()).unwrap();
398 assert!(!cut);
399 std::thread::sleep(Duration::from_millis(2));
400 let (_next, cut) = r.admit(1, stream()).unwrap();
401 assert!(cut);
402 assert!(mine.evicted());
403 assert!(!other.evicted(), "older, but another uid's");
404 }
405
406 #[test]
407 fn a_full_daemon_takes_the_quietest_of_any_uid() {
408 let r = Registry::new(limits(1, 8));
409 let (other, _) = r.admit(2, stream()).unwrap();
410 let (_mine, cut) = r.admit(1, stream()).unwrap();
411 assert!(cut);
412 assert!(other.evicted());
413 }
414
415 #[test]
416 fn eviction_ends_the_reads_of_the_connection() {
417 use std::io::Read;
418 let (a, _keep) = UnixStream::pair().unwrap();
419 let a = Arc::new(a);
420 let r = Registry::new(limits(1, 1));
421 let (_slot, _) = r.admit(1, a.clone()).unwrap();
422 let _next = r.admit(1, stream()).unwrap();
423 assert_eq!((&*a).read(&mut [0; 8]).unwrap(), 0);
424 }
425
426 #[test]
427 fn a_slot_gives_its_place_back() {
428 let r = Registry::new(limits(1, 1));
429 let (slot, _) = r.admit(1, stream()).unwrap();
430 slot.begin();
431 drop(slot);
432 let (_slot, cut) = r.admit(1, stream()).unwrap();
433 assert!(!cut);
434 }
435
436 #[test]
437 fn long_lines_share_a_budget() {
438 let r = Registry::new(Limits {
439 buffered: 100,
440 ..Limits::default()
441 });
442 let (slot, _) = r.admit(1, stream()).unwrap();
443 let mut a = slot.hold();
444 assert!(a.grow(60));
445 assert!(a.grow(100), "its own bytes are not counted twice");
446 let mut b = slot.hold();
447 assert!(!b.grow(1));
448 drop(a);
449 assert!(b.grow(100), "a line done with gives its room back");
450 assert!(!b.grow(101));
451 assert!(b.grow(100));
452 }
453
454 #[test]
455 fn a_head_reads_what_comes_before_the_cut() {
456 let line = br#"{"id":7,"method":"tree","repo":"r","token":"t","params":{"paths":["a","#;
457 let want = Head {
458 id: 7,
459 method: Some("tree".into()),
460 repo: Some("r".into()),
461 token: Some("t".into()),
462 };
463 assert_eq!(head_of(line), want);
464 let sorted = br#"{"id":1,"method":"blobs","params":{"paths":["a","#;
466 let head = head_of(sorted);
467 assert_eq!(head.method.as_deref(), Some("blobs"));
468 assert_eq!(head.token, None);
469 assert_eq!(head_of(b"nonsense"), Head::default());
470 assert_eq!(head_of(br#"{"params":{"x":"#), Head::default());
471 }
472
473 #[test]
474 fn what_a_head_expects_is_a_request() {
475 let mut head = Head::default();
476 let mut de = serde_json::Deserializer::from_slice(b"1");
477 let e = (&mut de).deserialize_map(&mut head).unwrap_err();
478 assert!(e.to_string().contains("expected a request"), "{e}");
479 }
480
481 #[test]
482 fn a_bucket_lets_a_minute_through_and_then_the_rate() {
483 let mut b = Bucket::full(60, 0);
484 for _ in 0..60 {
485 assert_eq!(b.take(60, 0), Ok(()));
486 }
487 assert_eq!(b.take(60, 0), Err(1000));
488 assert_eq!(b.take(60, 400), Err(600));
489 assert_eq!(b.take(60, 1000), Ok(()));
490 assert_eq!(b.take(60, 1000), Err(1000));
491 }
492
493 #[test]
494 fn a_bucket_holds_no_more_than_a_minute() {
495 let mut b = Bucket::full(2, 0);
496 assert_eq!(b.take(2, 10_000_000), Ok(()));
497 assert_eq!(b.take(2, 10_000_000), Ok(()));
498 assert!(b.take(2, 10_000_000).is_err());
499 }
500
501 #[test]
502 fn a_clock_that_goes_back_adds_nothing() {
503 let mut b = Bucket::full(1, 1_000_000);
504 assert_eq!(b.take(1, 1_000_000), Ok(()));
505 assert_eq!(b.take(1, 0), Err(60_000));
506 }
507
508 #[test]
509 fn a_lower_rate_cuts_what_was_saved() {
510 let mut b = Bucket::full(600, 0);
511 assert_eq!(b.take(1, 0), Ok(()));
512 assert_eq!(b.take(1, 0), Err(60_000));
513 }
514
515 #[test]
516 fn counters_show_each_refusal() {
517 let c = Counters::default();
518 Counters::add(&c.busy);
519 Counters::add(&c.too_large);
520 Counters::add(&c.too_large);
521 let shown = c.to_json();
522 assert_eq!(shown["busy"], 1);
523 assert_eq!(shown["too_large"], 2);
524 assert_eq!(shown["rate_limited"], 0);
525 }
526}