queueing_rabbit 0.5.0 → 0.6.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.
@@ -78,7 +78,7 @@ module QueueingRabbit
78
78
  def close
79
79
  info "closing AMQP broker connection..."
80
80
 
81
- connection.close do
81
+ connection.disconnect do
82
82
  yield if block_given?
83
83
 
84
84
  EM.stop if EM.reactor_running?
@@ -142,6 +142,14 @@ module QueueingRabbit
142
142
  EM.next_tick(&block)
143
143
  end
144
144
 
145
+ def wait_while_for(proc, period, _ = nil, &block)
146
+ if proc.call
147
+ EM.add_timer(period, &block)
148
+ else
149
+ block.call
150
+ end
151
+ end
152
+
145
153
  def begin_worker_loop
146
154
  EM.run do
147
155
  yield if block_given?
@@ -15,7 +15,11 @@ module QueueingRabbit
15
15
  end
16
16
 
17
17
  def ack
18
- @channel.ack(@delivery_info.delivery_tag, false)
18
+ @channel.ack(delivery_tag, false)
19
+ end
20
+
21
+ def delivery_tag
22
+ @delivery_info.delivery_tag
19
23
  end
20
24
 
21
25
  def headers
@@ -117,6 +121,17 @@ module QueueingRabbit
117
121
  end
118
122
  end
119
123
 
124
+ def wait_while_for(proc, seconds, interval = 0.5)
125
+ end_time = Time.now.to_i + seconds
126
+
127
+ while Time.now.to_i < end_time do
128
+ return unless proc.call
129
+ sleep interval
130
+ end
131
+
132
+ yield
133
+ end
134
+
120
135
  private
121
136
 
122
137
  def initialize(connection)
@@ -16,6 +16,10 @@ module QueueingRabbit
16
16
  @heartbeat ||= 10
17
17
  end
18
18
 
19
+ def jobs_wait_timeout
20
+ @jobs_wait_timeout ||= 5
21
+ end
22
+
19
23
  def default_client
20
24
  QueueingRabbit::Client::Bunny
21
25
  end
@@ -1,3 +1,3 @@
1
1
  module QueueingRabbit
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -1,3 +1,5 @@
1
+ require 'monitor'
2
+
1
3
  module QueueingRabbit
2
4
  class Worker
3
5
 
@@ -10,13 +12,44 @@ module QueueingRabbit
10
12
  def initialize(*jobs)
11
13
  self.jobs = jobs.map { |job| job.to_s.strip }
12
14
 
15
+ @messages_lock = Monitor.new
16
+ @messages = {}
17
+ @channels = []
18
+
13
19
  sync_stdio
14
20
  validate_jobs
15
21
  constantize_jobs
16
22
  end
17
23
 
24
+ def checked_messages_count
25
+ @messages_lock.synchronize do
26
+ @messages.count
27
+ end
28
+ end
29
+
30
+ def checkin_message(delivery_tag)
31
+ return unless @working
32
+
33
+ @messages_lock.synchronize do
34
+ @messages[delivery_tag] = true
35
+ end
36
+ end
37
+
38
+ def checkout_message(delivery_tag)
39
+ @messages_lock.synchronize do
40
+ @messages.delete(delivery_tag)
41
+ end
42
+ end
43
+
44
+ def working?
45
+ @working
46
+ end
47
+
18
48
  def work
19
- trap_signals
49
+ return if working?
50
+
51
+ @working = true
52
+ @channels = []
20
53
 
21
54
  QueueingRabbit.trigger_event(:worker_ready)
22
55
 
@@ -26,6 +59,10 @@ module QueueingRabbit
26
59
  end
27
60
 
28
61
  def work!
62
+ return if working?
63
+
64
+ trap_signals
65
+
29
66
  info "starting a new queueing_rabbit worker #{self}"
30
67
 
31
68
  QueueingRabbit.begin_worker_loop { work }
@@ -59,10 +96,12 @@ module QueueingRabbit
59
96
 
60
97
  def stop(connection = QueueingRabbit.connection)
61
98
  connection.next_tick do
62
- connection.close do
63
- info "gracefully shutting down the worker #{self}"
64
- remove_pidfile
65
- QueueingRabbit.trigger_event(:consuming_done)
99
+ @working = false
100
+ close_channels do
101
+ connection.close do
102
+ info "gracefully shutting down the worker #{self}"
103
+ remove_pidfile
104
+ end
66
105
  end
67
106
  end
68
107
  end
@@ -85,6 +124,15 @@ module QueueingRabbit
85
124
 
86
125
  private
87
126
 
127
+ def close_channels(connection = QueueingRabbit.connection)
128
+ connection.wait_while_for(Proc.new { checked_messages_count > 0 },
129
+ QueueingRabbit.jobs_wait_timeout) do
130
+ @channels.each(&:close)
131
+ QueueingRabbit.trigger_event(:consuming_done)
132
+ yield
133
+ end
134
+ end
135
+
88
136
  def validate_jobs
89
137
  if jobs.nil? || jobs.empty?
90
138
  fatal "no jobs specified to work on."
@@ -104,9 +152,13 @@ module QueueingRabbit
104
152
  end
105
153
 
106
154
  def run_job(conn, job)
