Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/stackable-operator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- BREAKING: Add support to gracefully shutdown `EosChecker`.
`EosChecker::run` now requires passing a shutdown signal, which is any `Future<Output = ()>` ([#1146]).

[#1146]: https://github.com/stackabletech/operator-rs/pull/1146

## [0.104.0] - 2026-01-26

### Added
Expand Down
56 changes: 38 additions & 18 deletions crates/stackable-operator/src/eos/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use chrono::{DateTime, Utc};
use futures::FutureExt;
use snafu::{ResultExt, Snafu};
use stackable_shared::time::Duration;
use tokio::select;
use tracing::{Level, instrument};

/// Available options to configure a [`EndOfSupportChecker`].
Expand Down Expand Up @@ -114,33 +116,51 @@ impl EndOfSupportChecker {
///
/// It is recommended to run the end-of-support checker via [`futures::try_join!`] or
/// [`tokio::join`] alongside other futures (eg. for controllers).
pub async fn run(self) {
pub async fn run<F>(self, shutdown_signal: F)
where
F: Future<Output = ()>,
{
// Immediately return if the end-of-support checker is disabled.
if self.disabled {
return;
}

// Construct an interval which can be polled.
let mut interval = tokio::time::interval(self.interval.into());

let shutdown_signal = shutdown_signal.fuse();
tokio::pin!(shutdown_signal);

loop {
// TODO: Add way to stop from the outside
// The first tick ticks immediately.
interval.tick().await;
let now = Utc::now();

tracing::info_span!(
"checking end-of-support state",
eos.interval = self.interval.to_string(),
eos.now = now.to_rfc3339(),
);

// Continue the loop and wait for the next tick to run the check again.
if now <= self.eos_datetime {
continue;
select! {
// We used a biased polling strategy to always check if a
Comment thread
Techassi marked this conversation as resolved.
Outdated
// shutdown signal was received before polling the EoS check
// interval.
biased;

_ = &mut shutdown_signal => {
tracing::trace!("received shutdown signal");
break;
}

// The first tick ticks immediately.
_ = interval.tick() => {
let now = Utc::now();

tracing::info_span!(
"checking end-of-support state",
eos.interval = self.interval.to_string(),
eos.now = now.to_rfc3339(),
);

// Continue the loop and wait for the next tick to run the
// check again.
if now <= self.eos_datetime {
continue;
}

self.emit_warning(now);
}
}

self.emit_warning(now);
}
}

Expand Down
Loading