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.
@@ -1,3 +1,3 @@
1
1
  module Que
2
- Version = '0.3.0'
2
+ Version = '0.4.0'
3
3
  end
@@ -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; let it.
89
+ # This process is shutting down - let it.
93
90
  ensure
94
91
  @state = :stopped
95
92
  end
96
93
 
97
- # Defaults for the Worker pool.
98
- @worker_count = 0
99
- @sleep_period = 5
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, :sleep_period, :worker_count
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
- wrangler # Make sure the wrangler thread is initialized.
108
- self.worker_count = 4
109
- else
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
- if count > workers.count
123
- (count - workers.count).times { workers << new }
124
- elsif count < workers.count
125
- workers.pop(workers.count - count).each(&:stop).each(&:wait_until_stopped)
126
- end
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 sleep_period=(period)
130
- @sleep_period = period
131
- wrangler.wakeup if period
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 wrangler
158
- @wrangler ||= Thread.new { loop { sleep(*sleep_period); wake! } }
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::Job.queue :run_at => 1.minute.from_now
40
- DB[:que_jobs].get(:run_at).should be_within(3).of Time.now + 60
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
- ARInterruptJob.queue
55
- Que.mode = :async
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
- DB[:que_jobs].where(:job_id => 0).should be_empty
60
- ensure
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
@@ -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 safely roll back in-process transactions when using Que.stop!" do
27
- begin
28
- class SequelInterruptJob < BlockJob
29
- def run
30
- SEQUEL_ADAPTER_DB.transaction do
31
- Que.execute "INSERT INTO que_jobs (job_id, job_class) VALUES (0, 'Que::Job')"
32
- super
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
- SequelInterruptJob.queue
38
- Que.mode = :async
39
- $q1.pop
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
@@ -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
- :user => uri.user,
16
- :password => uri.password,
17
- :port => uri.port || 5432,
18
- :dbname => uri.path[1..-1]
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
- # We use Sequel to introspect the database in specs.
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
- # Clean up between specs.
54
- RSpec.configure do |config|
55
- config.before do
56
- DB[:que_jobs].delete
57
- Que.adapter = QUE_ADAPTERS[:pg]
58
- Que.mode = :off
59
- Que.sleep_period = nil
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
- # Optionally log to STDOUT which spec is running at the moment. This is loud,
66
- # but helpful in tracking down what spec is hanging, if any.
67
- if ENV['LOG_SPEC']
68
- require 'logger'
69
- logger = Logger.new(STDOUT)
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
- description_builder = -> hash do
72
- if g = hash[:example_group]
73
- "#{description_builder.call(g)} #{hash[:description_args].first}"
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
- RSpec.configure do |config|
80
- config.around do |example|
81
- data = example.metadata
82
- desc = description_builder.call(data)
83
- line = "rspec #{data[:file_path]}:#{data[:line_number]}"
84
- logger.info "Running spec: #{desc} @ #{line}"
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
- example.run
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 allow a Postgres connection to be checked out" do
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.async_exec("SELECT 1 AS one").to_a.should == [{'one' => '1'}]
5
- conn.server_version.should > 0
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
 
@@ -1,4 +1,6 @@
1
- shared_examples "a multithreaded Que adapter" do
1
+ shared_examples "a multi-threaded Que adapter" do
2
+ it_behaves_like "a Que adapter"
3
+
2
4
  it "should allow multiple threads to check out their own connections" do
3
5
  one = nil
4
6
  two = nil
@@ -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
@@ -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
- workers = Que::Worker.workers
44
- workers.count.should be 4
45
- sleep_until { workers.all?(&:sleeping?) }
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::Worker.workers.count.should be 2
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::Worker.workers.count.should be 6
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::Worker.workers.length.should be 0
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::Worker.wake! should wake up a single worker" do
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::Worker.wake!
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::Worker.wake_all! should wake up all workers" do
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::Worker.wake_all!
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 "should poke a worker every Que.sleep_period seconds" do
124
- begin
125
- Que.mode = :async
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
- sleep_until { Que::Worker.workers.all? &:sleeping? }
128
- Que.sleep_period = 0.01 # 10 ms
129
- Que::Job.queue
130
- sleep_until { DB[:que_jobs].count == 0 }
131
- ensure
132
- Que.sleep_period = nil
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
- pg.close if pg
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