107
- QueueingRabbit.follow_job_requirements(job) do |_, _, queue|
155
+ QueueingRabbit.follow_job_requirements(job) do |ch, _, queue|
156
+ @channels << ch
108
157
  conn.listen_queue(queue, job.listening_options) do |payload, metadata|
109
- invoke_job(job, payload, metadata)
158
+ if checkin_message(metadata.object_id)
159
+ invoke_job(job, payload, metadata)
160
+ checkout_message(metadata.object_id)
161
+ end
110
162
  end
111
163
  end
112
164
  end
@@ -90,23 +90,27 @@ module QueueingRabbit
90
90
  end
91
91
 
92
92
  def follow_job_requirements(job)
93
+ ret = nil
93
94
  follow_bus_requirements(job) do |ch, ex|
94
95
  conn.define_queue(ch, job.queue_name, job.queue_options) do |q|
95
96
  if job.bind_queue?
96
97
  job.binding_declarations.each { |o| conn.bind_queue(q, ex, o) }
97
98
  end
98
99
 
99
- yield ch, ex, q
100
+ ret = yield ch, ex, q
100
101
  end
101
102
  end
103
+ ret
102
104
  end
103
105
 
104
106
  def follow_bus_requirements(bus)
107
+ ret = nil
105
108
  conn.open_channel(bus.channel_options) do |ch, _|
106
109
  conn.define_exchange(ch, bus.exchange_name, bus.exchange_options) do |ex|
107
- yield ch, ex
110
+ ret = yield ch, ex
108
111
  end
109
112
  end
113
+ ret
110
114
  end
111
115
 
112
116
  def queue_size(job)
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Terminating a worker' do
4
+
5
+ include_context 'Auto-disconnect'
6
+ include_context 'StringIO logger'
7
+
8
+ let(:queue) { Queue.new }
9
+ let(:job_name) { 'SleepAndAckJob' }
10
+ let(:job) {
11
+ Class.new(QueueingRabbit::AbstractJob) {
12
+ queue 'sleep_and_ack'
13
+ listen :ack => true
14
+
15
+ def self.complete!
16
+ @complete = true
17
+ end
18
+
19
+ def self.complete?
20
+ !!@complete
21
+ end
22
+
23
+ def perform
24
+ sleep 3
25
+ self.class.complete!
26
+ acknowledge
27
+ end
28
+ }
29
+ }
30
+
31
+ before do
32
+ QueueingRabbit.purge_queue(job)
33
+ stub_const(job_name, job)
34
+ end
35
+
36
+ it "waits for currently running consumers" do
37
+ worker = QueueingRabbit::Worker.new(job_name)
38
+ worker.work
39
+ job.enqueue('')
40
+ sleep 1
41
+ worker.stop
42
+ expect(job).to be_complete
43
+ end
44
+
45
+ end
@@ -139,7 +139,7 @@ describe QueueingRabbit::Client::AMQP do
139
139
  describe '#close' do
140
140
  before do
141
141
  subject.should_receive(:info)
142
- connection.should_receive(:close).and_yield
142
+ connection.should_receive(:disconnect).and_yield
143
143
  EM.should_receive(:stop)
144
144
  end
145
145
 
@@ -197,6 +197,7 @@ describe QueueingRabbit::Worker do
197
197
 
198
198
  it 'closes the connection, removes the pidfile and reports the event' do
199
199
  connection.should_receive(:next_tick).and_yield
200
+ connection.should_receive(:wait_while_for).and_yield
200
201
  connection.should_receive(:close).and_yield
201
202
  File.stub(:exists?).with(file_name).and_return(true)
202
203
  File.should_receive(:delete).with(file_name)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queueing_rabbit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-11 00:00:00.000000000 Z
12
+ date: 2014-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amqp
@@ -136,6 +136,7 @@ files:
136
136
  - spec/integration/synchronous_publishing_and_consuming_spec.rb
137
137
  - spec/integration/synchronous_publishing_and_threaded_consuming_spec.rb
138
138
  - spec/integration/synchronous_publishing_spec.rb
139
+ - spec/integration/worker_termination_spec.rb
139
140
  - spec/spec_helper.rb
140
141
  - spec/support/shared_contexts.rb
141
142
  - spec/unit/queueing_rabbit/buses/abstract_bus_spec.rb
@@ -171,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
171
172
  version: '0'
172
173
  segments:
173
174
  - 0
174
- hash: 2464755371986051547
175
+ hash: 657465743539399174
175
176
  required_rubygems_version: !ruby/object:Gem::Requirement
176
177
  none: false
177
178
  requirements:
@@ -180,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
181
  version: '0'
181
182
  segments:
182
183
  - 0
183
- hash: 2464755371986051547
184
+ hash: 657465743539399174
184
185
  requirements: []
185
186
  rubyforge_project:
186
187
  rubygems_version: 1.8.23
@@ -205,6 +206,7 @@ test_files:
205
206
  - spec/integration/synchronous_publishing_and_consuming_spec.rb
206
207
  - spec/integration/synchronous_publishing_and_threaded_consuming_spec.rb
207
208
  - spec/integration/synchronous_publishing_spec.rb
209
+ - spec/integration/worker_termination_spec.rb
208
210
  - spec/spec_helper.rb
209
211
  - spec/support/shared_contexts.rb
210
212
  - spec/unit/queueing_rabbit/buses/abstract_bus_spec.rb