queueing_rabbit 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -89,6 +89,8 @@ module QueueingRabbit
89
89
 
90
90
  def close
91
91
  @connection.close
92
+ yield if block_given?
93
+ @continue_worker_loop = false
92
94
  end
93
95
 
94
96
  def open?
@@ -97,7 +99,9 @@ module QueueingRabbit
97
99
 
98
100
  def begin_worker_loop
99
101
  yield
100
- @connection.reader_loop.join
102
+ @continue_worker_loop = true
103
+ # We may need to add signal handling here
104
+ sleep 2 while @continue_worker_loop
101
105
  end
102
106
 
103
107
  private
@@ -8,10 +8,6 @@ namespace :queueing_rabbit do
8
8
  task :work => :setup do
9
9
  require 'queueing_rabbit'
10
10
 
11
- if ENV['PIDFILE'] && File.exists?(ENV['PIDFILE'])
12
- abort "PID file already exists. Is the worker running?"
13
- end
14
-
15
11
  jobs = (ENV['JOBS'] || ENV['JOB']).to_s.split(',')
16
12
 
17
13
  begin
@@ -28,10 +24,11 @@ namespace :queueing_rabbit do
28
24
  Process.daemon(true)
29
25
  end
30
26
 
31
- worker.use_pidfile(ENV['PIDFILE']) if ENV['PIDFILE']
32
-
33
- worker.info "starting a new queueing_rabbit worker #{worker}"
34
-
35
- worker.work!
27
+ begin
28
+ worker.use_pidfile(ENV['PIDFILE']) if ENV['PIDFILE']
29
+ worker.work!
30
+ rescue QueueingRabbit::Worker::WorkerError => e
31
+ abort e.message
32
+ end
36
33
  end
37
34
  end
@@ -1,3 +1,3 @@
1
1
  module QueueingRabbit
2
- VERSION = "0.3.5"
2
+ VERSION = "0.3.6"
3
3
  end
@@ -1,5 +1,8 @@
1
1
  module QueueingRabbit
2
2
  class Worker
3
+
4
+ class WorkerError < RuntimeError; end
5
+
3
6
  include QueueingRabbit::Logging
4
7
 
5
8
  attr_accessor :jobs
@@ -13,28 +16,37 @@ module QueueingRabbit
13
16
  end
14
17
 
15
18
  def work
16
- conn = QueueingRabbit.connection
17
- trap_signals(conn)
19
+ trap_signals
18
20
 
19
21
  QueueingRabbit.trigger_event(:worker_ready)
20
22
 
21
- jobs.each { |job| run_job(conn, job) }
23
+ jobs.each { |job| run_job(QueueingRabbit.connection, job) }
22
24
 
23
25
  QueueingRabbit.trigger_event(:consuming_started)
24
26
  end
25
27
 
26
28
  def work!
27
- QueueingRabbit.begin_worker_loop do
28
- work
29
- end
29
+ info "starting a new queueing_rabbit worker #{self}"
30
+
31
+ QueueingRabbit.begin_worker_loop { work }
30
32
  end
31
33
 
32
34
  def use_pidfile(filename)
33
- File.open(@pidfile = filename, 'w') { |f| f << pid }
35
+ @pidfile = filename
36
+ cleanup_pidfile
37
+ File.open(@pidfile, 'w') { |f| f << pid }
34
38
  end
35
39
 
36
40
  def remove_pidfile
37
- File.delete(@pidfile) if @pidfile && File.exists?(@pidfile)
41
+ File.delete(@pidfile) if pidfile_exists?
42
+ end
43
+
44
+ def read_pidfile
45
+ File.read(@pidfile).to_i if pidfile_exists?
46
+ end
47
+
48
+ def pidfile_exists?
49
+ @pidfile && File.exists?(@pidfile)
38
50
  end
39
51
 
40
52
  def pid
@@ -45,6 +57,16 @@ module QueueingRabbit
45
57
  "PID=#{pid}, JOBS=#{jobs.join(',')}"
46
58
  end
47
59
 
60
+ def stop
61
+ return unless QueueingRabbit.connection
62
+
63
+ QueueingRabbit.connection.close {
64
+ info "gracefully shutting down the worker #{self}"
65
+ remove_pidfile
66
+ QueueingRabbit.trigger_event(:consuming_done)
67
+ }
68
+ end
69
+
48
70
  private
49
71
 
50
72
  def validate_jobs
@@ -80,7 +102,7 @@ module QueueingRabbit
80
102
  elsif job <= QueueingRabbit::AbstractJob
81
103
  job.new(payload, metadata).perform
82
104
  else
83
- error "do not know how to perform job #{job}"
105
+ error "don't know how to perform job #{job}"
84
106
  end
85
107
  end
86
108
 
@@ -89,16 +111,20 @@ module QueueingRabbit
89
111
  $stderr.sync = true
90
112
  end
91
113
 
92
- def trap_signals(connection)
93
- handler = Proc.new do
94
- connection.close {
95
- QueueingRabbit.trigger_event(:consuming_done)
96
- remove_pidfile
97
- }
98
- end
114
+ def trap_signals
115
+ Signal.trap("TERM") { stop }
116
+ Signal.trap("INT") { stop }
117
+ end
99
118
 
