itsi-scheduler 0.2.22-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +8 -0
  3. data/Cargo.lock +997 -0
  4. data/Cargo.toml +7 -0
  5. data/Rakefile +39 -0
  6. data/ext/itsi_acme/Cargo.toml +86 -0
  7. data/ext/itsi_acme/examples/high_level.rs +63 -0
  8. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  9. data/ext/itsi_acme/examples/low_level.rs +87 -0
  10. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  11. data/ext/itsi_acme/src/acceptor.rs +81 -0
  12. data/ext/itsi_acme/src/acme.rs +354 -0
  13. data/ext/itsi_acme/src/axum.rs +86 -0
  14. data/ext/itsi_acme/src/cache.rs +39 -0
  15. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  16. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  17. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  18. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  19. data/ext/itsi_acme/src/caches/no.rs +78 -0
  20. data/ext/itsi_acme/src/caches/test.rs +136 -0
  21. data/ext/itsi_acme/src/config.rs +172 -0
  22. data/ext/itsi_acme/src/https_helper.rs +69 -0
  23. data/ext/itsi_acme/src/incoming.rs +142 -0
  24. data/ext/itsi_acme/src/jose.rs +161 -0
  25. data/ext/itsi_acme/src/lib.rs +142 -0
  26. data/ext/itsi_acme/src/resolver.rs +59 -0
  27. data/ext/itsi_acme/src/state.rs +424 -0
  28. data/ext/itsi_error/Cargo.lock +368 -0
  29. data/ext/itsi_error/Cargo.toml +12 -0
  30. data/ext/itsi_error/src/lib.rs +140 -0
  31. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  32. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  33. data/ext/itsi_rb_helpers/Cargo.lock +355 -0
  34. data/ext/itsi_rb_helpers/Cargo.toml +11 -0
  35. data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
  36. data/ext/itsi_rb_helpers/src/lib.rs +232 -0
  37. data/ext/itsi_scheduler/Cargo.toml +24 -0
  38. data/ext/itsi_scheduler/extconf.rb +11 -0
  39. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  40. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  41. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  42. data/ext/itsi_scheduler/src/itsi_scheduler.rs +320 -0
  43. data/ext/itsi_scheduler/src/lib.rs +39 -0
  44. data/ext/itsi_server/Cargo.lock +2956 -0
  45. data/ext/itsi_server/Cargo.toml +94 -0
  46. data/ext/itsi_server/src/default_responses/mod.rs +14 -0
  47. data/ext/itsi_server/src/env.rs +43 -0
  48. data/ext/itsi_server/src/lib.rs +154 -0
  49. data/ext/itsi_server/src/prelude.rs +2 -0
  50. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +116 -0
  51. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +149 -0
  52. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +346 -0
  53. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +265 -0
  54. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +399 -0
  55. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +447 -0
  56. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +545 -0
  57. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +650 -0
  58. data/ext/itsi_server/src/ruby_types/itsi_server.rs +102 -0
  59. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  60. data/ext/itsi_server/src/server/binds/bind.rs +204 -0
  61. data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
  62. data/ext/itsi_server/src/server/binds/listener.rs +485 -0
  63. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  64. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
  65. data/ext/itsi_server/src/server/binds/tls.rs +278 -0
  66. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  67. data/ext/itsi_server/src/server/frame_stream.rs +143 -0
  68. data/ext/itsi_server/src/server/http_message_types.rs +230 -0
  69. data/ext/itsi_server/src/server/io_stream.rs +128 -0
  70. data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +93 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +329 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +300 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  80. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  81. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +188 -0
  82. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +168 -0
  83. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +183 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +133 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +122 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +407 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +155 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +54 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +138 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +269 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +62 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +218 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  99. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  100. data/ext/itsi_server/src/server/mod.rs +14 -0
  101. data/ext/itsi_server/src/server/process_worker.rs +247 -0
  102. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  103. data/ext/itsi_server/src/server/request_job.rs +11 -0
  104. data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
  105. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +411 -0
  106. data/ext/itsi_server/src/server/serve_strategy/mod.rs +31 -0
  107. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +449 -0
  108. data/ext/itsi_server/src/server/signal.rs +129 -0
  109. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  110. data/ext/itsi_server/src/server/thread_worker.rs +504 -0
  111. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  112. data/ext/itsi_server/src/services/itsi_http_service.rs +270 -0
  113. data/ext/itsi_server/src/services/mime_types.rs +2896 -0
  114. data/ext/itsi_server/src/services/mod.rs +6 -0
  115. data/ext/itsi_server/src/services/password_hasher.rs +89 -0
  116. data/ext/itsi_server/src/services/rate_limiter.rs +609 -0
  117. data/ext/itsi_server/src/services/static_file_server.rs +1400 -0
  118. data/ext/itsi_tracing/Cargo.lock +274 -0
  119. data/ext/itsi_tracing/Cargo.toml +17 -0
  120. data/ext/itsi_tracing/src/lib.rs +370 -0
  121. data/itsi-scheduler-100.png +0 -0
  122. data/lib/itsi/schedule_refinement.rb +96 -0
  123. data/lib/itsi/scheduler/3.1/itsi_scheduler.so +0 -0
  124. data/lib/itsi/scheduler/3.2/itsi_scheduler.so +0 -0
  125. data/lib/itsi/scheduler/3.3/itsi_scheduler.so +0 -0
  126. data/lib/itsi/scheduler/3.4/itsi_scheduler.so +0 -0
  127. data/lib/itsi/scheduler/4.0/itsi_scheduler.so +0 -0
  128. data/lib/itsi/scheduler/native_extension.rb +34 -0
  129. data/lib/itsi/scheduler/version.rb +7 -0
  130. data/lib/itsi/scheduler.rb +153 -0
  131. data/vendor/rb-sys-build/.cargo-ok +1 -0
  132. data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
  133. data/vendor/rb-sys-build/Cargo.lock +294 -0
  134. data/vendor/rb-sys-build/Cargo.toml +71 -0
  135. data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
  136. data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
  137. data/vendor/rb-sys-build/LICENSE-MIT +21 -0
  138. data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
  139. data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
  140. data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
  141. data/vendor/rb-sys-build/src/bindings.rs +280 -0
  142. data/vendor/rb-sys-build/src/cc.rs +421 -0
  143. data/vendor/rb-sys-build/src/lib.rs +12 -0
  144. data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
  145. data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
  146. data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
  147. data/vendor/rb-sys-build/src/rb_config.rs +906 -0
  148. data/vendor/rb-sys-build/src/utils.rs +53 -0
  149. metadata +210 -0
