1use 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 pub claim_wait_max: Duration,
36
37 pub claim_lease_ttl: Duration,
38
39 pub claim_max_parked: usize,
40}
41
42impl Default for StateConfig {
43 fn default() -> Self {
44 Self {
45 query_timeout: Duration::from_seconds(30).unwrap(),
46 request_timeout: Duration::from_seconds(60).unwrap(),
47 max_connections: 10_000,
48 admin_enabled: false,
49 subscribe_max_hydration_rows: 10_000,
50 subscribe_min_throttle: Duration::from_milliseconds(50).unwrap(),
51 subscribe_min_linger: Duration::zero(),
52 claim_wait_max: Duration::from_seconds(60).unwrap(),
53 claim_lease_ttl: Duration::from_seconds(30).unwrap(),
54 claim_max_parked: 10_000,
55 }
56 }
57}
58
59impl StateConfig {
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn query_timeout(mut self, timeout: Duration) -> Self {
65 self.query_timeout = timeout;
66 self
67 }
68
69 pub fn request_timeout(mut self, timeout: Duration) -> Self {
70 self.request_timeout = timeout;
71 self
72 }
73
74 pub fn max_connections(mut self, max: usize) -> Self {
75 self.max_connections = max;
76 self
77 }
78
79 pub fn admin_enabled(mut self, enabled: bool) -> Self {
80 self.admin_enabled = enabled;
81 self
82 }
83
84 pub fn subscribe_max_hydration_rows(mut self, n: u64) -> Self {
85 self.subscribe_max_hydration_rows = n;
86 self
87 }
88
89 pub fn subscribe_min_throttle(mut self, interval: Duration) -> Self {
90 self.subscribe_min_throttle = interval;
91 self
92 }
93
94 pub fn subscribe_min_linger(mut self, interval: Duration) -> Self {
95 self.subscribe_min_linger = interval;
96 self
97 }
98
99 pub fn claim_wait_max(mut self, budget: Duration) -> Self {
100 self.claim_wait_max = budget;
101 self
102 }
103
104 pub fn claim_lease_ttl(mut self, ttl: Duration) -> Self {
105 self.claim_lease_ttl = ttl;
106 self
107 }
108
109 pub fn claim_max_parked(mut self, max: usize) -> Self {
110 self.claim_max_parked = max;
111 self
112 }
113}
114
115#[derive(Clone)]
116pub struct AppState {
117 spawner: ActorSpawner,
118 engine: StandardEngine,
119 auth_service: AuthService,
120 config: StateConfig,
121 request_interceptors: RequestInterceptorChain,
122 clock: Clock,
123 rng: Rng,
124}
125
126impl AppState {
127 pub fn new(
128 spawner: ActorSpawner,
129 engine: StandardEngine,
130 auth_service: AuthService,
131 config: StateConfig,
132 request_interceptors: RequestInterceptorChain,
133 clock: Clock,
134 rng: Rng,
135 ) -> Self {
136 Self {
137 spawner,
138 engine,
139 auth_service,
140 config,
141 request_interceptors,
142 clock,
143 rng,
144 }
145 }
146
147 pub fn clone_with_config(&self, config: StateConfig) -> Self {
148 Self {
149 spawner: self.spawner.clone(),
150 engine: self.engine.clone(),
151 auth_service: self.auth_service.clone(),
152 config,
153 request_interceptors: self.request_interceptors.clone(),
154 clock: self.clock.clone(),
155 rng: self.rng.clone(),
156 }
157 }
158
159 #[inline]
160 pub fn spawner(&self) -> ActorSpawner {
161 self.spawner.clone()
162 }
163
164 #[inline]
165 pub fn engine(&self) -> &StandardEngine {
166 &self.engine
167 }
168
169 #[inline]
170 pub fn engine_clone(&self) -> StandardEngine {
171 self.engine.clone()
172 }
173
174 #[inline]
175 pub fn config(&self) -> &StateConfig {
176 &self.config
177 }
178
179 #[inline]
180 pub fn query_timeout(&self) -> Duration {
181 self.config.query_timeout
182 }
183
184 #[inline]
185 pub fn request_timeout(&self) -> Duration {
186 self.config.request_timeout
187 }
188
189 #[inline]
190 pub fn max_connections(&self) -> usize {
191 self.config.max_connections
192 }
193
194 #[inline]
195 pub fn admin_enabled(&self) -> bool {
196 self.config.admin_enabled
197 }
198
199 #[inline]
200 pub fn subscribe_max_hydration_rows(&self) -> u64 {
201 self.config.subscribe_max_hydration_rows
202 }
203
204 #[inline]
205 pub fn subscribe_min_throttle(&self) -> Duration {
206 self.config.subscribe_min_throttle
207 }
208
209 #[inline]
210 pub fn claim_wait_max(&self) -> Duration {
211 self.config.claim_wait_max
212 }
213
214 #[inline]
215 pub fn claim_lease_ttl(&self) -> Duration {
216 self.config.claim_lease_ttl
217 }
218
219 #[inline]
220 pub fn claim_max_parked(&self) -> usize {
221 self.config.claim_max_parked
222 }
223
224 pub fn clamp_throttle(&self, requested: Option<Duration>) -> Duration {
225 match requested {
226 Some(d) if !d.is_zero() => d.max(self.config.subscribe_min_throttle),
227 _ => Duration::zero(),
228 }
229 }
230
231 #[inline]
232 pub fn subscribe_min_linger(&self) -> Duration {
233 self.config.subscribe_min_linger
234 }
235
236 pub fn clamp_linger(&self, requested: Option<Duration>) -> Duration {
237 match requested {
238 Some(d) if !d.is_zero() => d.max(self.config.subscribe_min_linger),
239 _ => Duration::zero(),
240 }
241 }
242
243 #[inline]
244 pub fn request_interceptors(&self) -> &RequestInterceptorChain {
245 &self.request_interceptors
246 }
247
248 #[inline]
249 pub fn clock(&self) -> &Clock {
250 &self.clock
251 }
252
253 #[inline]
254 pub fn rng(&self) -> &Rng {
255 &self.rng
256 }
257
258 #[inline]
259 pub fn auth_service(&self) -> &AuthService {
260 &self.auth_service
261 }
262
263 #[instrument(name = "actor::spawn_server", level = "info", skip_all)]
264 pub fn spawn_server_actor(&self) -> (ActorRef<ServerMessage>, ActorHandle<ServerMessage>) {
265 let actor = ServerActor::new(self.engine.clone(), self.auth_service.clone(), self.clock.clone());
266 let handle = self.spawner.spawn_ephemeral("server-req", actor);
267 let actor_ref = handle.actor_ref().clone();
268 (actor_ref, handle)
269 }
270}
271
272#[cfg(test)]
273pub mod tests {
274 use super::*;
275
276 #[test]
277 fn test_query_defaults() {
278 let config = StateConfig::default();
279 assert_eq!(config.query_timeout, Duration::from_seconds(30).unwrap());
280 assert_eq!(config.request_timeout, Duration::from_seconds(60).unwrap());
281 assert_eq!(config.max_connections, 10_000);
282 assert_eq!(config.subscribe_max_hydration_rows, 10_000);
283 assert_eq!(config.subscribe_min_throttle, Duration::from_milliseconds(50).unwrap());
284 assert_eq!(config.subscribe_min_linger, Duration::zero());
285 }
286
287 #[test]
288 fn test_query_config_builder() {
289 let config = StateConfig::new()
290 .query_timeout(Duration::from_seconds(60).unwrap())
291 .request_timeout(Duration::from_seconds(120).unwrap())
292 .max_connections(5_000)
293 .subscribe_max_hydration_rows(50_000)
294 .subscribe_min_throttle(Duration::from_milliseconds(200).unwrap())
295 .subscribe_min_linger(Duration::from_milliseconds(20).unwrap());
296
297 assert_eq!(config.query_timeout, Duration::from_seconds(60).unwrap());
298 assert_eq!(config.request_timeout, Duration::from_seconds(120).unwrap());
299 assert_eq!(config.max_connections, 5_000);
300 assert_eq!(config.subscribe_max_hydration_rows, 50_000);
301 assert_eq!(config.subscribe_min_throttle, Duration::from_milliseconds(200).unwrap());
302 assert_eq!(config.subscribe_min_linger, Duration::from_milliseconds(20).unwrap());
303 }
304}