Skip to main content

reifydb_sub_server/
state.rs

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