queueing_rabbit 0.5.0 → 0.6.0

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