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}