itsi-server 0.1.1 → 0.1.2

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/exe/itsi +88 -28
  3. data/ext/itsi_error/Cargo.toml +2 -0
  4. data/ext/itsi_error/src/from.rs +70 -0
  5. data/ext/itsi_error/src/lib.rs +10 -37
  6. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  7. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  8. data/ext/itsi_rb_helpers/Cargo.toml +2 -0
  9. data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  10. data/ext/itsi_rb_helpers/src/lib.rs +90 -10
  11. data/ext/itsi_scheduler/Cargo.toml +24 -0
  12. data/ext/itsi_scheduler/extconf.rb +6 -0
  13. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  14. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  15. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  16. data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  17. data/ext/itsi_scheduler/src/lib.rs +38 -0
  18. data/ext/itsi_server/Cargo.toml +14 -2
  19. data/ext/itsi_server/extconf.rb +1 -1
  20. data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  21. data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  22. data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  23. data/ext/itsi_server/src/lib.rs +58 -7
  24. data/ext/itsi_server/src/request/itsi_request.rs +238 -104
  25. data/ext/itsi_server/src/response/itsi_response.rs +347 -0
  26. data/ext/itsi_server/src/response/mod.rs +1 -0
  27. data/ext/itsi_server/src/server/bind.rs +50 -20
  28. data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  29. data/ext/itsi_server/src/server/io_stream.rs +104 -0
  30. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
  31. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
  32. data/ext/itsi_server/src/server/itsi_server.rs +181 -133
  33. data/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
  34. data/ext/itsi_server/src/server/listener.rs +169 -128
  35. data/ext/itsi_server/src/server/mod.rs +7 -1
  36. data/ext/itsi_server/src/server/process_worker.rs +196 -0
  37. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +253 -0
  38. data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  39. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +238 -0
  40. data/ext/itsi_server/src/server/signal.rs +57 -0
  41. data/ext/itsi_server/src/server/thread_worker.rs +368 -0
  42. data/ext/itsi_server/src/server/tls.rs +42 -28
  43. data/ext/itsi_tracing/Cargo.toml +4 -0
  44. data/ext/itsi_tracing/src/lib.rs +36 -6
  45. data/lib/itsi/request.rb +30 -14
  46. data/lib/itsi/server/rack/handler/itsi.rb +25 -0
  47. data/lib/itsi/server/scheduler_mode.rb +6 -0
  48. data/lib/itsi/server/version.rb +1 -1
  49. data/lib/itsi/server.rb +68 -2
  50. data/lib/itsi/signals.rb +18 -0
  51. data/lib/itsi/stream_io.rb +38 -0
  52. metadata +41 -14
  53. data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  54. data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -1,22 +1,17 @@
1
1
  use super::bind::{Bind, BindAddress};
2
- use super::transfer_protocol::TransferProtocol;
3
- use hyper_util::rt::TokioIo;
2
+ use super::bind_protocol::BindProtocol;
3
+ use super::io_stream::IoStream;
4
4
  use itsi_error::Result;
5
5
  use itsi_tracing::info;
6
6
  use socket2::{Domain, Protocol, Socket, Type};
7
- use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
8
- use std::pin::Pin;
7
+ use std::net::{IpAddr, SocketAddr, TcpListener};
9
8
  use std::sync::Arc;
10
- use std::{os::unix::net::UnixListener as StdUnixListener, path::PathBuf};
11
- use tokio::net::{unix, TcpListener, TcpStream, UnixListener, UnixStream};
9
+ use std::{os::unix::net::UnixListener, path::PathBuf};
10
+ use tokio::net::TcpListener as TokioTcpListener;
11
+ use tokio::net::UnixListener as TokioUnixListener;
12
+ use tokio::net::{unix, TcpStream, UnixStream};
12
13
  use tokio_rustls::TlsAcceptor;
13
14
 
