Documenting worker pool (#104)
* Documenting worker pool * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * move comment to worker * documenting blocking module * Update src/asynk/async_worker.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker_pool.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/blocking/queue.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/blocking/queue.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> * Update src/asynk/async_worker.rs Co-authored-by: Ayrat Badykov <ayratin555@gmail.com> Co-authored-by: Ayrat Badykov <ayratin555@gmail.com>
This commit is contained in:
parent
aa4cd3f09b
commit
01934e231f
6 changed files with 91 additions and 16 deletions
|
@ -9,6 +9,7 @@ use crate::{RetentionMode, SleepParams};
|
||||||
use log::error;
|
use log::error;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
/// it executes tasks only of task_type type, it sleeps when there are no tasks in the queue
|
||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
pub struct AsyncWorker<AQueue>
|
pub struct AsyncWorker<AQueue>
|
||||||
where
|
where
|
||||||
|
@ -28,11 +29,7 @@ impl<AQueue> AsyncWorker<AQueue>
|
||||||
where
|
where
|
||||||
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
||||||
{
|
{
|
||||||
pub async fn run(
|
async fn run(&mut self, task: Task, runnable: Box<dyn AsyncRunnable>) -> Result<(), FangError> {
|
||||||
&mut self,
|
|
||||||
task: Task,
|
|
||||||
runnable: Box<dyn AsyncRunnable>,
|
|
||||||
) -> Result<(), FangError> {
|
|
||||||
let result = runnable.run(&mut self.queue).await;
|
let result = runnable.run(&mut self.queue).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
@ -86,13 +83,13 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn sleep(&mut self) {
|
async fn sleep(&mut self) {
|
||||||
self.sleep_params.maybe_increase_sleep_period();
|
self.sleep_params.maybe_increase_sleep_period();
|
||||||
|
|
||||||
tokio::time::sleep(self.sleep_params.sleep_period).await;
|
tokio::time::sleep(self.sleep_params.sleep_period).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_tasks(&mut self) -> Result<(), FangError> {
|
pub(crate) async fn run_tasks(&mut self) -> Result<(), FangError> {
|
||||||
loop {
|
loop {
|
||||||
//fetch task
|
//fetch task
|
||||||
match self
|
match self
|
||||||
|
|
|
@ -14,13 +14,18 @@ where
|
||||||
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
||||||
{
|
{
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
|
/// the AsyncWorkerPool uses a queue to control the tasks that will be executed.
|
||||||
pub queue: AQueue,
|
pub queue: AQueue,
|
||||||
|
/// sleep_params controls how much time a worker will sleep while waiting for tasks
|
||||||
#[builder(default, setter(into))]
|
#[builder(default, setter(into))]
|
||||||
pub sleep_params: SleepParams,
|
pub sleep_params: SleepParams,
|
||||||
|
/// retention_mode controls if tasks should be persisted after execution
|
||||||
#[builder(default, setter(into))]
|
#[builder(default, setter(into))]
|
||||||
pub retention_mode: RetentionMode,
|
pub retention_mode: RetentionMode,
|
||||||
|
/// the number of workers of the AsyncWorkerPool.
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
pub number_of_workers: u32,
|
pub number_of_workers: u32,
|
||||||
|
/// The type of tasks that will be executed by `AsyncWorkerPool`.
|
||||||
#[builder(default=DEFAULT_TASK_TYPE.to_string(), setter(into))]
|
#[builder(default=DEFAULT_TASK_TYPE.to_string(), setter(into))]
|
||||||
pub task_type: String,
|
pub task_type: String,
|
||||||
}
|
}
|
||||||
|
@ -29,6 +34,8 @@ impl<AQueue> AsyncWorkerPool<AQueue>
|
||||||
where
|
where
|
||||||
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
AQueue: AsyncQueueable + Clone + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
/// Starts the configured number of workers
|
||||||
|
/// This is necessary in order to execute tasks.
|
||||||
pub async fn start(&mut self) {
|
pub async fn start(&mut self) {
|
||||||
for idx in 0..self.number_of_workers {
|
for idx in 0..self.number_of_workers {
|
||||||
let pool = self.clone();
|
let pool = self.clone();
|
||||||
|
@ -37,7 +44,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn supervise_task(pool: AsyncWorkerPool<AQueue>, restarts: u64, worker_number: u32) {
|
async fn supervise_task(pool: AsyncWorkerPool<AQueue>, restarts: u64, worker_number: u32) {
|
||||||
let restarts = restarts + 1;
|
let restarts = restarts + 1;
|
||||||
let join_handle = Self::spawn_worker(
|
let join_handle = Self::spawn_worker(
|
||||||
pool.queue.clone(),
|
pool.queue.clone(),
|
||||||
|
@ -56,7 +63,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn spawn_worker(
|
async fn spawn_worker(
|
||||||
queue: AQueue,
|
queue: AQueue,
|
||||||
sleep_params: SleepParams,
|
sleep_params: SleepParams,
|
||||||
retention_mode: RetentionMode,
|
retention_mode: RetentionMode,
|
||||||
|
@ -66,7 +73,7 @@ where
|
||||||
Self::run_worker(queue, sleep_params, retention_mode, task_type).await
|
Self::run_worker(queue, sleep_params, retention_mode, task_type).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub async fn run_worker(
|
async fn run_worker(
|
||||||
queue: AQueue,
|
queue: AQueue,
|
||||||
sleep_params: SleepParams,
|
sleep_params: SleepParams,
|
||||||
retention_mode: RetentionMode,
|
retention_mode: RetentionMode,
|
||||||
|
|
|
@ -84,29 +84,46 @@ impl From<cron::error::Error> for QueueError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This trait defines operations for a synchronous queue.
|
||||||
|
/// The trait can be implemented for different storage backends.
|
||||||
|
/// For now, the trait is only implemented for PostgreSQL. More backends are planned to be implemented in the future.
|
||||||
pub trait Queueable {
|
pub trait Queueable {
|
||||||
|
/// This method should retrieve one task of the `task_type` type. If `task_type` is `None` it will try to
|
||||||
|
/// fetch a task of the type `common`. After fetching it should update the state of the task to
|
||||||
|
/// `FangTaskState::InProgress`.
|
||||||
fn fetch_and_touch_task(&self, task_type: String) -> Result<Option<Task>, QueueError>;
|
fn fetch_and_touch_task(&self, task_type: String) -> Result<Option<Task>, QueueError>;
|
||||||
|
|
||||||
|
/// Enqueue a task to the queue, The task will be executed as soon as possible by the worker of the same type
|
||||||
|
/// created by an `WorkerPool`.
|
||||||
fn insert_task(&self, params: &dyn Runnable) -> Result<Task, QueueError>;
|
fn insert_task(&self, params: &dyn Runnable) -> Result<Task, QueueError>;
|
||||||
|
|
||||||
|
/// The method will remove all tasks from the queue
|
||||||
fn remove_all_tasks(&self) -> Result<usize, QueueError>;
|
fn remove_all_tasks(&self) -> Result<usize, QueueError>;
|
||||||
|
|
||||||
|
/// Remove all tasks that are scheduled in the future.
|
||||||
fn remove_all_scheduled_tasks(&self) -> Result<usize, QueueError>;
|
fn remove_all_scheduled_tasks(&self) -> Result<usize, QueueError>;
|
||||||
|
|
||||||
|
/// Removes all tasks that have the specified `task_type`.
|
||||||
fn remove_tasks_of_type(&self, task_type: &str) -> Result<usize, QueueError>;
|
fn remove_tasks_of_type(&self, task_type: &str) -> Result<usize, QueueError>;
|
||||||
|
|
||||||
|
/// Remove a task by its id.
|
||||||
fn remove_task(&self, id: Uuid) -> Result<usize, QueueError>;
|
fn remove_task(&self, id: Uuid) -> Result<usize, QueueError>;
|
||||||
|
|
||||||
/// To use this function task has to be uniq. uniq() has to return true.
|
/// To use this function task has to be uniq. uniq() has to return true.
|
||||||
/// If task is not uniq this function will not do anything.
|
/// If task is not uniq this function will not do anything.
|
||||||
|
/// Remove a task by its metadata (struct fields values)
|
||||||
fn remove_task_by_metadata(&self, task: &dyn Runnable) -> Result<usize, QueueError>;
|
fn remove_task_by_metadata(&self, task: &dyn Runnable) -> Result<usize, QueueError>;
|
||||||
|
|
||||||
fn find_task_by_id(&self, id: Uuid) -> Option<Task>;
|
fn find_task_by_id(&self, id: Uuid) -> Option<Task>;
|
||||||
|
|
||||||
|
/// Update the state field of the specified task
|
||||||
|
/// See the `FangTaskState` enum for possible states.
|
||||||
fn update_task_state(&self, task: &Task, state: FangTaskState) -> Result<Task, QueueError>;
|
fn update_task_state(&self, task: &Task, state: FangTaskState) -> Result<Task, QueueError>;
|
||||||
|
|
||||||
|
/// Update the state of a task to `FangTaskState::Failed` and set an error_message.
|
||||||
fn fail_task(&self, task: &Task, error: &str) -> Result<Task, QueueError>;
|
fn fail_task(&self, task: &Task, error: &str) -> Result<Task, QueueError>;
|
||||||
|
|
||||||
|
/// Schedule a task.
|
||||||
fn schedule_task(&self, task: &dyn Runnable) -> Result<Task, QueueError>;
|
fn schedule_task(&self, task: &dyn Runnable) -> Result<Task, QueueError>;
|
||||||
|
|
||||||
fn schedule_retry(
|
fn schedule_retry(
|
||||||
|
@ -117,6 +134,27 @@ pub trait Queueable {
|
||||||
) -> Result<Task, QueueError>;
|
) -> Result<Task, QueueError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An async queue that can be used to enqueue tasks.
|
||||||
|
/// It uses a PostgreSQL storage. It must be connected to perform any operation.
|
||||||
|
/// To connect a `Queue` to the PostgreSQL database call the `get_connection` method.
|
||||||
|
/// A Queue can be created with the TypedBuilder.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // Set DATABASE_URL enviroment variable if you would like to try this function.
|
||||||
|
/// pub fn connection_pool(pool_size: u32) -> r2d2::Pool<r2d2::ConnectionManager<PgConnection>> {
|
||||||
|
/// let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
///
|
||||||
|
/// let manager = r2d2::ConnectionManager::<PgConnection>::new(database_url);
|
||||||
|
///
|
||||||
|
/// r2d2::Pool::builder()
|
||||||
|
/// .max_size(pool_size)
|
||||||
|
/// .build(manager)
|
||||||
|
/// .unwrap()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let queue = Queue::builder().connection_pool(connection_pool(3)).build();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
#[derive(Clone, TypedBuilder)]
|
#[derive(Clone, TypedBuilder)]
|
||||||
pub struct Queue {
|
pub struct Queue {
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
|
@ -208,6 +246,7 @@ impl Queueable for Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Queue {
|
impl Queue {
|
||||||
|
/// Connect to the db if not connected
|
||||||
pub fn get_connection(&self) -> Result<PoolConnection, QueueError> {
|
pub fn get_connection(&self) -> Result<PoolConnection, QueueError> {
|
||||||
let result = self.connection_pool.get();
|
let result = self.connection_pool.get();
|
||||||
|
|
||||||
|
|
|
@ -5,26 +5,48 @@ use crate::Scheduled;
|
||||||
pub const COMMON_TYPE: &str = "common";
|
pub const COMMON_TYPE: &str = "common";
|
||||||
pub const RETRIES_NUMBER: i32 = 20;
|
pub const RETRIES_NUMBER: i32 = 20;
|
||||||
|
|
||||||
|
/// Implement this trait to run your custom tasks.
|
||||||
#[typetag::serde(tag = "type")]
|
#[typetag::serde(tag = "type")]
|
||||||
pub trait Runnable {
|
pub trait Runnable {
|
||||||
|
/// Execute the task. This method should define its logic
|
||||||
fn run(&self, _queueable: &dyn Queueable) -> Result<(), FangError>;
|
fn run(&self, _queueable: &dyn Queueable) -> Result<(), FangError>;
|
||||||
|
|
||||||
|
/// Define the type of the task.
|
||||||
|
/// The `common` task type is used by default
|
||||||
fn task_type(&self) -> String {
|
fn task_type(&self) -> String {
|
||||||
COMMON_TYPE.to_string()
|
COMMON_TYPE.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If set to true, no new tasks with the same metadata will be inserted
|
||||||
|
/// By default it is set to false.
|
||||||
fn uniq(&self) -> bool {
|
fn uniq(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method defines if a task is periodic or it should be executed once in the future.
|
||||||
|
///
|
||||||
|
/// Be careful it works only with the UTC timezone.
|
||||||
|
/**
|
||||||
|
```rust
|
||||||
|
fn cron(&self) -> Option<Scheduled> {
|
||||||
|
let expression = "0/20 * * * Aug-Sep * 2022/1";
|
||||||
|
Some(Scheduled::CronPattern(expression.to_string()))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
/// In order to schedule a task once, use the `Scheduled::ScheduleOnce` enum variant.
|
||||||
fn cron(&self) -> Option<Scheduled> {
|
fn cron(&self) -> Option<Scheduled> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Define the maximum number of retries the task will be retried.
|
||||||
|
/// By default the number of retries is 20.
|
||||||
fn max_retries(&self) -> i32 {
|
fn max_retries(&self) -> i32 {
|
||||||
RETRIES_NUMBER
|
RETRIES_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Define the backoff mode
|
||||||
|
/// By default, it is exponential, 2^(attempt)
|
||||||
fn backoff(&self, attempt: u32) -> u32 {
|
fn backoff(&self, attempt: u32) -> u32 {
|
||||||
u32::pow(2, attempt)
|
u32::pow(2, attempt)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ use log::error;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
/// A executioner of tasks, it executes tasks only of one given task_type, it sleeps when they are
|
||||||
|
/// not tasks to be executed.
|
||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
pub struct Worker<BQueue>
|
pub struct Worker<BQueue>
|
||||||
where
|
where
|
||||||
|
@ -52,7 +54,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_tasks(&mut self) -> Result<(), FangError> {
|
pub(crate) fn run_tasks(&mut self) -> Result<(), FangError> {
|
||||||
loop {
|
loop {
|
||||||
match self.queue.fetch_and_touch_task(self.task_type.clone()) {
|
match self.queue.fetch_and_touch_task(self.task_type.clone()) {
|
||||||
Ok(Some(task)) => {
|
Ok(Some(task)) => {
|
||||||
|
@ -114,7 +116,7 @@ where
|
||||||
self.sleep_params.maybe_reset_sleep_period();
|
self.sleep_params.maybe_reset_sleep_period();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(&mut self) {
|
fn sleep(&mut self) {
|
||||||
self.sleep_params.maybe_increase_sleep_period();
|
self.sleep_params.maybe_increase_sleep_period();
|
||||||
|
|
||||||
thread::sleep(self.sleep_params.sleep_period);
|
thread::sleep(self.sleep_params.sleep_period);
|
||||||
|
|
|
@ -13,16 +13,22 @@ pub struct WorkerPool<BQueue>
|
||||||
where
|
where
|
||||||
BQueue: Queueable + Clone + Sync + Send + 'static,
|
BQueue: Queueable + Clone + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
#[builder(setter(into))]
|
/// the AsyncWorkerPool uses a queue to control the tasks that will be executed.
|
||||||
pub number_of_workers: u32,
|
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
pub queue: BQueue,
|
pub queue: BQueue,
|
||||||
#[builder(setter(into), default)]
|
/// sleep_params controls how much time a worker will sleep while waiting for tasks
|
||||||
pub task_type: String,
|
/// execute.
|
||||||
#[builder(setter(into), default)]
|
#[builder(setter(into), default)]
|
||||||
pub sleep_params: SleepParams,
|
pub sleep_params: SleepParams,
|
||||||
|
/// retention_mode controls if tasks should be persisted after execution
|
||||||
#[builder(setter(into), default)]
|
#[builder(setter(into), default)]
|
||||||
pub retention_mode: RetentionMode,
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, TypedBuilder)]
|
#[derive(Clone, TypedBuilder)]
|
||||||
|
@ -46,6 +52,8 @@ impl<BQueue> WorkerPool<BQueue>
|
||||||
where
|
where
|
||||||
BQueue: Queueable + Clone + Sync + Send + 'static,
|
BQueue: Queueable + Clone + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
|
/// Starts the configured number of workers
|
||||||
|
/// This is necessary in order to execute tasks.
|
||||||
pub fn start(&mut self) -> Result<(), FangError> {
|
pub fn start(&mut self) -> Result<(), FangError> {
|
||||||
for idx in 1..self.number_of_workers + 1 {
|
for idx in 1..self.number_of_workers + 1 {
|
||||||
let name = format!("worker_{}{}", self.task_type, idx);
|
let name = format!("worker_{}{}", self.task_type, idx);
|
||||||
|
|
Loading…
Reference in a new issue