creeper 0.0.5 → 1.0.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.
- data/Gemfile +2 -0
- data/LICENSE +1 -1
- data/README.md +16 -89
- data/Rakefile +0 -4
- data/creeper.gemspec +7 -8
- data/lib/creeper.rb +332 -224
- data/lib/creeper/celluloid_ext.rb +42 -0
- data/lib/creeper/creep.rb +10 -16
- data/lib/creeper/logger.rb +37 -0
- data/lib/creeper/version.rb +1 -1
- data/lib/creeper/worker.rb +260 -102
- metadata +43 -13
- data/lib/creeper/session.rb +0 -141
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,102 +1,29 @@
|
|
1
1
|
# Creeper
|
2
2
|
|
3
|
-
|
3
|
+
TODO: Write a gem description
|
4
4
|
|
5
|
-
|
5
|
+
## Installation
|
6
6
|
|
7
|
-
|
8
|
-
===================================
|
7
|
+
Add this line to your application's Gemfile:
|
9
8
|
|
10
|
-
|
9
|
+
gem 'creeper'
|
11
10
|
|
12
|
-
|
11
|
+
And then execute:
|
13
12
|
|
14
|
-
|
15
|
-
Creeper.work([<job>, ...], <runner_count>)
|
16
|
-
```
|
13
|
+
$ bundle
|
17
14
|
|
18
|
-
|
15
|
+
Or install it yourself as:
|
19
16
|
|
20
|
-
|
21
|
-
-------------
|
17
|
+
$ gem install creeper
|
22
18
|
|
23
|
-
|
19
|
+
## Usage
|
24
20
|
|
25
|
-
|
26
|
-
require 'creeper'
|
21
|
+
TODO: Write usage instructions here
|
27
22
|
|
28
|
-
|
29
|
-
Creeper.enqueue('post.cleanup.all')
|
30
|
-
Creeper.enqueue('post.cleanup', :id => post.id)
|
31
|
-
```
|
23
|
+
## Contributing
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
require 'creeper'
|
40
|
-
include Creeper::Creep
|
41
|
-
|
42
|
-
job 'email.send' do |args|
|
43
|
-
Pony.send(:to => args['to'], :subject => "Hello there")
|
44
|
-
end
|
45
|
-
|
46
|
-
job 'post.cleanup.all' do |args|
|
47
|
-
Post.all.each do |post|
|
48
|
-
enqueue('post.cleanup', :id => post.id)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
job 'post.cleanup' do |args|
|
53
|
-
Post.find(args['id']).cleanup
|
54
|
-
end
|
55
|
-
|
56
|
-
# All of these Creeper.work calls are equivalent:
|
57
|
-
|
58
|
-
Creeper.work(:all, 3) # work all jobs, 3 threads
|
59
|
-
Creeper.work(nil, 3) # same as previous line
|
60
|
-
Creeper.work([ 'email.send', 'post.cleanup.all', 'post.cleanup' ], 3) # same as previous line
|
61
|
-
|
62
|
-
# Here we work just one job:
|
63
|
-
Creeper.work('email.send', 5) # work 'email.send', 5 threads
|
64
|
-
```
|
65
|
-
|
66
|
-
Running
|
67
|
-
-------
|
68
|
-
|
69
|
-
First, make sure you have Beanstalkd installed and running:
|
70
|
-
|
71
|
-
```bash
|
72
|
-
$ sudo brew install beanstalkd
|
73
|
-
$ beanstalkd
|
74
|
-
```
|
75
|
-
|
76
|
-
Creeper:
|
77
|
-
|
78
|
-
```bash
|
79
|
-
$ sudo gem install creeper
|
80
|
-
```
|
81
|
-
|
82
|
-
Error Handling
|
83
|
-
-------------
|
84
|
-
|
85
|
-
If you include an `error` block in your jobs definition, that block will be invoked when a worker encounters an error. You might use this to report errors to an external monitoring service:
|
86
|
-
|
87
|
-
```ruby
|
88
|
-
error do |e, job, args|
|
89
|
-
Exceptional.handle(e)
|
90
|
-
end
|
91
|
-
```
|
92
|
-
|
93
|
-
Before filter
|
94
|
-
-------------
|
95
|
-
|
96
|
-
If you wish to run a block of code prior to any job:
|
97
|
-
|
98
|
-
```ruby
|
99
|
-
before do |job|
|
100
|
-
puts "About to work #{job}"
|
101
|
-
end
|
102
|
-
```
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
CHANGED
data/creeper.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
require File.expand_path('../lib/creeper/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = ["
|
6
|
-
gem.email = ["lyondhill@gmail.com"]
|
7
|
-
gem.description = %q{
|
8
|
-
gem.summary = %q{A better solution for io bound jobs, same as stalker in functionality but more
|
9
|
-
gem.homepage = "https://github.com/
|
5
|
+
gem.authors = ["Lyon Hill", "Andrew Bennett"]
|
6
|
+
gem.email = ["lyondhill@gmail.com", "potatosaladx@gmail.com"]
|
7
|
+
gem.description = %q{Creeper is an evented version of Stalker}
|
8
|
+
gem.summary = %q{A better solution for io bound jobs, same as stalker in functionality but more evented}
|
9
|
+
gem.homepage = "https://github.com/potatosalad/creeper"
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -18,10 +18,9 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.add_development_dependency 'pry'
|
19
19
|
gem.add_development_dependency 'rake'
|
20
20
|
gem.add_development_dependency 'rspec'
|
21
|
-
# gem.add_development_dependency 'stalker'
|
22
21
|
|
23
22
|
gem.add_dependency 'beanstalk-client'
|
23
|
+
gem.add_dependency 'celluloid'
|
24
|
+
gem.add_dependency 'em-jack'
|
24
25
|
gem.add_dependency 'kgio'
|
25
|
-
|
26
26
|
end
|
27
|
-
|
data/lib/creeper.rb
CHANGED
@@ -1,297 +1,405 @@
|
|
1
1
|
require 'beanstalk-client'
|
2
|
-
|
3
2
|
require 'json'
|
4
|
-
require 'uri'
|
5
|
-
require 'timeout'
|
6
|
-
|
7
|
-
require 'fcntl'
|
8
|
-
require 'etc'
|
9
|
-
require 'stringio'
|
10
|
-
require 'kgio'
|
11
|
-
|
12
3
|
require 'logger'
|
4
|
+
require 'thread'
|
5
|
+
require 'timeout'
|
6
|
+
require 'uri'
|
13
7
|
|
14
8
|
require 'creeper/version'
|
15
|
-
require 'creeper/creep'
|
16
|
-
require 'creeper/session'
|
17
|
-
require 'creeper/worker'
|
18
9
|
|
19
10
|
module Creeper
|
20
11
|
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
class BadURL < RuntimeError; end
|
13
|
+
|
14
|
+
HANDLERS = {
|
15
|
+
named: {},
|
16
|
+
before_each: [],
|
17
|
+
before_named: {},
|
18
|
+
after_each: [],
|
19
|
+
after_named: {},
|
20
|
+
error_each: [],
|
21
|
+
error_named: {},
|
22
|
+
finalizers: []
|
23
|
+
}
|
24
24
|
|
25
|
-
|
25
|
+
WORKERS = {}
|
26
26
|
|
27
|
-
|
28
|
-
SIG_QUEUE = []
|
27
|
+
## default configuration ##
|
29
28
|
|
30
|
-
|
31
|
-
|
29
|
+
@beanstalk_url = ENV['BEANSTALK_URL'] || 'beanstalk://127.0.0.1/'
|
30
|
+
@logger = ::Logger.new($stderr)
|
31
|
+
@patience_soft = 60
|
32
|
+
@patience_hard = 30
|
33
|
+
@pool_size = 2
|
34
|
+
@retry_count = 3
|
35
|
+
@reserve_timeout = 1
|
32
36
|
|
33
|
-
|
34
|
-
:argv => ARGV.map { |arg| arg.dup },
|
35
|
-
0 => $0.dup,
|
36
|
-
}
|
37
|
-
START_CTX[:cwd] = begin
|
38
|
-
a = File.stat(pwd = ENV['PWD'])
|
39
|
-
b = File.stat(Dir.pwd)
|
40
|
-
a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
|
41
|
-
rescue
|
42
|
-
Dir.pwd
|
43
|
-
end
|
37
|
+
@lock = Mutex.new
|
44
38
|
|
45
|
-
|
46
|
-
attr_accessor :job_file, :jobs, :patience, :ready_pipe, :runner_count, :soft_quit, :timeout
|
39
|
+
##
|
47
40
|
|
48
|
-
|
49
|
-
extend Creeper::Creep
|
41
|
+
class << self
|
50
42
|
|
51
|
-
|
43
|
+
## configuration ##
|
52
44
|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
45
|
+
attr_reader :lock
|
46
|
+
attr_accessor :beanstalk_url, :logger, :patience_soft, :patience_hard, :pool_size, :reserve_timeout, :retry_count
|
56
47
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
48
|
+
def worker_pool
|
49
|
+
lock.synchronize do
|
50
|
+
@worker_pool
|
51
|
+
end
|
52
|
+
end
|
63
53
|
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
def worker_pool=(worker_pool)
|
55
|
+
lock.synchronize do
|
56
|
+
@worker_pool = worker_pool
|
57
|
+
end
|
58
|
+
end
|
67
59
|
|
68
|
-
|
60
|
+
def shutdown?
|
61
|
+
lock.synchronize do
|
62
|
+
!!@shutdown
|
63
|
+
end
|
64
|
+
end
|
69
65
|
|
70
|
-
|
66
|
+
def shutdown=(shutdown)
|
67
|
+
lock.synchronize do
|
68
|
+
@shutdown = shutdown
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
71
73
|
|
72
|
-
|
74
|
+
## connection ##
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
require File.expand_path(job_file) if job_file
|
76
|
+
def beanstalk
|
77
|
+
Thread.current[:beanstalk_pool_connection] ||= connect
|
77
78
|
end
|
78
|
-
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
def beanstalk_addresses
|
81
|
+
uris = beanstalk_url.split(/[\s,]+/)
|
82
|
+
uris.map do |uri|
|
83
|
+
beanstalk_host_and_port(uri)
|
84
|
+
end
|
85
|
+
end
|
83
86
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
+
def connect(addresses = nil)
|
88
|
+
Beanstalk::Pool.new(addresses || beanstalk_addresses)
|
89
|
+
end
|
87
90
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
def disconnect
|
92
|
+
Thread.current[:beanstalk_pool_connection].close rescue nil
|
93
|
+
Thread.current[:beanstalk_pool_connection] = nil
|
91
94
|
end
|
92
|
-
end
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
##
|
97
|
+
|
98
|
+
## daemon ##
|
99
|
+
|
100
|
+
def work(jobs = nil, size = 2)
|
101
|
+
require 'creeper/worker'
|
98
102
|
|
99
|
-
|
100
|
-
(@soft_quit = soft_quit).tap do
|
101
|
-
awaken_creeper if soft_quit?
|
103
|
+
Creeper::Worker.work(jobs, size)
|
102
104
|
end
|
103
|
-
end
|
104
105
|
|
105
|
-
|
106
|
-
@timeout ||= 30
|
107
|
-
end
|
106
|
+
##
|
108
107
|
|
109
|
-
|
108
|
+
## handlers ##
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
send("#{key}=", value) if respond_to?("#{key}=")
|
110
|
+
def all_jobs
|
111
|
+
lock.synchronize do
|
112
|
+
HANDLERS[:named].keys
|
115
113
|
end
|
116
114
|
end
|
117
|
-
end
|
118
115
|
|
119
|
-
|
120
|
-
|
116
|
+
def job(name, &block)
|
117
|
+
lock.synchronize do
|
118
|
+
HANDLERS[:named][name] = block
|
119
|
+
HANDLERS[:before_named][name] ||= []
|
120
|
+
HANDLERS[:after_named][name] ||= []
|
121
|
+
HANDLERS[:error_named][name] ||= []
|
122
|
+
HANDLERS[:named][name]
|
123
|
+
end
|
124
|
+
end
|
121
125
|
|
122
|
-
|
126
|
+
def drop(name)
|
127
|
+
lock.synchronize do
|
128
|
+
HANDLERS[:named].delete(name)
|
129
|
+
HANDLERS[:before_named].delete(name)
|
130
|
+
HANDLERS[:after_named].delete(name)
|
131
|
+
HANDLERS[:error_named].delete(name)
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
123
135
|
|
124
|
-
|
125
|
-
|
136
|
+
def handler_for(name)
|
137
|
+
lock.synchronize do
|
138
|
+
HANDLERS[:named][name]
|
139
|
+
end
|
140
|
+
end
|
126
141
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
142
|
+
def before(name = nil, &block)
|
143
|
+
if name and name != :each
|
144
|
+
lock.synchronize do
|
145
|
+
HANDLERS[:before_named][name] << block
|
146
|
+
end
|
147
|
+
else
|
148
|
+
lock.synchronize do
|
149
|
+
HANDLERS[:before_each] << block
|
150
|
+
end
|
134
151
|
end
|
135
152
|
end
|
136
153
|
|
137
|
-
|
154
|
+
def before_handlers_for(name)
|
155
|
+
lock.synchronize do
|
156
|
+
HANDLERS[:before_each] + HANDLERS[:before_named][name]
|
157
|
+
end
|
158
|
+
end
|
138
159
|
|
139
|
-
|
140
|
-
|
160
|
+
def after(name = nil, &block)
|
161
|
+
if name and name != :each
|
162
|
+
lock.synchronize do
|
163
|
+
HANDLERS[:after_named][name] << block
|
164
|
+
end
|
165
|
+
else
|
166
|
+
lock.synchronize do
|
167
|
+
HANDLERS[:after_each] << block
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
141
171
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# break if soft_quit?
|
157
|
-
# avoid murdering runners after our master process (or the
|
158
|
-
# machine) comes out of suspend/hibernation
|
159
|
-
if (last_check + timeout) >= (last_check = Time.now)
|
160
|
-
sleep_time = timeout - 1
|
161
|
-
else
|
162
|
-
sleep_time = timeout/2.0 + 1
|
163
|
-
logger.debug("creeper waiting #{sleep_time}s after suspend/hibernation") if $DEBUG
|
172
|
+
def after_handlers_for(name)
|
173
|
+
lock.synchronize do
|
174
|
+
HANDLERS[:after_each] + HANDLERS[:after_named][name]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def error(name = nil, &block)
|
179
|
+
if name and name != :each
|
180
|
+
lock.synchronize do
|
181
|
+
HANDLERS[:error_named][name] << block
|
182
|
+
end
|
183
|
+
else
|
184
|
+
lock.synchronize do
|
185
|
+
HANDLERS[:error_each] << block
|
164
186
|
end
|
165
|
-
maintain_runner_count if respawn
|
166
|
-
logger.debug("creeper sleeping for #{sleep_time}s") if $DEBUG
|
167
|
-
creeper_sleep(sleep_time)
|
168
|
-
when :QUIT # graceful shutdown
|
169
|
-
break
|
170
|
-
when :TERM, :INT # immediate shutdown
|
171
|
-
stop(false)
|
172
|
-
break
|
173
|
-
when :WINCH
|
174
|
-
self.runner_count = 0
|
175
|
-
logger.debug "WINCH: setting runner_count to #{runner_count}" if $DEBUG
|
176
|
-
when :TTIN
|
177
|
-
self.runner_count += 1
|
178
|
-
logger.debug "TTIN: setting runner_count to #{runner_count}" if $DEBUG
|
179
|
-
when :TTOU
|
180
|
-
self.runner_count -= 1 if runner_count > 0
|
181
|
-
logger.debug "TTOU: setting runner_count to #{runner_count}" if $DEBUG
|
182
187
|
end
|
183
|
-
|
184
|
-
Creeper.log_exception("creeper loop error", e)
|
185
|
-
end while true
|
186
|
-
stop # gracefully shutdown all captains on our way out
|
187
|
-
logger.info "creeper complete"
|
188
|
-
end
|
188
|
+
end
|
189
189
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
reap_graveyard(graceful)
|
195
|
-
sleep(0.1)
|
190
|
+
def error_handlers_for(name)
|
191
|
+
lock.synchronize do
|
192
|
+
HANDLERS[:error_each] + HANDLERS[:error_named][name]
|
193
|
+
end
|
196
194
|
end
|
197
|
-
|
198
|
-
|
195
|
+
|
196
|
+
def finalizer(&block)
|
197
|
+
lock.synchronize do
|
198
|
+
HANDLERS[:finalizers] << block
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def finalizers
|
203
|
+
lock.synchronize do
|
204
|
+
HANDLERS[:finalizers]
|
205
|
+
end
|
199
206
|
end
|
200
|
-
reap_graveyard(false)
|
201
|
-
logger.debug graceful ? "creeper gracefully stopped" : "creeper hard stopped" if $DEBUG
|
202
|
-
end
|
203
207
|
|
204
|
-
|
208
|
+
##
|
205
209
|
|
206
|
-
|
207
|
-
def creeper_sleep(sec)
|
208
|
-
IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
|
209
|
-
SELF_PIPE[0].kgio_tryread(11)
|
210
|
-
end
|
210
|
+
## queue ##
|
211
211
|
|
212
|
-
|
213
|
-
|
214
|
-
|
212
|
+
def enqueue(job, data = {}, options = {})
|
213
|
+
# Logger.debug "#{Thread.current[:actor].inspect} Enqueueing #{job.inspect}, #{data.inspect}"#\n#{Celluloid::Actor.all.pretty_inspect}"
|
214
|
+
Logger.debug "[#{Thread.current[:actor] ? Thread.current[:actor].subject.number : nil}] Enqueueing #{job.inspect}, #{data.inspect}" if $DEBUG
|
215
|
+
enqueue!(job, data, options)
|
216
|
+
rescue Beanstalk::NotConnected => e
|
217
|
+
disconnected(self, :enqueue, job, data, options)
|
218
|
+
end
|
215
219
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
220
|
+
def enqueue!(job, data = {}, options = {})
|
221
|
+
priority = options[:priority] || options[:pri] || 65536
|
222
|
+
delay = [ 0, options[:delay].to_i ].max
|
223
|
+
time_to_run = options[:time_to_run] || options[:ttr] || 120
|
221
224
|
|
222
|
-
|
223
|
-
|
224
|
-
GRAVEYARD[thread] = RUNNERS.delete(thread)
|
225
|
+
beanstalk.use job
|
226
|
+
beanstalk.put JSON.dump([ job, data ]), priority, delay, time_to_run
|
225
227
|
end
|
226
|
-
end
|
227
228
|
|
228
|
-
|
229
|
-
current_runner_count = RUNNERS.size - runner_count
|
229
|
+
##
|
230
230
|
|
231
|
-
|
232
|
-
murder_extra_runners if current_runner_count > 0
|
233
|
-
reap_all_runners
|
234
|
-
reap_graveyard
|
235
|
-
end
|
231
|
+
## workers ##
|
236
232
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
if worker.working?
|
241
|
-
logger.debug "creeper [murder] => soft quit" if $DEBUG
|
242
|
-
worker.soft_quit = true
|
243
|
-
GRAVEYARD[thread] = worker
|
244
|
-
else
|
245
|
-
logger.debug "creeper [murder] => hard quit" if $DEBUG
|
246
|
-
thread.kill
|
247
|
-
thread.join
|
233
|
+
def error_work(worker, data, name, job)
|
234
|
+
(worker.stopped_at = Time.now).tap do |stopped_at|
|
235
|
+
Logger.error "#{worker.prefix} Error in #{worker.time_in_milliseconds}ms #{worker.dump(job, name, data)}"
|
248
236
|
end
|
249
237
|
end
|
250
|
-
end
|
251
238
|
|
252
|
-
|
253
|
-
|
254
|
-
|
239
|
+
def register_worker(worker)
|
240
|
+
lock.synchronize do
|
241
|
+
number = ((0..(WORKERS.keys.max || 0)+1).to_a - WORKERS.keys).first
|
242
|
+
WORKERS[number] = worker.tap do
|
243
|
+
worker.number = number
|
244
|
+
end
|
245
|
+
end
|
255
246
|
end
|
256
|
-
end
|
257
247
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
248
|
+
def shutdown_workers
|
249
|
+
begin
|
250
|
+
soft_shutdown_workers(Creeper.patience_soft)
|
251
|
+
rescue Timeout::Error
|
252
|
+
begin
|
253
|
+
hard_shutdown_workers(Creeper.patience_hard)
|
254
|
+
rescue Timeout::Error
|
255
|
+
kill_shutdown_workers
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def start_work(worker, data, name, job)
|
261
|
+
(worker.started_at = Time.now).tap do |started_at|
|
262
|
+
Logger.info "#{worker.prefix} Working #{Thread.list.count} #{worker.dump(job, name, data)}"
|
268
263
|
end
|
269
264
|
end
|
270
|
-
end
|
271
265
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
RUNNERS[thread] = worker
|
266
|
+
def stop_work(worker, data, name, job)
|
267
|
+
(worker.stopped_at = Time.now).tap do |stopped_at|
|
268
|
+
Logger.info "#{worker.prefix} Finished in #{worker.time_in_milliseconds}ms #{worker.dump(job, name, data)}"
|
269
|
+
end
|
277
270
|
end
|
278
|
-
end
|
279
271
|
|
280
|
-
|
281
|
-
|
282
|
-
|
272
|
+
def unregister_worker(worker, reason = nil)
|
273
|
+
reason ||= 'Stopping'
|
274
|
+
Logger.info "#{worker.prefix} #{reason}"
|
275
|
+
lock.synchronize do
|
276
|
+
WORKERS.delete(worker.number)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
|
282
|
+
protected
|
283
283
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
284
|
+
def beanstalk_host_and_port(uri_string)
|
285
|
+
uri = URI.parse(uri_string)
|
286
|
+
raise(BadURL, uri_string) if uri.scheme != 'beanstalk'
|
287
|
+
"#{uri.host}:#{uri.port || 11300}"
|
288
288
|
end
|
289
|
-
$0 = ([
|
290
|
-
File.basename(Creeper::START_CTX[0]),
|
291
|
-
tag
|
292
|
-
]).concat(Creeper::START_CTX[:argv]).join(' ')
|
293
|
-
end
|
294
289
|
|
295
|
-
|
290
|
+
def disconnected(target, method, *args, &block)
|
291
|
+
Thread.current[:beanstalk_connection_retries] ||= 0
|
292
|
+
|
293
|
+
if Thread.current[:beanstalk_connection_retries] >= retry_count
|
294
|
+
Logger.error "Unable to connect to beanstalk after #{Thread.current[:beanstalk_connection_retries]} attempts"
|
295
|
+
Thread.current[:beanstalk_connection_retries] = 0
|
296
|
+
return false
|
297
|
+
end
|
298
|
+
|
299
|
+
disconnect
|
300
|
+
|
301
|
+
Thread.current[:beanstalk_connection_retries] += 1
|
302
|
+
|
303
|
+
sleep Thread.current[:beanstalk_connection_retries] * 2
|
304
|
+
|
305
|
+
target.send(method, *args, &block)
|
306
|
+
end
|
307
|
+
|
308
|
+
def soft_shutdown_workers(timeout)
|
309
|
+
Timeout.timeout(timeout) do
|
310
|
+
actors = Celluloid::Actor.all
|
311
|
+
Logger.info "Gracefully stopping #{actors.size} actors..." if actors.size > 0
|
312
|
+
|
313
|
+
# Attempt to shut down the supervision tree, if available
|
314
|
+
Celluloid::Supervisor.root.terminate if Celluloid::Supervisor.root
|
315
|
+
|
316
|
+
# Actors cannot self-terminate, you must do it for them
|
317
|
+
starts = working_actors.map do |actor|
|
318
|
+
begin
|
319
|
+
if actor.alive?
|
320
|
+
actor.stop! # fire and forget for those already working
|
321
|
+
actor.future(:start, true) # ensures that the mailbox is cleared out
|
322
|
+
end
|
323
|
+
rescue Celluloid::DeadActorError, Celluloid::MailboxError
|
324
|
+
end
|
325
|
+
end.compact
|
326
|
+
|
327
|
+
starts.each do |start|
|
328
|
+
begin
|
329
|
+
start.value
|
330
|
+
rescue Celluloid::DeadActorError, Celluloid::MailboxError
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
Logger.info "Graceful stop completed cleanly"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def hard_shutdown_workers(timeout)
|
339
|
+
Timeout.timeout(timeout) do
|
340
|
+
actors = Celluloid::Actor.all
|
341
|
+
Logger.info "Terminating #{actors.size} actors..." if actors.size > 0
|
342
|
+
|
343
|
+
# Attempt to shut down the supervision tree, if available
|
344
|
+
Celluloid::Supervisor.root.terminate if Celluloid::Supervisor.root
|
345
|
+
|
346
|
+
pool_managers.each do |pool_manager|
|
347
|
+
begin
|
348
|
+
pool_manager.terminate
|
349
|
+
rescue Celluloid::DeadActorError, Celluloid::MailboxError
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# Actors cannot self-terminate, you must do it for them
|
354
|
+
working_actors.each do |actor|
|
355
|
+
begin
|
356
|
+
actor.terminate
|
357
|
+
rescue Celluloid::DeadActorError, Celluloid::MailboxError
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
Logger.info "Termination completed cleanly"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def kill_shutdown_workers
|
366
|
+
actors = Celluloid::Actor.all
|
367
|
+
Logger.info "Killing #{actors.size} actors..." if actors.size > 0
|
368
|
+
|
369
|
+
# Attempt to shut down the supervision tree, if available
|
370
|
+
Celluloid::Supervisor.root.kill if Celluloid::Supervisor.root
|
371
|
+
|
372
|
+
# Actors cannot self-terminate, you must do it for them
|
373
|
+
Celluloid::Actor.all.each do |actor|
|
374
|
+
begin
|
375
|
+
actor.kill
|
376
|
+
actor.join
|
377
|
+
rescue Celluloid::DeadActorError, Celluloid::MailboxError
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
Logger.info "Killing completed cleanly"
|
382
|
+
end
|
383
|
+
|
384
|
+
def pool_managers
|
385
|
+
Celluloid::Actor.all.tap do |actors|
|
386
|
+
actors.keep_if do |actor|
|
387
|
+
actor.is_a?(Celluloid::PoolManager) rescue false
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def working_actors
|
393
|
+
Celluloid::Actor.all.tap do |actors|
|
394
|
+
actors.delete_if do |actor|
|
395
|
+
actor.is_a?(Celluloid::PoolManager) rescue false
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
296
401
|
|
297
402
|
end
|
403
|
+
|
404
|
+
# require 'creeper/creep'
|
405
|
+
require 'creeper/logger'
|