Skip to content

Commit 3224261

Browse files
committed
feat: configurable grace period after SIGTERM
-q, --grace-period <grace-period> Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. 255 seconds maximum [env: SERVER_GRACE_PERIOD=] [default: 0] resolves #79
1 parent 5f9f9f9 commit 3224261

File tree

3 files changed

+36
-8
lines changed

3 files changed

+36
-8
lines changed

src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,8 @@ pub struct Config {
154154
/// It provides The "Basic" HTTP Authentication scheme using credentials as "user-id:password" pairs. Password must be encoded using the "BCrypt" password-hashing function.
155155
#[structopt(long, default_value = "", env = "SERVER_BASIC_AUTH")]
156156
pub basic_auth: String,
157+
158+
#[structopt(long, short = "q", default_value = "0", env = "SERVER_GRACE_PERIOD")]
159+
/// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds.
160+
pub grace_period: u8,
157161
}

src/server.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ impl Server {
121121
);
122122
let basic_auth = Arc::from(basic_auth);
123123

124+
// Grace period option
125+
let grace_period = opts.grace_period;
126+
tracing::info!("grace period before graceful shutdown: {}s", grace_period);
127+
124128
// Create a service router for Hyper
125129
let router_service = RouterService::new(RequestHandler {
126130
opts: RequestHandlerOpts {
@@ -170,9 +174,10 @@ impl Server {
170174
HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(router_service);
171175

172176
#[cfg(not(windows))]
173-
let server = server.with_graceful_shutdown(signals::wait_for_signals(signals));
177+
let server =
178+
server.with_graceful_shutdown(signals::wait_for_signals(signals, grace_period));
174179
#[cfg(windows)]
175-
let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c());
180+
let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c(grace_period));
176181

177182
tracing::info!(
178183
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
@@ -200,9 +205,10 @@ impl Server {
200205
.serve(router_service);
201206

202207
#[cfg(not(windows))]
203-
let server = server.with_graceful_shutdown(signals::wait_for_signals(signals));
208+
let server =
209+
server.with_graceful_shutdown(signals::wait_for_signals(signals, grace_period));
204210
#[cfg(windows)]
205-
let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c());
211+
let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c(grace_period));
206212

207213
tracing::info!(
208214
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),

src/signals.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use {
44
signal_hook_tokio::Signals,
55
};
66

7+
use tokio::time::{sleep, Duration};
8+
79
#[cfg(not(windows))]
810
/// It creates a common list of signals stream for `SIGTERM`, `SIGINT` and `SIGQUIT` to be observed.
911
pub fn create_signals() -> Result<Signals> {
@@ -12,27 +14,43 @@ pub fn create_signals() -> Result<Signals> {
1214

1315
#[cfg(not(windows))]
1416
/// It waits for a specific type of incoming signals included `ctrl+c`.
15-
pub async fn wait_for_signals(signals: Signals) {
17+
pub async fn wait_for_signals(signals: Signals, grace_period_secs: u8) {
1618
let mut signals = signals.fuse();
1719
while let Some(signal) = signals.next().await {
1820
match signal {
1921
SIGHUP => {
20-
// Note: for now we don't do something for SIGHUPs
22+
// NOTE: for now we don't do something for SIGHUPs
2123
tracing::debug!("SIGHUP caught, nothing to do about")
2224
}
2325
SIGTERM | SIGINT | SIGQUIT => {
24-
tracing::debug!("SIGTERM, SIGINT or SIGQUIT signal received, delegating graceful shutdown to the server");
26+
tracing::info!("SIGTERM, SIGINT or SIGQUIT signal caught");
2527
break;
2628
}
2729
_ => unreachable!(),
2830
}
2931
}
32+
// NOTE: once loop above is done then an upstream graceful shutdown should come next.
33+
delay_graceful_shutdown(grace_period_secs).await;
34+
tracing::info!("delegating server's graceful shutdown");
35+
}
36+
37+
/// Function intended to delay the server's graceful shutdown providing a grace period in seconds.
38+
async fn delay_graceful_shutdown(grace_period_secs: u8) {
39+
if grace_period_secs > 0 {
40+
tracing::info!(
41+
"grace period of {}s after the SIGTERM started",
42+
grace_period_secs
43+
);
44+
sleep(Duration::from_secs(grace_period_secs.into())).await;
45+
tracing::info!("grace period has elapsed");
46+
}
3047
}
3148

3249
#[cfg(windows)]
3350
/// It waits for an incoming `ctrl+c` signal on Windows.
34-
pub async fn wait_for_ctrl_c() {
51+
pub async fn wait_for_ctrl_c(grace_period_secs: u8) {
3552
tokio::signal::ctrl_c()
3653
.await
3754
.expect("failed to install ctrl+c signal handler");
55+
delay_graceful_shutdown(grace_period_secs).await;
3856
}

0 commit comments

Comments
 (0)