14
- pub(crate) trait IoStream:
15
- tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin
16
- {
17
- }
18
- impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin> IoStream for T {}
19
-
20
15
  pub(crate) enum Listener {
21
16
  Tcp(TcpListener),
22
17
  TcpTls((TcpListener, TlsAcceptor)),
@@ -24,149 +19,200 @@ pub(crate) enum Listener {
24
19
  UnixTls((UnixListener, TlsAcceptor)),
25
20
  }
26
21
 
27
- enum Stream {
28
- TcpStream((TcpStream, SocketAddr)),
29
- UnixStream((UnixStream, unix::SocketAddr)),
22
+ pub(crate) enum TokioListener {
23
+ Tcp {
24
+ listener: TokioTcpListener,
25
+ host: String,
26
+ port: u16,
27
+ },
28
+ TcpTls {
29
+ listener: TokioTcpListener,
30
+ acceptor: TlsAcceptor,
31
+ host: String,
32
+ port: u16,
33
+ },
34
+ Unix {
35
+ listener: TokioUnixListener,
36
+ },
37
+ UnixTls {
38
+ listener: TokioUnixListener,
39
+ acceptor: TlsAcceptor,
40
+ },
30
41
  }
31
42
 
32
- #[derive(Clone, Debug)]
33
- pub enum SockAddr {
34
- Tcp(Arc<SocketAddr>),
35
- Unix(Arc<unix::SocketAddr>),
36
- }
37
- impl std::fmt::Display for SockAddr {
38
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43
+ impl TokioListener {
44
+ pub(crate) async fn accept(&self) -> Result<IoStream> {
39
45
  match self {
40
- SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
41
- SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
42
- Some(path) => write!(f, "{:?}", path),
43
- None => write!(f, ""),
44
- },
46
+ TokioListener::Tcp { listener, .. } => TokioListener::accept_tcp(listener).await,
47
+ TokioListener::TcpTls {
48
+ listener, acceptor, ..
49
+ } => TokioListener::accept_tls(listener, acceptor).await,
50
+ TokioListener::Unix { listener, .. } => TokioListener::accept_unix(listener).await,
51
+ TokioListener::UnixTls {
52
+ listener, acceptor, ..
53
+ } => TokioListener::accept_unix_tls(listener, acceptor).await,
45
54
  }
46
55
  }
47
- }
48
56
 
49
- impl Listener {
50
- pub(crate) async fn accept(&self) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
51
- match self {
52
- Listener::Tcp(listener) => Listener::accept_tcp(listener).await,
53
- Listener::TcpTls((listener, acceptor)) => {
54
- Listener::accept_tls(listener, acceptor).await
55
- }
56
- Listener::Unix(stream) => Listener::accept_unix(stream).await,
57
- Listener::UnixTls((listener, acceptor)) => {
58
- Listener::accept_unix_tls(listener, acceptor).await
59
- }
60
- }
57
+ async fn accept_tcp(listener: &TokioTcpListener) -> Result<IoStream> {
58
+ let tcp_stream = listener.accept().await?;
59
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
60
+ }
61
+
62
+ async fn accept_tls(listener: &TokioTcpListener, acceptor: &TlsAcceptor) -> Result<IoStream> {
63
+ let tcp_stream = listener.accept().await?;
64
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
65
+ }
66
+
67
+ async fn accept_unix(listener: &TokioUnixListener) -> Result<IoStream> {
68
+ let unix_stream = listener.accept().await?;
69
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
70
+ }
71
+
72
+ async fn accept_unix_tls(
73
+ listener: &TokioUnixListener,
74
+ acceptor: &TlsAcceptor,
75
+ ) -> Result<IoStream> {
76
+ let unix_stream = listener.accept().await?;
77
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
61
78
  }
62
79
 
63
80
  async fn to_tokio_io(
64
81
  input_stream: Stream,
65
82
  tls_acceptor: Option<&TlsAcceptor>,
66
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
83
+ ) -> Result<IoStream> {
67
84
  match tls_acceptor {
68
85
  Some(acceptor) => match input_stream {
69
86
  Stream::TcpStream((tcp_stream, socket_address)) => {
70
87
  match acceptor.accept(tcp_stream).await {
71
- Ok(tls_stream) => Ok((
72
- TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
73
- SockAddr::Tcp(Arc::new(socket_address)),
74
- )),
88
+ Ok(tls_stream) => Ok(IoStream::TcpTls {
89
+ stream: tls_stream,
90
+ addr: SockAddr::Tcp(Arc::new(socket_address)),
91
+ }),
75
92
  Err(err) => Err(err.into()),
76
93
  }
77
94
  }
78
95
  Stream::UnixStream((unix_stream, socket_address)) => {
79
96
  match acceptor.accept(unix_stream).await {
80
- Ok(tls_stream) => Ok((
81
- TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
82
- SockAddr::Unix(Arc::new(socket_address)),
83
- )),
97
+ Ok(tls_stream) => Ok(IoStream::UnixTls {
98
+ stream: tls_stream,
99
+ addr: SockAddr::Unix(Arc::new(socket_address)),
100
+ }),
84
101
  Err(err) => Err(err.into()),
85
102
  }
86
103
  }
87
104
  },
88
105
  None => match input_stream {
89
- Stream::TcpStream((tcp_stream, socket_address)) => Ok((
90
- TokioIo::new(Box::pin(tcp_stream) as Pin<Box<dyn IoStream>>),
91
- SockAddr::Tcp(Arc::new(socket_address)),
92
- )),
93
- Stream::UnixStream((unix_stream, socket_address)) => Ok((
94
- TokioIo::new(Box::pin(unix_stream) as Pin<Box<dyn IoStream>>),
95
- SockAddr::Unix(Arc::new(socket_address)),
96
- )),
106
+ Stream::TcpStream((tcp_stream, socket_address)) => Ok(IoStream::Tcp {
107
+ stream: tcp_stream,
108
+ addr: SockAddr::Tcp(Arc::new(socket_address)),
109
+ }),
110
+ Stream::UnixStream((unix_stream, socket_address)) => Ok(IoStream::Unix {
111
+ stream: unix_stream,
112
+ addr: SockAddr::Unix(Arc::new(socket_address)),
113
+ }),
97
114
  },
98
115
  }
99
116
  }
100
117
 
101
- async fn accept_tcp(
102
- listener: &TcpListener,
103
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
104
- let tcp_stream = listener.accept().await?;
105
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
106
- }
107
-
108
- async fn accept_tls(
109
- listener: &TcpListener,
110
- acceptor: &TlsAcceptor,
111
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
112
- let tcp_stream = listener.accept().await?;
113
- Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
114
- }
115
-
116
- async fn accept_unix(
117
- listener: &UnixListener,
118
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
119
- let unix_stream = listener.accept().await?;
120
- Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
118
+ pub(crate) fn scheme(&self) -> String {
119
+ match self {
120
+ TokioListener::Tcp { .. } => "http".to_string(),
121
+ TokioListener::TcpTls { .. } => "https".to_string(),
122
+ TokioListener::Unix { .. } => "http".to_string(),
123
+ TokioListener::UnixTls { .. } => "https".to_string(),
124
+ }
121
125
  }
122
126
 
123
- async fn accept_unix_tls(
124
- listener: &UnixListener,
125
- acceptor: &TlsAcceptor,
126
- ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
127
- let unix_stream = listener.accept().await?;
128
- Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
127
+ pub(crate) fn port(&self) -> u16 {
128
+ match self {
129
+ TokioListener::Tcp { port, .. } => *port,
130
+ TokioListener::TcpTls { port, .. } => *port,
131
+ TokioListener::Unix { .. } => 0,
132
+ TokioListener::UnixTls { .. } => 0,
133
+ }
129
134
  }
130
135
 
131
- pub(crate) fn scheme(&self) -> String {
136
+ pub(crate) fn host(&self) -> String {
132
137
  match self {
133
- Listener::Tcp(_) => "http".to_string(),
134
- Listener::TcpTls(_) => "https".to_string(),
135
- Listener::Unix(_) => "http".to_string(),
136
- Listener::UnixTls(_) => "https".to_string(),
138
+ TokioListener::Tcp { host, .. } => host.to_string(),
139
+ TokioListener::TcpTls { host, .. } => host.to_string(),
140
+ TokioListener::Unix { .. } => "unix".to_string(),
141
+ TokioListener::UnixTls { .. } => "unix".to_string(),
137
142
  }
138
143
  }
144
+ }
139
145
 
140
- pub(crate) fn port(&self) -> u16 {
146
+ enum Stream {
147
+ TcpStream((TcpStream, SocketAddr)),
148
+ UnixStream((UnixStream, unix::SocketAddr)),
149
+ }
150
+
151
+ #[derive(Clone, Debug)]
152
+ pub enum SockAddr {
153
+ Tcp(Arc<SocketAddr>),
154
+ Unix(Arc<unix::SocketAddr>),
155
+ }
156
+ impl std::fmt::Display for SockAddr {
157
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141
158
  match self {
142
- Listener::Tcp(listener) => listener.local_addr().unwrap().port(),
143
- Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().port(),
144
- Listener::Unix(_) => 0,
145
- Listener::UnixTls(_) => 0,
159
+ SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
160
+ SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
161
+ Some(path) => write!(f, "{:?}", path),
162
+ None => write!(f, ""),
163
+ },
146
164
  }
147
165
  }
166
+ }
148
167
 
149
- pub(crate) fn host(&self) -> String {
168
+ impl Listener {
169
+ pub fn to_tokio_listener(&self) -> TokioListener {
150
170
  match self {
151
- Listener::Tcp(listener) => listener.local_addr().unwrap().ip().to_string(),
152
- Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().ip().to_string(),
153
- Listener::Unix(_) => "unix".to_string(),
154
- Listener::UnixTls(_) => "unix".to_string(),
171
+ Listener::Tcp(listener) => TokioListener::Tcp {
172
+ listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
173
+ .unwrap(),
174
+ host: listener
175
+ .local_addr()
176
+ .unwrap()
177
+ .ip()
178
+ .to_canonical()
179
+ .to_string(),
180
+ port: listener.local_addr().unwrap().port(),
181
+ },
182
+ Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls {
183
+ listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
184
+ .unwrap(),
185
+ acceptor: acceptor.clone(),
186
+ host: listener
187
+ .local_addr()
188
+ .unwrap()
189
+ .ip()
190
+ .to_canonical()
191
+ .to_string(),
192
+ port: listener.local_addr().unwrap().port(),
193
+ },
194
+ Listener::Unix(listener) => TokioListener::Unix {
195
+ listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
196
+ .unwrap(),
197
+ },
198
+ Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls {
199
+ listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
200
+ .unwrap(),
201
+ acceptor: acceptor.clone(),
202
+ },
155
203
  }
156
204
  }
157
205
  }
158
206
 
159
- impl From<Bind> for Listener {
160
- fn from(bind: Bind) -> Self {
161
- match bind.address {
207
+ impl TryFrom<Bind> for Listener {
208
+ type Error = itsi_error::ItsiError;
209
+
210
+ fn try_from(bind: Bind) -> std::result::Result<Self, Self::Error> {
211
+ let bound = match bind.address {
162
212
  BindAddress::Ip(addr) => match bind.protocol {
163
- TransferProtocol::Http => Listener::Tcp(
164
- TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap())).unwrap(),
165
- ),
166
- TransferProtocol::Https => {
167
- let tcp_listener =
168
- TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap()))
169
- .unwrap();
213
+ BindProtocol::Http => Listener::Tcp(connect_tcp_socket(addr, bind.port.unwrap())?),
214
+ BindProtocol::Https => {
215
+ let tcp_listener = connect_tcp_socket(addr, bind.port.unwrap())?;
170
216
  let tls_acceptor = TlsAcceptor::from(Arc::new(bind.tls_config.unwrap()));
171
217
  Listener::TcpTls((tcp_listener, tls_acceptor))
172
218
  }
@@ -175,44 +221,39 @@ impl From<Bind> for Listener {
175
221
  BindAddress::UnixSocket(path) => match bind.tls_config {
176
222
  Some(tls_config) => {
177
223
  let tls_acceptor = TlsAcceptor::from(Arc::new(tls_config));
178
- Listener::UnixTls((
179
- UnixListener::from_std(connect_unix_socket(&path)).unwrap(),
180
- tls_acceptor,
181
- ))
224
+ Listener::UnixTls((connect_unix_socket(&path)?, tls_acceptor))
182
225
  }
183
- None => Listener::Unix(UnixListener::from_std(connect_unix_socket(&path)).unwrap()),
226
+ None => Listener::Unix(connect_unix_socket(&path)?),
184
227
  },
185
- }
228
+ };
229
+ Ok(bound)
186
230
  }
187
231
  }
188
232
 
189
- fn connect_tcp_socket(addr: IpAddr, port: u16) -> StdTcpListener {
233
+ fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
190
234
  let domain = match addr {
191
235
  IpAddr::V4(_) => Domain::IPV4,
192
236
  IpAddr::V6(_) => Domain::IPV6,
193
237
  };
194
- let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
238
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
195
239
  let socket_address: SocketAddr = SocketAddr::new(addr, port);
196
- socket.set_reuse_address(true).ok();
197
- socket.set_reuse_port(true).ok();
198
240
  socket.set_nonblocking(true).ok();
199
241
  socket.set_nodelay(true).ok();
200
242
  socket.set_recv_buffer_size(1_048_576).ok();
201
- info!("Binding to {}", socket_address);
202
- socket.bind(&socket_address.into()).unwrap();
203
- socket.listen(1024).unwrap();
204
- socket.into()
243
+ socket.bind(&socket_address.into())?;
244
+ socket.listen(1024)?;
245
+ Ok(socket.into())
205
246
  }
206
247
 
207
- fn connect_unix_socket(path: &PathBuf) -> StdUnixListener {
248
+ fn connect_unix_socket(path: &PathBuf) -> Result<UnixListener> {
208
249
  let _ = std::fs::remove_file(path);
209
- let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap();
250
+ let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
210
251
  socket.set_nonblocking(true).ok();
211
- let socket_address = socket2::SockAddr::unix(path).unwrap();
252
+ let socket_address = socket2::SockAddr::unix(path)?;
212
253
 
213
254
  info!("Binding to {:?}", path);
214
- socket.bind(&socket_address).unwrap();
215
- socket.listen(1024).unwrap();
255
+ socket.bind(&socket_address)?;
256
+ socket.listen(1024)?;
216
257
 
217
- socket.into()
258
+ Ok(socket.into())
218
259
  }
@@ -1,5 +1,11 @@
1
1
  pub mod bind;
2
+ pub mod bind_protocol;
3
+ pub mod io_stream;
2
4
  pub mod itsi_server;
5
+ pub mod lifecycle_event;
3
6
  pub mod listener;
7
+ pub mod process_worker;
8
+ pub mod serve_strategy;
9
+ pub mod signal;
10
+ pub mod thread_worker;
4
11
  pub mod tls;
5
- pub mod transfer_protocol;
@@ -0,0 +1,196 @@
1
+ use super::serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode};
2
+ use itsi_error::{ItsiError, Result};
3
+ use itsi_rb_helpers::{call_with_gvl, call_without_gvl, create_ruby_thread, fork};
4
+ use itsi_tracing::error;
5
+ use nix::{
6
+ errno::Errno,
7
+ sys::{
8
+ signal::{
9
+ kill,
10
+ Signal::{SIGKILL, SIGTERM},
11
+ },
12
+ wait::{waitpid, WaitPidFlag, WaitStatus},
13
+ },
14
+ unistd::{setpgid, Pid},
15
+ };
16
+ use parking_lot::Mutex;
17
+ use std::{
18
+ process::{self, exit},
19
+ sync::Arc,
20
+ time::{Duration, Instant},
21
+ };
22
+ use sysinfo::System;
23
+
24
+ use tokio::{sync::watch, time::sleep};
25
+ use tracing::{info, instrument, warn};
26
+
27
+ #[derive(Clone, Debug)]
28
+ pub struct ProcessWorker {
29
+ pub worker_id: usize,
30
+ pub child_pid: Arc<Mutex<Option<Pid>>>,
31
+ pub started_at: Instant,
32
+ }
33
+
34
+ impl Default for ProcessWorker {
35
+ fn default() -> Self {
36
+ Self {
37
+ worker_id: 0,
38
+ child_pid: Arc::new(Mutex::new(None)),
39
+ started_at: Instant::now(),
40
+ }
41
+ }
42
+ }
43
+
44
+ impl ProcessWorker {
45
+ #[instrument(skip(self, cluster_template), fields(self.worker_id = %self.worker_id))]
46
+ pub(crate) fn boot(&self, cluster_template: Arc<ClusterMode>) -> Result<()> {
47
+ let child_pid = *self.child_pid.lock();
48
+ if let Some(pid) = child_pid {
49
+ if self.is_alive() {
50
+ if let Err(e) = kill(pid, SIGTERM) {
51
+ info!("Failed to send SIGTERM to process {}: {}", pid, e);
52
+ }
53
+ }
54
+ *self.child_pid.lock() = None;
55
+ }
56
+
57
+ match call_with_gvl(|_ruby| fork(cluster_template.server.after_fork.lock().clone())) {
58
+ Some(pid) => {
59
+ *self.child_pid.lock() = Some(Pid::from_raw(pid));
60
+ }
61
+ None => {
62
+ if let Err(e) = setpgid(
63
+ Pid::from_raw(process::id() as i32),
64
+ Pid::from_raw(process::id() as i32),
65
+ ) {
66
+ error!("Failed to set process group ID: {}", e);
67
+ }
68
+ match SingleMode::new(
69
+ cluster_template.server.clone(),
70
+ cluster_template.listeners.clone(),
71
+ cluster_template.lifecycle_channel.clone(),
72
+ ) {
73
+ Ok(single_mode) => {
74
+ Arc::new(single_mode).run().ok();
75
+ }
76
+ Err(e) => {
77
+ error!("Failed to boot into worker mode: {}", e);
78
+ }
79
+ }
80
+ exit(0)
81
+ }
82
+ }
83
+ Ok(())
84
+ }
85
+
86
+ pub(crate) fn memory_usage(&self) -> Option<u64> {
87
+ if let Some(pid) = *self.child_pid.lock() {
88
+ let s = System::new_all();
89
+ if let Some(process) = s.process(sysinfo::Pid::from(pid.as_raw() as usize)) {
90
+ return Some(process.memory());
91
+ }
92
+ }
93
+ None
94
+ }
95
+
96
+ pub(crate) async fn reboot(&self, cluster_template: Arc<ClusterMode>) -> Result<bool> {
97
+ self.graceful_shutdown(cluster_template.clone()).await;
98
+ let self_clone = self.clone();
99
+ let (booted_sender, mut booted_receiver) = watch::channel(false);
100
+ create_ruby_thread(move || {
101
+ call_without_gvl(move || {
102
+ if self_clone.boot(cluster_template).is_ok() {
103
+ booted_sender.send(true).ok()
104
+ } else {
105
+ booted_sender.send(false).ok()
106
+ };
107
+ })
108
+ });
109
+
110
+ booted_receiver
111
+ .changed()
112
+ .await
113
+ .map_err(|_| ItsiError::InternalServerError("Failed to boot worker".to_owned()))?;
114
+
115
+ let guard = booted_receiver.borrow();
116
+ let result = guard.to_owned();
117
+ // Not very robust, we should check to see if the worker is actually listening before considering this successful.
118
+ sleep(Duration::from_secs(1)).await;
119
+ Ok(result)
120
+ }
121
+
122
+ pub(crate) async fn graceful_shutdown(&self, cluster_template: Arc<ClusterMode>) {
123
+ let self_clone = self.clone();
124
+ self_clone.request_shutdown();
125
+ let force_kill_time =
126
+ Instant::now() + Duration::from_secs_f64(cluster_template.server.shutdown_timeout);
127
+ while self_clone.is_alive() && force_kill_time > Instant::now() {
128
+ tokio::time::sleep(Duration::from_millis(100)).await;
129
+ }
130
+ if self_clone.is_alive() {
131
+ self_clone.force_kill();
132
+ }
133
+ }
134
+
135
+ pub(crate) fn boot_if_dead(&self, cluster_template: Arc<ClusterMode>) -> bool {
136
+ if !self.is_alive() {
137
+ if self.just_started() {
138
+ error!(
139
+ "Worker in crash loop {:?}. Refusing to restart",
140
+ self.child_pid.lock()
141
+ );
142
+ return false;
143
+ } else {
144
+ let self_clone = self.clone();
145
+ create_ruby_thread(move || {
146
+ call_without_gvl(move || {
147
+ self_clone.boot(cluster_template).ok();
148
+ })
149
+ });
150
+ }
151
+ }
152
+ true
153
+ }
154
+
155
+ pub(crate) fn request_shutdown(&self) {
156
+ let child_pid = *self.child_pid.lock();
157
+ if let Some(pid) = child_pid {
158
+ if let Err(e) = kill(pid, SIGTERM) {
159
+ error!("Failed to send SIGTERM to process {}: {}", pid, e);
160
+ }
161
+ }
162
+ }
163
+
164
+ pub(crate) fn force_kill(&self) {
165
+ let child_pid = *self.child_pid.lock();
166
+ if let Some(pid) = child_pid {
167
+ if let Err(e) = kill(pid, SIGKILL) {
168
+ error!("Failed to force kill process {}: {}", pid, e);
169
+ }
170
+ }
171
+ }
172
+
173
+ pub(crate) fn just_started(&self) -> bool {
174
+ let now = Instant::now();
175
+ now.duration_since(self.started_at).as_millis() < 2000
176
+ }
177
+
178
+ pub(crate) fn is_alive(&self) -> bool {
179
+ let child_pid = *self.child_pid.lock();
180
+ if let Some(pid) = child_pid {
181
+ match waitpid(pid, Some(WaitPidFlag::WNOHANG)) {
182
+ Ok(WaitStatus::Exited(_, _)) | Ok(WaitStatus::Signaled(_, _, _)) => {
183
+ return false;
184
+ }
185
+ Ok(WaitStatus::StillAlive) | Ok(_) => {}
186
+ Err(_) => return false,
187
+ }
188
+ match kill(pid, None) {
189
+ Ok(_) => true,
190
+ Err(errno) => !matches!(errno, Errno::ESRCH),
191
+ }
192
+ } else {
193
+ false
194
+ }
195
+ }
196
+ }