hirefire 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +4 -0
- data/.infinity_test +11 -0
- data/.rspec +3 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +53 -0
- data/LICENSE.md +20 -0
- data/README.md +100 -0
- data/hirefire.gemspec +38 -0
- data/lib/hirefire.rb +85 -0
- data/lib/hirefire/backend.rb +22 -0
- data/lib/hirefire/backend/active_record.rb +20 -0
- data/lib/hirefire/backend/mongoid.rb +21 -0
- data/lib/hirefire/configuration.rb +46 -0
- data/lib/hirefire/delayed_job_extension.rb +58 -0
- data/lib/hirefire/environment.rb +50 -0
- data/lib/hirefire/environment/base.rb +102 -0
- data/lib/hirefire/environment/heroku.rb +46 -0
- data/lib/hirefire/environment/local.rb +84 -0
- data/lib/hirefire/environment/noop.rb +19 -0
- data/lib/hirefire/initializer.rb +37 -0
- data/lib/hirefire/logger.rb +98 -0
- data/lib/hirefire/railtie.rb +14 -0
- data/lib/hirefire/version.rb +13 -0
- data/spec/configuration_spec.rb +47 -0
- data/spec/logger_spec.rb +37 -0
- data/spec/spec_helper.rb +15 -0
- metadata +102 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.infinity_test
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
configuration (1.2.0)
|
5
|
+
diff-lcs (1.1.2)
|
6
|
+
fattr (2.2.0)
|
7
|
+
fuubar (0.0.3)
|
8
|
+
rspec (~> 2.0)
|
9
|
+
rspec-instafail (~> 0.1.4)
|
10
|
+
ruby-progressbar (~> 0.0.9)
|
11
|
+
heroku (1.20.1)
|
12
|
+
launchy (~> 0.3.2)
|
13
|
+
rest-client (< 1.7.0, >= 1.4.0)
|
14
|
+
infinity_test (1.0.2)
|
15
|
+
notifiers (>= 1.1.0)
|
16
|
+
watchr (>= 0.7)
|
17
|
+
launchy (0.3.7)
|
18
|
+
configuration (>= 0.0.5)
|
19
|
+
rake (>= 0.8.1)
|
20
|
+
mime-types (1.16)
|
21
|
+
mocha (0.9.12)
|
22
|
+
notifiers (1.1.0)
|
23
|
+
rake (0.8.7)
|
24
|
+
rest-client (1.6.1)
|
25
|
+
mime-types (>= 1.16)
|
26
|
+
rspec (2.5.0)
|
27
|
+
rspec-core (~> 2.5.0)
|
28
|
+
rspec-expectations (~> 2.5.0)
|
29
|
+
rspec-mocks (~> 2.5.0)
|
30
|
+
rspec-core (2.5.1)
|
31
|
+
rspec-expectations (2.5.0)
|
32
|
+
diff-lcs (~> 1.1.2)
|
33
|
+
rspec-instafail (0.1.6)
|
34
|
+
rspec-mocks (2.5.0)
|
35
|
+
ruby-progressbar (0.0.9)
|
36
|
+
rush (0.6.7)
|
37
|
+
session
|
38
|
+
session (3.1.0)
|
39
|
+
fattr
|
40
|
+
timecop (0.3.5)
|
41
|
+
watchr (0.7)
|
42
|
+
|
43
|
+
PLATFORMS
|
44
|
+
ruby
|
45
|
+
|
46
|
+
DEPENDENCIES
|
47
|
+
fuubar
|
48
|
+
heroku
|
49
|
+
infinity_test
|
50
|
+
mocha
|
51
|
+
rspec
|
52
|
+
rush
|
53
|
+
timecop
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Michael van Rooijen ( [@meskyanichi](http://twitter.com/#!/meskyanichi) )
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
HireFire - The Heroku Worker Manager
|
2
|
+
====================================
|
3
|
+
|
4
|
+
**HireFire automatically "hires" and "fires" (aka "scales") Delayed Job workers on Heroku**. When there are no queue jobs, HireFire will fire (shut down) all workers. If there are queued jobs, then it'll hire (spin up) workers. The amount of workers that get hired depends on the amount of queued jobs (the ratio can be configured by you). HireFire is great for both high, mid and low traffic applications. It can save you a lot of money by only hiring workers when there are pending jobs, and then firing them again once all the jobs have been processed. It's also capable to dramatically reducing processing time by automatically hiring more workers when the queue size increases.
|
5
|
+
|
6
|
+
**Low traffic example** say we have a small application that doesn't process for more than 2 hours in the background a month. Meanwhile, your worker is basically just idle the rest of the 718 hours in that month. Keeping that idle worker running costs $36/month ($0.05/hour). But, for the resources you're actually **making use of** (2 hours a month), you should be paying $0.10/month, not $36/month. This is what HireFire is for.
|
7
|
+
|
8
|
+
**High traffic example** say we have a high traffic application that needs to process a lot of jobs. There may be "traffic spikes" from time to time. In this case you can take advantage of the **job\_worker\_ratio**. Since this is application-specific, HireFire allows you to define how many workers there should be running, depending on the amount of queued jobs there are (see example configuration below). HireFire will then spin up more workers as traffic increases so it can work through the queue faster, then when the jobs are all finished, it'll shut down all the workers again until the next job gets queued (in which case it'll start with only a single worker again).
|
9
|
+
|
10
|
+
**Enough with the examples!** Read on to see how to set it, and configure it to your scaling and money saving needs.
|
11
|
+
|
12
|
+
Author
|
13
|
+
------
|
14
|
+
|
15
|
+
**Michael van Rooijen ( [@meskyanichi](http://twitter.com/#!/meskyanichi) )**
|
16
|
+
|
17
|
+
Drop me a message for any questions, suggestions, requests, bugs or submit them to the [issue log](https://github.com/meskyanichi/hirefire/issues).
|
18
|
+
|
19
|
+
|
20
|
+
Setting it up
|
21
|
+
-------------
|
22
|
+
|
23
|
+
A painless process. In a Ruby on Rails environment you would do something like this.
|
24
|
+
|
25
|
+
**Rails.root/Gemfile**
|
26
|
+
|
27
|
+
gem 'rails'
|
28
|
+
gem 'delayed_job'
|
29
|
+
gem 'hirefire'
|
30
|
+
|
31
|
+
**(The order is important: Delayed Job > HireFire)**
|
32
|
+
|
33
|
+
Be sure to add the following Heroku environment variables so HireFire can manage your workers.
|
34
|
+
|
35
|
+
heroku config:add HIREFIRE_EMAIL=<your_email> HIREFIRE_PASSWORD=<your_password>
|
36
|
+
|
37
|
+
These are the same email and password credentials you use to log in to the Heroku web interface to manage your workers.
|
38
|
+
|
39
|
+
And that's it. Next time you deploy to [Heroku](http://heroku.com/) it'll automatically hire and fire your workers. Now, there are defaults, but I highly recommend you configure it since it only takes a few seconds. Create an initializer file:
|
40
|
+
|
41
|
+
**Rails.root/config/initializers/hirefire.rb**
|
42
|
+
|
43
|
+
HireFire.configure do |config|
|
44
|
+
config.max_workers = 5 # default is 1
|
45
|
+
config.job_worker_ratio = [
|
46
|
+
{ :jobs => 1, :workers => 1 },
|
47
|
+
{ :jobs => 15, :workers => 2 },
|
48
|
+
{ :jobs => 35, :workers => 3 },
|
49
|
+
{ :jobs => 60, :workers => 4 },
|
50
|
+
{ :jobs => 80, :workers => 5 }
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
Basically what it comes down to is that we say **NEVER** to hire more than 5 workers at a time (`config.max_workers = 5`). And then we define an array of hashes that represents our **job\_worker\_ratio**. In the above example we are basically saying:
|
55
|
+
|
56
|
+
* Hire 1 worker if there are 1-14 queued jobs
|
57
|
+
* Hire 2 workers if there are 15-34 queued jobs
|
58
|
+
* Hire 3 workers if there are 35-59 queued jobs
|
59
|
+
* Hire 4 workers if there are 60-79 queued jobs
|
60
|
+
* Hire 5 workers if there are more than 80 queued jobs
|
61
|
+
|
62
|
+
Once all the jobs in the queue have been processed, it'll fire (shut down) all the workers and start with a single worker the next time a new job gets queued. And then the next time the queue hits 15 jobs mark, in which case the single worker isn't fast enough on it's own, it'll spin up the 2nd worker again.
|
63
|
+
|
64
|
+
|
65
|
+
In a non-Ruby on Rails environment
|
66
|
+
----------------------------------
|
67
|
+
|
68
|
+
Almost the same setup, except that you have to initialize HireFire yourself after Delayed Job is done loading.
|
69
|
+
|
70
|
+
require 'delayed_job'
|
71
|
+
require 'hirefire'
|
72
|
+
HireFire::Initializer.initialize!
|
73
|
+
|
74
|
+
**(Again, the order is important: Delayed Job > HireFire)**
|
75
|
+
|
76
|
+
If all goes well you should see a message similar to this when you boot your application:
|
77
|
+
|
78
|
+
[HireFire] Delayed::Backend::ActiveRecord::Job detected!
|
79
|
+
|
80
|
+
|
81
|
+
Mapper Support
|
82
|
+
--------------
|
83
|
+
|
84
|
+
* [ActiveRecord ORM](https://github.com/rails/rails/tree/master/activerecord)
|
85
|
+
* [Mongoid ODM](https://github.com/mongoid/mongoid) (using [delayed_job_mongoid](https://github.com/collectiveidea/delayed_job_mongoid))
|
86
|
+
|
87
|
+
|
88
|
+
Worker Support
|
89
|
+
--------------
|
90
|
+
|
91
|
+
Currently only [Delayed Job](https://github.com/collectiveidea/delayed_job) with either [ActiveRecord ORM](https://github.com/rails/rails/tree/master/activerecord) and [Mongoid ODM](https://github.com/mongoid/mongoid).
|
92
|
+
Might have plans to implement this for other workers in the future.
|
93
|
+
|
94
|
+
|
95
|
+
Other potentially interesting gems
|
96
|
+
----------------------------------
|
97
|
+
|
98
|
+
* [Backup](https://github.com/meskyanichi/backup)
|
99
|
+
* [GitPusshuTen](https://github.com/meskyanichi/gitpusshuten)
|
100
|
+
* [Mongoid::Paperclip](https://github.com/meskyanichi/mongoid-paperclip)
|
data/hirefire.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/lib/hirefire')
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
|
7
|
+
##
|
8
|
+
# General configuration / information
|
9
|
+
gem.name = 'hirefire'
|
10
|
+
gem.version = HireFire::Version.current
|
11
|
+
gem.platform = Gem::Platform::RUBY
|
12
|
+
gem.authors = 'Michael van Rooijen'
|
13
|
+
gem.email = 'meskyanichi@gmail.com'
|
14
|
+
gem.homepage = 'http://rubygems.org/gems/hirefire'
|
15
|
+
gem.summary = 'HireFire automatically "hires" and "fires" (aka "scales") Delayed Job workers on Heroku.'
|
16
|
+
gem.description = <<-EOS
|
17
|
+
HireFire automatically "hires" and "fires" (aka "scales") Delayed Job workers on Heroku.
|
18
|
+
When there are no queue jobs, HireFire will fire (shut down) all workers. If there are
|
19
|
+
queued jobs, then it'll hire (spin up) workers. The amount of workers that get hired
|
20
|
+
depends on the amount of queued jobs (the ratio can be configured by you). HireFire
|
21
|
+
is great for both high, mid and low traffic applications. It can save you a lot of
|
22
|
+
money by only hiring workers when there are pending jobs, and then firing them again
|
23
|
+
once all the jobs have been processed. It's also capable to dramatically reducing
|
24
|
+
processing time by automatically hiring more workers when the queue size increases.
|
25
|
+
EOS
|
26
|
+
|
27
|
+
##
|
28
|
+
# Files and folder that need to be compiled in to the Ruby Gem
|
29
|
+
gem.files = %x[git ls-files].split("\n")
|
30
|
+
gem.test_files = %x[git ls-files -- {spec}/*].split("\n")
|
31
|
+
gem.require_path = 'lib'
|
32
|
+
|
33
|
+
##
|
34
|
+
# Production gem dependencies
|
35
|
+
gem.add_dependency 'heroku', ['~> 1.20.1']
|
36
|
+
gem.add_dependency 'rush', ['~> 0.6.7']
|
37
|
+
|
38
|
+
end
|
data/lib/hirefire.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
|
5
|
+
##
|
6
|
+
# HireFire constants
|
7
|
+
LIB_PATH = File.dirname(__FILE__)
|
8
|
+
FREELANCER_PATH = File.join(LIB_PATH, 'hirefire')
|
9
|
+
ENVIRONMENT_PATH = File.join(FREELANCER_PATH, 'environment')
|
10
|
+
BACKEND_PATH = File.join(FREELANCER_PATH, 'backend')
|
11
|
+
|
12
|
+
##
|
13
|
+
# HireFire namespace
|
14
|
+
autoload :Configuration, File.join(FREELANCER_PATH, 'configuration')
|
15
|
+
autoload :Environment, File.join(FREELANCER_PATH, 'environment')
|
16
|
+
autoload :Initializer, File.join(FREELANCER_PATH, 'initializer')
|
17
|
+
autoload :Backend, File.join(FREELANCER_PATH, 'backend')
|
18
|
+
autoload :Logger, File.join(FREELANCER_PATH, 'logger')
|
19
|
+
autoload :Version, File.join(FREELANCER_PATH, 'version')
|
20
|
+
|
21
|
+
##
|
22
|
+
# HireFire::Environment namespace
|
23
|
+
module Environment
|
24
|
+
autoload :Base, File.join(ENVIRONMENT_PATH, 'base')
|
25
|
+
autoload :Heroku, File.join(ENVIRONMENT_PATH, 'heroku')
|
26
|
+
autoload :Local, File.join(ENVIRONMENT_PATH, 'local')
|
27
|
+
autoload :Noop, File.join(ENVIRONMENT_PATH, 'noop')
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# HireFire::Backend namespace
|
32
|
+
module Backend
|
33
|
+
autoload :ActiveRecord, File.join(BACKEND_PATH, 'active_record')
|
34
|
+
autoload :Mongoid, File.join(BACKEND_PATH, 'mongoid')
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# This method is used to configure HireFire
|
39
|
+
#
|
40
|
+
# @yield [config] the instance of HireFire::Configuration class
|
41
|
+
# @yieldparam [Fixnum] max_workers default: 1 (set at least 1)
|
42
|
+
# @yieldparam [Array] job_worker_ratio default: see example
|
43
|
+
# @yieldparam [Symbol, nil] environment (:heroku, :local, :noop or nil) - default: nil
|
44
|
+
#
|
45
|
+
# @note Every param has it's own defaults. It's best to leave the environment param at "nil".
|
46
|
+
# When environment is set to "nil", it'll default to the :noop environment. This basically means
|
47
|
+
# that you have to run "rake jobs:work" yourself from the console to get the jobs running in development mode.
|
48
|
+
# In production, it'll automatically use :heroku if deployed to the Heroku platform.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# HireFire.configure do |config|
|
52
|
+
# config.environment = nil
|
53
|
+
# config.max_workers = 5
|
54
|
+
# config.job_worker_ratio = [
|
55
|
+
# { :jobs => 1, :workers => 1 },
|
56
|
+
# { :jobs => 15, :workers => 2 },
|
57
|
+
# { :jobs => 35, :workers => 3 },
|
58
|
+
# { :jobs => 60, :workers => 4 },
|
59
|
+
# { :jobs => 80, :workers => 5 }
|
60
|
+
# ]
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# @return [nil]
|
64
|
+
def self.configure
|
65
|
+
yield(configuration); nil
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Instantiates a new HireFire::Configuration
|
70
|
+
# instance and instance variable caches it
|
71
|
+
def self.configuration
|
72
|
+
@configuration ||= HireFire::Configuration.new
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# If Ruby on Rails is detected, it'll automatically initialize HireFire
|
79
|
+
# so that the developer doesn't have to manually invoke it from an initializer file
|
80
|
+
#
|
81
|
+
# Users not using Ruby on Rails will have to run "HireFire::Initializer.initialize!"
|
82
|
+
# in their application manually, after loading Delayed Job and the desired mapper (ActiveRecord or Mongoid)
|
83
|
+
if defined?(Rails)
|
84
|
+
require File.join(HireFire::FREELANCER_PATH, 'railtie')
|
85
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Backend
|
5
|
+
|
6
|
+
##
|
7
|
+
# Load the correct module (ActiveRecord or Mongoid)
|
8
|
+
# based on which Delayed::Backend has been loaded
|
9
|
+
#
|
10
|
+
# @return [nil]
|
11
|
+
def self.included(base)
|
12
|
+
if defined?(Delayed::Backend::ActiveRecord::Job)
|
13
|
+
base.send(:include, ActiveRecord)
|
14
|
+
end
|
15
|
+
|
16
|
+
if defined?(Delayed::Backend::Mongoid::Job)
|
17
|
+
base.send(:include, Mongoid)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Backend
|
5
|
+
module ActiveRecord
|
6
|
+
|
7
|
+
##
|
8
|
+
# Counts the amount of queued jobs in the database,
|
9
|
+
# failed jobs are excluded from the sum
|
10
|
+
#
|
11
|
+
# @return [Fixnum]
|
12
|
+
def jobs
|
13
|
+
Delayed::Job.
|
14
|
+
where(:failed_at => nil).
|
15
|
+
where('run_at <= ?', Time.now).count
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Backend
|
5
|
+
module Mongoid
|
6
|
+
|
7
|
+
##
|
8
|
+
# Counts the amount of queued jobs in the database,
|
9
|
+
# failed jobs and jobs scheduled for the future are excluded
|
10
|
+
#
|
11
|
+
# @return [Fixnum]
|
12
|
+
def jobs
|
13
|
+
Delayed::Job.where(
|
14
|
+
:failed_at => nil,
|
15
|
+
:run_at.lte => Time.now
|
16
|
+
).count
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
##
|
7
|
+
# Contains the max amount of workers that are allowed to run concurrently
|
8
|
+
#
|
9
|
+
# @return [Fixnum] default: 1
|
10
|
+
attr_accessor :max_workers
|
11
|
+
|
12
|
+
##
|
13
|
+
# Contains the job/worker ratio which determines
|
14
|
+
# how many workers need to be running depending on
|
15
|
+
# the amount of pending jobs
|
16
|
+
#
|
17
|
+
# @return [Array] containing one or more hashes
|
18
|
+
attr_accessor :job_worker_ratio
|
19
|
+
|
20
|
+
##
|
21
|
+
# Default is nil, in which case it'll auto-detect either :heroku or :noop,
|
22
|
+
# depending on the environment. It will never use :local, unless explicitly defined by the user.
|
23
|
+
#
|
24
|
+
# @param [Symbol, nil] environment Contains the name of the environment to run in.
|
25
|
+
# @return [Symbol, nil] default: nil
|
26
|
+
attr_accessor :environment
|
27
|
+
|
28
|
+
##
|
29
|
+
# Instantiates a new HireFire::Configuration object
|
30
|
+
# with the default configuration. These default configurations
|
31
|
+
# may be overwritten using the HireFire.configure class method
|
32
|
+
#
|
33
|
+
# @return [HireFire::Configuration]
|
34
|
+
def initialize
|
35
|
+
@max_workers = 1
|
36
|
+
@job_worker_ratio = [
|
37
|
+
{ :jobs => 1, :workers => 1 },
|
38
|
+
{ :jobs => 25, :workers => 2 },
|
39
|
+
{ :jobs => 50, :workers => 3 },
|
40
|
+
{ :jobs => 75, :workers => 4 },
|
41
|
+
{ :jobs => 100, :workers => 5 }
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Delayed
|
4
|
+
class Worker
|
5
|
+
|
6
|
+
##
|
7
|
+
# @note
|
8
|
+
# This method gets invoked on heroku by the rake task "jobs:work"
|
9
|
+
#
|
10
|
+
# This is basically the same method as the Delayed Job version,
|
11
|
+
# except for the following:
|
12
|
+
#
|
13
|
+
# 1. All ouput will now go through the HireFire::Logger.
|
14
|
+
# 2. When HireFire cannot find any jobs to process it sends the "fire"
|
15
|
+
# signal to all workers, ending all the processes simultaneously. The reason
|
16
|
+
# we wait for all the processes to finish before sending the signal is because it'll
|
17
|
+
# otherwise interrupt workers and leave jobs unfinished.
|
18
|
+
#
|
19
|
+
def start
|
20
|
+
HireFire::Logger.message "Starting job worker!"
|
21
|
+
|
22
|
+
trap('TERM') { HireFire::Logger.message 'Exiting...'; $exit = true }
|
23
|
+
trap('INT') { HireFire::Logger.message 'Exiting...'; $exit = true }
|
24
|
+
|
25
|
+
queued = Delayed::Job.new
|
26
|
+
|
27
|
+
loop do
|
28
|
+
result = nil
|
29
|
+
|
30
|
+
realtime = Benchmark.realtime do
|
31
|
+
result = work_off
|
32
|
+
end
|
33
|
+
|
34
|
+
count = result.sum
|
35
|
+
|
36
|
+
break if $exit
|
37
|
+
|
38
|
+
if count.zero?
|
39
|
+
sleep(1)
|
40
|
+
else
|
41
|
+
HireFire::Logger.message "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last]
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# If there are no jobs currently queued,
|
46
|
+
# and the worker is still running, it'll kill itself
|
47
|
+
if queued.jobs == 0
|
48
|
+
Delayed::Job.environment.fire
|
49
|
+
end
|
50
|
+
|
51
|
+
break if $exit
|
52
|
+
end
|
53
|
+
|
54
|
+
ensure
|
55
|
+
Delayed::Job.clear_locks!(name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Environment
|
5
|
+
|
6
|
+
##
|
7
|
+
# This gets included in to the Delayed::Backend::(ActiveRecord|Mongoid)::Job
|
8
|
+
# classes and will add the necessary hooks (after_create, after_destroy and after_update)
|
9
|
+
# to spawn or kill Delayed Job worker processes on either Heroku or your local machine
|
10
|
+
#
|
11
|
+
# @param (Class) base This is the class in which this module will be included
|
12
|
+
def self.included(base)
|
13
|
+
base.send :extend, ClassMethods
|
14
|
+
base.class_eval do
|
15
|
+
after_create 'self.class.environment.hire'
|
16
|
+
after_destroy 'self.class.environment.fire'
|
17
|
+
after_update 'self.class.environment.fire',
|
18
|
+
:unless => Proc.new { |job| job.failed_at.nil? }
|
19
|
+
end
|
20
|
+
|
21
|
+
Logger.message("#{ base.name } detected!")
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Class methods that will be added to the Delayed::Job backend
|
26
|
+
module ClassMethods
|
27
|
+
|
28
|
+
##
|
29
|
+
# Returns the environment class method (for Delayed::Job ORM/ODM class)
|
30
|
+
#
|
31
|
+
# If HireFire.configuration.environment is nil (the default) then it'll
|
32
|
+
# auto-detect which environment to run in (either Heroku or Local)
|
33
|
+
#
|
34
|
+
# If HireFire.configuration.environment isn't nil (explicitly set) then
|
35
|
+
# it'll run in the specified environment (Heroku, Local or Noop)
|
36
|
+
#
|
37
|
+
# @return [HireFire::Environment::Heroku, HireFire::Environment::Local, HireFire::Environment::Noop]
|
38
|
+
def environment
|
39
|
+
@environment ||= HireFire::Environment.const_get(
|
40
|
+
if environment = HireFire.configuration.environment
|
41
|
+
environment.to_s.camelize
|
42
|
+
else
|
43
|
+
ENV.include?('HEROKU_UPID') ? 'Heroku' : 'Noop'
|
44
|
+
end
|
45
|
+
).new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Environment
|
5
|
+
class Base
|
6
|
+
|
7
|
+
##
|
8
|
+
# Include HireFire::Backend helpers
|
9
|
+
include HireFire::Backend
|
10
|
+
|
11
|
+
##
|
12
|
+
# This method gets invoked when a new job has been queued
|
13
|
+
#
|
14
|
+
# Iterates through the default (or user-defined) job/worker ratio until
|
15
|
+
# it finds a match for the for the current situation (see example).
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # Say we have 40 queued jobs, and we configured our job/worker ratio like so:
|
19
|
+
#
|
20
|
+
# HireFire.configure do |config|
|
21
|
+
# config.max_workers = 5
|
22
|
+
# config.job_worker_ratio = [
|
23
|
+
# { :jobs => 1, :workers => 1 },
|
24
|
+
# { :jobs => 15, :workers => 2 },
|
25
|
+
# { :jobs => 35, :workers => 3 },
|
26
|
+
# { :jobs => 60, :workers => 4 },
|
27
|
+
# { :jobs => 80, :workers => 5 }
|
28
|
+
# ]
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # It'll match at { :jobs => 35, :workers => 3 }, (35 jobs or more: hire 3 workers)
|
32
|
+
# # meaning that it'll ensure there are 3 workers running.
|
33
|
+
#
|
34
|
+
# # If there were already were 3 workers, it'll leave it as is
|
35
|
+
#
|
36
|
+
# # If there were more than 3 workers running (say, 4 or 5), it will NOT reduce
|
37
|
+
# # the number. This is because when you reduce the number of workers, you cannot
|
38
|
+
# # tell which worker Heroku will shut down, meaning you might interrupt a worker
|
39
|
+
# # that's currently working, causing the job to fail. Also, consider the fact that
|
40
|
+
# # there are, for example, 35 jobs still to be picked up, so the more workers,
|
41
|
+
# # the faster it processes. You aren't even paying more because it doesn't matter whether
|
42
|
+
# # you have 1 worker, or 5 workers processing jobs, because workers are pro-rated to the second.
|
43
|
+
# # So basically 5 workers would cost 5 times more, but will also process 5 times faster.
|
44
|
+
#
|
45
|
+
# # Once all jobs finished processing (e.g. Delayed::Job.jobs == 0), HireFire will invoke a signal
|
46
|
+
# # which will set the workers back to 0 and shuts down all the workers simultaneously.
|
47
|
+
#
|
48
|
+
# @return [nil]
|
49
|
+
def hire
|
50
|
+
jobs_count = jobs
|
51
|
+
workers_count = workers
|
52
|
+
|
53
|
+
ratio.each do |ratio|
|
54
|
+
if jobs_count >= ratio[:jobs] and max_workers >= ratio[:workers]
|
55
|
+
if not workers_count == ratio[:workers]
|
56
|
+
Logger.message("Hiring more workers so we have #{ ratio[:workers] } in total.")
|
57
|
+
workers(ratio[:workers])
|
58
|
+
end
|
59
|
+
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# This method gets invoked when a job is either "destroyed"
|
67
|
+
# or "updated, unless the job didn't fail"
|
68
|
+
#
|
69
|
+
# If there are workers active, but there are no more pending jobs,
|
70
|
+
# then fire all the workers
|
71
|
+
#
|
72
|
+
# @return [nil]
|
73
|
+
def fire
|
74
|
+
if jobs == 0 and workers > 0
|
75
|
+
Logger.message("All queued jobs have been processed. Firing all workers.")
|
76
|
+
workers(0)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
##
|
83
|
+
# Wrapper method for HireFire.configuration
|
84
|
+
# Returns the max amount of workers that may run concurrently
|
85
|
+
#
|
86
|
+
# @return [Fixnum] the max amount of workers that are allowed to run concurrently
|
87
|
+
def max_workers
|
88
|
+
HireFire.configuration.max_workers
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Wrapper method for HireFire.configuration
|
93
|
+
# Returns the job/worker ratio array (in reversed order)
|
94
|
+
#
|
95
|
+
# @return [Array] the array of hashes containing the job/worker ratio (in reversed order)
|
96
|
+
def ratio
|
97
|
+
HireFire.configuration.job_worker_ratio.reverse
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'heroku'
|
4
|
+
|
5
|
+
module HireFire
|
6
|
+
module Environment
|
7
|
+
class Heroku < Base
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
##
|
12
|
+
# Either retrieves the amount of currently running workers,
|
13
|
+
# or set the amount of workers to a specific amount by providing a value
|
14
|
+
#
|
15
|
+
# @overload workers(amount = nil)
|
16
|
+
# @param [Fixnum] amount will tell heroku to run N workers
|
17
|
+
# @return [nil]
|
18
|
+
# @overload workers(amount = nil)
|
19
|
+
# @param [nil] amount
|
20
|
+
# @return [Fixnum] will request the amount of currently running workers from Heroku
|
21
|
+
def workers(amount = nil)
|
22
|
+
|
23
|
+
#
|
24
|
+
# Returns the amount of Delayed Job
|
25
|
+
# workers that are currently running on Heroku
|
26
|
+
if amount.nil?
|
27
|
+
return client.info(ENV['APP_NAME'])[:workers].to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Sets the amount of Delayed Job
|
32
|
+
# workers that need to be running on Heroku
|
33
|
+
client.set_workers(ENV['APP_NAME'], amount)
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# @return [Heroku::Client] instance of the heroku client
|
38
|
+
def client
|
39
|
+
@client ||= ::Heroku::Client.new(
|
40
|
+
ENV['HIREFIRE_EMAIL'], ENV['HIREFIRE_PASSWORD']
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rush'
|
4
|
+
|
5
|
+
module HireFire
|
6
|
+
module Environment
|
7
|
+
class Local < Base
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
##
|
12
|
+
# Either retrieve the amount of currently running workers,
|
13
|
+
# or set the amount of workers to a specific amount by providing a value
|
14
|
+
#
|
15
|
+
# @overload workers(amount = nil)
|
16
|
+
# @param [Fixnum] amount will tell the local machine to run N workers
|
17
|
+
# @return [nil]
|
18
|
+
# @overload workers(amount = nil)
|
19
|
+
# @param [nil] amount
|
20
|
+
# @return [Fixnum] will request the amount of currently running workers from the local machine
|
21
|
+
def workers(amount = nil)
|
22
|
+
|
23
|
+
##
|
24
|
+
# Returns the amount of Delayed Job workers that are currently
|
25
|
+
# running on the local machine if amount is nil
|
26
|
+
if amount.nil?
|
27
|
+
return Rush::Box.new.processes.filter(
|
28
|
+
:cmdline => /rake jobs:work WORKER=HIREFIRE/
|
29
|
+
).size
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Fire workers
|
34
|
+
#
|
35
|
+
# If the amount of workers required is set to 0
|
36
|
+
# then we fire all the workers and we return
|
37
|
+
#
|
38
|
+
# The worker that finished the last job will go ahead and
|
39
|
+
# kill all the other (if any) workers first, and then kill itself afterwards
|
40
|
+
if amount == 0
|
41
|
+
|
42
|
+
##
|
43
|
+
# Gather process ids from all HireFire workers
|
44
|
+
pids = Rush::Box.new.processes.filter(
|
45
|
+
:cmdline => /rake jobs:work WORKER=HIREFIRE/
|
46
|
+
).map(&:pid)
|
47
|
+
|
48
|
+
##
|
49
|
+
# Instantiate a new local (shell) connection
|
50
|
+
shell = Rush::Connection::Local.new
|
51
|
+
|
52
|
+
##
|
53
|
+
# Kill all Freelance workers,
|
54
|
+
# except the one that's doing the killing
|
55
|
+
(pids - [Rush.my_process.pid]).each do |pid|
|
56
|
+
shell.kill_process(pid)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Kill the last Freelance worker (self)
|
61
|
+
Logger.message("There are now #{ amount } workers.")
|
62
|
+
shell.kill_process(Rush.my_process.pid)
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Hire workers
|
68
|
+
#
|
69
|
+
# If the amount of workers required is greater than
|
70
|
+
# the amount of workers already working, then hire the
|
71
|
+
# additional amount of workers required
|
72
|
+
workers_count = workers
|
73
|
+
if amount > workers_count
|
74
|
+
(amount - workers_count).times do
|
75
|
+
Rush::Box.new[Rails.root].bash(
|
76
|
+
'rake jobs:work WORKER=HIREFIRE', :background => true
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
module Environment
|
5
|
+
class Noop
|
6
|
+
|
7
|
+
##
|
8
|
+
# Will invoke the #hire method, but won't actually do anything
|
9
|
+
def hire
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Will invoke the #fire method, but won't actually do anything
|
14
|
+
def fire
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
class Initializer
|
5
|
+
|
6
|
+
##
|
7
|
+
# Loads the HireFire extension in to Delayed Job and
|
8
|
+
# extends the Delayed Job "jobs:work" rake task command
|
9
|
+
#
|
10
|
+
# @return [nil]
|
11
|
+
def self.initialize!
|
12
|
+
##
|
13
|
+
# If DelayedJob is using ActiveRecord, then include
|
14
|
+
# HireFire::Environment in to the ActiveRecord Delayed Job Backend
|
15
|
+
if defined?(Delayed::Backend::ActiveRecord::Job)
|
16
|
+
Delayed::Backend::ActiveRecord::Job.
|
17
|
+
send(:include, HireFire::Environment).
|
18
|
+
send(:include, HireFire::Backend)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# If DelayedJob is using Mongoid, then include
|
23
|
+
# HireFire::Environment in to the Mongoid Delayed Job Backend
|
24
|
+
if defined?(Delayed::Backend::Mongoid::Job)
|
25
|
+
Delayed::Backend::Mongoid::Job.
|
26
|
+
send(:include, HireFire::Environment).
|
27
|
+
send(:include, HireFire::Backend)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Load Delayed Job extension, this is the start
|
32
|
+
# method that gets invoked when running "rake jobs:work"
|
33
|
+
require File.dirname(__FILE__) + '/delayed_job_extension'
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
class Logger
|
5
|
+
|
6
|
+
##
|
7
|
+
# Outputs a messages to the console
|
8
|
+
#
|
9
|
+
# @param [String] string prints a string to the console (green color)
|
10
|
+
# @return [nil]
|
11
|
+
def self.message(string)
|
12
|
+
puts loggify(string, :green)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Outputs an error to the console
|
17
|
+
#
|
18
|
+
# @param [String] string prints a string to the console (red color)
|
19
|
+
# @return [nil]
|
20
|
+
def self.error(string)
|
21
|
+
puts loggify(string, :red)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Outputs a notice to the console
|
26
|
+
#
|
27
|
+
# @param [String] string prints a string to the console (yellow color)
|
28
|
+
# @return [nil]
|
29
|
+
def self.warn(string)
|
30
|
+
puts loggify(string, :yellow)
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Outputs the data as if it were a regular 'puts' command
|
35
|
+
#
|
36
|
+
# @param [String] string prints a string to the console (standard color)
|
37
|
+
# @return [nil]
|
38
|
+
def self.normal(string)
|
39
|
+
puts string
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Builds the string in a log format with the date/time, the type (colorized)
|
44
|
+
# based on whether it's a message, notice or error, and the message itself.
|
45
|
+
#
|
46
|
+
# @param [String] string the string to print to the console
|
47
|
+
# @param [Symbol, false] color the color to print the string in
|
48
|
+
# @return [String] the log-like formatted string
|
49
|
+
def self.loggify(string, color = false)
|
50
|
+
return "[#{time}][HireFire] #{string}" unless color
|
51
|
+
"[#{time}][#{send(color, 'HireFire')}] #{string}"
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# @return [Time] the time in [YYYY-MM-DD HH:MM:SS] format
|
56
|
+
def self.time
|
57
|
+
Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Invokes the #colorize method with the provided string
|
62
|
+
# and the color code "32" (for green)
|
63
|
+
#
|
64
|
+
# @param [String] string
|
65
|
+
# @return [String] the provided string in special tags to color it green in the console
|
66
|
+
def self.green(string)
|
67
|
+
colorize(string, 32)
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Invokes the #colorize method with the provided string
|
72
|
+
# and the color code "33" (for yellow)
|
73
|
+
#
|
74
|
+
# @param [String] string
|
75
|
+
# @return [String] the provided string in special tags to color it yellow in the console
|
76
|
+
def self.yellow(string)
|
77
|
+
colorize(string, 33)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Invokes the #colorize method the with provided string
|
82
|
+
# and the color code "31" (for red)
|
83
|
+
def self.red(string)
|
84
|
+
colorize(string, 31)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Wraps the provided string in colorizing tags to provide
|
89
|
+
# easier to view output to the client
|
90
|
+
#
|
91
|
+
# @param [String] string
|
92
|
+
# @return [String] the provided string in special tags to color it red in the console
|
93
|
+
def self.colorize(string, code)
|
94
|
+
"\e[#{code}m#{string}\e[0m"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HireFire
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
|
6
|
+
##
|
7
|
+
# Initializes HireFire for Delayed Job when
|
8
|
+
# the Ruby on Rails web framework is done loading
|
9
|
+
initializer :after_initialize do
|
10
|
+
HireFire::Initializer.initialize!
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __FILE__)
|
4
|
+
|
5
|
+
describe HireFire::Configuration do
|
6
|
+
|
7
|
+
it 'should have defaults' do
|
8
|
+
configuration = HireFire.configuration
|
9
|
+
|
10
|
+
configuration.environment.should == nil
|
11
|
+
configuration.max_workers.should == 1
|
12
|
+
configuration.job_worker_ratio.should == [
|
13
|
+
{ :jobs => 1, :workers => 1 },
|
14
|
+
{ :jobs => 25, :workers => 2 },
|
15
|
+
{ :jobs => 50, :workers => 3 },
|
16
|
+
{ :jobs => 75, :workers => 4 },
|
17
|
+
{ :jobs => 100, :workers => 5 }
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should be configurable' do
|
22
|
+
HireFire.configure do |config|
|
23
|
+
config.environment = :noop
|
24
|
+
config.max_workers = 10
|
25
|
+
config.job_worker_ratio = [
|
26
|
+
{ :jobs => 1, :workers => 1 },
|
27
|
+
{ :jobs => 15, :workers => 2 },
|
28
|
+
{ :jobs => 35, :workers => 3 },
|
29
|
+
{ :jobs => 60, :workers => 4 },
|
30
|
+
{ :jobs => 80, :workers => 5 }
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
configuration = HireFire.configuration
|
35
|
+
|
36
|
+
configuration.environment.should == :noop
|
37
|
+
configuration.max_workers.should == 10
|
38
|
+
configuration.job_worker_ratio.should == [
|
39
|
+
{ :jobs => 1, :workers => 1 },
|
40
|
+
{ :jobs => 15, :workers => 2 },
|
41
|
+
{ :jobs => 35, :workers => 3 },
|
42
|
+
{ :jobs => 60, :workers => 4 },
|
43
|
+
{ :jobs => 80, :workers => 5 }
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __FILE__)
|
4
|
+
|
5
|
+
require 'timecop'
|
6
|
+
|
7
|
+
describe HireFire::Logger do
|
8
|
+
|
9
|
+
before do
|
10
|
+
Timecop.freeze( Time.now )
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when logging regular messages' do
|
14
|
+
it do
|
15
|
+
HireFire::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y-%m-%d %H:%M:%S") }][\e[32mHireFire\e[0m] This has been logged.")
|
16
|
+
|
17
|
+
HireFire::Logger.message "This has been logged."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when logging error messages' do
|
22
|
+
it do
|
23
|
+
HireFire::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y-%m-%d %H:%M:%S") }][\e[31mHireFire\e[0m] This has been logged.")
|
24
|
+
|
25
|
+
HireFire::Logger.error "This has been logged."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when logging warn messages' do
|
30
|
+
it do
|
31
|
+
HireFire::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y-%m-%d %H:%M:%S") }][\e[33mHireFire\e[0m] This has been logged.")
|
32
|
+
|
33
|
+
HireFire::Logger.warn "This has been logged."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# Path to the lib directory
|
5
|
+
LIB_PATH = File.expand_path('../../lib', __FILE__)
|
6
|
+
|
7
|
+
##
|
8
|
+
# Load the HireFire Ruby library
|
9
|
+
require File.join(LIB_PATH, 'hirefire')
|
10
|
+
|
11
|
+
##
|
12
|
+
# Use Mocha to mock with RSpec
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.mock_with :mocha
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hirefire
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael van Rooijen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-10 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: heroku
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.20.1
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rush
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.6.7
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
description: " HireFire automatically \"hires\" and \"fires\" (aka \"scales\") Delayed Job workers on Heroku.\n When there are no queue jobs, HireFire will fire (shut down) all workers. If there are\n queued jobs, then it'll hire (spin up) workers. The amount of workers that get hired\n depends on the amount of queued jobs (the ratio can be configured by you). HireFire\n is great for both high, mid and low traffic applications. It can save you a lot of\n money by only hiring workers when there are pending jobs, and then firing them again\n once all the jobs have been processed. It's also capable to dramatically reducing\n processing time by automatically hiring more workers when the queue size increases.\n"
|
38
|
+
email: meskyanichi@gmail.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- .document
|
47
|
+
- .gitignore
|
48
|
+
- .infinity_test
|
49
|
+
- .rspec
|
50
|
+
- Gemfile
|
51
|
+
- Gemfile.lock
|
52
|
+
- LICENSE.md
|
53
|
+
- README.md
|
54
|
+
- hirefire.gemspec
|
55
|
+
- lib/hirefire.rb
|
56
|
+
- lib/hirefire/backend.rb
|
57
|
+
- lib/hirefire/backend/active_record.rb
|
58
|
+
- lib/hirefire/backend/mongoid.rb
|
59
|
+
- lib/hirefire/configuration.rb
|
60
|
+
- lib/hirefire/delayed_job_extension.rb
|
61
|
+
- lib/hirefire/environment.rb
|
62
|
+
- lib/hirefire/environment/base.rb
|
63
|
+
- lib/hirefire/environment/heroku.rb
|
64
|
+
- lib/hirefire/environment/local.rb
|
65
|
+
- lib/hirefire/environment/noop.rb
|
66
|
+
- lib/hirefire/initializer.rb
|
67
|
+
- lib/hirefire/logger.rb
|
68
|
+
- lib/hirefire/railtie.rb
|
69
|
+
- lib/hirefire/version.rb
|
70
|
+
- spec/configuration_spec.rb
|
71
|
+
- spec/logger_spec.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
homepage: http://rubygems.org/gems/hirefire
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.7.2
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: HireFire automatically "hires" and "fires" (aka "scales") Delayed Job workers on Heroku.
|
100
|
+
test_files: []
|
101
|
+
|
102
|
+
has_rdoc:
|