backie/src/blocking/worker_pool.rs

123 lines
3.4 KiB
Rust
Raw Normal View History

use crate::queue::Queueable;
use crate::worker::Worker;
use crate::FangError;
use crate::RetentionMode;
use crate::SleepParams;
use log::error;
use log::info;
2021-06-23 10:48:03 +00:00
use std::thread;
use typed_builder::TypedBuilder;
#[derive(Clone, TypedBuilder)]
pub struct WorkerPool<BQueue>
where
BQueue: Queueable + Clone + Sync + Send + 'static,
{
/// the AsyncWorkerPool uses a queue to control the tasks that will be executed.
#[builder(setter(into))]
pub queue: BQueue,
/// sleep_params controls how much time a worker will sleep while waiting for tasks
/// execute.
#[builder(setter(into), default)]
pub sleep_params: SleepParams,
/// retention_mode controls if tasks should be persisted after execution
#[builder(setter(into), default)]
pub retention_mode: RetentionMode,
/// the number of workers of the AsyncWorkerPool.
#[builder(setter(into))]
pub number_of_workers: u32,
/// The type of tasks that will be executed by `AsyncWorkerPool`.
#[builder(setter(into), default)]
pub task_type: String,
2021-06-23 10:48:03 +00:00
}
#[derive(Clone, TypedBuilder)]
pub struct WorkerThread<BQueue>
where
BQueue: Queueable + Clone + Sync + Send + 'static,
{
2021-06-23 10:48:03 +00:00
pub name: String,
pub restarts: u64,
pub worker_pool: WorkerPool<BQueue>,
2021-06-23 10:48:03 +00:00
}
#[derive(Clone)]
pub struct WorkerParams {
pub retention_mode: Option<RetentionMode>,
pub sleep_params: Option<SleepParams>,
pub task_type: Option<String>,
}
impl<BQueue> WorkerPool<BQueue>
where
BQueue: Queueable + Clone + Sync + Send + 'static,
{
/// Starts the configured number of workers
/// This is necessary in order to execute tasks.
Support graceful shutdown of worker (#14) * Include shared state to allow graceful shutdown Graceful shutdown of executors allows the current task to finish before exiting. This prevents half completed tasks in the general case: when workers are being scaled down. To accomplish this a shared state (using an `Arc<RwLock<WorkerState>>`) is created in a WorkerPool on instantiation. This shared state is then passed to each thread (spawned with `WorkerThread::spawn_in_pool`), and finally passed to the `Executor` instantiated by the `WorkerThread`. This allows the infinit loop in the executor to receive signals from the `WorkerPool`, and exit gracefully when requested. * Add basic error handling Add `FangError` enum derived from `thiserror::Error`. This should be the default the error type for the Fang library, all errors returned by Fang should be a value in this enum. Use FangError for errors returned by start and shutdown, remove unwraps. * Include instructions for running tests locally * Track handles of panic'd worker threads Allows the Drop trait impl of ThreadWorker to access the `thread_join_handles` of WorkerPool so it can update the thread handle when the previous thread unexpectedly exited and a new one is being started. This is done in a way that prevents data leaks (by using a Hashmap keyed off the name of the worker thread). It also ensures that threads started from the Drop impl are properly joined on shutdown. * Fix WorkerThread drop implementation WorkerThread can not have `Clone` derived on it, as each cloned copy will try to restart the thread when it's dropped, leading to an infinite number of thread spawns till stack overflow. Oops * Remove Option from SharedState type declaration Instead of having an Option wrapping an enum, have the option codified as a state in the enum. * Bump version to 0.5.0 * Add integration test for shutdown * Update simple_worker example to include signal monitoring and shutdown * Update readme to mention using signal-hook to gracefully shutdown worker
2021-12-05 06:19:08 +00:00
pub fn start(&mut self) -> Result<(), FangError> {
2021-06-23 10:48:03 +00:00
for idx in 1..self.number_of_workers + 1 {
let name = format!("worker_{}{idx}", self.task_type);
Support graceful shutdown of worker (#14) * Include shared state to allow graceful shutdown Graceful shutdown of executors allows the current task to finish before exiting. This prevents half completed tasks in the general case: when workers are being scaled down. To accomplish this a shared state (using an `Arc<RwLock<WorkerState>>`) is created in a WorkerPool on instantiation. This shared state is then passed to each thread (spawned with `WorkerThread::spawn_in_pool`), and finally passed to the `Executor` instantiated by the `WorkerThread`. This allows the infinit loop in the executor to receive signals from the `WorkerPool`, and exit gracefully when requested. * Add basic error handling Add `FangError` enum derived from `thiserror::Error`. This should be the default the error type for the Fang library, all errors returned by Fang should be a value in this enum. Use FangError for errors returned by start and shutdown, remove unwraps. * Include instructions for running tests locally * Track handles of panic'd worker threads Allows the Drop trait impl of ThreadWorker to access the `thread_join_handles` of WorkerPool so it can update the thread handle when the previous thread unexpectedly exited and a new one is being started. This is done in a way that prevents data leaks (by using a Hashmap keyed off the name of the worker thread). It also ensures that threads started from the Drop impl are properly joined on shutdown. * Fix WorkerThread drop implementation WorkerThread can not have `Clone` derived on it, as each cloned copy will try to restart the thread when it's dropped, leading to an infinite number of thread spawns till stack overflow. Oops * Remove Option from SharedState type declaration Instead of having an Option wrapping an enum, have the option codified as a state in the enum. * Bump version to 0.5.0 * Add integration test for shutdown * Update simple_worker example to include signal monitoring and shutdown * Update readme to mention using signal-hook to gracefully shutdown worker
2021-12-05 06:19:08 +00:00
let worker_thread = WorkerThread::builder()
.name(name.clone())
.restarts(0)
.worker_pool(self.clone())
.build();
Support graceful shutdown of worker (#14) * Include shared state to allow graceful shutdown Graceful shutdown of executors allows the current task to finish before exiting. This prevents half completed tasks in the general case: when workers are being scaled down. To accomplish this a shared state (using an `Arc<RwLock<WorkerState>>`) is created in a WorkerPool on instantiation. This shared state is then passed to each thread (spawned with `WorkerThread::spawn_in_pool`), and finally passed to the `Executor` instantiated by the `WorkerThread`. This allows the infinit loop in the executor to receive signals from the `WorkerPool`, and exit gracefully when requested. * Add basic error handling Add `FangError` enum derived from `thiserror::Error`. This should be the default the error type for the Fang library, all errors returned by Fang should be a value in this enum. Use FangError for errors returned by start and shutdown, remove unwraps. * Include instructions for running tests locally * Track handles of panic'd worker threads Allows the Drop trait impl of ThreadWorker to access the `thread_join_handles` of WorkerPool so it can update the thread handle when the previous thread unexpectedly exited and a new one is being started. This is done in a way that prevents data leaks (by using a Hashmap keyed off the name of the worker thread). It also ensures that threads started from the Drop impl are properly joined on shutdown. * Fix WorkerThread drop implementation WorkerThread can not have `Clone` derived on it, as each cloned copy will try to restart the thread when it's dropped, leading to an infinite number of thread spawns till stack overflow. Oops * Remove Option from SharedState type declaration Instead of having an Option wrapping an enum, have the option codified as a state in the enum. * Bump version to 0.5.0 * Add integration test for shutdown * Update simple_worker example to include signal monitoring and shutdown * Update readme to mention using signal-hook to gracefully shutdown worker
2021-12-05 06:19:08 +00:00
worker_thread.spawn()?;
2021-06-23 10:48:03 +00:00
}
Support graceful shutdown of worker (#14) * Include shared state to allow graceful shutdown Graceful shutdown of executors allows the current task to finish before exiting. This prevents half completed tasks in the general case: when workers are being scaled down. To accomplish this a shared state (using an `Arc<RwLock<WorkerState>>`) is created in a WorkerPool on instantiation. This shared state is then passed to each thread (spawned with `WorkerThread::spawn_in_pool`), and finally passed to the `Executor` instantiated by the `WorkerThread`. This allows the infinit loop in the executor to receive signals from the `WorkerPool`, and exit gracefully when requested. * Add basic error handling Add `FangError` enum derived from `thiserror::Error`. This should be the default the error type for the Fang library, all errors returned by Fang should be a value in this enum. Use FangError for errors returned by start and shutdown, remove unwraps. * Include instructions for running tests locally * Track handles of panic'd worker threads Allows the Drop trait impl of ThreadWorker to access the `thread_join_handles` of WorkerPool so it can update the thread handle when the previous thread unexpectedly exited and a new one is being started. This is done in a way that prevents data leaks (by using a Hashmap keyed off the name of the worker thread). It also ensures that threads started from the Drop impl are properly joined on shutdown. * Fix WorkerThread drop implementation WorkerThread can not have `Clone` derived on it, as each cloned copy will try to restart the thread when it's dropped, leading to an infinite number of thread spawns till stack overflow. Oops * Remove Option from SharedState type declaration Instead of having an Option wrapping an enum, have the option codified as a state in the enum. * Bump version to 0.5.0 * Add integration test for shutdown * Update simple_worker example to include signal monitoring and shutdown * Update readme to mention using signal-hook to gracefully shutdown worker
2021-12-05 06:19:08 +00:00
Ok(())
2021-06-23 10:48:03 +00:00
}
}
impl<BQueue> WorkerThread<BQueue>
where
BQueue: Queueable + Clone + Sync + Send + 'static,
{
fn spawn(self) -> Result<(), FangError> {
2021-06-23 10:48:03 +00:00
info!(
"starting a worker thread {}, number of restarts {}",
self.name, self.restarts
2021-06-23 10:48:03 +00:00
);
let builder = thread::Builder::new().name(self.name.clone());
Support graceful shutdown of worker (#14) * Include shared state to allow graceful shutdown Graceful shutdown of executors allows the current task to finish before exiting. This prevents half completed tasks in the general case: when workers are being scaled down. To accomplish this a shared state (using an `Arc<RwLock<WorkerState>>`) is created in a WorkerPool on instantiation. This shared state is then passed to each thread (spawned with `WorkerThread::spawn_in_pool`), and finally passed to the `Executor` instantiated by the `WorkerThread`. This allows the infinit loop in the executor to receive signals from the `WorkerPool`, and exit gracefully when requested. * Add basic error handling Add `FangError` enum derived from `thiserror::Error`. This should be the default the error type for the Fang library, all errors returned by Fang should be a value in this enum. Use FangError for errors returned by start and shutdown, remove unwraps. * Include instructions for running tests locally * Track handles of panic'd worker threads Allows the Drop trait impl of ThreadWorker to access the `thread_join_handles` of WorkerPool so it can update the thread handle when the previous thread unexpectedly exited and a new one is being started. This is done in a way that prevents data leaks (by using a Hashmap keyed off the name of the worker thread). It also ensures that threads started from the Drop impl are properly joined on shutdown. * Fix WorkerThread drop implementation WorkerThread can not have `Clone` derived on it, as each cloned copy will try to restart the thread when it's dropped, leading to an infinite number of thread spawns till stack overflow. Oops * Remove Option from SharedState type declaration Instead of having an Option wrapping an enum, have the option codified as a state in the enum. * Bump version to 0.5.0 * Add integration test for shutdown * Update simple_worker example to include signal monitoring and shutdown * Update readme to mention using signal-hook to gracefully shutdown worker
2021-12-05 06:19:08 +00:00
2021-06-23 10:48:03 +00:00
builder
.spawn(move || {
let mut worker: Worker<BQueue> = Worker::builder()
.queue(self.worker_pool.queue.clone())
.task_type(self.worker_pool.task_type.clone())
.retention_mode(self.worker_pool.retention_mode.clone())
.sleep_params(self.worker_pool.sleep_params.clone())
.build();
// Run worker
if let Err(error) = worker.run_tasks() {
error!(
"Error executing tasks in worker '{}': {:?}",
self.name, error
);
}
2021-06-23 10:48:03 +00:00
})
.map_err(FangError::from)?;
2021-06-23 10:48:03 +00:00
Ok(())
2021-06-23 10:48:03 +00:00
}
}
impl<BQueue> Drop for WorkerThread<BQueue>
where
BQueue: Queueable + Clone + Sync + Send + 'static,
{
fn drop(&mut self) {
self.restarts += 1;
error!(
"Worker {} stopped. Restarting. The number of restarts {}",
self.name, self.restarts,
);
2021-06-23 10:48:03 +00:00
self.clone().spawn().unwrap();
2021-06-23 10:48:03 +00:00
}
}