Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,3 @@ coverage
rdoc
pkg
.rvmrc

## PROJECT::SPECIFIC
Gemfile.lock
*.gem
gemfiles/*.lock
1 change: 0 additions & 1 deletion .ruby-version

This file was deleted.

185 changes: 185 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
PATH
remote: .
specs:
workless (2.2.0)
delayed_job (>= 2.0.7)
platform-api
rails
rush

GEM
remote: https://rubygems.org/
specs:
actioncable (5.1.5)
actionpack (= 5.1.5)
nio4r (~> 2.0)
websocket-driver (~> 0.6.1)
actionmailer (5.1.5)
actionpack (= 5.1.5)
actionview (= 5.1.5)
activejob (= 5.1.5)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.1.5)
actionview (= 5.1.5)
activesupport (= 5.1.5)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.1.5)
activesupport (= 5.1.5)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.1.5)
activesupport (= 5.1.5)
globalid (>= 0.3.6)
activemodel (5.1.5)
activesupport (= 5.1.5)
activerecord (5.1.5)
activemodel (= 5.1.5)
activesupport (= 5.1.5)
arel (~> 8.0)
activesupport (5.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
arel (8.0.0)
builder (3.2.3)
codeclimate-test-reporter (1.0.8)
simplecov (<= 0.13)
coderay (1.1.2)
concurrent-ruby (1.0.5)
coveralls (0.7.2)
multi_json (~> 1.3)
rest-client (= 1.6.7)
simplecov (>= 0.7)
term-ansicolor (= 1.2.2)
thor (= 0.18.1)
crass (1.0.3)
delayed_job (4.1.4)
activesupport (>= 3.0, < 5.2)
diff-lcs (1.3)
docile (1.1.5)
erubi (1.7.1)
erubis (2.7.0)
excon (0.60.0)
globalid (0.4.1)
activesupport (>= 4.2.0)
heroics (0.0.24)
erubis (~> 2.0)
excon
moneta
multi_json (>= 1.9.2)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
json (2.1.0)
loofah (2.2.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
mini_mime (>= 0.1.1)
method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.3)
moneta (0.8.1)
multi_json (1.13.1)
nio4r (2.2.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
platform-api (2.1.0)
heroics (~> 0.0.23)
moneta (~> 0.8.1)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-rails (0.3.6)
pry (>= 0.10.4)
rack (2.0.4)
rack-test (0.8.3)
rack (>= 1.0, < 3)
rails (5.1.5)
actioncable (= 5.1.5)
actionmailer (= 5.1.5)
actionpack (= 5.1.5)
actionview (= 5.1.5)
activejob (= 5.1.5)
activemodel (= 5.1.5)
activerecord (= 5.1.5)
activesupport (= 5.1.5)
bundler (>= 1.3.0)
railties (= 5.1.5)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (5.1.5)
actionpack (= 5.1.5)
activesupport (= 5.1.5)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (12.3.0)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.1)
rush (0.6.8)
session
session (3.2.0)
simplecov (0.13.0)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
term-ansicolor (1.2.2)
tins (~> 0.8)
thor (0.18.1)
thread_safe (0.3.6)
tins (0.13.2)
tzinfo (1.2.5)
thread_safe (~> 0.1)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)

PLATFORMS
ruby

DEPENDENCIES
codeclimate-test-reporter (~> 1.0.0)
coveralls
pry-rails (~> 0.3)
rspec
simplecov
workless!

BUNDLED WITH
1.16.1
92 changes: 43 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,73 @@
This is an addon for delayed_job (> 2.0.0) http://github.com/collectiveidea/delayed_job
It is designed to be used when you're using Heroku as a host and have the need to do background work with delayed job but you don't want to leave the workers running all the time as it costs money.

By adding the gem to your project and configuring our Heroku app with some config variables workless should do the rest.
## Installation

:warning: **[The Legacy API will be sunset on April 15th, 2017](https://devcenter.heroku.com/changelog-items/862)** :warning:
Please upgrade to version 2.0.0 as soon as you can. Version 2.0.0 is released on March 1st, 2017.
Add the workless gem and the delayed_job gem to your project Gemfile and update your bundle. Its is recommended to specify the gem version for delayed_job

## Heroku Stack Heroku-16 update
Version 2.2.0 changed the config for setting the Heroku API key. This will now reside in WORKLESS_API_KEY. Please change this key in your Heroku setup when upgrading this gem!
<pre>
gem "delayed_job_active_record"
gem 'workless', git: 'https://github.com/patricklindsay/workless.git', tag: 'v3.0.0'
</pre>

## Updates
If you don't specify delayed_job in your Gemfile workless will bring it in, most likely the latest version (4.1.2)

* Version 2.2.0 Revitalized by @davidakachaos through his workless_revived project, now merged for a 2.2.0 release.
* Version 1.3.0 DROPS SUPPORT FOR OLDER RUBY AND RAILS VERSIONS!
* Version 1.2.5 Added middleware to check on delayed jobs, fixed Rails 5 support
* Version 1.2.4 drops support for older versions!
* Version 1.2.3 replaces multiple commit callback with two callbacks for compatibility by @lostboy
* Version 1.2.2 includes after_commit fix by @collectiveip
* Version 1.2.1 includes support for Rails 4 & DJ 4 by @florentmorin
* Version 1.2.0 includes new support for Sequel by @davidakachaos
* Version 1.1.3 includes changes by @radanskoric to reduce number of heroku api calls
* Version 1.1.2 includes a change by @davidakachaos to scale workers using after_commit
* Version 1.1.1 includes a fix from @filiptepper and @fixr to correctly scale workers
* Version 1.1.0 has been released, this adds support for scaling using multiple workers thanks to @jaimeiniesta and @davidakachaos.
* Version 1.0.0 has been released, this brings compatibility with delayed_job 3 and compatibility with Rails 2.3.x and up.
Add your Heroku app name & [API key](https://devcenter.heroku.com/articles/authentication) as config vars to your Heroku instance.

## Compatibility
<pre>
heroku config:add WORKLESS_API_KEY=yourapikey HEROKU_APP_NAME=yourherokuappname
</pre>

Workless should work correctly with Rubies 2.0.0 and up. It is compatible with Delayed Job since version 2.0.7 up to the latest version 4.1.2, the table below shows tested compatibility with ruby, rails and delayed_job
Lastly, add the below callback to your `ApplicationController`.

Ruby | Rails | Delayed Job
---------- | ------ | -----
2.2.5 | 4.2 | 2.1.4
2.3.1 | 5.0 | 4.1.2
2.4.1 | 5.1 | 4.1.3
<pre>
before_action :work_off_delayed_jobs
</pre>

## Installation
You're good to go! Whenever a job is created Workless will automatically provision a workers and turn them off when all jobs are complete.

Add the workless gem and the delayed_job gem to your project Gemfile and update your bundle. Its is recommended to specify the gem version for delayed_job

### For rails 4.x with latest delayed_job 3.x using active record
## How does Workless work?

Workless activates workers in two ways;
1. When a job is created a callback starts a worker so long as the job is to be ran straight away or before the next check (defined by `Workless.work_off_timeout`)
2. Upon each controller request Workless checks if workers need to be activated. This picks up scheduled or previously failed jobs.


## Configuration

### Run At Timing
Configure the timeout Workless uses between checking if workers are required (default is 1 minute);

<pre>
gem "delayed_job_active_record"
gem "workless", "~> 2.2.0"
Workless.work_off_timeout = 30.seconds
</pre>

### For rails 5.x with latest delayed_job 3.x using active record
### Specifying the Application

You can specify what Heroku application you're using either by setting the environment variable `HEROKU_APP_NAME` or by setting configuration variable. By default this configuration variable is set to `ENV['HEROKU_APP_NAME']`

<pre>
gem "delayed_job_active_record"
gem "workless", "~> 2.0.0"
Workless.heroku_app_name = 'skynet-app'
</pre>

### Heroku Error Handling

If you don't specify delayed_job in your Gemfile workless will bring it in, most likely the latest version (4.1.2)
By default if any error occurs when communicating with Heroku this will be raised up into the application. Although it's good to be aware of the error this may end up bringing down your application. This default behaviour can be changed by implementing a custom Heroku error handler. The example below silents the error whilst also raising it through Rollbar.

Add your Heroku app name / [API key](https://devcenter.heroku.com/articles/authentication) as config vars to your Heroku instance.
```
class MyHerokuErrorHandler
def self.handle(error)
Rollbar.error(error)
end
end
```

<pre>
heroku config:add WORKLESS_API_KEY=yourapikey APP_NAME=yourherokuappname
Workless.heroku_error_handler = 'MyHerokuErrorHandler'
</pre>

## Failing Jobs

In the case of failed jobs Workless will only shut down the dj worker if all attempts have been tried. By default Delayed Job will try 25 times to process a job with ever increasing time delays between each unsucessful attempt. Because of this Workless configures Delayed Job to try failed jobs only 3 times to reduce the amount of time a worker can be running while trying to process them.

## Configuration
### Disabling

Workless can be disabled by using the null scaler that will ignore the workers requests to scale up and down. In an environment file add this in the config block:

Expand Down Expand Up @@ -106,13 +107,6 @@ heroku config:add WORKLESS_WORKERS_RATIO=50

In this example, it will scale up to a maximum of 10 workers, firing up 1 worker for every 50 jobs on the queue. The minimum will be 0 workers, but you could set it to a higher value if you want.

## How does Workless work?

- `Delayed::Workless::Scaler` is mixed into the `Delayed::Job` class, which adds a bunch of callbacks to it.
- When a job is created on the database, a `create` callback starts a worker.
- The worker runs the job, which removes it from the database.
- A `destroy` callback stops the worker.

## Note on Patches/Pull Requests

* Please fork the project.
Expand Down
21 changes: 20 additions & 1 deletion lib/workless.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,24 @@

require File.dirname(__FILE__) + '/workless/scalers/base'
require File.dirname(__FILE__) + '/workless/scaler'
require File.dirname(__FILE__) + '/workless/middleware/workless_checker' if defined?(Rails::Railtie)
require File.dirname(__FILE__) + '/workless/controllers/helpers'
require File.dirname(__FILE__) + '/workless/heroku_error_handler'
require File.dirname(__FILE__) + '/workless/railtie' if defined?(Rails::Railtie)

ActiveSupport.on_load(:action_controller) do
include Workless::Controllers::Helpers
end

module Workless
# The minimum timeout between Workless checking if jobs need to be worked
mattr_accessor :work_off_timeout
@@work_off_timeout = 1.minute

# The name of your Heroku application which will be scaled
mattr_accessor :heroku_app_name
@@heroku_app_name = ENV['HEROKU_APP_NAME']

# Handler used when an error occurs communicating with Heroku
mattr_accessor :heroku_error_handler
@@heroku_error_handler = 'Workless::HerokuErrorHandler'
end
22 changes: 22 additions & 0 deletions lib/workless/controllers/helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Workless
module Controllers
module Helpers
# Keep a timestamp of when job queue & worker count was checked
@@last_job_work_off_timestamp = nil

# Checks if workers need to be provisioned. Limited to once every 'work_off_timeout'
def work_off_delayed_jobs
return unless work_off_delayed_jobs?

@@last_job_work_off_timestamp = Time.now
Delayed::Job.scaler.up unless Delayed::Job.scaler.jobs.empty?
end

def work_off_delayed_jobs?
return true unless @@last_job_work_off_timestamp.present?

Time.now >= @@last_job_work_off_timestamp + Workless.work_off_timeout
end
end
end
end
12 changes: 12 additions & 0 deletions lib/workless/heroku_error_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Workless
# Default behaviour to handle Heroku errors is to raise the error. This can be changed by implementing your own class which defines .handle and then updating Workless.heroku_error_handler configuration to link your custom class
class HerokuErrorHandler

# Raises error
#
# @param error [Error] raised from communicating with Heroku
def self.handle(error)
raise error
end
end
end
2 changes: 1 addition & 1 deletion lib/workless/initialize.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

Delayed::Worker.max_attempts ||= 3
Delayed::Backend::ActiveRecord::Job.send(:include, Delayed::Workless::Scaler) if defined?(Delayed::Backend::ActiveRecord::Job)
Delayed::Backend::Mongoid::Job.send(:include, Delayed::Workless::Scaler) if defined?(Delayed::Backend::Mongoid::Job)
Delayed::Backend::MongoMapper::Job.send(:include, Delayed::Workless::Scaler) if defined?(Delayed::Backend::MongoMapper::Job)
Delayed::Backend::Sequel::Job.send(:include, Delayed::Workless::Scaler) if defined?(Delayed::Backend::Sequel::Job)

Loading