Skip to main content

reifydb_sub_server/
state.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_auth::service::AuthService;
5use reifydb_core::actors::server::ServerMessage;
6use reifydb_engine::engine::StandardEngine;
7use reifydb_runtime::{
8	actor::{
9		mailbox::ActorRef,
10		system::{ActorHandle, ActorSpawner},
11	},
12	context::{clock::Clock, rng::Rng},
13};
14use reifydb_value::value::duration::Duration;
15use tracing::instrument;
16
17use crate::{actor::ServerActor, interceptor::RequestInterceptorChain};
18
19#[derive(Debug, Clone)]
20pub struct StateConfig {
21	pub query_timeout: Duration,
22
23	pub request_timeout: Duration,
24
25	pub max_connections: usize,
26
27	pub admin_enabled: bool,
28
29	pub subscribe_max_hydration_rows: u64,
30
31	pub subscribe_min_throttle: Duration,
32
33	pub subscribe_min_linger: Duration,
34}
35
36impl Default for StateConfig {
37	fn default() -> Self {
38		Self {
39			query_timeout: Duration::from_seconds(30).unwrap(),
40			request_timeout: Duration::from_seconds(60).unwrap(),
41			max_connections: 10_000,
42			admin_enabled: false,
43			subscribe_max_hydration_rows: 10_000,
44			subscribe_min_throttle: Duration::from_milliseconds(50).unwrap(),
45			subscribe_min_linger: Duration::zero(),
46		}
47	}
48}
49
50impl StateConfig {
51	pub fn new() -> Self {
52		Self::default()
53	}
54
55	pub fn query_timeout(mut self, timeout: Duration) -> Self {
56		self.query_timeout = timeout;
57		self
58	}
59
60	pub fn request_timeout(mut self, timeout: Duration) -> Self {
61		self.request_timeout = timeout;
62		self
63	}
64
65	pub fn max_connections(mut self, max: usize) -> Self {
66		self.max_connections = max;
67		self
68	}
69
70	pub fn admin_enabled(mut self, enabled: bool) -> Self {
71		self.admin_enabled = enabled;
72		self
73	}
74
75	pub fn subscribe_max_hydration_rows(mut self, n: u64) -> Self {
76		self.subscribe_max_hydration_rows = n;
77		self
78	}
79
80	pub fn subscribe_min_throttle(mut self, interval: Duration) -> Self {
81		self.subscribe_min_throttle = interval;
82		self
83	}
84
85	pub fn subscribe_min_linger(mut self, interval: Duration) -> Self {
86		self.subscribe_min_linger = interval;
87		self
88	}
89}
90
91#[derive(Clone)]
92pub struct AppState {
93	spawner: ActorSpawner,
94	engine: StandardEngine,
95	auth_service: AuthService,
96	config: StateConfig,
97	request_interceptors: RequestInterceptorChain,
98	clock: Clock,
99	rng: Rng,
100}
101
102impl AppState {
103	pub fn new(
104		spawner: ActorSpawner,
105		engine: StandardEngine,
106		auth_service: AuthService,
107		config: StateConfig,
108		request_interceptors: RequestInterceptorChain,
109		clock: Clock,
110		rng: Rng,
111	) -> Self {
112		Self {
113			spawner,
114			engine,
115			auth_service,
116			config,
117			request_interceptors,
118			clock,
119			rng,
120		}
121	}
122
123	pub fn clone_with_config(&self, config: StateConfig) -> Self {
124		Self {
125			spawner: self.spawner.clone(),
126			engine: self.engine.clone(),
127			auth_service: self.auth_service.clone(),
128			config,
129			request_interceptors: self.request_interceptors.clone(),
130			clock: self.clock.clone(),
131			rng: self.rng.clone(),
132		}
133	}
134
135	#[inline]
136	pub fn spawner(&self) -> ActorSpawner {
137		self.spawner.clone()
138	}
139
140	#[inline]
141	pub fn engine(&self) -> &StandardEngine {
142		&self.engine
143	}
144
145	#[inline]
146	pub fn engine_clone(&self) -> StandardEngine {
147		self.engine.clone()
148	}
149
150	#[inline]
151	pub fn config(&self) -> &StateConfig {
152		&self.config
153	}
154
155	#[inline]
156	pub fn query_timeout(&self) -> Duration {
157		self.config.query_timeout
158	}
159
160	#[inline]
161	pub fn request_timeout(&self) -> Duration {
162		self.config.request_timeout
163	}
164
165	#[inline]
166	pub fn max_connections(&self) -> usize {
167		self.config.max_connections
168	}
169
170	#[inline]
171	pub fn admin_enabled(&self) -> bool {
172		self.config.admin_enabled
173	}
174
175	#[inline]
176	pub fn subscribe_max_hydration_rows(&self) -> u64 {
177		self.config.subscribe_max_hydration_rows
178	}
179
180	#[inline]
181	pub fn subscribe_min_throttle(&self) -> Duration {
182		self.config.subscribe_min_throttle
183	}
184
185	pub fn clamp_throttle(&self, requested: Option<Duration>) -> Duration {
186		match requested {
187			Some(d) if !d.is_zero() => d.max(self.config.subscribe_min_throttle),
188			_ => Duration::zero(),
189		}
190	}
191
192	#[inline]
193	pub fn subscribe_min_linger(&self) -> Duration {
194		self.config.subscribe_min_linger
195	}
196
197	pub fn clamp_linger(&self, requested: Option<Duration>) -> Duration {
198		match requested {
199			Some(d) if !d.is_zero() => d.max(self.config.subscribe_min_linger),
200			_ => Duration::zero(),
201		}
202	}
203
204	#[inline]
205	pub fn request_interceptors(&self) -> &RequestInterceptorChain {
206		&self.request_interceptors
207	}
208
209	#[inline]
210	pub fn clock(&self) -> &Clock {
211		&self.clock
212	}
213
214	#[inline]
215	pub fn rng(&self) -> &Rng {
216		&self.rng
217	}
218
219	#[inline]
220	pub fn auth_service(&self) -> &AuthService {
221		&self.auth_service
222	}
223
224	#[instrument(name = "actor::spawn_server", level = "info", skip_all)]
225	pub fn spawn_server_actor(&self) -> (ActorRef<ServerMessage>, ActorHandle<ServerMessage>) {
226		let actor = ServerActor::new(self.engine.clone(), self.auth_service.clone(), self.clock.clone());
227		let handle = self.spawner.spawn_query("server-req", actor);
228		let actor_ref = handle.actor_ref().clone();
229		(actor_ref, handle)
230	}
231}
232
233#[cfg(test)]
234pub mod tests {
235	use super::*;
236
237	#[test]
238	fn test_query_defaults() {
239		let config = StateConfig::default();
240		assert_eq!(config.query_timeout, Duration::from_seconds(30).unwrap());
241		assert_eq!(config.request_timeout, Duration::from_seconds(60).unwrap());
242		assert_eq!(config.max_connections, 10_000);
243		assert_eq!(config.subscribe_max_hydration_rows, 10_000);
244		assert_eq!(config.subscribe_min_throttle, Duration::from_milliseconds(50).unwrap());
245		assert_eq!(config.subscribe_min_linger, Duration::zero());
246	}
247
248	#[test]
249	fn test_query_config_builder() {
250		let config = StateConfig::new()
251			.query_timeout(Duration::from_seconds(60).unwrap())
252			.request_timeout(Duration::from_seconds(120).unwrap())
253			.max_connections(5_000)
254			.subscribe_max_hydration_rows(50_000)
255			.subscribe_min_throttle(Duration::from_milliseconds(200).unwrap())
256			.subscribe_min_linger(Duration::from_milliseconds(20).unwrap());
257
258		assert_eq!(config.query_timeout, Duration::from_seconds(60).unwrap());
259		assert_eq!(config.request_timeout, Duration::from_seconds(120).unwrap());
260		assert_eq!(config.max_connections, 5_000);
261		assert_eq!(config.subscribe_max_hydration_rows, 50_000);
262		assert_eq!(config.subscribe_min_throttle, Duration::from_milliseconds(200).unwrap());
263		assert_eq!(config.subscribe_min_linger, Duration::from_milliseconds(20).unwrap());
264	}
265}