100
- Signal.trap("TERM", &handler)
101
- Signal.trap("INT", &handler)
119
+ def cleanup_pidfile
120
+ return unless pid_in_file = read_pidfile
121
+ Process.getpgid(pid_in_file)
122
+ fatal "failed to use the pidfile #{@pidfile}. It is already " \
123
+ "in use by a process with pid=#{pid_in_file}."
124
+ raise WorkerError.new('The pidfile is already in use.')
125
+ rescue Errno::ESRCH
126
+ info "found abandoned pidfile: #{@pidfile}. Can be safely overwritten."
102
127
  end
128
+
103
129
  end
104
130
  end
@@ -126,5 +126,23 @@ describe QueueingRabbit::Client::Bunny do
126
126
  end
127
127
  end
128
128
 
129
+ describe '#close' do
130
+
131
+ before do
132
+ connection.should_receive(:close)
133
+ end
134
+
135
+ it 'closes the connection and yields a block if given' do
136
+ expect { |b| client.close(&b) }.to yield_control
137
+ end
138
+
139
+ it 'discontinues the worker loop' do
140
+ expect { |b| client.close(&b) }.
141
+ to change{client.instance_variable_get(:@continue_worker_loop)}.
142
+ to(false)
143
+ end
144
+
145
+ end
146
+
129
147
  end
130
148
  end
@@ -69,7 +69,7 @@ describe QueueingRabbit::Worker do
69
69
 
70
70
  describe '#work' do
71
71
  before do
72
- QueueingRabbit.should_receive(:connection).and_return(connection)
72
+ QueueingRabbit.stub(:connection).and_return(connection)
73
73
  [class_based_job, instance_based_job].each do |job|
74
74
  QueueingRabbit.should_receive(:follow_job_requirements).
75
75
  with(job).
@@ -106,13 +106,45 @@ describe QueueingRabbit::Worker do
106
106
  let(:file_name) { mock }
107
107
  let(:file) { mock }
108
108
 
109
- before do
110
- File.should_receive(:open).with(file_name, 'w').and_yield(file)
111
- file.should_receive(:<<).with(Process.pid)
109
+ context 'given pidfile is already in use' do
110
+
111
+ it 'raises a worker error' do
112
+ File.stub(:exists?).with(file_name).and_return(true)
113
+ File.should_receive(:read).with(file_name).and_return('123')
114
+ Process.should_receive(:getpgid).with(123).and_return(123)
115
+ expect { subject.use_pidfile(file_name) }.
116
+ to raise_error(QueueingRabbit::Worker::WorkerError)
117
+ end
118
+
112
119
  end
113
120
 
114
- it 'writes pid to a file' do
115
- subject.use_pidfile(file_name)
121
+ context 'given pidfile is not in use' do
122
+
123
+ before do
124
+ File.should_receive(:open).with(file_name, 'w').and_yield(file)
125
+ file.should_receive(:<<).with(Process.pid)
126
+ end
127
+
128
+ context 'there is an abandoned pidfile' do
129
+
130
+ it 'removes the abandoned pidfile and writes pid to a file' do
131
+ File.stub(:exists?).with(file_name).and_return(true)
132
+ File.should_receive(:read).with(file_name).and_return('123')
133
+ Process.should_receive(:getpgid).with(123).and_raise(Errno::ESRCH)
134
+ subject.use_pidfile(file_name)
135
+ end
136
+
137
+ end
138
+
139
+ context 'new pidfile' do
140
+
141
+ it 'creates a pidfile' do
142
+ File.stub(:exists?).with(file_name).and_return(false)
143
+ subject.use_pidfile(file_name)
144
+ end
145
+
146
+ end
147
+
116
148
  end
117
149
  end
118
150
 
@@ -133,5 +165,25 @@ describe QueueingRabbit::Worker do
133
165
  describe "#pid" do
134
166
  its(:pid) { should == Process.pid }
135
167
  end
168
+
169
+ describe '#stop' do
170
+
171
+ let(:file_name) { mock }
172
+
173
+ before do
174
+ subject.instance_variable_set(:@pidfile, file_name)
175
+ QueueingRabbit.stub(:connection).and_return(connection)
176
+ end
177
+
178
+ it 'closes the connection, removes the pidfile and reports the event' do
179
+ connection.should_receive(:close).and_yield
180
+ File.stub(:exists?).with(file_name).and_return(true)
181
+ File.should_receive(:delete).with(file_name)
182
+ QueueingRabbit.should_receive(:trigger_event).with(:consuming_done)
183
+ subject.stop
184
+ end
185
+
186
+ end
187
+
136
188
  end
137
189
  end
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.3.5
4
+ version: 0.3.6
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-01-08 00:00:00.000000000 Z
12
+ date: 2014-01-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amqp
@@ -172,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
172
172
  version: '0'
173
173
  segments:
174
174
  - 0
175
- hash: -2062286161310013900
175
+ hash: -1747676710658043461
176
176
  required_rubygems_version: !ruby/object:Gem::Requirement
177
177
  none: false
178
178
  requirements:
@@ -181,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
181
  version: '0'
182
182
  segments:
183
183
  - 0
184
- hash: -2062286161310013900
184
+ hash: -1747676710658043461
185
185
  requirements: []
186
186
  rubyforge_project:
187
187
  rubygems_version: 1.8.23