hirefire 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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: