que 0.3.0 → 0.4.0
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.
- 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
|