Skip to main content

reifydb_sub_server/
state.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! Application state shared across request handler.
5//!
6//! This module provides the shared state that is passed to all HTTP and WebSocket
7//! handler, including the database engine and query configuration.
8
9use std::time::Duration;
10
11use reifydb_auth::service::AuthService;
12use reifydb_engine::engine::StandardEngine;
13use reifydb_runtime::{actor::system::ActorSystem, context::clock::Clock};
14
15use crate::interceptor::RequestInterceptorChain;
16
17/// Configuration for query execution.
18#[derive(Debug, Clone)]
19pub struct StateConfig {
20	/// Timeout for individual query execution.
21	/// If a query takes longer than this, it will be cancelled.
22	pub query_timeout: Duration,
23	/// Timeout for entire HTTP request lifecycle.
24	/// This includes reading the request, executing the query, and writing the response.
25	pub request_timeout: Duration,
26	/// Maximum concurrent connections allowed.
27	/// New connections beyond this limit will be rejected.
28	pub max_connections: usize,
29	/// Whether admin (DDL) operations are enabled on this listener.
30	pub admin_enabled: bool,
31}
32
33impl Default for StateConfig {
34	fn default() -> Self {
35		Self {
36			query_timeout: Duration::from_secs(30),
37			request_timeout: Duration::from_secs(60),
38			max_connections: 10_000,
39			admin_enabled: false,
40		}
41	}
42}
43
44impl StateConfig {
45	/// Create a new QueryConfig with default values.
46	pub fn new() -> Self {
47		Self::default()
48	}
49
50	/// Set the query timeout.
51	pub fn query_timeout(mut self, timeout: Duration) -> Self {
52		self.query_timeout = timeout;
53		self
54	}
55
56	/// Set the request timeout.
57	pub fn request_timeout(mut self, timeout: Duration) -> Self {
58		self.request_timeout = timeout;
59		self
60	}
61
62	/// Set the maximum connections.
63	pub fn max_connections(mut self, max: usize) -> Self {
64		self.max_connections = max;
65		self
66	}
67
68	/// Set whether admin operations are enabled.
69	pub fn admin_enabled(mut self, enabled: bool) -> Self {
70		self.admin_enabled = enabled;
71		self
72	}
73}
74
75/// Shared application state passed to all request handler.
76///
77/// This struct is cloneable and cheap to clone since `StandardEngine` uses
78/// `Arc` internally. Each handler receives a clone of this state.
79///
80/// # Example
81///
82/// ```ignore
83/// let state = AppState::new(actor_system, engine, QueryConfig::default(), interceptors);
84///
85/// // In an axum handler:
86/// async fn handle_query(State(state): State<AppState>, ...) {
87///     let system = state.actor_system();
88///     let engine = state.engine();
89///     // ...
90/// }
91/// ```
92#[derive(Clone)]
93pub struct AppState {
94	actor_system: ActorSystem,
95	engine: StandardEngine,
96	auth_service: AuthService,
97	config: StateConfig,
98	request_interceptors: RequestInterceptorChain,
99	clock: Clock,
100}
101
102impl AppState {
103	/// Create a new AppState with the given actor system, engine, configuration,
104	/// and request interceptor chain.
105	pub fn new(
106		actor_system: ActorSystem,
107		engine: StandardEngine,
108		auth_service: AuthService,
109		config: StateConfig,
110		request_interceptors: RequestInterceptorChain,
111		clock: Clock,
112	) -> Self {
113		Self {
114			actor_system,
115			engine,
116			auth_service,
117			config,
118			request_interceptors,
119			clock,
120		}
121	}
122
123	/// Clone this state with a different configuration, preserving the
124	/// interceptor chain and other shared resources.
125	pub fn clone_with_config(&self, config: StateConfig) -> Self {
126		Self {
127			actor_system: self.actor_system.clone(),
128			engine: self.engine.clone(),
129			auth_service: self.auth_service.clone(),
130			config,
131			request_interceptors: self.request_interceptors.clone(),
132			clock: self.clock.clone(),
133		}
134	}
135
136	/// Get a clone of the actor system.
137	///
138	/// This is cheap since `ActorSystem` uses `Arc` internally.
139	#[inline]
140	pub fn actor_system(&self) -> ActorSystem {
141		self.actor_system.clone()
142	}
143
144	/// Get a reference to the database engine.
145	#[inline]
146	pub fn engine(&self) -> &StandardEngine {
147		&self.engine
148	}
149
150	/// Get a clone of the database engine.
151	///
152	/// This is cheap since `StandardEngine` uses `Arc` internally.
153	#[inline]
154	pub fn engine_clone(&self) -> StandardEngine {
155		self.engine.clone()
156	}
157
158	/// Get a reference to the query configuration.
159	#[inline]
160	pub fn config(&self) -> &StateConfig {
161		&self.config
162	}
163
164	/// Get the query timeout from configuration.
165	#[inline]
166	pub fn query_timeout(&self) -> Duration {
167		self.config.query_timeout
168	}
169
170	/// Get the request timeout from configuration.
171	#[inline]
172	pub fn request_timeout(&self) -> Duration {
173		self.config.request_timeout
174	}
175
176	/// Get the maximum connections from configuration.
177	#[inline]
178	pub fn max_connections(&self) -> usize {
179		self.config.max_connections
180	}
181
182	/// Get whether admin operations are enabled.
183	#[inline]
184	pub fn admin_enabled(&self) -> bool {
185		self.config.admin_enabled
186	}
187
188	/// Get a reference to the request interceptor chain.
189	#[inline]
190	pub fn request_interceptors(&self) -> &RequestInterceptorChain {
191		&self.request_interceptors
192	}
193
194	/// Get a reference to the clock.
195	#[inline]
196	pub fn clock(&self) -> &Clock {
197		&self.clock
198	}
199
200	/// Get a reference to the authentication service.
201	#[inline]
202	pub fn auth_service(&self) -> &AuthService {
203		&self.auth_service
204	}
205}
206
207#[cfg(test)]
208pub mod tests {
209	use super::*;
210
211	#[test]
212	fn test_query_configaults() {
213		let config = StateConfig::default();
214		assert_eq!(config.query_timeout, Duration::from_secs(30));
215		assert_eq!(config.request_timeout, Duration::from_secs(60));
216		assert_eq!(config.max_connections, 10_000);
217	}
218
219	#[test]
220	fn test_query_config_builder() {
221		let config = StateConfig::new()
222			.query_timeout(Duration::from_secs(60))
223			.request_timeout(Duration::from_secs(120))
224			.max_connections(5_000);
225
226		assert_eq!(config.query_timeout, Duration::from_secs(60));
227		assert_eq!(config.request_timeout, Duration::from_secs(120));
228		assert_eq!(config.max_connections, 5_000);
229	}
230}