use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use crate::activity::ActivityRegistry;
use crate::config::WorkerConfig;
use crate::error::WorkerError;
use crate::runtime::liminal::{AgentHarnessConfig, LiminalActivityWorker};
use crate::runtime::liminal_drain::LiveWriter;
use crate::runtime::liminal_redial::{
CandidateCursor, RedialBackoff, RedialError, ServeResult, run_redial_loop,
};
#[derive(Clone, Copy, Debug)]
pub struct RedialTiming {
pub initial_backoff: Duration,
pub max_backoff: Duration,
}
impl RedialTiming {
#[must_use]
pub const fn new(initial: Duration, max: Duration) -> Self {
Self {
initial_backoff: initial,
max_backoff: max,
}
}
}
pub fn serve_with_redial<Ready>(
candidates: Vec<String>,
config: &WorkerConfig,
registry: &Arc<ActivityRegistry>,
timing: RedialTiming,
stop: &AtomicBool,
agent: Option<&AgentHarnessConfig>,
mut on_first_ready: Ready,
) -> Result<(), WorkerError>
where
Ready: FnMut() + Send,
{
let mut cursor = CandidateCursor::new(candidates).map_err(redial_setup_error)?;
let mut backoff = RedialBackoff::new(timing.initial_backoff, timing.max_backoff);
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(WorkerError::registration)?;
let mut announced_ready = false;
let agent_types = agent
.map(|config| config.agent_activity_types().clone())
.unwrap_or_default();
let live_writer = LiveWriter::default();
let connect = |address: &str| -> Result<LiminalActivityWorker, WorkerError> {
LiminalActivityWorker::connect_advertising(
address,
config,
Arc::clone(registry),
&agent_types,
)
.map(|worker| {
worker
.with_agent_config(agent.cloned())
.with_live_writer(live_writer.clone())
})
};
let serve = |worker: LiminalActivityWorker| -> ServeResult {
if !announced_ready {
announced_ready = true;
on_first_ready();
}
runtime.block_on(worker.serve_until_drop(|| stop.load(Ordering::Relaxed)))
};
run_redial_loop(
&mut cursor,
&mut backoff,
connect,
serve,
std::thread::sleep,
|| stop.load(Ordering::Relaxed),
WorkerError::is_retryable,
)
}
fn redial_setup_error(error: RedialError) -> WorkerError {
WorkerError::registration(error)
}