blob: 5ef2ba80beeb6e84a2cc7aea00abd9e4207fe044 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! Thin wrappers around `sd_notify` so every systemd interaction on the
//! server side lives in one place (mirrors `core/ai/mcp/src/systemd.rs`).
use crate::server_error::{ServerError, ShardJoinFailureKind};
use message_bus::{IggyMessageBus, ShutdownToken};
use std::time::Duration;
use tracing::{info, warn};
/// Tell systemd the service has finished start-up (`READY=1`).
pub fn notify_ready() {
if let Err(error) = sd_notify::notify(&[sd_notify::NotifyState::Ready]) {
warn!("Failed to send systemd READY=1 notification: {error}");
}
}
/// Tell systemd the service has begun shutting down (`STOPPING=1`), which
/// also stops the watchdog timer from counting against a long drain.
pub fn notify_stopping() {
let _ = sd_notify::notify(&[sd_notify::NotifyState::Stopping]);
}
/// Surface a dirty shutdown in `systemctl status` / journald.
pub fn notify_shutdown_failure(error: &ServerError) {
let wedged = matches!(
error,
ServerError::ShardJoinFailures { failures }
if failures
.iter()
.any(|failure| matches!(failure.kind, ShardJoinFailureKind::Wedged { .. }))
);
let status = if wedged {
"graceful shutdown timed out"
} else {
"shard threads failed during shutdown"
};
let _ = sd_notify::notify(&[sd_notify::NotifyState::Status(status)]);
}
/// Start the `WATCHDOG=1` keep-alive. Does nothing unless the unit set
/// `WatchdogSec=`. Tracked on the bus so `bus.shutdown()` reaps the task.
pub fn spawn_watchdog(bus: &IggyMessageBus) {
let Some(timeout) = sd_notify::watchdog_enabled() else {
return;
};
let interval = timeout / 2;
info!(
"Systemd watchdog enabled, pinging every {}s (timeout: {}s).",
interval.as_secs(),
timeout.as_secs()
);
let handle = compio::runtime::spawn(run_watchdog(bus.token(), interval));
bus.track_background(handle);
}
async fn run_watchdog(token: ShutdownToken, interval: Duration) {
while token.sleep_or_shutdown(interval).await {
if let Err(error) = sd_notify::notify(&[sd_notify::NotifyState::Watchdog]) {
warn!("Failed to send systemd watchdog ping: {error}");
}
}
}