No description
Find a file
Ayrat Badykov 45eb336b8a
Periodic tasks (#5)
* Periodic tasks

* schedule next execution

* add scheduler

* ignore test

* fix clippy

* make start public

* check if the period task already exists

* do not insert task if it's already in the queue

* fix tests
2021-07-18 22:09:30 +03:00
.github fix workflow 2021-07-11 13:44:28 +03:00
migrations Periodic tasks (#5) 2021-07-18 22:09:30 +03:00
src Periodic tasks (#5) 2021-07-18 22:09:30 +03:00
.env start working on storage level 2021-06-05 14:39:19 +03:00
.gitignore initial commit 2021-05-30 11:35:00 +03:00
Cargo.toml bump version and add badges 2021-07-11 13:55:52 +03:00
CHANGELOG.md bump version and add badges 2021-07-11 13:55:52 +03:00
diesel.toml start working on storage level 2021-06-05 14:39:19 +03:00
logo.png add README 2021-06-24 12:58:02 +03:00
README.md bump version and add badges 2021-07-11 13:55:52 +03:00

fang

Crates.io docs page test style

Fang

Background job processing library for Rust.

Currently, it uses Postgres to store state. But in the future, more backends will be supported.

Note that the README follows the master branch, to see instructions for the latest published version, check crates.io.

Installation

  1. Add this to your Cargo.toml
[dependencies]
fang = "0.3.1"
  1. Create fang_tasks table in the Postgres database. The migration can be found in the migrations directory.

Usage

Defining a job

Every job should implement fang::Runnable trait which is used by fang to execute it.

use fang::Error;
use fang::Runnable;
use fang::{Deserialize, Serialize};
use fang::typetag;


#[derive(Serialize, Deserialize)]
struct Job {
    pub number: u16,
}

#[typetag::serde]
impl Runnable for Job {
    fn run(&self) -> Result<(), Error> {
        println!("the number is {}", self.number);

        Ok(())
    }
}

As you can see from the example above, the trait implementation has #[typetag::serde] attribute which is used to deserialize the job.

Enqueuing a job

To enqueue a job use Postgres::enqueue_task

use fang::Postgres;

...

Postgres::enqueue_task(&Job { number: 10 }).unwrap();

The example above creates a new postgres connection on every call. If you want to reuse the same postgres connection to enqueue several jobs use Postgres struct instance:

let postgres = Postgres::new();

for id in &unsynced_feed_ids {
    postgres.push_task(&SyncFeedJob { feed_id: *id }).unwrap();
}

Starting workers

Every worker runs in a separate thread. In case of panic, they are always restarted.

Use WorkerPool to start workers. WorkerPool::new accepts one parameter - the number of workers.

use fang::WorkerPool;

WorkerPool::new(10).start();

Configuration

To configure workers, instead of WorkerPool::new which uses default values, use WorkerPool.new_with_params. It accepts two parameters - the number of workers and WorkerParams struct.

Configuring the type of workers

You can start workers for a specific types of tasks. These workers will be executing only tasks of the specified type.

Add task_type method to the Runnable trait implementation:

...

#[typetag::serde]
impl Runnable for Job {
    fn run(&self) -> Result<(), Error> {
        println!("the number is {}", self.number);

        Ok(())
    }

    fn task_type(&self) -> String {
        "number".to_string()
    }
}

Set task_type to the WorkerParamas:

let mut worker_params = WorkerParams::new();
worker_params.set_task_type("number".to_string());

WorkerPool::new_with_params(10, worker_params).start();

Without setting task_type workers will be executing any type of task.

Configuring retention mode

By default, all successfully finished tasks are removed from the DB, failed tasks aren't.

There are three retention modes you can use:

pub enum RetentionMode {
    KeepAll,        \\ doesn't remove tasks
    RemoveAll,      \\ removes all tasks
    RemoveFinished, \\ default value
}

Set retention mode with set_retention_mode:

let mut worker_params = WorkerParams::new();
worker_params.set_retention_mode(RetentionMode::RemoveAll);

WorkerPool::new_with_params(10, worker_params).start();

Configuring sleep values

You can use use SleepParams to confugure sleep values:

pub struct SleepParams {
    pub sleep_period: u64,     \\ default value is 5
    pub max_sleep_period: u64, \\ default value is 15
    pub min_sleep_period: u64, \\ default value is 5
    pub sleep_step: u64,       \\ default value is 5
}p

If there are no tasks in the DB, a worker sleeps for sleep_period and each time this value increases by sleep_step until it reaches max_sleep_period. min_sleep_period is the initial value for sleep_period. All values are in seconds.

Use set_sleep_params to set it:

let sleep_params = SleepParams {
    sleep_period: 2,
    max_sleep_period: 6,
    min_sleep_period: 2,
    sleep_step: 1,
};
let mut worker_params = WorkerParams::new();
worker_params.set_sleep_params(sleep_params);

WorkerPool::new_with_params(10, worker_params).start();

Potential/future features

  • Retries
  • Scheduled tasks
  • Extendable/new backends

Contributing

  1. Fork it!
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Author

Ayrat Badykov (@ayrat555)