Skip to main content

soothe_client/appkit/
query_gate.rs

1//! Single-flight query gate per session.
2
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use tokio::sync::Mutex;
7
8/// Session already has an in-flight query.
9#[derive(Debug, Clone, thiserror::Error)]
10#[error("query busy for session {0}")]
11pub struct ErrQueryBusy(pub String);
12
13/// Single-flight gate keyed by session id.
14#[derive(Clone, Default)]
15pub struct QueryGate {
16    inflight: Arc<Mutex<HashMap<String, bool>>>,
17}
18
19impl QueryGate {
20    /// Create a gate.
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Acquire exclusive query slot.
26    pub async fn acquire(&self, session_id: &str) -> Result<(), ErrQueryBusy> {
27        let mut map = self.inflight.lock().await;
28        if map.get(session_id).copied().unwrap_or(false) {
29            return Err(ErrQueryBusy(session_id.to_string()));
30        }
31        map.insert(session_id.to_string(), true);
32        Ok(())
33    }
34
35    /// Mark cancelled (slot still held until release).
36    pub async fn cancel(&self, _session_id: &str) {}
37
38    /// Release exclusive slot.
39    pub async fn release(&self, session_id: &str) {
40        self.inflight.lock().await.remove(session_id);
41    }
42}