@cryptotaxi247 / netdata-1 / commits / c2cd26f16

Set status flag of active journal file to archived on shutdown. (#21707)

* Change status flag to `archived` when dropping `Log`. Otherwise, journal files will be closed with the status flag set to online. Considering that the journal-viewer plugin indexes online files, this will cause performance issues over time, eg. due to users running nightlies which restarts the agent daily. * Add signal handlers for both SIGTERM and SIGINT. Other than the fact that we should shutdown gracefully whenever we receive these signals, the log service needs to properly mark any actively written journal file as `archived`. Otherwise, their status flag will remain `online` which will not reflect the truth and it will cause the journal-viewer plugin to re-index them on each request. * Handle signal handler registration failure. Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Fix left-over line --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

vkalintiris committed Feb 4, 2026 at 21:03 UTC c2cd26f16658e128a5785309958b2f26d3166941
2 files changed +55 -13
src/crates/journal-log-writer/src/log/mod.rs
+22 -1
@@ -223,7 +223,11 @@ impl Log {
223 /// If `source_realtime_usec` is provided, a `_SOURCE_REALTIME_TIMESTAMP` field will be added
224 /// to record the original timestamp from the source (in microseconds since Unix epoch).
225 /// This is useful when ingesting logs from external sources that have their own timestamps.
226 - pub fn write_entry(&mut self, items: &[&[u8]], source_realtime_usec: Option<u64>) -> Result<()> {
226 + pub fn write_entry(
227 + &mut self,
228 + items: &[&[u8]],
229 + source_realtime_usec: Option<u64>,
230 + ) -> Result<()> {
231 if items.is_empty() {
232 return Ok(());
233 }
@@ -525,3 +529,20 @@ impl Log {
529 self.write_entry(&field_refs, None)
530 }
531 }
532 +
533 +impl Drop for Log {
534 + fn drop(&mut self) {
535 + use journal_core::file::JournalState;
536 +
537 + if let Some(ref mut active_file) = self.active_file {
538 + // A single file is opened for writing exactly once. Once closed, we
539 + // treat them as immutable. We need a custom impl for `Drop` to keep
540 + // this invariant true whenever the plugin receives a `SIGTERM` or
541 + // a `SIGINT`.
542 + active_file.journal_file.journal_header_mut().state = JournalState::Archived as u8;
543 +
544 + // Best/Last-effort sync just to be on the cautious side.
545 + let _ = active_file.journal_file.sync();
546 + }
547 + }
548 +}
src/crates/netdata-plugin/rt/src/lib.rs
+33 -12
@@ -699,11 +699,11 @@ where
699 ///
700 /// # Note
701 ///
702 - /// This method runs indefinitely until shutdown is requested (via Ctrl-C or stdin closing).
702 + /// This method runs indefinitely until shutdown is requested (via signals or stdin closing).
703 pub async fn run(mut self) -> Result<()> {
704 info!("starting plugin runtime: {}", self.plugin_name);
705
706 - self.handle_ctr_c();
706 + self.handle_shutdown_signals();
707
708 // Start chart registry if charts were registered
709 if let Some(registry) = self.chart_registry.take() {
@@ -736,23 +736,44 @@ where
736 Ok(())
737 }
738
739 - /// Setup Ctrl-C signal handler for graceful shutdown.
740 - fn handle_ctr_c(&self) {
739 + /// Setup signal handlers for graceful shutdown
740 + fn handle_shutdown_signals(&self) {
741 let shutdown_token = self.shutdown_token.clone();
742
743 tokio::spawn(async move {
744 - match tokio::signal::ctrl_c().await {
745 - Ok(()) => {
746 - info!("received ctrl-c signal, initiating graceful shutdown");
747 - shutdown_token.cancel();
748 - }
749 - Err(e) => {
750 - error!("failed to listen for Ctrl-C signal: {}", e);
751 - }
744 + match wait_for_shutdown_signal().await {
745 + Ok(()) => info!("received shutdown signal, initiating graceful shutdown"),
746 + Err(e) => error!(
747 + "failed to wait for shutdown signal: {}, initiating shutdown",
748 + e
749 + ),
750 }
751 + shutdown_token.cancel();
752 });
753 }
754 +}
755 +
756 +/// Waits for a shutdown signal (SIGINT or SIGTERM on Unix, SIGINT on other platforms).
757 +async fn wait_for_shutdown_signal() -> std::io::Result<()> {
758 + #[cfg(unix)]
759 + {
760 + use tokio::signal::unix::{SignalKind, signal};
761 +
762 + let mut sigterm = signal(SignalKind::terminate())?;
763 +
764 + tokio::select! {
765 + result = tokio::signal::ctrl_c() => result,
766 + _ = sigterm.recv() => Ok(()),
767 + }
768 + }
769 +
770 + #[cfg(not(unix))]
771 + {
772 + tokio::signal::ctrl_c().await
773 + }
774 +}
775
776 +impl<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> PluginRuntime<R, W> {
777 /// Declare all registered functions to Netdata.
778 ///
779 /// Sends a [`FunctionDeclaration`] message for each registered handler,