Skip to main content

rvoip_core/
config.rs

1use std::sync::Arc;
2use std::thread;
3use std::time::Duration;
4
5use crate::store::{
6    ConversationStore, MemoryConversationStore, MemoryMessageStore, MemoryVconStore, MessageStore,
7    VconStore,
8};
9
10/// P6 — per-tenant quota envelope. Each `None` means "unlimited".
11#[derive(Clone, Copy, Debug, Default)]
12pub struct TenantQuotas {
13    pub max_concurrent_sessions: Option<usize>,
14    pub max_concurrent_recordings: Option<usize>,
15    pub max_concurrent_ai_sessions: Option<usize>,
16}
17
18/// Orchestrator configuration.
19///
20/// Phase-2 admission semaphore default per `PERFORMANCE_PLAN.md`:
21/// `max_concurrent_setups = 256 * available_parallelism()`.
22#[derive(Clone)]
23pub struct Config {
24    pub max_concurrent_setups: usize,
25    /// Maximum number of distinct direct-media subscriber Connections
26    /// admitted by this Orchestrator across every UCTP ingress substrate.
27    ///
28    /// A subscriber with several stream routes consumes one slot. The slot is
29    /// released only after its final route is removed, its Connection closes,
30    /// or its Session is dropped. Relay-backed broadcast fanout is accounted
31    /// separately by the relay implementation.
32    pub max_direct_subscribers: usize,
33    pub conversation_store: Arc<dyn ConversationStore>,
34    pub vcon_store: Arc<dyn VconStore>,
35    /// P4 — message log + history pager. Default in-memory.
36    pub message_store: Arc<dyn MessageStore>,
37    /// How long `bridge_connections` waits for both peers' audio streams
38    /// to appear before failing the admission check. Adapters populate
39    /// streams lazily (typically on `connection.ready`), so a caller
40    /// that triggers a bridge from `Event::ConnectionInbound` may race
41    /// the stream registration. Setting this to zero disables the wait
42    /// (strict legacy behavior).
43    ///
44    /// **Default raised to 5 seconds** (plan §7 architectural concern
45    /// #7 / D3): the previous 2 seconds was tight for cold WebTransport
46    /// dials on a high-latency mobile link, where the TLS + HTTP/3 +
47    /// extended-CONNECT handshake routinely exceeds 2s. 5s covers
48    /// realistic mobile network jitter without holding admission for
49    /// pathologically dead peers.
50    pub bridge_stream_deadline: Duration,
51    /// Maximum time an outbound route may remain prepared or activating
52    /// without winning its final commit acknowledgement.
53    ///
54    /// A prepared route is deliberately invisible to Sessions and event
55    /// consumers while an application durably records its Connection ID.
56    /// Core aborts and closes the provisional adapter route when this
57    /// deadline expires. The same deadline fences a hung staged activation.
58    /// A finite default prevents abandoned durable-bind attempts or adapter
59    /// activation futures from retaining route capacity indefinitely.
60    pub outbound_preparation_timeout: Duration,
61    /// P6 — `Event::CapacityReport` emit cadence. None disables the
62    /// scheduler entirely.
63    pub capacity_report_interval: Option<Duration>,
64}
65
66impl Default for Config {
67    fn default() -> Self {
68        let cpus = thread::available_parallelism()
69            .map(|n| n.get())
70            .unwrap_or(1);
71        Self {
72            max_concurrent_setups: 256 * cpus,
73            max_direct_subscribers: 1_000,
74            conversation_store: Arc::new(MemoryConversationStore::new()),
75            vcon_store: Arc::new(MemoryVconStore::new()),
76            message_store: Arc::new(MemoryMessageStore::new()),
77            bridge_stream_deadline: Duration::from_secs(5),
78            outbound_preparation_timeout: Duration::from_secs(30),
79            capacity_report_interval: Some(Duration::from_secs(30)),
80        }
81    }
82}