2023-03-07 15:41:20 +00:00
|
|
|
use std::error::Error;
|
|
|
|
use crate::errors::BackieError;
|
|
|
|
use crate::queue::Queueable;
|
|
|
|
use crate::runnable::RunnableTask;
|
|
|
|
use crate::task::{Task, TaskType};
|
2023-03-04 18:07:17 +00:00
|
|
|
use crate::Scheduled::*;
|
2023-03-07 15:41:20 +00:00
|
|
|
use crate::{RetentionMode};
|
2023-03-04 18:07:17 +00:00
|
|
|
use log::error;
|
|
|
|
use typed_builder::TypedBuilder;
|
|
|
|
|
|
|
|
/// it executes tasks only of task_type type, it sleeps when there are no tasks in the queue
|
|
|
|
#[derive(TypedBuilder)]
|
2023-03-07 15:41:20 +00:00
|
|
|
pub struct AsyncWorker<Q>
|
2023-03-04 18:07:17 +00:00
|
|
|
where
|
2023-03-07 15:41:20 +00:00
|
|
|
Q: Queueable + Clone + Sync + 'static,
|
2023-03-04 18:07:17 +00:00
|
|
|
{
|
|
|
|
#[builder(setter(into))]
|
2023-03-07 15:41:20 +00:00
|
|
|
pub queue: Q,
|
|
|
|
|
2023-03-04 18:07:17 +00:00
|
|
|
#[builder(default, setter(into))]
|
2023-03-07 15:41:20 +00:00
|
|
|
pub task_type: Option<TaskType>,
|
|
|
|
|
2023-03-04 18:07:17 +00:00
|
|
|
#[builder(default, setter(into))]
|
|
|
|
pub retention_mode: RetentionMode,
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +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<Q> AsyncWorker<Q>
|
2023-03-04 18:07:17 +00:00
|
|
|
where
|
2023-03-07 15:41:20 +00:00
|
|
|
Q: Queueable + Clone + Sync + 'static,
|
2023-03-04 18:07:17 +00:00
|
|
|
{
|
2023-03-07 15:41:20 +00:00
|
|
|
async fn run(&mut self, task: Task, runnable: Box<dyn RunnableTask>) -> Result<(), BackieError> {
|
|
|
|
// TODO: catch panics
|
2023-03-04 18:07:17 +00:00
|
|
|
let result = runnable.run(&mut self.queue).await;
|
|
|
|
match result {
|
2023-03-07 15:41:20 +00:00
|
|
|
Ok(_) => self.finalize_task(task, result).await?,
|
|
|
|
Err(error) => {
|
2023-03-04 18:07:17 +00:00
|
|
|
if task.retries < runnable.max_retries() {
|
|
|
|
let backoff_seconds = runnable.backoff(task.retries as u32);
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!(
|
|
|
|
"Task {} failed to run and will be retried in {} seconds",
|
|
|
|
task.id,
|
|
|
|
backoff_seconds
|
|
|
|
);
|
|
|
|
let error_message = format!("{}", error);
|
2023-03-04 18:07:17 +00:00
|
|
|
self.queue
|
2023-03-07 15:41:20 +00:00
|
|
|
.schedule_task_retry(task.id, backoff_seconds, &error_message)
|
2023-03-04 18:07:17 +00:00
|
|
|
.await?;
|
|
|
|
} else {
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!("Task {} failed and reached the maximum retries", task.id);
|
|
|
|
self.finalize_task(task, Err(error)).await?;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn finalize_task(
|
|
|
|
&mut self,
|
|
|
|
task: Task,
|
2023-03-07 15:41:20 +00:00
|
|
|
result: Result<(), Box<dyn Error + Send + 'static>>,
|
|
|
|
) -> Result<(), BackieError> {
|
2023-03-04 18:07:17 +00:00
|
|
|
match self.retention_mode {
|
|
|
|
RetentionMode::KeepAll => match result {
|
|
|
|
Ok(_) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
self.queue.set_task_done(task.id).await?;
|
|
|
|
log::debug!("Task {} done and kept in the database", task.id);
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
Err(error) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!("Task {} failed and kept in the database", task.id);
|
|
|
|
self.queue.set_task_failed(task.id, &format!("{}", error)).await?;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
RetentionMode::RemoveAll => {
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!("Task {} finalized and deleted from the database", task.id);
|
2023-03-04 18:07:17 +00:00
|
|
|
self.queue.remove_task(task.id).await?;
|
|
|
|
}
|
2023-03-07 15:41:20 +00:00
|
|
|
RetentionMode::RemoveDone => match result {
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(_) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!("Task {} done and deleted from the database", task.id);
|
2023-03-04 18:07:17 +00:00
|
|
|
self.queue.remove_task(task.id).await?;
|
|
|
|
}
|
|
|
|
Err(error) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
log::debug!("Task {} failed and kept in the database", task.id);
|
|
|
|
self.queue.set_task_failed(task.id, &format!("{}", error)).await?;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
async fn wait(&mut self) {
|
|
|
|
// TODO: add a way to stop the worker
|
|
|
|
// Listen to postgres pubsub notification
|
|
|
|
// Listen to watchable future
|
|
|
|
// All that until a max timeout
|
|
|
|
//
|
|
|
|
// select! {
|
|
|
|
// _ = self.queue.wait_for_task(Some(self.task_type.clone())).fuse() => {},
|
|
|
|
// _ = SleepParams::default().sleep().fuse() => {},
|
|
|
|
// }
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
pub(crate) async fn run_tasks(&mut self) {
|
2023-03-04 18:07:17 +00:00
|
|
|
loop {
|
2023-03-07 15:41:20 +00:00
|
|
|
// TODO: check if should stop the worker
|
2023-03-04 19:46:09 +00:00
|
|
|
match self
|
|
|
|
.queue
|
2023-03-07 15:41:20 +00:00
|
|
|
.pull_next_task(self.task_type.clone())
|
2023-03-04 19:46:09 +00:00
|
|
|
.await
|
|
|
|
{
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(Some(task)) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
let actual_task: Box<dyn RunnableTask> = serde_json::from_value(task.payload.clone()).unwrap();
|
2023-03-04 18:07:17 +00:00
|
|
|
|
|
|
|
// check if task is scheduled or not
|
|
|
|
if let Some(CronPattern(_)) = actual_task.cron() {
|
|
|
|
// program task
|
2023-03-07 15:41:20 +00:00
|
|
|
//self.queue.schedule_task(&*actual_task).await?;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
// run scheduled task
|
2023-03-07 15:41:20 +00:00
|
|
|
// TODO: what do we do if the task fails? it's an internal error, inform the logs
|
|
|
|
let _ = self.run(task, actual_task).await;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
Ok(None) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
self.wait().await;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Err(error) => {
|
|
|
|
error!("Failed to fetch a task {:?}", error);
|
2023-03-07 15:41:20 +00:00
|
|
|
self.wait().await;
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2023-03-07 15:41:20 +00:00
|
|
|
pub async fn run_tasks_until_none(&mut self) -> Result<(), BackieError> {
|
2023-03-04 19:46:09 +00:00
|
|
|
loop {
|
|
|
|
match self
|
|
|
|
.queue
|
2023-03-07 15:41:20 +00:00
|
|
|
.pull_next_task(self.task_type.clone())
|
2023-03-04 19:46:09 +00:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(Some(task)) => {
|
2023-03-07 15:41:20 +00:00
|
|
|
let actual_task: Box<dyn RunnableTask> =
|
|
|
|
serde_json::from_value(task.payload.clone()).unwrap();
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
// check if task is scheduled or not
|
|
|
|
if let Some(CronPattern(_)) = actual_task.cron() {
|
|
|
|
// program task
|
2023-03-07 15:41:20 +00:00
|
|
|
// self.queue.schedule_task(&*actual_task).await?;
|
2023-03-04 19:46:09 +00:00
|
|
|
}
|
2023-03-07 15:41:20 +00:00
|
|
|
self.wait().await;
|
2023-03-04 19:46:09 +00:00
|
|
|
// run scheduled task
|
|
|
|
self.run(task, actual_task).await?;
|
|
|
|
}
|
|
|
|
Ok(None) => {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(error) => {
|
|
|
|
error!("Failed to fetch a task {:?}", error);
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
self.wait().await;
|
2023-03-04 19:46:09 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod async_worker_tests {
|
|
|
|
use super::*;
|
2023-03-07 15:41:20 +00:00
|
|
|
use crate::errors::BackieError;
|
|
|
|
use crate::queue::Queueable;
|
2023-03-04 19:46:09 +00:00
|
|
|
use crate::queue::PgAsyncQueue;
|
2023-03-04 18:07:17 +00:00
|
|
|
use crate::worker::Task;
|
|
|
|
use crate::RetentionMode;
|
|
|
|
use crate::Scheduled;
|
|
|
|
use async_trait::async_trait;
|
|
|
|
use chrono::Duration;
|
|
|
|
use chrono::Utc;
|
2023-03-04 19:46:09 +00:00
|
|
|
use diesel_async::pooled_connection::{bb8::Pool, AsyncDieselConnectionManager};
|
|
|
|
use diesel_async::AsyncPgConnection;
|
2023-03-04 18:07:17 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2023-03-07 15:41:20 +00:00
|
|
|
use crate::task::TaskState;
|
2023-03-04 18:07:17 +00:00
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
struct WorkerAsyncTask {
|
|
|
|
pub number: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for WorkerAsyncTask {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
struct WorkerAsyncTaskSchedule {
|
|
|
|
pub number: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for WorkerAsyncTaskSchedule {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
fn cron(&self) -> Option<Scheduled> {
|
|
|
|
Some(Scheduled::ScheduleOnce(Utc::now() + Duration::seconds(1)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
struct AsyncFailedTask {
|
|
|
|
pub number: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for AsyncFailedTask {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
let message = format!("number {} is wrong :(", self.number);
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
Err(Box::new(BackieError {
|
2023-03-04 18:07:17 +00:00
|
|
|
description: message,
|
2023-03-07 15:41:20 +00:00
|
|
|
}))
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn max_retries(&self) -> i32 {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
|
|
struct AsyncRetryTask {}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for AsyncRetryTask {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
let message = "Failed".to_string();
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
Err(Box::new(BackieError {
|
2023-03-04 18:07:17 +00:00
|
|
|
description: message,
|
2023-03-07 15:41:20 +00:00
|
|
|
}))
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn max_retries(&self) -> i32 {
|
|
|
|
2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
struct AsyncTaskType1 {}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for AsyncTaskType1 {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
fn task_type(&self) -> TaskType {
|
|
|
|
TaskType::from("type1")
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
struct AsyncTaskType2 {}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
#[async_trait]
|
2023-03-07 15:41:20 +00:00
|
|
|
impl RunnableTask for AsyncTaskType2 {
|
|
|
|
async fn run(&self, _queueable: &mut dyn Queueable) -> Result<(), Box<(dyn std::error::Error + Send + 'static)>> {
|
2023-03-04 18:07:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
fn task_type(&self) -> TaskType {
|
|
|
|
TaskType::from("type2")
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-04 19:46:09 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn execute_and_finishes_task() {
|
|
|
|
let pool = pool().await;
|
2023-03-07 15:41:20 +00:00
|
|
|
let mut test = PgAsyncQueue::new(pool);
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let actual_task = WorkerAsyncTask { number: 1 };
|
|
|
|
|
|
|
|
let task = insert_task(&mut test, &actual_task).await;
|
|
|
|
let id = task.id;
|
|
|
|
|
|
|
|
let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
.queue(test.clone())
|
|
|
|
.retention_mode(RetentionMode::KeepAll)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run(task, Box::new(actual_task)).await.unwrap();
|
|
|
|
let task_finished = test.find_task_by_id(id).await.unwrap();
|
|
|
|
assert_eq!(id, task_finished.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Done, task_finished.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
test.remove_all_tasks().await.unwrap();
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
// #[tokio::test]
|
|
|
|
// async fn schedule_task_test() {
|
|
|
|
// let pool = pool().await;
|
|
|
|
// let mut test = PgAsyncQueue::new(pool);
|
|
|
|
//
|
|
|
|
// let actual_task = WorkerAsyncTaskSchedule { number: 1 };
|
|
|
|
//
|
|
|
|
// let task = test.schedule_task(&actual_task).await.unwrap();
|
|
|
|
//
|
|
|
|
// let id = task.id;
|
|
|
|
//
|
|
|
|
// let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
// .queue(test.clone())
|
|
|
|
// .retention_mode(RetentionMode::KeepAll)
|
|
|
|
// .build();
|
|
|
|
//
|
|
|
|
// worker.run_tasks_until_none().await.unwrap();
|
|
|
|
//
|
|
|
|
// let task = worker.queue.find_task_by_id(id).await.unwrap();
|
|
|
|
//
|
|
|
|
// assert_eq!(id, task.id);
|
|
|
|
// assert_eq!(TaskState::Ready, task.state());
|
|
|
|
//
|
|
|
|
// tokio::time::sleep(core::time::Duration::from_secs(3)).await;
|
|
|
|
//
|
|
|
|
// worker.run_tasks_until_none().await.unwrap();
|
|
|
|
//
|
|
|
|
// let task = test.find_task_by_id(id).await.unwrap();
|
|
|
|
// assert_eq!(id, task.id);
|
|
|
|
// assert_eq!(TaskState::Done, task.state());
|
|
|
|
//
|
|
|
|
// test.remove_all_tasks().await.unwrap();
|
|
|
|
// }
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn retries_task_test() {
|
|
|
|
let pool = pool().await;
|
2023-03-07 15:41:20 +00:00
|
|
|
let mut test = PgAsyncQueue::new(pool);
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let actual_task = AsyncRetryTask {};
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
let task = test.create_task(&actual_task).await.unwrap();
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let id = task.id;
|
|
|
|
|
|
|
|
let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
.queue(test.clone())
|
|
|
|
.retention_mode(RetentionMode::KeepAll)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run_tasks_until_none().await.unwrap();
|
|
|
|
|
|
|
|
let task = worker.queue.find_task_by_id(id).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(id, task.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Ready, task.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
assert_eq!(1, task.retries);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert!(task.error_message.is_some());
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
tokio::time::sleep(core::time::Duration::from_secs(5)).await;
|
|
|
|
worker.run_tasks_until_none().await.unwrap();
|
|
|
|
|
|
|
|
let task = worker.queue.find_task_by_id(id).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(id, task.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Ready, task.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
assert_eq!(2, task.retries);
|
|
|
|
|
|
|
|
tokio::time::sleep(core::time::Duration::from_secs(10)).await;
|
|
|
|
worker.run_tasks_until_none().await.unwrap();
|
|
|
|
|
|
|
|
let task = test.find_task_by_id(id).await.unwrap();
|
|
|
|
assert_eq!(id, task.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Failed, task.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
assert_eq!("Failed".to_string(), task.error_message.unwrap());
|
|
|
|
|
|
|
|
test.remove_all_tasks().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn saves_error_for_failed_task() {
|
|
|
|
let pool = pool().await;
|
2023-03-07 15:41:20 +00:00
|
|
|
let mut test = PgAsyncQueue::new(pool);
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let failed_task = AsyncFailedTask { number: 1 };
|
|
|
|
|
|
|
|
let task = insert_task(&mut test, &failed_task).await;
|
|
|
|
let id = task.id;
|
|
|
|
|
|
|
|
let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
.queue(test.clone())
|
|
|
|
.retention_mode(RetentionMode::KeepAll)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run(task, Box::new(failed_task)).await.unwrap();
|
|
|
|
let task_finished = test.find_task_by_id(id).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(id, task_finished.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Failed, task_finished.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
assert_eq!(
|
|
|
|
"number 1 is wrong :(".to_string(),
|
|
|
|
task_finished.error_message.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
test.remove_all_tasks().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn executes_task_only_of_specific_type() {
|
|
|
|
let pool = pool().await;
|
2023-03-07 15:41:20 +00:00
|
|
|
let mut test = PgAsyncQueue::new(pool);
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let task1 = insert_task(&mut test, &AsyncTaskType1 {}).await;
|
|
|
|
let task12 = insert_task(&mut test, &AsyncTaskType1 {}).await;
|
|
|
|
let task2 = insert_task(&mut test, &AsyncTaskType2 {}).await;
|
|
|
|
|
|
|
|
let id1 = task1.id;
|
|
|
|
let id12 = task12.id;
|
|
|
|
let id2 = task2.id;
|
|
|
|
|
|
|
|
let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
.queue(test.clone())
|
2023-03-07 15:41:20 +00:00
|
|
|
.task_type(TaskType::from("type1"))
|
2023-03-04 19:46:09 +00:00
|
|
|
.retention_mode(RetentionMode::KeepAll)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run_tasks_until_none().await.unwrap();
|
|
|
|
let task1 = test.find_task_by_id(id1).await.unwrap();
|
|
|
|
let task12 = test.find_task_by_id(id12).await.unwrap();
|
|
|
|
let task2 = test.find_task_by_id(id2).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(id1, task1.id);
|
|
|
|
assert_eq!(id12, task12.id);
|
|
|
|
assert_eq!(id2, task2.id);
|
2023-03-07 15:41:20 +00:00
|
|
|
assert_eq!(TaskState::Done, task1.state());
|
|
|
|
assert_eq!(TaskState::Done, task12.state());
|
|
|
|
assert_eq!(TaskState::Ready, task2.state());
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
test.remove_all_tasks().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn remove_when_finished() {
|
|
|
|
let pool = pool().await;
|
2023-03-07 15:41:20 +00:00
|
|
|
let mut test = PgAsyncQueue::new(pool);
|
2023-03-04 19:46:09 +00:00
|
|
|
|
|
|
|
let task1 = insert_task(&mut test, &AsyncTaskType1 {}).await;
|
|
|
|
let task12 = insert_task(&mut test, &AsyncTaskType1 {}).await;
|
|
|
|
let task2 = insert_task(&mut test, &AsyncTaskType2 {}).await;
|
|
|
|
|
|
|
|
let _id1 = task1.id;
|
|
|
|
let _id12 = task12.id;
|
|
|
|
let id2 = task2.id;
|
|
|
|
|
|
|
|
let mut worker = AsyncWorker::<PgAsyncQueue>::builder()
|
|
|
|
.queue(test.clone())
|
2023-03-07 15:41:20 +00:00
|
|
|
.task_type(TaskType::from("type1"))
|
2023-03-04 19:46:09 +00:00
|
|
|
.build();
|
|
|
|
|
|
|
|
worker.run_tasks_until_none().await.unwrap();
|
|
|
|
let task = test
|
2023-03-07 15:41:20 +00:00
|
|
|
.pull_next_task(Some(TaskType::from("type1")))
|
2023-03-04 19:46:09 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(None, task);
|
|
|
|
|
|
|
|
let task2 = test
|
2023-03-07 15:41:20 +00:00
|
|
|
.pull_next_task(Some(TaskType::from("type2")))
|
2023-03-04 19:46:09 +00:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(id2, task2.id);
|
|
|
|
|
|
|
|
test.remove_all_tasks().await.unwrap();
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:41:20 +00:00
|
|
|
async fn insert_task(test: &mut PgAsyncQueue, task: &dyn RunnableTask) -> Task {
|
|
|
|
test.create_task(task).await.unwrap()
|
2023-03-04 19:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn pool() -> Pool<AsyncPgConnection> {
|
|
|
|
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new(
|
2023-03-07 15:41:20 +00:00
|
|
|
"postgres://postgres:password@localhost/backie",
|
2023-03-04 19:46:09 +00:00
|
|
|
);
|
|
|
|
Pool::builder()
|
|
|
|
.max_size(1)
|
|
|
|
.min_idle(Some(1))
|
|
|
|
.build(manager)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
2023-03-04 18:07:17 +00:00
|
|
|
}
|