use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Duration;
use crate::engine::FlowEngine;
use crate::error::Result;
use crate::model::WorkflowRunSuspension;
use crate::worker::{FlowTask, FlowTaskQueue};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct FlowSchedulerTick {
pub due_waits: Vec<(String, String)>,
pub due_retries: Vec<(String, String)>,
pub enqueued_tasks: usize,
}
impl FlowSchedulerTick {
pub fn has_due_work(&self) -> bool {
!self.due_waits.is_empty() || !self.due_retries.is_empty()
}
}
#[derive(Clone)]
pub struct FlowScheduler {
engine: FlowEngine,
queue: Arc<dyn FlowTaskQueue>,
}
impl FlowScheduler {
pub fn new(engine: FlowEngine, queue: Arc<dyn FlowTaskQueue>) -> Self {
Self { engine, queue }
}
pub fn engine(&self) -> &FlowEngine {
&self.engine
}
pub fn queue(&self) -> Arc<dyn FlowTaskQueue> {
Arc::clone(&self.queue)
}
pub async fn next_wakeup(&self, now: DateTime<Utc>) -> Result<Option<WorkflowRunSuspension>> {
self.engine.next_wakeup(now).await
}
pub async fn next_wakeup_delay(&self, now: DateTime<Utc>) -> Result<Option<Duration>> {
let Some(wakeup) = self.next_wakeup(now).await? else {
return Ok(None);
};
let Some(scheduled_at) = wakeup.scheduled_at() else {
return Ok(None);
};
Ok(Some(
scheduled_at
.signed_duration_since(now)
.to_std()
.unwrap_or(Duration::ZERO),
))
}
pub async fn enqueue_due_work(&self, now: DateTime<Utc>) -> Result<FlowSchedulerTick> {
let due_waits = self.engine.list_due_waits(now).await?;
let due_retries = self.engine.list_due_retries(now).await?;
let mut enqueued_tasks = 0usize;
if !due_waits.is_empty() {
self.queue.enqueue(FlowTask::ResumeDueWaits { now }).await?;
enqueued_tasks += 1;
}
if !due_retries.is_empty() {
self.queue
.enqueue(FlowTask::ResumeDueRetries { now })
.await?;
enqueued_tasks += 1;
}
Ok(FlowSchedulerTick {
due_waits,
due_retries,
enqueued_tasks,
})
}
}