que 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +3 -1
- data/docs/advanced_setup.md +13 -2
- data/lib/que.rb +8 -4
- data/lib/que/railtie.rb +1 -2
- data/lib/que/sql.rb +1 -1
- data/lib/que/version.rb +1 -1
- data/lib/que/worker.rb +56 -59
- data/spec/adapters/active_record_spec.rb +2 -0
- data/spec/adapters/sequel_spec.rb +1 -0
- data/spec/spec_helper.rb +9 -3
- data/spec/unit/logging_spec.rb +22 -0
- data/spec/unit/pool_spec.rb +271 -126
- data/spec/unit/work_spec.rb +4 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0030251d843d06847bfb8e9e12658b952edbdf2
|
4
|
+
data.tar.gz: 455c5a6732b86d0e3ade3c961613d206160f9fd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 669370c39a3b68c3cdc9ff6c9d8ebdd865690451175bc84062696a961a5cf5e3dbe81761fb97c08892731895ebd398001b73aed4158c99010bcdd2707bd10073
|
7
|
+
data.tar.gz: 8c7270f5fc20dee14e28a69468cd01a348e7e24c358ba48fb19a1c2d8562e3d05b747649a800edefda5a4d68fcf5a2c623ba1ffc09441dbdf20bed53c32c97c9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
### 0.8.0 (2014-07-12)
|
2
|
+
|
3
|
+
* A callable can now be set as the logger, like `Que.logger = proc { MyLogger.new }`. Que uses this in its Railtie for cleaner initialization, but it is also available for public use.
|
4
|
+
|
5
|
+
* `Que.mode=` and `Que.worker_count=` now function independently. That is, setting the worker_count to a nonzero number no longer sets mode = :async (triggering the pool to start working jobs), and setting it to zero no longer sets mode = :off. Similarly, setting the mode to :async no longer sets the worker_count to 4 from 0, and setting the mode to :off no longer sets the worker_count to 0. This behavior was changed because it was interfering with configuration during initialization of Rails applications, and because it was unexpected. (#47)
|
6
|
+
|
7
|
+
* Fixed a similar bug wherein setting a wake_interval during application startup would break worker awakening after the process was forked.
|
8
|
+
|
1
9
|
### 0.7.3 (2014-05-19)
|
2
10
|
|
3
11
|
* When mode = :sync, don't touch the database at all when running jobs inline. Needed for ActiveJob compatibility (#46).
|
data/README.md
CHANGED
@@ -102,6 +102,8 @@ To determine what happens when a job is queued, you can set Que's mode in your a
|
|
102
102
|
* `config.que.mode = :async` - In this mode, a pool of background workers is spun up, each running in their own thread. This is the default when running `bin/rails server`. See the docs for [more information on managing workers](https://github.com/chanks/que/blob/master/docs/managing_workers.md).
|
103
103
|
* `config.que.mode = :sync` - In this mode, any jobs you queue will be run in the same thread, synchronously (that is, `MyJob.enqueue` runs the job and won't return until it's completed). This makes your application's behavior easier to test, so it's the default in the test environment.
|
104
104
|
|
105
|
+
If you're using ActiveRecord to dump your database's schema, you'll probably want to [set schema_format to :sql](http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps) so that Que's table structure is managed correctly.
|
106
|
+
|
105
107
|
## Contributing
|
106
108
|
|
107
109
|
1. Fork it
|
@@ -109,7 +111,7 @@ To determine what happens when a job is queued, you can set Que's mode in your a
|
|
109
111
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
110
112
|
4. Push to the branch (`git push origin my-new-feature`)
|
111
113
|
5. Create new Pull Request
|
112
|
-
|
114
|
+
|
113
115
|
A note on running specs - Que's worker system is multithreaded and therefore prone to race conditions (especially on Rubinius). As such, if you've touched that code, a single spec run passing isn't a guarantee that any changes you've made haven't introduced bugs. One thing I like to do before pushing changes is rerun the specs many times and watching for hangs. You can do this from the command line with something like:
|
114
116
|
|
115
117
|
for i in {1..1000}; do rspec -b --seed $i; done
|
data/docs/advanced_setup.md
CHANGED
@@ -20,14 +20,25 @@ There are other docs to read if you're using [Sequel](https://github.com/chanks/
|
|
20
20
|
|
21
21
|
### Forking Servers
|
22
22
|
|
23
|
-
If you want to run a worker pool in your web process and you're using a forking webserver like Unicorn or Puma in some configurations, you'll want to set `Que.mode = :off` in your application configuration and only start up the worker pool in the child processes. So, for Puma:
|
23
|
+
If you want to run a worker pool in your web process and you're using a forking webserver like Unicorn or Puma in some configurations, you'll want to set `Que.mode = :off` in your application configuration and only start up the worker pool in the child processes after the DB connection has been reestablished. So, for Puma:
|
24
24
|
|
25
25
|
# config/puma.rb
|
26
26
|
on_worker_boot do
|
27
|
-
|
27
|
+
ActiveRecord::Base.establish_connection
|
28
|
+
|
28
29
|
Que.mode = :async
|
29
30
|
end
|
30
31
|
|
32
|
+
And for Unicorn:
|
33
|
+
|
34
|
+
# config/unicorn.rb
|
35
|
+
after_fork do |server, worker|
|
36
|
+
ActiveRecord::Base.establish_connection
|
37
|
+
|
38
|
+
Que.mode = :async
|
39
|
+
end
|
40
|
+
|
41
|
+
|
31
42
|
### Managing the Jobs Table
|
32
43
|
|
33
44
|
After you've connected Que to the database, you can manage the jobs table:
|
data/lib/que.rb
CHANGED
@@ -17,8 +17,8 @@ module Que
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class << self
|
20
|
-
attr_accessor :
|
21
|
-
attr_writer :adapter, :log_formatter
|
20
|
+
attr_accessor :error_handler
|
21
|
+
attr_writer :logger, :adapter, :log_formatter
|
22
22
|
|
23
23
|
def connection=(connection)
|
24
24
|
self.adapter =
|
@@ -83,11 +83,15 @@ module Que
|
|
83
83
|
level = data.delete(:level) || :info
|
84
84
|
data = {:lib => 'que', :hostname => Socket.gethostname, :pid => Process.pid, :thread => Thread.current.object_id}.merge(data)
|
85
85
|
|
86
|
-
if logger && output = log_formatter.call(data)
|
87
|
-
|
86
|
+
if (l = logger) && output = log_formatter.call(data)
|
87
|
+
l.send level, output
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
def logger
|
92
|
+
@logger.respond_to?(:call) ? @logger.call : @logger
|
93
|
+
end
|
94
|
+
|
91
95
|
def log_formatter
|
92
96
|
@log_formatter ||= JSON_MODULE.method(:dump)
|
93
97
|
end
|
data/lib/que/railtie.rb
CHANGED
@@ -2,6 +2,7 @@ module Que
|
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
config.que = Que
|
4
4
|
|
5
|
+
Que.logger = proc { Rails.logger }
|
5
6
|
Que.mode = :sync if Rails.env.test?
|
6
7
|
Que.connection = ::ActiveRecord if defined? ::ActiveRecord
|
7
8
|
|
@@ -11,8 +12,6 @@ module Que
|
|
11
12
|
|
12
13
|
initializer 'que.setup' do
|
13
14
|
ActiveSupport.on_load :after_initialize do
|
14
|
-
Que.logger ||= Rails.logger
|
15
|
-
|
16
15
|
# Only start up the worker pool if running as a server.
|
17
16
|
Que.mode ||= :async if defined? Rails::Server
|
18
17
|
|
data/lib/que/sql.rb
CHANGED
@@ -48,7 +48,7 @@ module Que
|
|
48
48
|
:set_error => %{
|
49
49
|
UPDATE que_jobs
|
50
50
|
SET error_count = $1::integer,
|
51
|
-
run_at = now() + $2::
|
51
|
+
run_at = now() + $2::bigint * '1 second'::interval,
|
52
52
|
last_error = $3::text
|
53
53
|
WHERE queue = $4::text
|
54
54
|
AND priority = $5::smallint
|
data/lib/que/version.rb
CHANGED
data/lib/que/worker.rb
CHANGED
@@ -71,30 +71,33 @@ module Que
|
|
71
71
|
|
72
72
|
def work_loop
|
73
73
|
loop do
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
74
|
+
cycle = nil
|
75
|
+
|
76
|
+
if Que.mode == :async
|
77
|
+
time = Time.now
|
78
|
+
result = Job.work(queue)
|
79
|
+
|
80
|
+
case result[:event]
|
81
|
+
when :job_unavailable
|
82
|
+
cycle = false
|
83
|
+
result[:level] = :debug
|
84
|
+
when :job_race_condition
|
85
|
+
cycle = true
|
86
|
+
result[:level] = :debug
|
87
|
+
when :job_worked
|
88
|
+
cycle = true
|
89
|
+
result[:elapsed] = (Time.now - time).round(5)
|
90
|
+
when :job_errored
|
91
|
+
# For PG::Errors, assume we had a problem reaching the database, and
|
92
|
+
# don't hit it again right away.
|
93
|
+
cycle = !result[:error].is_a?(PG::Error)
|
94
|
+
result[:error] = {:class => result[:error].class.to_s, :message => result[:error].message}
|
95
|
+
else
|
96
|
+
raise "Unknown Event: #{result[:event].inspect}"
|
97
|
+
end
|
96
98
|
|
97
|
-
|
99
|
+
Que.log(result)
|
100
|
+
end
|
98
101
|
|
99
102
|
synchronize { @state = :sleeping unless cycle || @stop }
|
100
103
|
sleep if @state == :sleeping
|
@@ -111,35 +114,40 @@ module Que
|
|
111
114
|
# changed in Que.wake_interval= below.
|
112
115
|
@wake_interval = 5
|
113
116
|
|
117
|
+
# Four workers is a sensible default for most use cases.
|
118
|
+
@worker_count = 4
|
119
|
+
|
114
120
|
class << self
|
115
|
-
attr_reader :mode, :wake_interval
|
121
|
+
attr_reader :mode, :wake_interval, :worker_count
|
122
|
+
|
123
|
+
# In order to work in a forking webserver, we need to be able to accept
|
124
|
+
# worker_count and wake_interval settings without actually instantiating
|
125
|
+
# the relevant threads until the mode is actually set to :async in a
|
126
|
+
# post-fork hook (since forking will kill any running background threads).
|
116
127
|
|
117
128
|
def mode=(mode)
|
118
|
-
|
119
|
-
|
120
|
-
set_worker_count 4 if worker_count.zero?
|
121
|
-
when :sync, :off
|
122
|
-
set_worker_count 0
|
123
|
-
end
|
124
|
-
end
|
129
|
+
Que.log :event => 'mode_change', :value => mode.to_s
|
130
|
+
@mode = mode
|
125
131
|
|
126
|
-
|
127
|
-
|
132
|
+
if mode == :async
|
133
|
+
set_up_workers
|
134
|
+
wrangler
|
135
|
+
end
|
128
136
|
end
|
129
137
|
|
130
138
|
def worker_count=(count)
|
131
|
-
|
132
|
-
|
133
|
-
|
139
|
+
Que.log :event => 'worker_count_change', :value => count.to_s
|
140
|
+
@worker_count = count
|
141
|
+
set_up_workers if mode == :async
|
134
142
|
end
|
135
143
|
|
136
|
-
def
|
137
|
-
workers
|
144
|
+
def workers
|
145
|
+
@workers ||= []
|
138
146
|
end
|
139
147
|
|
140
148
|
def wake_interval=(interval)
|
141
149
|
@wake_interval = interval
|
142
|
-
wrangler.wakeup
|
150
|
+
wrangler.wakeup if mode == :async
|
143
151
|
end
|
144
152
|
|
145
153
|
def wake!
|
@@ -152,30 +160,19 @@ module Que
|
|
152
160
|
|
153
161
|
private
|
154
162
|
|
163
|
+
def set_up_workers
|
164
|
+
if worker_count > workers.count
|
165
|
+
workers.push *(worker_count - workers.count).times.map{new(ENV['QUE_QUEUE'] || '')}
|
166
|
+
elsif worker_count < workers.count
|
167
|
+
workers.pop(workers.count - worker_count).each(&:stop).each(&:wait_until_stopped)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
155
171
|
def wrangler
|
156
172
|
@wrangler ||= Thread.new do
|
157
173
|
loop do
|
158
174
|
sleep *@wake_interval
|
159
|
-
wake! if @wake_interval
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def set_mode(mode)
|
165
|
-
if mode != @mode
|
166
|
-
Que.log :event => 'mode_change', :value => mode.to_s
|
167
|
-
@mode = mode
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def set_worker_count(count)
|
172
|
-
if count != worker_count
|
173
|
-
Que.log :event => 'worker_count_change', :value => count.to_s
|
174
|
-
|
175
|
-
if count > worker_count
|
176
|
-
workers.push *(count - worker_count).times.map{new(ENV['QUE_QUEUE'] || '')}
|
177
|
-
elsif count < worker_count
|
178
|
-
workers.pop(worker_count - count).each(&:stop).each(&:wait_until_stopped)
|
175
|
+
wake! if @wake_interval && mode == :async
|
179
176
|
end
|
180
177
|
end
|
181
178
|
end
|
@@ -71,6 +71,7 @@ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
71
71
|
|
72
72
|
it "should support Rails' special extensions for times" do
|
73
73
|
Que.mode = :async
|
74
|
+
Que.worker_count = 4
|
74
75
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
75
76
|
|
76
77
|
Que::Job.enqueue :run_at => 1.minute.ago
|
@@ -82,6 +83,7 @@ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
82
83
|
|
83
84
|
it "should wake up a Worker after queueing a job in async mode, waiting for a transaction to commit if necessary" do
|
84
85
|
Que.mode = :async
|
86
|
+
Que.worker_count = 4
|
85
87
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
86
88
|
|
87
89
|
# Wakes a worker immediately when not in a transaction.
|
@@ -28,6 +28,7 @@ describe "Que using the Sequel adapter" do
|
|
28
28
|
|
29
29
|
it "should wake up a Worker after queueing a job in async mode, waiting for a transaction to commit if necessary" do
|
30
30
|
Que.mode = :async
|
31
|
+
Que.worker_count = 4
|
31
32
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
32
33
|
|
33
34
|
# Wakes a worker immediately when not in a transaction.
|
data/spec/spec_helper.rb
CHANGED
@@ -92,15 +92,21 @@ RSpec.configure do |config|
|
|
92
92
|
# helpful in identifying hanging specs.
|
93
93
|
stdout.info "Running spec: #{desc} @ #{line}" if ENV['LOG_SPEC']
|
94
94
|
|
95
|
-
$logger.messages.clear
|
96
95
|
Que.adapter = QUE_ADAPTERS[:pg]
|
97
96
|
|
97
|
+
Que.worker_count = 0
|
98
|
+
Que.mode = :async
|
99
|
+
Que.wake_interval = nil
|
100
|
+
|
101
|
+
$logger.messages.clear
|
102
|
+
|
98
103
|
spec.run
|
99
104
|
|
100
|
-
|
105
|
+
Que.worker_count = 0
|
101
106
|
Que.mode = :off
|
102
107
|
Que.wake_interval = nil
|
103
|
-
|
108
|
+
|
109
|
+
DB[:que_jobs].delete
|
104
110
|
|
105
111
|
# A bit of lint: make sure that no advisory locks are left open.
|
106
112
|
unless DB[:pg_locks].where(:locktype => 'advisory').empty?
|
data/spec/unit/logging_spec.rb
CHANGED
@@ -14,8 +14,30 @@ describe "Logging" do
|
|
14
14
|
message['thread'].should == Thread.current.object_id
|
15
15
|
end
|
16
16
|
|
17
|
+
it "should allow a callable to be set as the logger" do
|
18
|
+
begin
|
19
|
+
# Make sure we can get through a work cycle without a logger.
|
20
|
+
Que.logger = proc { $logger }
|
21
|
+
|
22
|
+
Que::Job.enqueue
|
23
|
+
worker = Que::Worker.new
|
24
|
+
sleep_until { worker.sleeping? }
|
25
|
+
|
26
|
+
DB[:que_jobs].should be_empty
|
27
|
+
|
28
|
+
worker.stop
|
29
|
+
worker.wait_until_stopped
|
30
|
+
|
31
|
+
$logger.messages.count.should be 2
|
32
|
+
$logger.messages.map{|m| JSON.load(m)['event']}.should == ['job_worked', 'job_unavailable']
|
33
|
+
ensure
|
34
|
+
Que.logger = $logger
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
17
38
|
it "should not raise an error when no logger is present" do
|
18
39
|
begin
|
40
|
+
# Make sure we can get through a work cycle without a logger.
|
19
41
|
Que.logger = nil
|
20
42
|
|
21
43
|
Que::Job.enqueue
|
data/spec/unit/pool_spec.rb
CHANGED
@@ -4,140 +4,277 @@ describe "Managing the Worker pool" do
|
|
4
4
|
it "should log mode changes" do
|
5
5
|
Que.mode = :sync
|
6
6
|
Que.mode = :off
|
7
|
+
Que.mode = :off
|
7
8
|
|
8
|
-
$logger.messages.count.should be
|
9
|
-
m1, m2 = $logger.messages.map{|m| JSON.load(m)}
|
9
|
+
$logger.messages.count.should be 3
|
10
|
+
m1, m2, m3 = $logger.messages.map { |m| JSON.load(m) }
|
10
11
|
|
11
12
|
m1['event'].should == 'mode_change'
|
12
13
|
m1['value'].should == 'sync'
|
13
14
|
|
14
15
|
m2['event'].should == 'mode_change'
|
15
16
|
m2['value'].should == 'off'
|
17
|
+
|
18
|
+
m3['event'].should == 'mode_change'
|
19
|
+
m3['value'].should == 'off'
|
16
20
|
end
|
17
21
|
|
18
|
-
describe "Que.mode
|
19
|
-
|
20
|
-
|
22
|
+
describe "Que.mode=" do
|
23
|
+
describe ":off" do
|
24
|
+
it "with worker_count 0 should not instantiate workers or hit the db" do
|
25
|
+
Que.connection = nil
|
26
|
+
Que.worker_count = 0
|
27
|
+
Que.mode = :off
|
28
|
+
Que::Worker.workers.should == []
|
29
|
+
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
31
|
+
it "with worker_count > 0 should not instantiate workers or hit the db" do
|
32
|
+
Que.connection = nil
|
33
|
+
Que.mode = :off
|
34
|
+
Que.worker_count = 5
|
35
|
+
Que.mode = :off
|
36
|
+
Que::Worker.workers.should == []
|
37
|
+
end
|
25
38
|
end
|
26
39
|
|
27
|
-
|
28
|
-
|
29
|
-
|
40
|
+
describe ":sync" do
|
41
|
+
it "with worker_count 0 should not instantiate workers or hit the db" do
|
42
|
+
Que.connection = nil
|
43
|
+
Que.worker_count = 0
|
44
|
+
Que.mode = :sync
|
45
|
+
Que::Worker.workers.should == []
|
46
|
+
end
|
47
|
+
|
48
|
+
it "with worker_count > 0 should not instantiate workers or hit the db" do
|
49
|
+
Que.connection = nil
|
50
|
+
Que.mode = :sync
|
51
|
+
Que.worker_count = 5
|
52
|
+
Que.mode = :sync
|
53
|
+
Que::Worker.workers.should == []
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should make jobs run in the same thread as they are queued" do
|
57
|
+
Que.mode = :sync
|
58
|
+
|
59
|
+
ArgsJob.enqueue(5, :testing => "synchronous").should be_an_instance_of ArgsJob
|
60
|
+
$passed_args.should == [5, {:testing => "synchronous"}]
|
61
|
+
DB[:que_jobs].count.should be 0
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should work fine with enqueuing jobs without a DB connection" do
|
65
|
+
Que.connection = nil
|
66
|
+
Que.mode = :sync
|
67
|
+
|
68
|
+
ArgsJob.enqueue(5, :testing => "synchronous").should be_an_instance_of ArgsJob
|
69
|
+
$passed_args.should == [5, {:testing => "synchronous"}]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not affect jobs that are queued with specific run_ats" do
|
73
|
+
Que.mode = :sync
|
30
74
|
|
31
|
-
|
32
|
-
|
75
|
+
ArgsJob.enqueue(5, :testing => "synchronous", :run_at => Time.now + 60)
|
76
|
+
DB[:que_jobs].select_map(:job_class).should == ["ArgsJob"]
|
77
|
+
end
|
33
78
|
end
|
34
79
|
|
35
|
-
|
36
|
-
|
80
|
+
describe ":async" do
|
81
|
+
it "with worker_count 0 should not instantiate workers or hit the db" do
|
82
|
+
Que.connection = nil
|
83
|
+
Que.worker_count = 0
|
84
|
+
Que.mode = :async
|
85
|
+
Que::Worker.workers.map{|w| [w.state, w.thread.status]}.should == []
|
86
|
+
end
|
87
|
+
|
88
|
+
it "with worker_count > 0 should instantiate workers and hit the db" do
|
89
|
+
Que::Job.enqueue
|
90
|
+
Que.worker_count = 5
|
91
|
+
Que.mode = :async
|
92
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
93
|
+
DB[:que_jobs].count.should == 0
|
94
|
+
Que::Worker.workers.map{|w| [w.state, w.thread.status]}.should == [[:sleeping, 'sleep']] * 5
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should wake a worker every Que.wake_interval seconds" do
|
98
|
+
Que.worker_count = 4
|
99
|
+
Que.mode = :async
|
100
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
101
|
+
Que.wake_interval = 0.01 # 10 ms
|
102
|
+
Que::Job.enqueue
|
103
|
+
sleep_until { DB[:que_jobs].count == 0 }
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should work jobs in the queue defined by QUE_QUEUE" do
|
107
|
+
begin
|
108
|
+
Que::Job.enqueue 1
|
109
|
+
Que::Job.enqueue 2, :queue => 'my_queue'
|
110
|
+
|
111
|
+
ENV['QUE_QUEUE'] = 'my_queue'
|
37
112
|
|
38
|
-
|
39
|
-
|
113
|
+
Que.mode = :async
|
114
|
+
Que.worker_count = 2
|
115
|
+
|
116
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
117
|
+
DB[:que_jobs].count.should be 1
|
118
|
+
|
119
|
+
job = DB[:que_jobs].first
|
120
|
+
job[:queue].should == ''
|
121
|
+
job[:args].should == '[1]'
|
122
|
+
ensure
|
123
|
+
ENV.delete('QUE_QUEUE')
|
124
|
+
end
|
125
|
+
end
|
40
126
|
end
|
41
127
|
end
|
42
128
|
|
43
|
-
describe "Que.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
129
|
+
describe "Que.worker_count=" do
|
130
|
+
describe "when the mode is :off" do
|
131
|
+
it "should record the setting but not instantiate any workers" do
|
132
|
+
Que.worker_count.should == 0
|
133
|
+
Que.connection = nil
|
134
|
+
Que.mode = :off
|
135
|
+
Que::Worker.workers.should == []
|
136
|
+
|
137
|
+
Que.worker_count = 4
|
138
|
+
Que.worker_count.should == 4
|
139
|
+
Que::Worker.workers.should == []
|
140
|
+
|
141
|
+
Que.worker_count = 6
|
142
|
+
Que.worker_count.should == 6
|
143
|
+
Que::Worker.workers.should == []
|
144
|
+
|
145
|
+
Que.worker_count = 2
|
146
|
+
Que.worker_count.should == 2
|
147
|
+
Que::Worker.workers.should == []
|
148
|
+
|
149
|
+
Que.worker_count = 0
|
150
|
+
Que.worker_count.should == 0
|
151
|
+
Que::Worker.workers.should == []
|
152
|
+
end
|
51
153
|
end
|
52
154
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
155
|
+
describe "when the mode is :sync" do
|
156
|
+
it "should record the setting but not instantiate any workers" do
|
157
|
+
Que.worker_count.should == 0
|
158
|
+
Que.connection = nil
|
159
|
+
Que.mode = :sync
|
160
|
+
Que::Worker.workers.should == []
|
58
161
|
|
59
|
-
|
60
|
-
|
61
|
-
|
162
|
+
Que.worker_count = 4
|
163
|
+
Que.worker_count.should == 4
|
164
|
+
Que::Worker.workers.should == []
|
62
165
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
Que.worker_count.should be 1
|
67
|
-
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
166
|
+
Que.worker_count = 6
|
167
|
+
Que.worker_count.should == 6
|
168
|
+
Que::Worker.workers.should == []
|
68
169
|
|
69
|
-
|
70
|
-
|
170
|
+
Que.worker_count = 2
|
171
|
+
Que.worker_count.should == 2
|
172
|
+
Que::Worker.workers.should == []
|
173
|
+
|
174
|
+
Que.worker_count = 0
|
175
|
+
Que.worker_count.should == 0
|
176
|
+
Que::Worker.workers.should == []
|
177
|
+
end
|
71
178
|
end
|
72
179
|
|
73
|
-
|
74
|
-
|
75
|
-
|
180
|
+
describe "when the mode is :async" do
|
181
|
+
it "should start hitting the DB when transitioning to a non-zero value" do
|
182
|
+
Que.mode = :async
|
183
|
+
Que::Job.enqueue
|
184
|
+
Que.worker_count = 4
|
185
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
186
|
+
Que::Worker.workers.map{|w| [w.state, w.thread.status]}.should == [[:sleeping, 'sleep']] * 4
|
187
|
+
DB[:que_jobs].count.should == 0
|
188
|
+
end
|
76
189
|
|
77
|
-
|
190
|
+
it "should stop hitting the DB when transitioning to zero" do
|
191
|
+
Que.mode = :async
|
192
|
+
Que.worker_count = 4
|
193
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
194
|
+
Que.connection = nil
|
195
|
+
Que.worker_count = 0
|
196
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
197
|
+
[['mode_change', 'async'], ['worker_count_change', '4']] + [['job_unavailable', nil]] * 4 + [['worker_count_change', '0']]
|
198
|
+
end
|
78
199
|
|
79
|
-
|
80
|
-
|
81
|
-
|
200
|
+
it "should be able to scale down the number of workers gracefully" do
|
201
|
+
Que.mode = :async
|
202
|
+
Que.worker_count = 4
|
82
203
|
|
83
|
-
|
84
|
-
|
85
|
-
|
204
|
+
workers = Que::Worker.workers.dup
|
205
|
+
workers.count.should be 4
|
206
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
86
207
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
workers.count.should be 4
|
91
|
-
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
208
|
+
Que.worker_count = 2
|
209
|
+
Que::Worker.workers.count.should be 2
|
210
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
92
211
|
|
93
|
-
|
94
|
-
|
95
|
-
|
212
|
+
workers[0..1].should == Que::Worker.workers
|
213
|
+
workers[2..3].each do |worker|
|
214
|
+
worker.should be_an_instance_of Que::Worker
|
215
|
+
worker.thread.status.should == false
|
216
|
+
end
|
96
217
|
|
97
|
-
|
98
|
-
|
99
|
-
worker.should be_an_instance_of Que::Worker
|
100
|
-
worker.thread.status.should == false
|
218
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
219
|
+
[['mode_change', 'async'], ['worker_count_change', '4']] + [['job_unavailable', nil]] * 4 + [['worker_count_change', '2']]
|
101
220
|
end
|
102
221
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
Que.mode = :async
|
109
|
-
workers = Que::Worker.workers.dup
|
110
|
-
workers.count.should be 4
|
222
|
+
it "should be able to scale up the number of workers gracefully" do
|
223
|
+
Que.mode = :async
|
224
|
+
Que.worker_count = 4
|
225
|
+
workers = Que::Worker.workers.dup
|
226
|
+
workers.count.should be 4
|
111
227
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
228
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
229
|
+
Que.worker_count = 6
|
230
|
+
Que::Worker.workers.count.should be 6
|
231
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
116
232
|
|
117
|
-
|
233
|
+
workers.should == Que::Worker.workers[0..3]
|
118
234
|
|
119
|
-
|
120
|
-
|
235
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
236
|
+
[['mode_change', 'async'], ['worker_count_change', '4']] + [['job_unavailable', nil]] * 4 + [['worker_count_change', '6']] + [['job_unavailable', nil]] * 2
|
237
|
+
end
|
121
238
|
end
|
239
|
+
end
|
122
240
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
workers.count.should be 4
|
127
|
-
|
128
|
-
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
241
|
+
describe "Que.wake!" do
|
242
|
+
it "when mode = :off should do nothing" do
|
243
|
+
Que.connection = nil
|
129
244
|
Que.mode = :off
|
130
|
-
Que.worker_count
|
245
|
+
Que.worker_count = 4
|
246
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
247
|
+
Que.wake!
|
248
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
249
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
250
|
+
[['mode_change', 'off'], ['worker_count_change', '4']]
|
251
|
+
end
|
131
252
|
|
132
|
-
|
133
|
-
|
253
|
+
it "when mode = :sync should do nothing" do
|
254
|
+
Que.connection = nil
|
255
|
+
Que.mode = :sync
|
256
|
+
Que.worker_count = 4
|
257
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
258
|
+
Que.wake!
|
259
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
260
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
261
|
+
[['mode_change', 'sync'], ['worker_count_change', '4']]
|
262
|
+
end
|
134
263
|
|
264
|
+
it "when mode = :async and worker_count = 0 should do nothing" do
|
265
|
+
Que.connection = nil
|
266
|
+
Que.mode = :async
|
267
|
+
Que.worker_count = 0
|
268
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
269
|
+
Que.wake!
|
270
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
135
271
|
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
136
|
-
[['mode_change', 'async'], ['worker_count_change', '
|
272
|
+
[['mode_change', 'async'], ['worker_count_change', '0']]
|
137
273
|
end
|
138
274
|
|
139
|
-
it "
|
275
|
+
it "when mode = :async and worker_count > 0 should wake up a single worker" do
|
140
276
|
Que.mode = :async
|
277
|
+
Que.worker_count = 4
|
141
278
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
142
279
|
|
143
280
|
BlockJob.enqueue
|
@@ -153,17 +290,55 @@ describe "Managing the Worker pool" do
|
|
153
290
|
DB[:que_jobs].count.should be 0
|
154
291
|
end
|
155
292
|
|
156
|
-
it "
|
293
|
+
it "when mode = :async and worker_count > 0 should be thread-safe" do
|
157
294
|
Que.mode = :async
|
295
|
+
Que.worker_count = 4
|
296
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
158
297
|
threads = 4.times.map { Thread.new { 100.times { Que.wake! } } }
|
159
298
|
threads.each(&:join)
|
160
299
|
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "Que.wake_all!" do
|
303
|
+
it "when mode = :off should do nothing" do
|
304
|
+
Que.connection = nil
|
305
|
+
Que.mode = :off
|
306
|
+
Que.worker_count = 4
|
307
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
308
|
+
Que.wake_all!
|
309
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
310
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
311
|
+
[['mode_change', 'off'], ['worker_count_change', '4']]
|
312
|
+
end
|
161
313
|
|
162
|
-
it "
|
163
|
-
|
164
|
-
Que.
|
314
|
+
it "when mode = :sync should do nothing" do
|
315
|
+
Que.connection = nil
|
316
|
+
Que.mode = :sync
|
317
|
+
Que.worker_count = 4
|
318
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
319
|
+
Que.wake_all!
|
320
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
321
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
322
|
+
[['mode_change', 'sync'], ['worker_count_change', '4']]
|
323
|
+
end
|
324
|
+
|
325
|
+
it "when mode = :async and worker_count = 0 should do nothing" do
|
326
|
+
Que.connection = nil
|
327
|
+
Que.mode = :async
|
328
|
+
Que.worker_count = 0
|
329
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
330
|
+
Que.wake_all!
|
331
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
332
|
+
$logger.messages.map{|m| JSON.load(m).values_at('event', 'value')}.should ==
|
333
|
+
[['mode_change', 'async'], ['worker_count_change', '0']]
|
334
|
+
end
|
335
|
+
|
336
|
+
# This spec requires at least four connections.
|
337
|
+
it "when mode = :async and worker_count > 0 should wake up all workers" do
|
338
|
+
Que.adapter = QUE_ADAPTERS[:pond]
|
165
339
|
|
166
340
|
Que.mode = :async
|
341
|
+
Que.worker_count = 4
|
167
342
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
168
343
|
|
169
344
|
4.times { BlockJob.enqueue }
|
@@ -175,44 +350,14 @@ describe "Managing the Worker pool" do
|
|
175
350
|
|
176
351
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
177
352
|
DB[:que_jobs].count.should be 0
|
178
|
-
end if QUE_ADAPTERS[:
|
353
|
+
end if QUE_ADAPTERS[:pond]
|
179
354
|
|
180
|
-
it "
|
355
|
+
it "when mode = :async and worker_count > 0 should be thread-safe" do
|
181
356
|
Que.mode = :async
|
357
|
+
Que.worker_count = 4
|
358
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
182
359
|
threads = 4.times.map { Thread.new { 100.times { Que.wake_all! } } }
|
183
360
|
threads.each(&:join)
|
184
361
|
end
|
185
|
-
|
186
|
-
it "should wake a worker every Que.wake_interval seconds" do
|
187
|
-
Que.mode = :async
|
188
|
-
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
189
|
-
Que.wake_interval = 0.01 # 10 ms
|
190
|
-
Que::Job.enqueue
|
191
|
-
sleep_until { DB[:que_jobs].count == 0 }
|
192
|
-
end
|
193
|
-
|
194
|
-
it "should work jobs in the queue defined by QUE_QUEUE" do
|
195
|
-
begin
|
196
|
-
Que::Job.enqueue 1
|
197
|
-
Que::Job.enqueue 2, :queue => 'my_queue'
|
198
|
-
|
199
|
-
ENV['QUE_QUEUE'] = 'my_queue'
|
200
|
-
|
201
|
-
Que.mode = :async
|
202
|
-
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
203
|
-
DB[:que_jobs].count.should be 1
|
204
|
-
|
205
|
-
job = DB[:que_jobs].first
|
206
|
-
job[:queue].should == ''
|
207
|
-
job[:args].should == '[1]'
|
208
|
-
ensure
|
209
|
-
ENV.delete('QUE_QUEUE')
|
210
|
-
|
211
|
-
if @worker
|
212
|
-
@worker.stop
|
213
|
-
@worker.wait_until_stopped
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
362
|
end
|
218
363
|
end
|
data/spec/unit/work_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'active_support/core_ext/date' # For the .seconds.from_now below
|
2
3
|
|
3
4
|
describe Que::Job, '.work' do
|
4
5
|
it "should pass a job's arguments to the run method and delete it from the database" do
|
@@ -237,7 +238,7 @@ describe Que::Job, '.work' do
|
|
237
238
|
|
238
239
|
it "should respect a custom retry interval" do
|
239
240
|
class RetryIntervalJob < ErrorJob
|
240
|
-
@retry_interval =
|
241
|
+
@retry_interval = 3155760000000 # 100,000 years from now
|
241
242
|
end
|
242
243
|
|
243
244
|
RetryIntervalJob.enqueue
|
@@ -251,7 +252,7 @@ describe Que::Job, '.work' do
|
|
251
252
|
job = DB[:que_jobs].first
|
252
253
|
job[:error_count].should be 1
|
253
254
|
job[:last_error].should =~ /\AErrorJob!\n/
|
254
|
-
job[:run_at].should be_within(3).of Time.now +
|
255
|
+
job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
|
255
256
|
|
256
257
|
DB[:que_jobs].update :error_count => 5,
|
257
258
|
:run_at => Time.now - 60
|
@@ -265,7 +266,7 @@ describe Que::Job, '.work' do
|
|
265
266
|
job = DB[:que_jobs].first
|
266
267
|
job[:error_count].should be 6
|
267
268
|
job[:last_error].should =~ /\AErrorJob!\n/
|
268
|
-
job[:run_at].should be_within(3).of Time.now +
|
269
|
+
job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
|
269
270
|
end
|
270
271
|
|
271
272
|
it "should respect a custom retry interval formula" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hanks
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|