salsify/delayed_job_groups_plugin

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Delayed Job Groups
[![Gem Version](https://badge.fury.io/rb/delayed_job_groups_plugin.png)][gem]
[![Build Status](https://circleci.com/gh/salsify/delayed_job_groups_plugin.svg?style=svg)][circleci]
[![Code Climate](https://codeclimate.com/github/salsify/delayed_job_groups_plugin.png)][codeclimate]
[![Coverage Status](https://coveralls.io/repos/salsify/delayed_job_groups_plugin/badge.png)][coveralls]

[gem]: https://rubygems.org/gems/delayed_job_groups_plugin
[circleci]: https://circleci.com/gh/salsify/delayed_job_groups_plugin
[codeclimate]: https://codeclimate.com/github/salsify/delayed_job_groups_plugin
[coveralls]: https://coveralls.io/r/salsify/delayed_job_groups_plugin

A [Delayed Job](https://github.com/collectiveidea/delayed_job) plugin that adds job groups supporting:

* Canceling all jobs in a job group
* Canceling the job group when a job in the job group fails
* Blocking and unblocking all jobs in a job group
* Running additional processing after all jobs in a job group complete or a job group fails

## Installation

Add this line to your application's Gemfile:

    gem 'delayed_job_groups_plugin'

And then execute:

    $ bundle install

Or install it yourself as:

    $ gem install delayed_job_groups_plugin

Run the required database migrations:

    $ rails generate delayed_job_groups_plugin:install
    $ rake db:migrate

## Upgrading from 0.1.2
run the following generator to create a migration for the new configuration column.

    $ rails generate migration add_failure_cancels_group_to_delayed_job_groups failure_cancels_group:boolean
    # add `default: true, null: false` to the generated migration for the failure_cancels_group column
    $ rake db:migrate

## Usage

Creating a job group and queueing some jobs:

```ruby
job_group = Delayed::JobGroups::JobGroup.create!

# JobGroup#enqueue has the same signature as Delayed::Job.enqueue
# i.e. it takes a job and an optional hash of options.
job_group.enqueue(MyJob.new('some arg'), queue: 'general')
job_group.enqueue(MyJob.new('some other arg'), queue: 'general', priority: 10)

# Tell the JobGroup we're done queueing jobs
job_group.mark_queueing_complete
```

Registering a job to run after all jobs in the job group have completed:

```ruby
# We can optionally pass options that will be used when queueing the on completion job
job_group = Delayed::JobGroups::JobGroup.create!(on_completion_job: MyCompletionJob.new,
                                                 on_completion_job_options: { queue: 'general' })
```

Registering a job to run if the job group is canceled or fails:

```ruby
# We can optionally pass options that will be used when queueing the on cancellation job
job_group = Delayed::JobGroups::JobGroup.create!(on_cancellation_job: MyCancellationJob.new,
                                                 on_cancellation_job_options: { queue: 'general' })
```

Block and unblock jobs in a job group:

```ruby
# Construct the JobGroup in a blocked state
job_group = Delayed::JobGroups::JobGroup.create!(blocked: true)
job_group.enqueue(MyJob.new('some arg'), queue: 'general')
job_group.mark_queueing_complete

# Do more stuff...

# Unblock the JobGroup so its jobs can run
job_group.unblock
```

Cancel a job group:

```ruby
job_group = Delayed::JobGroups::JobGroup.create!

# Do more stuff...

job_group.cancel
```

Configuration to allow failed jobs not to cancel the group
```ruby
# We can optionally pass options that will allow jobs to fail without cancelling the group.
# This also allows the on_completion job to fire once all jobs have either succeeded or failed.
job_group = Delayed::JobGroups::JobGroup.create!(failure_cancels_group: false)
```

### Maintenance

It's possible to end up in a state where all jobs in a group have been completed, but the completion job has not run.
This is due a race condition where the final job in a group is completed, and the worker running it is terminated before
the completion job can be enqueued.

As a remedy for the above scenario, a job is provided which cleans up any job groups in this state. It is recommended to
run this job periodically (for example in a cron job), especially in high-thoughput applications.

```ruby
Delayed::JobGroups::CompleteStuckJobGroupsJob.enqueue
```

## Supported Platforms

* Only the Delayed Job Active Record backend is supported.
* Tested with Rails 4.2 through 5.2.
* Tested with MRI 2.3 through 2.5.

## 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