Build a Job Queue with Rust Using Aide-De-Camp (Part 3)
Published: 2022-12-06 00:23:34

This is the third installment in our series, where we continue to run you through creating durable job queues using [Zero Assumption](https://zeroassumptions.dev/)'s open source solution, [Aide-De-Camp](https://github.com/ZeroAssumptions/aide-de-camp), which is built using Rust. 

In this post, we'll go over feature requests we've received and how we created those features. 

## Aide-De-Camp Code

Code from this series is available in [our open source Aide-De-Camp GitHub repository](https://github.com/ZeroAssumptions/aide-de-camp). 

The Crate is published to [crates.io](https://crates.io/crates/aide-de-camp) for everyone to use.

## Feature Requests

We'll address two feature requests we've implemented:

* [Cancel job by ID](https://github.com/ZeroAssumptions/aide-de-camp/issues/6)
* [Add job priority](https://github.com/ZeroAssumptions/aide-de-camp/issues/7)

These two requests required changes to our public API, which created breaking changes. 

To submit your own feature request for Aide-De-Camp, open an Issue on our [open source GitHub repository](https://github.com/ZeroAssumptions/aide-de-camp/issues).

## Cancel Job By ID Feature

In this Issue, we were asked to implement cancelling a job by providing the job ID. See the [Issue submitted on GitHub](https://github.com/ZeroAssumptions/aide-de-camp/issues/6).

### Cancel a Job That Hasn't Started

In our implementation, you can only cancel a job when it has not yet started.

A job ID is available once the job is submitted. We use this job ID value in a new `Queue` trait. In a method of the `Queue` trait, we do a couple things:

* Check if the job has already started. If so, ignore the cancellation request.
* Check if the job ID does not exist. If so, return an error indicating that the job can't be found. 

If the job ID exists and the job has not started, the cancellation goes through. A payload for the cancelled job is returned when the job type is specified. If no job type is specified, then no payload returns but the job is still cancelled. You won't always be able to provide the job type as it's not always available.

In the following code, you'll see two cancel job functions:

* Cancel scheduled job using job id, and return no payload upon success
* Cancel scheduled job using job id and job type, and return payload upon success

```rust
/// Cancel job that has been scheduled. Right now this will only cancel if the job hasn't started yet.
async fn cancel_job(&self, job_id: Xid) -> Result<(), QueueError>;

/// The same as [`cancel_job`](struct.cancel_job.html), but returns payload of canceled job.
/// If deserialization fails, then job won't be cancelled.
async fn unschedule_job<J>(&self, job_id: Xid) -> Result<J::Payload, QueueError>
where
	J: JobProcessor + 'static,
	J::Payload: Decode;
```

To review the full job cancellation feature implementation, see [Aide-De-Camp feature #9](https://github.com/ZeroAssumptions/aide-de-camp/pull/9/files).

#### Unit Testing for Solution

With the above functionality, we tested against the following cases:

* Job ID cannot be found
* Job was cancelled successfully with payload returned since the job type was included in the cancellation request
* Job was cancelled successfully without payload returned
* `job_type` that does NOT match `jid` (for method that returns payload)
* Cancelling the job with payload, but the payload couldn't be deserialized
* Cannot cancel a job that has already started

## Job Priority Feature

In this Issue, we were asked to provide a job prioritization ability. See the [Issue submitted on GitHub](https://github.com/ZeroAssumptions/aide-de-camp/issues/7).

Priority becomes important when there is worker exhaustion, or more jobs ready to be worked on than there are job processors. In that case, it's beneficial to run jobs with higher priority first even if they were submitted later than lower priority jobs.

We chose to implement priority using an integer, i8 or the range -128 to 127. The higher the number, the higher the priority.

Given this, the queue interface needed to be changed. We added a required argument `priority: <i8 value>`. We recommend setting low priority jobs to `priority: 0`.

A SQL query looks at jobs in the queue and runs through the `priority` values, ordering them from highest number to lowest number. 

```sql
SELECT jid FROM adc_queue WHERE started_at IS NULL AND scheduled_at <= now() AND job_type IN (“my_job”) ORDER BY priority DESC LIMIT 1
```

To review the full job prioritization feature implementation, see [Aide-De-Camp feature #10](https://github.com/ZeroAssumptions/aide-de-camp/pull/10/files).

## Up Next

In this post, we explained two feature requests, job cancellation and job prioritization, and how we implemented them. In part 4, we'll go over building cron job support.

Follow us here on DEV, [Twitter](https://twitter.com/ZeroAssumptions), and [LinkedIn](https://www.linkedin.com/company/zero-assumptions) for updates on [Aide-De-Camp](https://github.com/ZeroAssumptions/aide-de-camp). 

Aide-De-Camp is a product of [Zero Assumptions](https://zeroassumptions.dev/).