abstract shutdown/reload functionality.
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
30e7298dd7
commit
08f2b8579c
6 changed files with 49 additions and 26 deletions
|
@ -5,7 +5,7 @@ use std::{
|
||||||
|
|
||||||
use tokio::{runtime, sync::broadcast};
|
use tokio::{runtime, sync::broadcast};
|
||||||
|
|
||||||
use crate::{config::Config, log};
|
use crate::{config::Config, log, Error, Result};
|
||||||
|
|
||||||
/// Server runtime state; public portion
|
/// Server runtime state; public portion
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
|
@ -59,6 +59,38 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reload(&self) -> Result<()> {
|
||||||
|
if cfg!(not(conduit_mods)) {
|
||||||
|
return Err(Error::Err("Reloading not enabled".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.reloading.swap(true, Ordering::AcqRel) {
|
||||||
|
return Err(Error::Err("Reloading already in progress".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.stopping.swap(true, Ordering::AcqRel) {
|
||||||
|
return Err(Error::Err("Shutdown already in progress".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.signal("SIGINT")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&self) -> Result<()> {
|
||||||
|
if self.stopping.swap(true, Ordering::AcqRel) {
|
||||||
|
return Err(Error::Err("Shutdown already in progress".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.signal("SIGTERM")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signal(&self, sig: &'static str) -> Result<()> {
|
||||||
|
if let Err(e) = self.signal.send(sig) {
|
||||||
|
return Err(Error::Err(format!("Failed to send signal: {e}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn runtime(&self) -> &runtime::Handle {
|
pub fn runtime(&self) -> &runtime::Handle {
|
||||||
self.runtime
|
self.runtime
|
||||||
|
|
|
@ -4,11 +4,7 @@ mod server;
|
||||||
|
|
||||||
extern crate conduit_core as conduit;
|
extern crate conduit_core as conduit;
|
||||||
|
|
||||||
use std::{
|
use std::{cmp, sync::Arc, time::Duration};
|
||||||
cmp,
|
|
||||||
sync::{atomic::Ordering, Arc},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use conduit::{debug_error, debug_info, error, trace, utils::available_parallelism, warn, Error, Result};
|
use conduit::{debug_error, debug_info, error, trace, utils::available_parallelism, warn, Error, Result};
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
@ -107,7 +103,7 @@ async fn signal(server: Arc<Server>) {
|
||||||
use unix::SignalKind;
|
use unix::SignalKind;
|
||||||
|
|
||||||
const CONSOLE: bool = cfg!(feature = "console");
|
const CONSOLE: bool = cfg!(feature = "console");
|
||||||
const RELOADING: bool = cfg!(all(unix, debug_assertions, not(CONSOLE)));
|
const RELOADING: bool = cfg!(all(conduit_mods, not(CONSOLE)));
|
||||||
|
|
||||||
let mut quit = unix::signal(SignalKind::quit()).expect("SIGQUIT handler");
|
let mut quit = unix::signal(SignalKind::quit()).expect("SIGQUIT handler");
|
||||||
let mut term = unix::signal(SignalKind::terminate()).expect("SIGTERM handler");
|
let mut term = unix::signal(SignalKind::terminate()).expect("SIGTERM handler");
|
||||||
|
@ -120,19 +116,17 @@ async fn signal(server: Arc<Server>) {
|
||||||
_ = term.recv() => { sig = "SIGTERM"; },
|
_ = term.recv() => { sig = "SIGTERM"; },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate the SIGINT is requesting a hot-reload.
|
|
||||||
if RELOADING && sig == "SIGINT" {
|
|
||||||
server.server.reloading.store(true, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate the signal is requesting a shutdown
|
|
||||||
if matches!(sig, "SIGQUIT" | "SIGTERM") || (!CONSOLE && sig == "SIGINT") {
|
|
||||||
server.server.stopping.store(true, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
warn!("Received {sig}");
|
warn!("Received {sig}");
|
||||||
if let Err(e) = server.server.signal.send(sig) {
|
let result = if RELOADING && sig == "SIGINT" {
|
||||||
debug_error!("signal channel: {e}");
|
server.server.reload()
|
||||||
|
} else if matches!(sig, "SIGQUIT" | "SIGTERM") || (!CONSOLE && sig == "SIGINT") {
|
||||||
|
server.server.shutdown()
|
||||||
|
} else {
|
||||||
|
server.server.signal(sig)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
debug_error!(?sig, "signal: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub(crate) async fn run(server: &Arc<Server>, starts: bool) -> Result<(bool, boo
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
server.server.stopping.store(false, Ordering::Release);
|
||||||
let run = main_mod.get::<RunFuncProto>("run")?;
|
let run = main_mod.get::<RunFuncProto>("run")?;
|
||||||
if let Err(error) = run(&server.server).await {
|
if let Err(error) = run(&server.server).await {
|
||||||
error!("Running server: {error}");
|
error!("Running server: {error}");
|
||||||
|
|
|
@ -13,7 +13,7 @@ use tracing::{debug, error, trace};
|
||||||
pub(crate) async fn spawn(
|
pub(crate) async fn spawn(
|
||||||
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||||
) -> Result<axum::response::Response, StatusCode> {
|
) -> Result<axum::response::Response, StatusCode> {
|
||||||
if server.stopping.load(Ordering::Relaxed) {
|
if !server.running() {
|
||||||
debug_warn!("unavailable pending shutdown");
|
debug_warn!("unavailable pending shutdown");
|
||||||
return Err(StatusCode::SERVICE_UNAVAILABLE);
|
return Err(StatusCode::SERVICE_UNAVAILABLE);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ pub(crate) async fn spawn(
|
||||||
pub(crate) async fn handle(
|
pub(crate) async fn handle(
|
||||||
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||||
) -> Result<axum::response::Response, StatusCode> {
|
) -> Result<axum::response::Response, StatusCode> {
|
||||||
if server.stopping.load(Ordering::Relaxed) {
|
if !server.running() {
|
||||||
debug_warn!(
|
debug_warn!(
|
||||||
method = %req.method(),
|
method = %req.method(),
|
||||||
uri = %req.uri(),
|
uri = %req.uri(),
|
||||||
|
|
|
@ -25,7 +25,6 @@ pub(crate) async fn run(server: Arc<Server>) -> Result<(), Error> {
|
||||||
_ = services().admin.handle.lock().await.insert(admin::handle);
|
_ = services().admin.handle.lock().await.insert(admin::handle);
|
||||||
|
|
||||||
// Setup shutdown/signal handling
|
// Setup shutdown/signal handling
|
||||||
server.stopping.store(false, Ordering::Release);
|
|
||||||
let handle = ServerHandle::new();
|
let handle = ServerHandle::new();
|
||||||
let (tx, _) = broadcast::channel::<()>(1);
|
let (tx, _) = broadcast::channel::<()>(1);
|
||||||
let sigs = server
|
let sigs = server
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
sync::{atomic, Arc, Mutex as StdMutex},
|
sync::{Arc, Mutex as StdMutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use conduit::{debug_info, Result, Server};
|
use conduit::{debug_info, Result, Server};
|
||||||
|
@ -301,8 +301,6 @@ bad_signature_ratelimiter: {bad_signature_ratelimiter}
|
||||||
|
|
||||||
pub async fn interrupt(&self) {
|
pub async fn interrupt(&self) {
|
||||||
trace!("Interrupting services...");
|
trace!("Interrupting services...");
|
||||||
self.server.stopping.store(true, atomic::Ordering::Release);
|
|
||||||
|
|
||||||
self.sending.interrupt();
|
self.sending.interrupt();
|
||||||
self.presence.interrupt();
|
self.presence.interrupt();
|
||||||
self.admin.interrupt();
|
self.admin.interrupt();
|
||||||
|
@ -310,7 +308,6 @@ bad_signature_ratelimiter: {bad_signature_ratelimiter}
|
||||||
trace!("Services interrupt complete.");
|
trace!("Services interrupt complete.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn stop(&self) {
|
pub async fn stop(&self) {
|
||||||
info!("Shutting down services");
|
info!("Shutting down services");
|
||||||
self.interrupt().await;
|
self.interrupt().await;
|
||||||
|
|
Loading…
Add table
Reference in a new issue