objectstore_server/web/request_counter.rs
1//! In-flight HTTP request counter with a configurable limit.
2//!
3//! [`RequestCounter`] pairs a [`tower_http`] in-flight counter with the configured
4//! maximum, so both the tracking layer and the concurrency-limit middleware share a
5//! single, self-contained type.
6
7use std::time::Duration;
8
9use tower_http::metrics::InFlightRequestsLayer;
10use tower_http::metrics::in_flight_requests::InFlightRequestsCounter;
11
12/// Interval for the periodic in-flight gauge metric emitter.
13const EMITTER_INTERVAL: Duration = Duration::from_secs(1);
14
15/// Tracks in-flight HTTP requests and enforces a configured maximum.
16///
17/// Clone this to share the same underlying atomic across the tower layer, the
18/// concurrency-limit middleware, and any endpoint that needs to read the count.
19/// Call [`layer`](Self::layer) to obtain the [`InFlightRequestsLayer`] that
20/// increments the counter for every request.
21#[derive(Clone, Debug)]
22pub struct RequestCounter {
23 counter: InFlightRequestsCounter,
24 limit: usize,
25}
26
27impl RequestCounter {
28 /// Creates a new counter with the given maximum.
29 pub fn new(limit: usize) -> Self {
30 Self {
31 counter: InFlightRequestsCounter::new(),
32 limit,
33 }
34 }
35
36 /// Returns a [`InFlightRequestsLayer`] that increments this counter for every request.
37 pub fn layer(&self) -> InFlightRequestsLayer {
38 InFlightRequestsLayer::new(self.counter.clone())
39 }
40
41 /// Returns the number of requests currently in flight.
42 pub fn count(&self) -> usize {
43 self.counter.get()
44 }
45
46 /// Returns the configured maximum number of concurrent requests.
47 pub fn limit(&self) -> usize {
48 self.limit
49 }
50
51 /// Periodically calls `emit` with the current in-flight count.
52 ///
53 /// Runs forever; intended to be spawned as a background task.
54 pub async fn run_emitter(self) {
55 let limit = self.limit;
56 self.counter
57 .run_emitter(EMITTER_INTERVAL, move |count| async move {
58 objectstore_metrics::gauge!("server.requests.in_flight": count);
59 objectstore_metrics::gauge!("server.requests.limit": limit);
60 })
61 .await;
62 }
63}