hirefire 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -44,6 +44,7 @@ And that's it. Next time you deploy to [Heroku](http://heroku.com/) it'll automa
44
44
  HireFire.configure do |config|
45
45
  config.environment = nil # default in production is :heroku. default in development is :noop
46
46
  config.max_workers = 5 # default is 1
47
+ config.min_workers = 0 # default is 0
47
48
  config.job_worker_ratio = [
48
49
  { :jobs => 1, :workers => 1 },
49
50
  { :jobs => 15, :workers => 2 },
data/lib/hirefire.rb CHANGED
@@ -44,8 +44,9 @@ module HireFire
44
44
  ##
45
45
  # HireFire::Backend::DelayedJob namespace
46
46
  module DelayedJob
47
- autoload :ActiveRecord, File.join(DELAYED_JOB_PATH, 'active_record')
48
- autoload :Mongoid, File.join(DELAYED_JOB_PATH, 'mongoid')
47
+ autoload :ActiveRecord, File.join(DELAYED_JOB_PATH, 'active_record')
48
+ autoload :ActiveRecord2, File.join(DELAYED_JOB_PATH, 'active_record_2')
49
+ autoload :Mongoid, File.join(DELAYED_JOB_PATH, 'mongoid')
49
50
  end
50
51
 
51
52
  ##
@@ -72,6 +73,7 @@ module HireFire
72
73
  # HireFire.configure do |config|
73
74
  # config.environment = nil
74
75
  # config.max_workers = 5
76
+ # config.min_workers = 0
75
77
  # config.job_worker_ratio = [
76
78
  # { :jobs => 1, :workers => 1 },
77
79
  # { :jobs => 15, :workers => 2 },
@@ -103,9 +105,10 @@ end
103
105
  # in their application manually, after loading the worker library (either "Delayed Job" or "Resque")
104
106
  # and the desired mapper (ActiveRecord, Mongoid or Redis)
105
107
  if defined?(Rails)
106
- if Rails.version >= '3.0.0'
108
+ if defined?(Rails::Railtie)
107
109
  require File.join(HireFire::HIREFIRE_PATH, 'railtie')
108
110
  else
109
111
  HireFire::Initializer.initialize!
110
112
  end
111
113
  end
114
+
@@ -16,9 +16,13 @@ module HireFire
16
16
 
17
17
  ##
18
18
  # Delayed Job specific backends
19
- if defined?(::Delayed::Job)
19
+ if defined?(::Delayed)
20
20
  if defined?(::Delayed::Backend::ActiveRecord::Job)
21
- base.send(:include, HireFire::Backend::DelayedJob::ActiveRecord)
21
+ if defined?(::ActiveRecord::Relation)
22
+ base.send(:include, HireFire::Backend::DelayedJob::ActiveRecord)
23
+ else
24
+ base.send(:include, HireFire::Backend::DelayedJob::ActiveRecord2)
25
+ end
22
26
  end
23
27
 
24
28
  if defined?(::Delayed::Backend::Mongoid::Job)
@@ -35,3 +39,4 @@ module HireFire
35
39
 
36
40
  end
37
41
  end
42
+
@@ -18,9 +18,11 @@ module HireFire
18
18
 
19
19
  ##
20
20
  # Counts the amount of jobs that are locked by a worker
21
+ # There is no other performant way to determine the amount
22
+ # of workers there currently are
21
23
  #
22
24
  # @return [Fixnum] the amount of (assumably working) workers
23
- def workers
25
+ def working
24
26
  ::Delayed::Job.
25
27
  where('locked_by IS NOT NULL').count
26
28
  end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module HireFire
4
+ module Backend
5
+ module DelayedJob
6
+ module ActiveRecord2
7
+
8
+ ##
9
+ # Counts the amount of queued jobs in the database,
10
+ # failed jobs are excluded from the sum
11
+ #
12
+ # @return [Fixnum] the amount of pending jobs
13
+ def jobs
14
+ ::Delayed::Job.all(
15
+ :conditions => ['failed_at IS NULL and run_at <= ?', Time.now.utc]
16
+ ).count
17
+ end
18
+
19
+ ##
20
+ # Counts the amount of jobs that are locked by a worker
21
+ # There is no other performant way to determine the amount
22
+ # of workers there currently are
23
+ #
24
+ # @return [Fixnum] the amount of (assumably working) workers
25
+ def working
26
+ ::Delayed::Job.all(
27
+ :conditions => 'locked_by IS NOT NULL'
28
+ ).count
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -19,9 +19,11 @@ module HireFire
19
19
 
20
20
  ##
21
21
  # Counts the amount of jobs that are locked by a worker
22
+ # There is no other performant way to determine the amount
23
+ # of workers there currently are
22
24
  #
23
25
  # @return [Fixnum] the amount of (assumably working) workers
24
- def workers
26
+ def working
25
27
  ::Delayed::Job.
26
28
  where(:locked_by.ne => nil).count
27
29
  end
@@ -6,12 +6,30 @@ module HireFire
6
6
  module Redis
7
7
 
8
8
  ##
9
- # Counts the amount of queued jobs in the database,
10
- # failed jobs and jobs scheduled for the future are excluded
9
+ # Counts the amount of pending jobs in Redis
10
+ #
11
+ # Failed jobs are excluded because they are not listed as "pending"
12
+ # and jobs cannot be scheduled for the future in Resque
11
13
  #
12
14
  # @return [Fixnum]
13
15
  def jobs
14
- ::Resque.info[:pending].to_i + ::Resque.info[:working].to_i
16
+ ::Resque.info[:pending].to_i
17
+ end
18
+
19
+ ##
20
+ # Counts the amount of workers
21
+ #
22
+ # @return [Fixnum]
23
+ def workers
24
+ ::Resque.info[:workers].to_i
25
+ end
26
+
27
+ ##
28
+ # Counts the amount of jobs that are being processed by workers
29
+ #
30
+ # @return [Fixnum]
31
+ def working
32
+ ::Resque.info[:working].to_i
15
33
  end
16
34
 
17
35
  end
@@ -9,6 +9,12 @@ module HireFire
9
9
  # @return [Fixnum] default: 1
10
10
  attr_accessor :max_workers
11
11
 
12
+ ##
13
+ # Contains the min amount of workers that should always be running
14
+ #
15
+ # @return [Fixnum] default: 0
16
+ attr_accessor :min_workers
17
+
12
18
  ##
13
19
  # Contains the job/worker ratio which determines
14
20
  # how many workers need to be running depending on
@@ -33,6 +39,7 @@ module HireFire
33
39
  # @return [HireFire::Configuration]
34
40
  def initialize
35
41
  @max_workers = 1
42
+ @min_workers = 0
36
43
  @job_worker_ratio = [
37
44
  { :jobs => 1, :workers => 1 },
38
45
  { :jobs => 25, :workers => 2 },
@@ -91,7 +91,7 @@ module HireFire
91
91
  # @return [nil]
92
92
  def hirefire_hire
93
93
  delayed_job = ::Delayed::Job.new
94
- if delayed_job.workers == 0 \
94
+ if delayed_job.working == 0 \
95
95
  or delayed_job.jobs == 1
96
96
  environment.hire
97
97
  end
@@ -19,6 +19,7 @@ module HireFire
19
19
  #
20
20
  # HireFire.configure do |config|
21
21
  # config.max_workers = 5
22
+ # config.min_workers = 0
22
23
  # config.job_worker_ratio = [
23
24
  # { :jobs => 1, :workers => 1 },
24
25
  # { :jobs => 15, :workers => 2 },
@@ -60,7 +61,7 @@ module HireFire
60
61
  # @return [nil]
61
62
  def hire
62
63
  jobs_count = jobs
63
- workers_count = workers
64
+ workers_count = workers || return
64
65
 
65
66
  ##
66
67
  # Use "Standard Notation"
@@ -160,13 +161,13 @@ module HireFire
160
161
  # or "updated, unless the job didn't fail"
161
162
  #
162
163
  # If there are workers active, but there are no more pending jobs,
163
- # then fire all the workers
164
+ # then fire all the workers or set to the minimum_workers
164
165
  #
165
166
  # @return [nil]
166
167
  def fire
167
- if jobs == 0 and workers > 0
168
- Logger.message("All queued jobs have been processed. Firing all workers.")
169
- workers(0)
168
+ if jobs == 0 and workers > min_workers
169
+ Logger.message("All queued jobs have been processed. " + (min_workers > 0 ? "Setting workers to #{min_workers}." : "Firing all workers."))
170
+ workers(min_workers)
170
171
  end
171
172
  end
172
173
 
@@ -192,7 +193,16 @@ module HireFire
192
193
 
193
194
  ##
194
195
  # Wrapper method for HireFire.configuration
195
- # Returns the job/worker ratio array (in reversed order)
196
+ # Returns the min amount of workers that should always be running
197
+ #
198
+ # @return [Fixnum] the min amount of workers that should always be running
199
+ def min_workers
200
+ HireFire.configuration.min_workers
201
+ end
202
+
203
+ ##
204
+ # Wrapper method for HireFire.configuration
205
+ # Returns the job/worker ratio array
196
206
  #
197
207
  # @return [Array] the array of hashes containing the job/worker ratio
198
208
  def ratio
@@ -31,6 +31,12 @@ module HireFire
31
31
  # Sets the amount of Delayed Job
32
32
  # workers that need to be running on Heroku
33
33
  client.set_workers(ENV['APP_NAME'], amount)
34
+
35
+ rescue RestClient::Exception
36
+ # Heroku library uses rest-client, currently, and it is quite
37
+ # possible to receive RestClient exceptions through the client.
38
+ HireFire::Logger.message("Worker query request failed with #{ $!.class.name } #{ $!.message }")
39
+ nil
34
40
  end
35
41
 
36
42
  ##
@@ -24,7 +24,7 @@ module HireFire
24
24
 
25
25
  ##
26
26
  # Initialize Delayed::Job extensions if Delayed::Job is found
27
- if defined?(::Delayed::Job)
27
+ if defined?(::Delayed)
28
28
  ##
29
29
  # If DelayedJob is using ActiveRecord, then include
30
30
  # HireFire::Environment in to the ActiveRecord Delayed Job Backend
@@ -71,3 +71,4 @@ module HireFire
71
71
 
72
72
  end
73
73
  end
74
+
@@ -6,7 +6,7 @@ module HireFire
6
6
  ##
7
7
  # @return [String] the current version of the HireFire gem
8
8
  def self.current
9
- '0.1.1'
9
+ '0.1.2'
10
10
  end
11
11
 
12
12
  end
@@ -15,7 +15,13 @@ module Delayed
15
15
  # except for the following:
16
16
  #
17
17
  # 1. All ouput will now go through the HireFire::Logger.
18
- # 2. When HireFire cannot find any jobs to process it sends the "fire"
18
+ # 2. Invoke the ::Delayed::Job.environment.hire method at every loop
19
+ # to see whether we need to hire more workers so that we can delegate
20
+ # this task to the workers, rather than the web servers to improve web-throughput
21
+ # by avoiding any unnecessary API calls to Heroku.
22
+ # If there are any workers running, then the front end will never invoke API calls
23
+ # since the worker(s) can handle this itself.
24
+ # 3. When HireFire cannot find any jobs to process it sends the "fire"
19
25
  # signal to all workers, ending all the processes simultaneously. The reason
20
26
  # we wait for all the processes to finish before sending the signal is because it'll
21
27
  # otherwise interrupt workers and leave jobs unfinished.
@@ -29,6 +35,7 @@ module Delayed
29
35
  queued = Delayed::Job.new
30
36
 
31
37
  loop do
38
+ ::Delayed::Job.environment.hire
32
39
  result = nil
33
40
 
34
41
  realtime = Benchmark.realtime do
@@ -16,11 +16,12 @@ module ::Resque
16
16
 
17
17
  ##
18
18
  # HireFire Hook
19
- # After a new job gets queued, we command the current environment
20
- # to calculate the amount of workers we need to process the jobs
21
- # that are currently queued, and hire them accordingly.
22
- if ::Resque.info[:working].to_i == 0 \
23
- or ::Resque.info[:jobs] == 1
19
+ # After a new job gets enqueued we check to see if there are currently
20
+ # any workers up and running. If this is the case then we do nothing and
21
+ # let the worker pick up the jobs (and potentially hire more workers)
22
+ #
23
+ # If there are no workers, then we manually hire workers.
24
+ if ::Resque::Job.workers == 0
24
25
  ::Resque::Job.environment.hire
25
26
  end
26
27
 
@@ -40,7 +40,7 @@ module ::Resque
40
40
  # This means that there aren't any more jobs to process for any of the workers.
41
41
  # If this is the case it'll command the current environment to fire all the hired workers
42
42
  # and then immediately break out of this infinite loop.
43
- if ::Resque::Job.jobs == 0
43
+ if (::Resque::Job.jobs + ::Resque::Job.working) == 0
44
44
  ::Resque::Job.environment.fire
45
45
  break
46
46
  else
@@ -9,6 +9,7 @@ describe HireFire::Configuration do
9
9
 
10
10
  configuration.environment.should == nil
11
11
  configuration.max_workers.should == 1
12
+ configuration.min_workers.should == 0
12
13
  configuration.job_worker_ratio.should == [
13
14
  { :jobs => 1, :workers => 1 },
14
15
  { :jobs => 25, :workers => 2 },
@@ -22,6 +23,7 @@ describe HireFire::Configuration do
22
23
  HireFire.configure do |config|
23
24
  config.environment = :noop
24
25
  config.max_workers = 10
26
+ config.min_workers = 0
25
27
  config.job_worker_ratio = [
26
28
  { :jobs => 1, :workers => 1 },
27
29
  { :jobs => 15, :workers => 2 },
@@ -35,6 +37,7 @@ describe HireFire::Configuration do
35
37
 
36
38
  configuration.environment.should == :noop
37
39
  configuration.max_workers.should == 10
40
+ configuration.min_workers.should == 0
38
41
  configuration.job_worker_ratio.should == [
39
42
  { :jobs => 1, :workers => 1 },
40
43
  { :jobs => 15, :workers => 2 },
@@ -100,6 +100,16 @@ describe HireFire::Environment::Base do
100
100
  base.expects(:workers).with(0).once
101
101
  base.fire
102
102
  end
103
+
104
+ it 'should set the workers to minimum workers when there arent any jobs' do
105
+ base.jobs = 0
106
+ base.workers = 10
107
+ base.stubs(:min_workers).returns(2)
108
+
109
+ HireFire::Logger.expects(:message).with('All queued jobs have been processed. Setting workers to 2.')
110
+ base.expects(:workers).with(2).once
111
+ base.fire
112
+ end
103
113
  end
104
114
 
105
115
  describe '#hire' do
@@ -228,6 +238,15 @@ describe HireFire::Environment::Base do
228
238
  base.expects(:workers).with(5).never
229
239
  base.hire
230
240
  end
241
+
242
+ it 'should NEVER do API requests to Heroku if the workers query returns nil' do
243
+ base.jobs = 100
244
+ base.workers = nil
245
+
246
+ base.expects(:log_and_hire).never
247
+ base.expects(:fire).never
248
+ base.hire
249
+ end
231
250
  end
232
251
 
233
252
  describe 'the Lambda (functional) notation' do
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hirefire
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
5
+ version: 0.1.2
11
6
  platform: ruby
12
7
  authors:
13
8
  - Michael van Rooijen
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-04-15 00:00:00 Z
13
+ date: 2011-05-01 00:00:00 Z
19
14
  dependencies:
20
15
  - !ruby/object:Gem::Dependency
21
16
  name: heroku
@@ -25,11 +20,6 @@ dependencies:
25
20
  requirements:
26
21
  - - ~>
27
22
  - !ruby/object:Gem::Version
28
- hash: 69
29
- segments:
30
- - 1
31
- - 20
32
- - 1
33
23
  version: 1.20.1
34
24
  type: :runtime
35
25
  version_requirements: *id001
@@ -41,11 +31,6 @@ dependencies:
41
31
  requirements:
42
32
  - - ~>
43
33
  - !ruby/object:Gem::Version
44
- hash: 9
45
- segments:
46
- - 0
47
- - 6
48
- - 7
49
34
  version: 0.6.7
50
35
  type: :runtime
51
36
  version_requirements: *id002
@@ -70,6 +55,7 @@ files:
70
55
  - lib/hirefire.rb
71
56
  - lib/hirefire/backend.rb
72
57
  - lib/hirefire/backend/delayed_job/active_record.rb
58
+ - lib/hirefire/backend/delayed_job/active_record_2.rb
73
59
  - lib/hirefire/backend/delayed_job/mongoid.rb
74
60
  - lib/hirefire/backend/resque/redis.rb
75
61
  - lib/hirefire/configuration.rb
@@ -105,25 +91,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
91
  requirements:
106
92
  - - ">="
107
93
  - !ruby/object:Gem::Version
108
- hash: 3
109
- segments:
110
- - 0
111
94
  version: "0"
112
95
  required_rubygems_version: !ruby/object:Gem::Requirement
113
96
  none: false
114
97
  requirements:
115
98
  - - ">="
116
99
  - !ruby/object:Gem::Version
117
- hash: 3
118
- segments:
119
- - 0
120
100
  version: "0"
121
101
  requirements: []
122
102
 
123
103
  rubyforge_project:
124
- rubygems_version: 1.7.1
104
+ rubygems_version: 1.7.2
125
105
  signing_key:
126
106
  specification_version: 3
127
107
  summary: HireFire automatically "hires" and "fires" (aka "scales") Delayed Job and Resque workers on Heroku.
128
108
  test_files: []
129
109
 
110
+ has_rdoc: