2023-03-09 15:59:45 +00:00
|
|
|
use crate::errors::BackieError;
|
2023-03-07 15:41:20 +00:00
|
|
|
use crate::queue::Queueable;
|
2023-03-07 16:52:26 +00:00
|
|
|
use crate::task::TaskType;
|
2023-03-09 15:59:45 +00:00
|
|
|
use crate::worker::Worker;
|
2023-03-07 16:52:26 +00:00
|
|
|
use crate::RetentionMode;
|
2022-07-31 13:32:37 +00:00
|
|
|
use async_recursion::async_recursion;
|
|
|
|
use log::error;
|
2023-03-09 15:59:45 +00:00
|
|
|
use std::future::Future;
|
|
|
|
use tokio::sync::watch::Receiver;
|
2022-07-31 13:32:37 +00:00
|
|
|
use typed_builder::TypedBuilder;
|
|
|
|
|
|
|
|
#[derive(TypedBuilder, Clone)]
|
2023-03-09 15:59:45 +00:00
|
|
|
pub struct WorkerPool<AQueue>
|
2022-07-31 13:32:37 +00:00
|
|
|
where
|
2023-03-07 15:41:20 +00:00
|
|
|
AQueue: Queueable + Clone + Sync + 'static,
|
2022-07-31 13:32:37 +00:00
|
|
|
{
|
|
|
|
#[builder(setter(into))]
|
2022-12-22 15:38:56 +00:00
|
|
|
/// the AsyncWorkerPool uses a queue to control the tasks that will be executed.
|
2022-08-02 14:32:58 +00:00
|
|
|
pub queue: AQueue,
|
2023-03-07 15:41:20 +00:00
|
|
|
|
|
|
|
/// retention_mode controls if tasks should be persisted after execution
|
2022-08-01 16:55:36 +00:00
|
|
|
#[builder(default, setter(into))]
|
|
|
|
pub retention_mode: RetentionMode,
|
2023-03-07 15:41:20 +00:00
|
|
|
|
2022-12-22 15:38:56 +00:00
|
|
|
/// the number of workers of the AsyncWorkerPool.
|
2022-07-31 13:32:37 +00:00
|
|
|
#[builder(setter(into))]
|
2022-08-01 19:37:32 +00:00
|
|
|
pub number_of_workers: u32,
|
2023-03-07 15:41:20 +00:00
|
|
|
|
2022-12-22 15:38:56 +00:00
|
|
|
/// The type of tasks that will be executed by `AsyncWorkerPool`.
|
2023-03-09 15:59:45 +00:00
|
|
|
#[builder(default, setter(into))]
|
2023-03-07 15:41:20 +00:00
|
|
|
pub task_type: Option<TaskType>,
|
2022-07-31 13:32:37 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 15:59:45 +00:00
|
|
|
// impl<TypedBuilderFields, Q> AsyncWorkerBuilder<TypedBuilderFields, Q>
|
|
|
|
// where
|
|
|
|
// TypedBuilderFields: Clone,
|
|
|
|
// Q: Queueable + Clone + Sync + 'static,
|
|
|
|
// {
|
|
|
|
// pub fn with_graceful_shutdown<F>(self, signal: F) -> Self<TypedBuilderFields, Q>
|
|
|
|
// where
|
|
|
|
// F: Future<Output = ()>,
|
|
|
|
// {
|
|
|
|
// self
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
impl<AQueue> WorkerPool<AQueue>
|
2022-07-31 13:32:37 +00:00
|
|
|
where
|
2023-03-07 15:41:20 +00:00
|
|
|
AQueue: Queueable + Clone + Sync + 'static,
|
2022-07-31 13:32:37 +00:00
|
|
|
{
|
2022-12-22 15:38:56 +00:00
|
|
|
/// Starts the configured number of workers
|
|
|
|
/// This is necessary in order to execute tasks.
|
2023-03-09 15:59:45 +00:00
|
|
|
pub async fn start<F>(&mut self, graceful_shutdown: F) -> Result<(), BackieError>
|
|
|
|
where
|
|
|
|
F: Future<Output = ()> + Send + 'static,
|
|
|
|
{
|
|
|
|
let (tx, rx) = tokio::sync::watch::channel(());
|
2022-08-04 15:22:53 +00:00
|
|
|
for idx in 0..self.number_of_workers {
|
|
|
|
let pool = self.clone();
|
2023-03-09 15:59:45 +00:00
|
|
|
// TODO: the worker pool keeps track of the number of workers and spawns new workers as needed.
|
|
|
|
// There should be always a minimum number of workers active waiting for tasks to execute
|
|
|
|
// or for a gracefull shutdown.
|
|
|
|
tokio::spawn(Self::supervise_task(pool, rx.clone(), 0, idx));
|
2022-07-31 13:32:37 +00:00
|
|
|
}
|
2023-03-09 15:59:45 +00:00
|
|
|
graceful_shutdown.await;
|
|
|
|
tx.send(())?;
|
|
|
|
log::info!("Worker pool stopped gracefully");
|
|
|
|
Ok(())
|
2022-07-31 13:32:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_recursion]
|
2023-03-09 15:59:45 +00:00
|
|
|
async fn supervise_task(
|
|
|
|
pool: WorkerPool<AQueue>,
|
|
|
|
receiver: Receiver<()>,
|
|
|
|
restarts: u64,
|
|
|
|
worker_number: u32,
|
|
|
|
) {
|
2022-08-04 15:22:53 +00:00
|
|
|
let restarts = restarts + 1;
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let inner_pool = pool.clone();
|
2023-03-09 15:59:45 +00:00
|
|
|
let inner_receiver = receiver.clone();
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let join_handle = tokio::spawn(async move {
|
2023-03-09 15:59:45 +00:00
|
|
|
let mut worker: Worker<AQueue> = Worker::builder()
|
2023-03-04 19:46:09 +00:00
|
|
|
.queue(inner_pool.queue.clone())
|
2023-03-07 16:52:26 +00:00
|
|
|
.retention_mode(inner_pool.retention_mode)
|
2023-03-04 19:46:09 +00:00
|
|
|
.task_type(inner_pool.task_type.clone())
|
2023-03-09 15:59:45 +00:00
|
|
|
.shutdown(inner_receiver)
|
2023-03-04 19:46:09 +00:00
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run_tasks().await
|
|
|
|
});
|
2022-07-31 13:32:37 +00:00
|
|
|
|
2022-08-04 15:22:53 +00:00
|
|
|
if (join_handle.await).is_err() {
|
|
|
|
error!(
|
|
|
|
"Worker {} stopped. Restarting. the number of restarts {}",
|
|
|
|
worker_number, restarts,
|
|
|
|
);
|
2023-03-09 15:59:45 +00:00
|
|
|
Self::supervise_task(pool, receiver, restarts, worker_number).await;
|
2022-07-31 13:32:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|