izta 0.1.2

Izta is a drop-in job queue for Rust
Documentation

Izta - Rust Asynchronous Task Scheduler

Izta is a drop-in asynchronous job queue for Rust. It is designed to run in the vast majority of Rust applications without changes to software architecture or system infrastructure. It can run inside your application process as a separate thread or by itself on a separate server. Multiple instances may run concurrently to execute tasks in parallel. Izta also implements named queues, so that tasks in one queue can be processed by Izta instance A and those in another queue by Izta instance B - allowing busy queues to be scaled out separately from others. Currently, Postgres is the only supported backend.

Examples

Before starting the job queue, the jobs themselves need to be defined. Any struct that implements the Job (see Job documentation for more details), Serialize and Deserialize traits are valid Job types.

Here's an example of a job that simply divides two numbers together:

#[macro_use] extern crate serde;
use izta::job::Job;

// Jobs can be any type that implements the Job, Serialize and Deserialize traits
#[derive(Serialize, Deserialize)]
struct DivideJob {
a: i64,
b: i64,
}

// Jobs must have a serializable error type. Could be `()` for jobs that always succeed
#[derive(Serialize, Deserialize)]
enum DivideJobErr {
DivideByZero,
}

impl Job for DivideJob {
// Specify the result and error types
type R = i64;
type E = DivideJobErr;

// All jobs must have a UUID
const UUID: &'static str = "74f3a15b-75c0-4889-9546-63b02ff304e4";

const MAX_ATTEMPTS: usize = 3;

// Job logic - return an `Err` for errors and `Ok` if successful.
fn run(&self) -> Result<Self::R, Self::E> {
if self.b == 0 {
return Err(DivideJobErr::DivideByZero);
}
Ok(self.a / self.b)
}
}

With a job defined, we can now create a task runner:

# #[macro_use] extern crate serde;
# use izta::job::Job;
#
# // Jobs can be any type that implements the Job, Serialize and Deserialize traits
# #[derive(Serialize, Deserialize)]
# struct DivideJob {
#     a: i64,
#     b: i64,
# }
#
# // Jobs must have a serializable error type. Could be `()` for jobs that always succeed
# #[derive(Serialize, Deserialize)]
# enum DivideJobErr {
#     DivideByZero,
# }
#
# impl Job for DivideJob {
#     // Specify the result and error types
#     type R = i64;
#     type E = DivideJobErr;
#
#     // All jobs must have a UUID
#     const UUID: &'static str = "74f3a15b-75c0-4889-9546-63b02ff304e4";
#
#     const MAX_ATTEMPTS: usize = 3;
#
#     // Job logic - return an `Err` for errors and `Ok` if successful.
#     fn run(&self) -> Result<Self::R, Self::E> {
#         if self.b == 0 {
#           return Err(DivideJobErr::DivideByZero);
#         }
#         Ok(self.a / self.b)
#     }
# }
use izta::process_jobs;
use izta::runner::Runner;

let runner = Runner::new(
process_jobs!(DivideJob),
"postgres://izta:password@localhost:5432/izta_test",
"tasks",
vec![],
);

Defining and adding tasks is easy with TaskReq::new:

# #[macro_use] extern crate serde;
# use izta::job::Job;
#
# // Jobs can be any type that implements the Job, Serialize and Deserialize traits
# #[derive(Serialize, Deserialize)]
# struct DivideJob {
#     a: i64,
#     b: i64,
# }
#
# // Jobs must have a serializable error type. Could be `()` for jobs that always succeed
# #[derive(Serialize, Deserialize)]
# enum DivideJobErr {
#     DivideByZero,
# }
#
# impl Job for DivideJob {
#     // Specify the result and error types
#     type R = i64;
#     type E = DivideJobErr;
#
#     // All jobs must have a UUID
#     const UUID: &'static str = "74f3a15b-75c0-4889-9546-63b02ff304e4";
#
#     const MAX_ATTEMPTS: usize = 3;
#
#     // Job logic - return an `Err` for errors and `Ok` if successful.
#     fn run(&self) -> Result<Self::R, Self::E> {
#         if self.b == 0 {
#           return Err(DivideJobErr::DivideByZero);
#         }
#         Ok(self.a / self.b)
#     }
# }
# use izta::process_jobs;
# use izta::runner::Runner;
# let runner = Runner::new(
#     process_jobs!(DivideJob),
#    "postgres://izta:password@localhost:5432/izta_test",
#    "tasks",
#    vec![],
# );
use izta::task::task_req::TaskReq;

let task_req = TaskReq::new(DivideJob { a: 1, b: 2 });
runner.add_task(&task_req);

Starting the task runner will begin executing tasks

# #[macro_use] extern crate serde;
# use izta::job::Job;
#
# // Jobs can be any type that implements the Job, Serialize and Deserialize traits
# #[derive(Serialize, Deserialize)]
# struct DivideJob {
#     a: i64,
#     b: i64,
# }
#
# // Jobs must have a serializable error type. Could be `()` for jobs that always succeed
# #[derive(Serialize, Deserialize)]
# enum DivideJobErr {
#     DivideByZero,
# }
#
# impl Job for DivideJob {
#     // Specify the result and error types
#     type R = i64;
#     type E = DivideJobErr;
#
#     // All jobs must have a UUID
#     const UUID: &'static str = "74f3a15b-75c0-4889-9546-63b02ff304e4";
#
#     const MAX_ATTEMPTS: usize = 3;
#
#     // Job logic - return an `Err` for errors and `Ok` if successful.
#     fn run(&self) -> Result<Self::R, Self::E> {
#         if self.b == 0 {
#           return Err(DivideJobErr::DivideByZero);
#         }
#         Ok(self.a / self.b)
#     }
# }
# use izta::process_jobs;
# use izta::runner::Runner;
# let runner = Runner::new(
#     process_jobs!(DivideJob),
#    "postgres://izta:password@localhost:5432/izta_test",
#    "tasks",
#    vec![],
# );
# use izta::task::task_req::TaskReq;
#
# let task_req = TaskReq::new(DivideJob { a: 1, b: 2 });
# runner.add_task(&task_req);
runner.start();

Of course, it's possible to add new tasks after the task runner has been started. Running start() multiple times will spawn multiple instances of the task runner that will execute tasks in parallel.