@@ -0,0 +1,411 @@
1
+ use crate::ruby_types::itsi_server::itsi_server_config::ItsiServerConfig;
2
+ use crate::server::signal::{subscribe_runtime_to_signals, unsubscribe_runtime};
3
+ use crate::server::{lifecycle_event::LifecycleEvent, process_worker::ProcessWorker};
4
+ use itsi_error::{ItsiError, Result};
5
+ use itsi_rb_helpers::{call_with_gvl, call_without_gvl, create_ruby_thread};
6
+ use itsi_tracing::{error, info, warn};
7
+ use magnus::Value;
8
+ use nix::{libc::exit, unistd::Pid};
9
+
10
+ use std::sync::atomic::{AtomicBool, Ordering};
11
+ use std::{
12
+ sync::Arc,
13
+ time::{Duration, Instant},
14
+ };
15
+ use tokio::{
16
+ runtime::{Builder as RuntimeBuilder, Runtime},
17
+ sync::{watch, Mutex},
18
+ time::{self, sleep},
19
+ };
20
+ use tracing::{debug, instrument};
21
+ pub(crate) struct ClusterMode {
22
+ pub server_config: Arc<ItsiServerConfig>,
23
+ pub process_workers: parking_lot::Mutex<Vec<ProcessWorker>>,
24
+ }
25
+
26
+ static CHILD_SIGNAL_SENDER: parking_lot::Mutex<Option<watch::Sender<()>>> =
27
+ parking_lot::Mutex::new(None);
28
+
29
+ static RELOAD_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
30
+
31
+ impl ClusterMode {
32
+ pub fn new(server_config: Arc<ItsiServerConfig>) -> Self {
33
+ let process_workers = (0..server_config.server_params.read().workers)
34
+ .map(|id| ProcessWorker {
35
+ worker_id: id as usize,
36
+ ..Default::default()
37
+ })
38
+ .collect();
39
+
40
+ Self {
41
+ server_config,
42
+ process_workers: parking_lot::Mutex::new(process_workers),
43
+ }
44
+ }
45
+
46
+ pub fn build_runtime(&self) -> Runtime {
47
+ let mut builder: RuntimeBuilder = RuntimeBuilder::new_current_thread();
48
+ builder
49
+ .thread_name("itsi-server-accept-loop")
50
+ .thread_stack_size(3 * 1024 * 1024)
51
+ .enable_io()
52
+ .enable_time()
53
+ .build()
54
+ .expect("Failed to build Tokio runtime")
55
+ }
56
+
57
+ pub fn invoke_hook(&self, hook_name: &str) {
58
+ if let Some(hook) = self.server_config.server_params.read().hooks.get(hook_name) {
59
+ call_with_gvl(|_| hook.call::<_, Value>(()).ok());
60
+ }
61
+ }
62
+
63
+ fn next_worker_id(&self) -> usize {
64
+ let mut ids: Vec<usize> = self
65
+ .process_workers
66
+ .lock()
67
+ .iter()
68
+ .map(|w| w.worker_id)
69
+ .collect();
70
+ self.next_available_id_in(&mut ids)
71
+ }
72
+
73
+ fn next_available_id_in(&self, list: &mut [usize]) -> usize {
74
+ list.sort_unstable();
75
+ for (expected, &id) in list.iter().enumerate() {
76
+ if id != expected {
77
+ return expected;
78
+ }
79
+ }
80
+ list.len()
81
+ }
82
+
83
+ #[allow(clippy::await_holding_lock)]
84
+ pub async fn handle_lifecycle_event(
85
+ self: Arc<Self>,
86
+ lifecycle_event: LifecycleEvent,
87
+ ) -> Result<()> {
88
+ match lifecycle_event {
89
+ LifecycleEvent::Start => Ok(()),
90
+ LifecycleEvent::PrintInfo => {
91
+ self.print_info().await?;
92
+ Ok(())
93
+ }
94
+ LifecycleEvent::Shutdown => {
95
+ self.server_config.stop_watcher()?;
96
+ self.shutdown().await?;
97
+ self.invoke_hook("before_shutdown");
98
+ Ok(())
99
+ }
100
+ LifecycleEvent::Restart => {
101
+ if self.server_config.check_config().await {
102
+ self.invoke_hook("before_restart");
103
+ self.server_config.stop_watcher()?;
104
+ self.server_config.dup_fds()?;
105
+ self.shutdown().await.ok();
106
+ info!("Shutdown complete. Calling reload exec");
107
+
108
+ self.server_config.reload_exec()?;
109
+ }
110
+ Ok(())
111
+ }
112
+ LifecycleEvent::Reload => {
113
+ if !self.server_config.check_config().await {
114
+ return Ok(());
115
+ }
116
+
117
+ let should_reexec = self.server_config.clone().reload(true)?;
118
+
119
+ if should_reexec {
120
+ self.server_config.stop_watcher()?;
121
+ self.server_config.dup_fds()?;
122
+ self.shutdown().await.ok();
123
+ self.server_config.reload_exec()?;
124
+ }
125
+
126
+ if RELOAD_IN_PROGRESS
127
+ .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
128
+ .is_err()
129
+ {
130
+ warn!("Reload already in progress, ignoring request");
131
+ return Ok(());
132
+ }
133
+ let workers_to_load = self.server_config.server_params.read().workers;
134
+ let mut next_workers = Vec::new();
135
+ let mut old_workers = self.process_workers.lock().drain(..).collect::<Vec<_>>();
136
+
137
+ // Spawn new workers
138
+ for i in 0..workers_to_load {
139
+ let worker = ProcessWorker {
140
+ worker_id: i as usize,
141
+ ..Default::default()
142
+ };
143
+ let worker_clone = worker.clone();
144
+ let self_clone = self.clone();
145
+
146
+ call_with_gvl(|_| {
147
+ create_ruby_thread(move || {
148
+ call_without_gvl(move || {
149
+ if let Err(err) = worker_clone.boot(self_clone) {
150
+ error!("Worker boot failed {:?}", err);
151
+ }
152
+ })
153
+ });
154
+ });
155
+
156
+ next_workers.push(worker);
157
+
158
+ if let Some(old) = old_workers.pop() {
159
+ old.graceful_shutdown(self.clone()).await;
160
+ }
161
+ }
162
+
163
+ for worker in old_workers {
164
+ worker.graceful_shutdown(self.clone()).await;
165
+ }
166
+
167
+ self.process_workers.lock().extend(next_workers);
168
+ RELOAD_IN_PROGRESS.store(false, Ordering::SeqCst);
169
+
170
+ Ok(())
171
+ }
172
+ LifecycleEvent::IncreaseWorkers => {
173
+ let mut workers = self.process_workers.lock();
174
+ let worker = ProcessWorker {
175
+ worker_id: self.next_worker_id(),
176
+ ..Default::default()
177
+ };
178
+ let worker_clone = worker.clone();
179
+ let self_clone = self.clone();
180
+ create_ruby_thread(move || {
181
+ call_without_gvl(move || {
182
+ worker_clone.boot(self_clone).ok();
183
+ })
184
+ });
185
+ workers.push(worker);
186
+ Ok(())
187
+ }
188
+ LifecycleEvent::DecreaseWorkers => {
189
+ let worker = {
190
+ let mut workers = self.process_workers.lock();
191
+ workers.pop()
192
+ };
193
+ if let Some(dropped_worker) = worker {
194
+ dropped_worker.request_shutdown();
195
+ let force_kill_time = Instant::now()
196
+ + Duration::from_secs_f64(
197
+ self.server_config.server_params.read().shutdown_timeout,
198
+ );
199
+ while dropped_worker.is_alive() && force_kill_time > Instant::now() {
200
+ tokio::time::sleep(Duration::from_millis(100)).await;
201
+ }
202
+ if dropped_worker.is_alive() {
203
+ dropped_worker.force_kill();
204
+ }
205
+ };
206
+ Ok(())
207
+ }
208
+ LifecycleEvent::ForceShutdown => {
209
+ for worker in self.process_workers.lock().iter() {
210
+ worker.force_kill();
211
+ }
212
+ error!("Force shutdown!");
213
+ unsafe { exit(0) };
214
+ }
215
+ LifecycleEvent::ChildTerminated => {
216
+ if RELOAD_IN_PROGRESS.load(Ordering::SeqCst) {
217
+ warn!("Reload already in progress, ignoring child signal");
218
+ return Ok(());
219
+ }
220
+ CHILD_SIGNAL_SENDER.lock().as_ref().inspect(|i| {
221
+ i.send(()).ok();
222
+ });
223
+ Ok(())
224
+ }
225
+ }
226
+ }
227
+
228
+ pub async fn shutdown(&self) -> Result<()> {
229
+ let shutdown_timeout = self.server_config.server_params.read().shutdown_timeout;
230
+ let workers = self.process_workers.lock().clone();
231
+
232
+ workers.iter().for_each(|worker| worker.request_shutdown());
233
+
234
+ let remaining_children = Arc::new(Mutex::new(workers.len()));
235
+ let monitor_handle = {
236
+ let remaining_children: Arc<Mutex<usize>> = Arc::clone(&remaining_children);
237
+ let mut workers = workers.clone();
238
+ tokio::spawn(async move {
239
+ loop {
240
+ // Check if all workers have exited
241
+ let mut remaining = remaining_children.lock().await;
242
+ workers.retain(|worker| worker.is_alive());
243
+ *remaining = workers.len();
244
+ if *remaining == 0 {
245
+ break;
246
+ }
247
+ sleep(Duration::from_millis(100)).await;
248
+ }
249
+ })
250
+ };
251
+
252
+ tokio::select! {
253
+ _ = monitor_handle => {
254
+ debug!("All children exited early, exit normally")
255
+ }
256
+ _ = sleep(Duration::from_secs_f64(shutdown_timeout)) => {
257
+ warn!("Graceful shutdown timeout reached, force killing remaining children");
258
+ workers.iter().for_each(|worker| worker.force_kill());
259
+ }
260
+ }
261
+
262
+ Err(ItsiError::Break)
263
+ }
264
+
265
+ pub async fn print_info(self: Arc<Self>) -> Result<()> {
266
+ println!("Itsi Cluster Info:");
267
+ println!("Master PID: {:?}", Pid::this());
268
+ if let Some(memory_limit) = self.server_config.server_params.read().worker_memory_limit {
269
+ println!("Worker Memory Limit: {}", memory_limit);
270
+ }
271
+
272
+ if self.server_config.watcher_fd.is_some() {
273
+ println!("File Watcher Enabled: true",);
274
+ if let Some(watchers) = self
275
+ .server_config
276
+ .server_params
277
+ .read()
278
+ .notify_watchers
279
+ .as_ref()
280
+ {
281
+ for watcher in watchers {
282
+ println!(
283
+ "Watching path: {} => {}",
284
+ watcher.0,
285
+ watcher
286
+ .1
287
+ .iter()
288
+ .map(|path| path.join(","))
289
+ .collect::<Vec<String>>()
290
+ .join(" ")
291
+ );
292
+ }
293
+ }
294
+ }
295
+ println!(
296
+ "Silent Mode: {}",
297
+ self.server_config.server_params.read().silence
298
+ );
299
+ println!(
300
+ "Preload: {}",
301
+ self.server_config.server_params.read().preload
302
+ );
303
+ let workers = self.process_workers.lock().clone();
304
+ for worker in workers {
305
+ worker.print_info()?;
306
+ sleep(Duration::from_millis(50)).await;
307
+ }
308
+ Ok(())
309
+ }
310
+
311
+ pub fn stop(&self) -> Result<()> {
312
+ for worker in self.process_workers.lock().iter() {
313
+ if worker.is_alive() {
314
+ worker.force_kill();
315
+ }
316
+ }
317
+ Ok(())
318
+ }
319
+
320
+ #[instrument(skip(self), fields(mode = "cluster", pid=format!("{:?}", Pid::this())))]
321
+ pub fn run(self: Arc<Self>) -> Result<()> {
322
+ info!("Starting in Cluster mode");
323
+ self.invoke_hook("before_fork");
324
+
325
+ self.process_workers
326
+ .lock()
327
+ .iter()
328
+ .try_for_each(|worker| worker.boot(Arc::clone(&self)))?;
329
+
330
+ let (sender, mut receiver) = watch::channel(());
331
+ *CHILD_SIGNAL_SENDER.lock() = Some(sender);
332
+
333
+ let self_ref = self.clone();
334
+
335
+ self.build_runtime().block_on(async {
336
+ let mut lifecycle_rx = subscribe_runtime_to_signals();
337
+
338
+ let self_ref = self_ref.clone();
339
+ let memory_check_duration = if self_ref.server_config.server_params.read().worker_memory_limit.is_some(){
340
+ time::Duration::from_secs(15)
341
+ } else {
342
+ time::Duration::from_secs(60 * 60 * 24 * 365 * 100)
343
+ };
344
+
345
+ let mut memory_check_interval = time::interval(memory_check_duration);
346
+
347
+ self.invoke_hook("after_start");
348
+
349
+ loop {
350
+ tokio::select! {
351
+ _ = receiver.changed() => {
352
+ let mut workers = self_ref.process_workers.lock();
353
+ workers.retain(|worker| {
354
+ worker.boot_if_dead(self_ref.clone())
355
+ });
356
+ if workers.is_empty() {
357
+ warn!("No workers running. Send SIGTTIN to increase worker count");
358
+ }
359
+ }
360
+ _ = memory_check_interval.tick() => {
361
+ let worker_memory_limit = self_ref.server_config.server_params.read().worker_memory_limit;
362
+ if let Some(memory_limit) = worker_memory_limit {
363
+ let largest_worker = {
364
+ let workers = self_ref.process_workers.lock();
365
+ workers.iter().max_by(|wa, wb| wa.memory_usage().cmp(&wb.memory_usage())).cloned()
366
+ };
367
+ if let Some(largest_worker) = largest_worker {
368
+ if let Some(current_mem_usage) = largest_worker.memory_usage(){
369
+ if current_mem_usage > memory_limit {
370
+ largest_worker.reboot(self_ref.clone()).await.ok();
371
+ if let Some(hook) = self_ref.server_config.server_params.read().hooks.get("after_memory_limit_reached") {
372
+ call_with_gvl(|_| hook.call::<_, Value>((largest_worker.pid(),)).ok() );
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+ lifecycle_event = lifecycle_rx.recv() => match lifecycle_event{
380
+ Ok(lifecycle_event) => {
381
+ debug!("Cluster mode received lifecycle event: {:?}", lifecycle_event);
382
+ if let Err(e) = self_ref.clone().handle_lifecycle_event(lifecycle_event).await{
383
+ match e {
384
+ ItsiError::Break => {
385
+ debug!("Lifecycle event triggered shutdown, breaking cluster monitor loop");
386
+ break;
387
+ },
388
+ _ => error!("Error in handle_lifecycle_event {:?}", e)
389
+ }
390
+ }
391
+
392
+ },
393
+ Err(e) => {
394
+ debug!("Lifecycle channel closed: {:?}, exiting cluster monitor loop", e);
395
+ break
396
+ },
397
+ }
398
+ }
399
+ }
400
+ });
401
+
402
+ unsubscribe_runtime();
403
+ self.server_config
404
+ .server_params
405
+ .write()
406
+ .listeners
407
+ .lock()
408
+ .drain(..);
409
+ Ok(())
410
+ }
411
+ }
@@ -0,0 +1,31 @@
1
+ use std::sync::Arc;
2
+
3
+ use cluster_mode::ClusterMode;
4
+ use itsi_error::Result;
5
+ use single_mode::SingleMode;
6
+
7
+ pub mod acceptor;
8
+ pub mod cluster_mode;
9
+ pub mod single_mode;
10
+
11
+ #[derive(Clone)]
12
+ pub(crate) enum ServeStrategy {
13
+ Single(Arc<SingleMode>),
14
+ Cluster(Arc<ClusterMode>),
15
+ }
16
+
17
+ impl ServeStrategy {
18
+ pub fn run(self) -> Result<()> {
19
+ match self {
20
+ ServeStrategy::Single(single_router) => single_router.run(),
21
+ ServeStrategy::Cluster(cluster_router) => cluster_router.run(),
22
+ }
23
+ }
24
+
25
+ pub(crate) fn stop(&self) -> Result<()> {
26
+ match self {
27
+ ServeStrategy::Single(single_router) => single_router.stop(),
28
+ ServeStrategy::Cluster(cluster_router) => cluster_router.stop(),
29
+ }
30
+ }
31
+ }