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 crate::{config::Config, log};
|
||||
use crate::{config::Config, log, Error, Result};
|
||||
|
||||
/// Server runtime state; public portion
|
||||
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]
|
||||
pub fn runtime(&self) -> &runtime::Handle {
|
||||
self.runtime
|
||||
|
|
|
@ -4,11 +4,7 @@ mod server;
|
|||
|
||||
extern crate conduit_core as conduit;
|
||||
|
||||
use std::{
|
||||
cmp,
|
||||
sync::{atomic::Ordering, Arc},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{cmp, sync::Arc, time::Duration};
|
||||
|
||||
use conduit::{debug_error, debug_info, error, trace, utils::available_parallelism, warn, Error, Result};
|
||||
use server::Server;
|
||||
|
@ -107,7 +103,7 @@ async fn signal(server: Arc<Server>) {
|
|||
use unix::SignalKind;
|
||||
|
||||
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 term = unix::signal(SignalKind::terminate()).expect("SIGTERM handler");
|
||||
|
@ -120,19 +116,17 @@ async fn signal(server: Arc<Server>) {
|
|||
_ = 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}");
|
||||
if let Err(e) = server.server.signal.send(sig) {
|
||||
debug_error!("signal channel: {e}");
|
||||
let result = if RELOADING && sig == "SIGINT" {
|
||||
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);
|
||||
}
|
||||
}
|
||||
server.server.stopping.store(false, Ordering::Release);
|
||||
let run = main_mod.get::<RunFuncProto>("run")?;
|
||||
if let Err(error) = run(&server.server).await {
|
||||
error!("Running server: {error}");
|
||||
|
|
|
@ -13,7 +13,7 @@ use tracing::{debug, error, trace};
|
|||
pub(crate) async fn spawn(
|
||||
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
if server.stopping.load(Ordering::Relaxed) {
|
||||
if !server.running() {
|
||||
debug_warn!("unavailable pending shutdown");
|
||||
return Err(StatusCode::SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ pub(crate) async fn spawn(
|
|||
pub(crate) async fn handle(
|
||||
State(server): State<Arc<Server>>, req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
if server.stopping.load(Ordering::Relaxed) {
|
||||
if !server.running() {
|
||||
debug_warn!(
|
||||
method = %req.method(),
|
||||
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);
|
||||
|
||||
// Setup shutdown/signal handling
|
||||
server.stopping.store(false, Ordering::Release);
|
||||
let handle = ServerHandle::new();
|
||||
let (tx, _) = broadcast::channel::<()>(1);
|
||||
let sigs = server
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::{atomic, Arc, Mutex as StdMutex},
|
||||
sync::{Arc, Mutex as StdMutex},
|
||||
};
|
||||
|
||||
use conduit::{debug_info, Result, Server};
|
||||
|
@ -301,8 +301,6 @@ bad_signature_ratelimiter: {bad_signature_ratelimiter}
|
|||
|
||||
pub async fn interrupt(&self) {
|
||||
trace!("Interrupting services...");
|
||||
self.server.stopping.store(true, atomic::Ordering::Release);
|
||||
|
||||
self.sending.interrupt();
|
||||
self.presence.interrupt();
|
||||
self.admin.interrupt();
|
||||
|
@ -310,7 +308,6 @@ bad_signature_ratelimiter: {bad_signature_ratelimiter}
|
|||
trace!("Services interrupt complete.");
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn stop(&self) {
|
||||
info!("Shutting down services");
|
||||
self.interrupt().await;
|
||||
|
|
Loading…
Add table
Reference in a new issue