que 0.3.0 → 0.4.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 +20 -0
- data/README.md +12 -31
- data/docs/advanced_setup.md +50 -0
- data/docs/error_handling.md +17 -0
- data/docs/inspecting_the_queue.md +100 -0
- data/docs/managing_workers.md +67 -0
- data/docs/using_plain_connections.md +34 -0
- data/docs/using_sequel.md +27 -0
- data/docs/writing_reliable_jobs.md +62 -0
- data/lib/que.rb +33 -5
- data/lib/que/adapters/base.rb +9 -1
- data/lib/que/adapters/pg.rb +2 -0
- data/lib/que/adapters/sequel.rb +4 -0
- data/lib/que/job.rb +30 -64
- data/lib/que/rake_tasks.rb +0 -1
- data/lib/que/sql.rb +119 -74
- data/lib/que/version.rb +1 -1
- data/lib/que/worker.rb +39 -26
- data/spec/adapters/active_record_spec.rb +7 -26
- data/spec/adapters/connection_pool_spec.rb +1 -2
- data/spec/adapters/pg_spec.rb +34 -0
- data/spec/adapters/sequel_spec.rb +16 -28
- data/spec/spec_helper.rb +46 -34
- data/spec/support/shared_examples/adapter.rb +16 -3
- data/spec/support/shared_examples/{multithreaded_adapter.rb → multi_threaded_adapter.rb} +3 -1
- data/spec/unit/helper_spec.rb +0 -5
- data/spec/unit/pool_spec.rb +75 -23
- data/spec/unit/queue_spec.rb +5 -1
- data/spec/unit/states_spec.rb +52 -0
- data/spec/unit/stats_spec.rb +42 -0
- data/spec/unit/work_spec.rb +15 -20
- data/spec/unit/worker_spec.rb +18 -3
- data/tasks/safe_shutdown.rb +5 -3
- metadata +16 -5
data/lib/que/version.rb
CHANGED
data/lib/que/worker.rb
CHANGED
@@ -81,37 +81,34 @@ module Que
|
|
81
81
|
def work_loop
|
82
82
|
loop do
|
83
83
|
job = Job.work
|
84
|
-
|
85
|
-
# Grab the lock and figure out what we should do next.
|
86
84
|
synchronize { @state = :sleeping unless @stop || job }
|
87
|
-
|
88
85
|
sleep if @state == :sleeping
|
89
86
|
break if @stop
|
90
87
|
end
|
91
88
|
rescue Stop
|
92
|
-
# This process is shutting down
|
89
|
+
# This process is shutting down - let it.
|
93
90
|
ensure
|
94
91
|
@state = :stopped
|
95
92
|
end
|
96
93
|
|
97
|
-
#
|
98
|
-
|
99
|
-
@
|
94
|
+
# Setting Que.wake_interval = nil should ensure that the wrangler thread
|
95
|
+
# doesn't wake up a worker again, even if it's currently sleeping for a
|
96
|
+
# set period. So, we double-check that @wake_interval is set before waking
|
97
|
+
# a worker, and make sure to wake up the wrangler when @wake_interval is
|
98
|
+
# changed in Que.wake_interval= below.
|
99
|
+
@wake_interval = 5
|
100
|
+
@wrangler = Thread.new { loop { sleep(*@wake_interval); wake! if @wake_interval } }
|
100
101
|
|
101
102
|
class << self
|
102
|
-
attr_reader :mode, :
|
103
|
+
attr_reader :mode, :wake_interval
|
103
104
|
|
104
105
|
def mode=(mode)
|
105
|
-
case mode
|
106
|
+
case set_mode(mode)
|
106
107
|
when :async
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
self.worker_count = 0
|
108
|
+
set_worker_count 4 if worker_count.zero?
|
109
|
+
when :sync, :off
|
110
|
+
set_worker_count 0
|
111
111
|
end
|
112
|
-
|
113
|
-
@mode = mode
|
114
|
-
Que.log :info, "Set mode to #{mode.inspect}"
|
115
112
|
end
|
116
113
|
|
117
114
|
def workers
|
@@ -119,16 +116,17 @@ module Que
|
|
119
116
|
end
|
120
117
|
|
121
118
|
def worker_count=(count)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
119
|
+
set_mode(count > 0 ? :async : :off)
|
120
|
+
set_worker_count(count)
|
121
|
+
end
|
122
|
+
|
123
|
+
def worker_count
|
124
|
+
workers.count
|
127
125
|
end
|
128
126
|
|
129
|
-
def
|
130
|
-
@
|
131
|
-
wrangler.wakeup
|
127
|
+
def wake_interval=(interval)
|
128
|
+
@wake_interval = interval
|
129
|
+
@wrangler.wakeup
|
132
130
|
end
|
133
131
|
|
134
132
|
def stop!
|
@@ -154,8 +152,23 @@ module Que
|
|
154
152
|
|
155
153
|
private
|
156
154
|
|
157
|
-
def
|
158
|
-
|
155
|
+
def set_mode(mode)
|
156
|
+
if mode != @mode
|
157
|
+
Que.log :info, "Set mode to #{mode.inspect}"
|
158
|
+
@mode = mode
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_worker_count(count)
|
163
|
+
if count != worker_count
|
164
|
+
Que.log :info, "Set worker_count to #{count.inspect}"
|
165
|
+
|
166
|
+
if count > worker_count
|
167
|
+
workers.push *(count - worker_count).times.map { new }
|
168
|
+
elsif count < worker_count
|
169
|
+
workers.pop(worker_count - count).each(&:stop).each(&:wait_until_stopped)
|
170
|
+
end
|
171
|
+
end
|
159
172
|
end
|
160
173
|
end
|
161
174
|
end
|
@@ -11,8 +11,7 @@ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
11
11
|
describe "Que using the ActiveRecord adapter" do
|
12
12
|
before { Que.adapter = QUE_ADAPTERS[:active_record] }
|
13
13
|
|
14
|
-
it_behaves_like "a Que adapter"
|
15
|
-
it_behaves_like "a multithreaded Que adapter"
|
14
|
+
it_behaves_like "a multi-threaded Que adapter"
|
16
15
|
|
17
16
|
it "should use the same connection that ActiveRecord does" do
|
18
17
|
class ActiveRecordJob < Que::Job
|
@@ -36,32 +35,14 @@ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
36
35
|
end
|
37
36
|
|
38
37
|
it "should support Rails' special extensions for times" do
|
39
|
-
Que
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should safely roll back in-process transactions when using Que.stop!" do
|
44
|
-
begin
|
45
|
-
class ARInterruptJob < BlockJob
|
46
|
-
def run
|
47
|
-
ActiveRecord::Base.transaction do
|
48
|
-
Que.execute "INSERT INTO que_jobs (job_id, job_class) VALUES (0, 'Que::Job')"
|
49
|
-
super
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
38
|
+
Que.mode = :async
|
39
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
53
40
|
|
54
|
-
|
55
|
-
|
56
|
-
$q1.pop
|
57
|
-
Que.stop!
|
41
|
+
Que::Job.queue :run_at => 1.minute.ago
|
42
|
+
DB[:que_jobs].get(:run_at).should be_within(3).of Time.now - 60
|
58
43
|
|
59
|
-
|
60
|
-
|
61
|
-
# Que.stop! can affect DB connections in an unpredictable fashion, so
|
62
|
-
# force a reconnection for the sake of the other specs.
|
63
|
-
ActiveRecord::Base.establish_connection(QUE_URL)
|
64
|
-
end
|
44
|
+
Que.wake_interval = 0.005.seconds
|
45
|
+
sleep_until { DB[:que_jobs].empty? }
|
65
46
|
end
|
66
47
|
end
|
67
48
|
end
|
@@ -7,6 +7,5 @@ QUE_ADAPTERS[:connection_pool] = Que.adapter
|
|
7
7
|
describe "Que using the ConnectionPool adapter" do
|
8
8
|
before { Que.adapter = QUE_ADAPTERS[:connection_pool] }
|
9
9
|
|
10
|
-
it_behaves_like "a Que adapter"
|
11
|
-
it_behaves_like "a multithreaded Que adapter"
|
10
|
+
it_behaves_like "a multi-threaded Que adapter"
|
12
11
|
end
|
data/spec/adapters/pg_spec.rb
CHANGED
@@ -2,4 +2,38 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Que using a bare PG connection" do
|
4
4
|
it_behaves_like "a Que adapter"
|
5
|
+
|
6
|
+
it "should synchronize access to that connection" do
|
7
|
+
lock = Que.adapter.lock
|
8
|
+
q1, q2 = Queue.new, Queue.new
|
9
|
+
|
10
|
+
thread1 = Thread.new do
|
11
|
+
Que.adapter.checkout do
|
12
|
+
q1.push nil
|
13
|
+
q2.pop
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
q1.pop
|
18
|
+
|
19
|
+
thread2 = Thread.new do
|
20
|
+
Que.adapter.checkout do
|
21
|
+
q1.push nil
|
22
|
+
q2.pop
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sleep_until { thread2.status == 'sleep' }
|
27
|
+
|
28
|
+
thread1.should be_alive
|
29
|
+
thread2.should be_alive
|
30
|
+
|
31
|
+
lock.send(:instance_variable_get, :@mon_owner).should == thread1
|
32
|
+
q2.push nil
|
33
|
+
q1.pop
|
34
|
+
lock.send(:instance_variable_get, :@mon_owner).should == thread2
|
35
|
+
q2.push nil
|
36
|
+
thread1.join
|
37
|
+
thread2.join
|
38
|
+
end
|
5
39
|
end
|
@@ -6,8 +6,7 @@ QUE_ADAPTERS[:sequel] = Que.adapter
|
|
6
6
|
describe "Que using the Sequel adapter" do
|
7
7
|
before { Que.adapter = QUE_ADAPTERS[:sequel] }
|
8
8
|
|
9
|
-
it_behaves_like "a Que adapter"
|
10
|
-
it_behaves_like "a multithreaded Que adapter"
|
9
|
+
it_behaves_like "a multi-threaded Que adapter"
|
11
10
|
|
12
11
|
it "should use the same connection that Sequel does" do
|
13
12
|
class SequelJob < Que::Job
|
@@ -23,33 +22,22 @@ describe "Que using the Sequel adapter" do
|
|
23
22
|
$pid1.should == $pid2
|
24
23
|
end
|
25
24
|
|
26
|
-
it "should
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
25
|
+
it "should wake up a Worker after queueing a job in async mode, waiting for a transaction to commit if necessary" do
|
26
|
+
Que.mode = :async
|
27
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
28
|
+
|
29
|
+
# Wakes a worker immediately when not in a transaction.
|
30
|
+
Que::Job.queue
|
31
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
|
36
32
|
|
37
|
-
|
38
|
-
Que.
|
39
|
-
|
40
|
-
Que.stop!
|
41
|
-
|
42
|
-
DB[:que_jobs].where(:job_id => 0).should be_empty
|
43
|
-
ensure
|
44
|
-
# Que.stop! can affect DB connections in an unpredictable fashion, and
|
45
|
-
# Sequel's built-in reconnection logic may not be able to recover them.
|
46
|
-
# So, force a reconnection for the sake of the other specs...
|
47
|
-
SEQUEL_ADAPTER_DB.disconnect
|
48
|
-
|
49
|
-
# ...and that's not even foolproof, because threads may have died with
|
50
|
-
# connections checked out.
|
51
|
-
SEQUEL_ADAPTER_DB.pool.allocated.each_value(&:close)
|
52
|
-
SEQUEL_ADAPTER_DB.pool.allocated.clear
|
33
|
+
SEQUEL_ADAPTER_DB.transaction do
|
34
|
+
Que::Job.queue
|
35
|
+
Que::Worker.workers.each { |worker| worker.should be_sleeping }
|
53
36
|
end
|
37
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) && DB[:que_jobs].empty? }
|
38
|
+
|
39
|
+
# Do nothing when queueing with a specific :run_at.
|
40
|
+
BlockJob.queue :run_at => Time.now
|
41
|
+
Que::Worker.workers.each { |worker| worker.should be_sleeping }
|
54
42
|
end
|
55
43
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,23 +2,30 @@ require 'que'
|
|
2
2
|
require 'uri'
|
3
3
|
require 'pg'
|
4
4
|
require 'json'
|
5
|
+
require 'logger'
|
5
6
|
|
6
7
|
Dir['./spec/support/**/*.rb'].sort.each &method(:require)
|
7
8
|
|
8
9
|
|
10
|
+
|
9
11
|
# Handy constants for initializing PG connections:
|
10
12
|
QUE_URL = ENV['DATABASE_URL'] || 'postgres://postgres:@localhost/que-test'
|
11
13
|
|
12
14
|
NEW_PG_CONNECTION = proc do
|
13
15
|
uri = URI.parse(QUE_URL)
|
14
|
-
PG::Connection.open :host => uri.host,
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
pg = PG::Connection.open :host => uri.host,
|
17
|
+
:user => uri.user,
|
18
|
+
:password => uri.password,
|
19
|
+
:port => uri.port || 5432,
|
20
|
+
:dbname => uri.path[1..-1]
|
21
|
+
|
22
|
+
# Avoid annoying NOTICE messages in specs.
|
23
|
+
pg.async_exec "SET client_min_messages TO 'warning'"
|
24
|
+
pg
|
19
25
|
end
|
20
26
|
|
21
27
|
|
28
|
+
|
22
29
|
# Adapters track which statements have been prepared for their connections,
|
23
30
|
# and if Que.connection= is called before each spec, we're constantly creating
|
24
31
|
# new adapters and losing that information, which is bad. So instead, we hang
|
@@ -31,59 +38,64 @@ Que.connection = NEW_PG_CONNECTION.call
|
|
31
38
|
QUE_ADAPTERS = {:pg => Que.adapter}
|
32
39
|
|
33
40
|
|
34
|
-
|
41
|
+
|
42
|
+
# We use Sequel to examine the database in specs.
|
35
43
|
require 'sequel'
|
36
44
|
DB = Sequel.connect(QUE_URL)
|
37
45
|
DB.drop_table? :que_jobs
|
38
46
|
DB.run Que::SQL[:create_table]
|
39
47
|
|
40
48
|
|
49
|
+
|
41
50
|
# Set up a dummy logger.
|
42
51
|
Que.logger = $logger = Object.new
|
52
|
+
$logger_mutex = Mutex.new # Protect against rare errors on Rubinius/JRuby.
|
43
53
|
|
44
54
|
def $logger.messages
|
45
55
|
@messages ||= []
|
46
56
|
end
|
47
57
|
|
48
58
|
def $logger.method_missing(m, message)
|
49
|
-
messages << message
|
59
|
+
$logger_mutex.synchronize { messages << message }
|
50
60
|
end
|
51
61
|
|
52
62
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
$logger.messages.clear
|
63
|
+
|
64
|
+
# Helper to display spec descriptions.
|
65
|
+
description_builder = -> hash do
|
66
|
+
if g = hash[:example_group]
|
67
|
+
"#{description_builder.call(g)} #{hash[:description_args].first}"
|
68
|
+
else
|
69
|
+
hash[:description_args].first
|
61
70
|
end
|
62
71
|
end
|
63
72
|
|
73
|
+
stdout = Logger.new(STDOUT)
|
64
74
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
75
|
+
RSpec.configure do |config|
|
76
|
+
config.around do |spec|
|
77
|
+
# Figure out which spec is about to run, for logging purposes.
|
78
|
+
data = example.metadata
|
79
|
+
desc = description_builder.call(data)
|
80
|
+
line = "rspec #{data[:file_path]}:#{data[:line_number]}"
|
70
81
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
else
|
75
|
-
hash[:description_args].first
|
76
|
-
end
|
77
|
-
end
|
82
|
+
# Optionally log to STDOUT which spec is about to run. This is noisy, but
|
83
|
+
# helpful in identifying hanging specs.
|
84
|
+
stdout.info "Running spec: #{desc} @ #{line}" if ENV['LOG_SPEC']
|
78
85
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
86
|
+
$logger.messages.clear
|
87
|
+
Que.adapter = QUE_ADAPTERS[:pg]
|
88
|
+
|
89
|
+
spec.run
|
90
|
+
|
91
|
+
DB[:que_jobs].delete
|
92
|
+
Que.mode = :off
|
93
|
+
Que.wake_interval = nil
|
94
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
85
95
|
|
86
|
-
|
96
|
+
# A bit of lint: make sure that no advisory locks are left open.
|
97
|
+
unless DB[:pg_locks].where(:locktype => 'advisory').empty?
|
98
|
+
stdout.info "Advisory lock left open: #{desc} @ #{line}"
|
87
99
|
end
|
88
100
|
end
|
89
101
|
end
|
@@ -1,8 +1,21 @@
|
|
1
1
|
shared_examples "a Que adapter" do
|
2
|
-
it "should
|
2
|
+
it "should be able to execute arbitrary SQL and return indifferent hashes" do
|
3
|
+
result = Que.execute("SELECT 1 AS one")
|
4
|
+
result.should == [{'one'=>'1'}]
|
5
|
+
result.first[:one].should == '1'
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be able to queue and work a job" do
|
9
|
+
Que::Job.queue
|
10
|
+
Que::Job.work.should be_an_instance_of Que::Job
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should yield the same Postgres connection for the duration of the block" do
|
3
14
|
Que.adapter.checkout do |conn|
|
4
|
-
conn.
|
5
|
-
|
15
|
+
conn.should be_a PG::Connection
|
16
|
+
pid1 = Que.execute "SELECT pg_backend_pid()"
|
17
|
+
pid2 = Que.execute "SELECT pg_backend_pid()"
|
18
|
+
pid1.should == pid2
|
6
19
|
end
|
7
20
|
end
|
8
21
|
|
data/spec/unit/helper_spec.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Que, 'helpers' do
|
4
|
-
it "should be able to run arbitrary SQL" do
|
5
|
-
Que.execute("SELECT 1 AS one").to_a.should == [{'one' => '1'}]
|
6
|
-
end
|
7
|
-
|
8
4
|
it "should be able to drop and create the jobs table" do
|
9
5
|
DB.table_exists?(:que_jobs).should be true
|
10
6
|
Que.drop!
|
11
7
|
DB.table_exists?(:que_jobs).should be false
|
12
|
-
Que.execute "SET client_min_messages TO 'warning'" # Avoid annoying NOTICE messages.
|
13
8
|
Que.create!
|
14
9
|
DB.table_exists?(:que_jobs).should be true
|
15
10
|
end
|
data/spec/unit/pool_spec.rb
CHANGED
@@ -2,8 +2,9 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Managing the Worker pool" do
|
4
4
|
it "should log mode changes" do
|
5
|
+
Que.mode = :sync
|
5
6
|
Que.mode = :off
|
6
|
-
$logger.messages.should == ["[Que] Set mode to :off"]
|
7
|
+
$logger.messages.should == ["[Que] Set mode to :sync", "[Que] Set mode to :off"]
|
7
8
|
end
|
8
9
|
|
9
10
|
it "Que.stop! should do nothing if there are no workers running" do
|
@@ -40,18 +41,51 @@ describe "Managing the Worker pool" do
|
|
40
41
|
describe "Que.mode = :async" do
|
41
42
|
it "should spin up 4 workers" do
|
42
43
|
Que.mode = :async
|
43
|
-
|
44
|
-
workers.
|
45
|
-
|
44
|
+
Que.worker_count.should be 4
|
45
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
46
|
+
|
47
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 4"] + ["[Que] No jobs available..."] * 4
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be done automatically when setting a worker count" do
|
51
|
+
Que.worker_count = 2
|
52
|
+
Que.mode.should == :async
|
53
|
+
Que.worker_count.should == 2
|
54
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
55
|
+
|
56
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 2"] + ["[Que] No jobs available..."] * 2
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not affect the number of workers if a worker_count has already been set" do
|
60
|
+
Que.worker_count = 1
|
61
|
+
Que.mode = :async
|
62
|
+
Que.worker_count.should be 1
|
63
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
64
|
+
|
65
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 1", "[Que] No jobs available..."]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "then Que.worker_count = 0 should set the mode to :off" do
|
69
|
+
Que.mode = :async
|
70
|
+
Que.worker_count.should be 4
|
71
|
+
|
72
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
73
|
+
|
74
|
+
Que.worker_count = 0
|
75
|
+
Que.worker_count.should == 0
|
76
|
+
Que.mode.should == :off
|
77
|
+
|
78
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 4"] + ["[Que] No jobs available..."] * 4 + ["[Que] Set mode to :off", "[Que] Set worker_count to 0"]
|
46
79
|
end
|
47
80
|
|
48
81
|
it "then Que.worker_count = 2 should gracefully decrease the number of workers" do
|
49
82
|
Que.mode = :async
|
50
83
|
workers = Que::Worker.workers.dup
|
51
84
|
workers.count.should be 4
|
85
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
52
86
|
|
53
87
|
Que.worker_count = 2
|
54
|
-
Que
|
88
|
+
Que.worker_count.should be 2
|
55
89
|
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
56
90
|
|
57
91
|
workers[0..1].should == Que::Worker.workers
|
@@ -59,6 +93,8 @@ describe "Managing the Worker pool" do
|
|
59
93
|
worker.should be_an_instance_of Que::Worker
|
60
94
|
worker.thread.status.should == false
|
61
95
|
end
|
96
|
+
|
97
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 4"] + ["[Que] No jobs available..."] * 4 + ["[Que] Set worker_count to 2"]
|
62
98
|
end
|
63
99
|
|
64
100
|
it "then Que.worker_count = 6 should gracefully increase the number of workers" do
|
@@ -66,11 +102,14 @@ describe "Managing the Worker pool" do
|
|
66
102
|
workers = Que::Worker.workers.dup
|
67
103
|
workers.count.should be 4
|
68
104
|
|
105
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
69
106
|
Que.worker_count = 6
|
70
|
-
Que
|
71
|
-
sleep_until { workers.all?(&:sleeping?) }
|
107
|
+
Que.worker_count.should be 6
|
108
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
72
109
|
|
73
110
|
workers.should == Que::Worker.workers[0..3]
|
111
|
+
|
112
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 4"] + ["[Que] No jobs available..."] * 4 + ["[Que] Set worker_count to 6"] + ["[Que] No jobs available..."] * 2
|
74
113
|
end
|
75
114
|
|
76
115
|
it "then Que.mode = :off should gracefully shut down workers" do
|
@@ -78,19 +117,22 @@ describe "Managing the Worker pool" do
|
|
78
117
|
workers = Que::Worker.workers.dup
|
79
118
|
workers.count.should be 4
|
80
119
|
|
120
|
+
sleep_until { Que::Worker.workers.all?(&:sleeping?) }
|
81
121
|
Que.mode = :off
|
82
|
-
Que
|
122
|
+
Que.worker_count.should be 0
|
83
123
|
|
84
124
|
workers.count.should be 4
|
85
125
|
workers.each { |worker| worker.thread.status.should be false }
|
126
|
+
|
127
|
+
$logger.messages.should == ["[Que] Set mode to :async", "[Que] Set worker_count to 4"] + ["[Que] No jobs available..."] * 4 + ["[Que] Set mode to :off", "[Que] Set worker_count to 0"]
|
86
128
|
end
|
87
129
|
|
88
|
-
it "then Que
|
130
|
+
it "then Que.wake! should wake up a single worker" do
|
89
131
|
Que.mode = :async
|
90
132
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
91
133
|
|
92
134
|
BlockJob.queue
|
93
|
-
Que
|
135
|
+
Que.wake!
|
94
136
|
|
95
137
|
$q1.pop
|
96
138
|
Que::Worker.workers.first.should be_working
|
@@ -102,7 +144,13 @@ describe "Managing the Worker pool" do
|
|
102
144
|
DB[:que_jobs].count.should be 0
|
103
145
|
end
|
104
146
|
|
105
|
-
it "then Que
|
147
|
+
it "then Que.wake! should be thread-safe" do
|
148
|
+
Que.mode = :async
|
149
|
+
threads = 4.times.map { Thread.new { 100.times { Que.wake! } } }
|
150
|
+
threads.each(&:join)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "then Que.wake_all! should wake up all workers" do
|
106
154
|
# This spec requires at least four connections.
|
107
155
|
Que.adapter = QUE_ADAPTERS[:connection_pool]
|
108
156
|
|
@@ -110,7 +158,7 @@ describe "Managing the Worker pool" do
|
|
110
158
|
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
111
159
|
|
112
160
|
4.times { BlockJob.queue }
|
113
|
-
Que
|
161
|
+
Que.wake_all!
|
114
162
|
4.times { $q1.pop }
|
115
163
|
|
116
164
|
Que::Worker.workers.each{ |worker| worker.should be_working }
|
@@ -120,17 +168,18 @@ describe "Managing the Worker pool" do
|
|
120
168
|
DB[:que_jobs].count.should be 0
|
121
169
|
end if QUE_ADAPTERS[:connection_pool]
|
122
170
|
|
123
|
-
it "
|
124
|
-
|
125
|
-
|
171
|
+
it "then Que.wake_all! should be thread-safe" do
|
172
|
+
Que.mode = :async
|
173
|
+
threads = 4.times.map { Thread.new { 100.times { Que.wake_all! } } }
|
174
|
+
threads.each(&:join)
|
175
|
+
end
|
126
176
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
177
|
+
it "should wake a worker every Que.wake_interval seconds" do
|
178
|
+
Que.mode = :async
|
179
|
+
sleep_until { Que::Worker.workers.all? &:sleeping? }
|
180
|
+
Que.wake_interval = 0.01 # 10 ms
|
181
|
+
Que::Job.queue
|
182
|
+
sleep_until { DB[:que_jobs].count == 0 }
|
134
183
|
end
|
135
184
|
|
136
185
|
it "then Que.stop! should interrupt all running jobs" do
|
@@ -145,7 +194,10 @@ describe "Managing the Worker pool" do
|
|
145
194
|
$q1.pop
|
146
195
|
Que.stop!
|
147
196
|
ensure
|
148
|
-
|
197
|
+
if pg
|
198
|
+
# Closing the connection can raise a NullPointerException on JRuby.
|
199
|
+
pg.close rescue nil
|
200
|
+
end
|
149
201
|
end
|
150
202
|
end
|
151
203
|
end
|