que 0.3.0 → 0.4.0

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