creeper 1.0.9 → 2.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/.gitignore +1 -0
- data/.rvmrc +48 -0
- data/Gemfile +17 -1
- data/Guardfile +32 -0
- data/Rakefile +9 -1
- data/bin/creeper +10 -58
- data/bin/creeperctl +74 -0
- data/config.ru +18 -0
- data/creeper.gemspec +19 -9
- data/lib/creeper.rb +108 -413
- data/lib/creeper/beanstalk_connection.rb +35 -0
- data/lib/creeper/cli.rb +225 -0
- data/lib/creeper/client.rb +93 -0
- data/lib/creeper/core_ext.rb +54 -0
- data/lib/creeper/exception_handler.rb +30 -0
- data/lib/creeper/extensions/action_mailer.rb +33 -0
- data/lib/creeper/extensions/active_record.rb +30 -0
- data/lib/creeper/extensions/generic_proxy.rb +26 -0
- data/lib/creeper/fetch.rb +94 -0
- data/lib/creeper/legacy.rb +46 -0
- data/lib/creeper/logging.rb +46 -0
- data/lib/creeper/manager.rb +164 -0
- data/lib/creeper/middleware/chain.rb +100 -0
- data/lib/creeper/middleware/server/active_record.rb +13 -0
- data/lib/creeper/middleware/server/logging.rb +31 -0
- data/lib/creeper/middleware/server/retry_jobs.rb +79 -0
- data/lib/creeper/middleware/server/timeout.rb +21 -0
- data/lib/creeper/paginator.rb +31 -0
- data/lib/creeper/processor.rb +116 -0
- data/lib/creeper/rails.rb +21 -0
- data/lib/creeper/redis_connection.rb +28 -0
- data/lib/creeper/testing.rb +44 -0
- data/lib/creeper/util.rb +45 -0
- data/lib/creeper/version.rb +1 -1
- data/lib/creeper/web.rb +248 -0
- data/lib/creeper/worker.rb +62 -313
- data/spec/dummy/.gitignore +15 -0
- data/spec/dummy/Gemfile +51 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/work_controller.rb +71 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/mailers/user_mailer.rb +9 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/post.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/user_mailer/greetings.html.erb +3 -0
- data/spec/dummy/app/views/work/index.html.erb +1 -0
- data/spec/dummy/app/workers/fast_worker.rb +10 -0
- data/spec/dummy/app/workers/hard_worker.rb +11 -0
- data/spec/dummy/app/workers/lazy_worker.rb +12 -0
- data/spec/dummy/app/workers/suicidal_worker.rb +33 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +68 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/creeper.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/creeper.rb +8 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +13 -0
- data/spec/dummy/db/migrate/20120123214055_create_posts.rb +10 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +241 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy/vendor/plugins/.gitkeep +0 -0
- data/spec/lib/creeper/cli_spec.rb +208 -0
- data/spec/lib/creeper/client_spec.rb +110 -0
- data/spec/lib/creeper/exception_handler_spec.rb +110 -0
- data/spec/lib/creeper/processor_spec.rb +92 -0
- data/spec/lib/creeper/testing_spec.rb +105 -0
- data/spec/lib/creeper_spec.rb +54 -120
- data/spec/spec_helper.rb +81 -7
- data/spec/support/config.yml +9 -0
- data/spec/support/fake_env.rb +0 -0
- data/spec/support/workers/base_worker.rb +11 -0
- data/spec/support/workers/my_worker.rb +4 -0
- data/spec/support/workers/queued_worker.rb +5 -0
- data/spec/support/workers/real_worker.rb +10 -0
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/javascripts/application.js +49 -0
- data/web/assets/javascripts/vendor/bootstrap.js +12 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-alert.js +91 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-button.js +98 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-carousel.js +154 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-collapse.js +136 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-dropdown.js +92 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-modal.js +210 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-popover.js +95 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-scrollspy.js +125 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tab.js +130 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tooltip.js +270 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-transition.js +51 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-typeahead.js +271 -0
- data/web/assets/javascripts/vendor/jquery.js +9266 -0
- data/web/assets/javascripts/vendor/jquery.timeago.js +148 -0
- data/web/assets/stylesheets/application.css +6 -0
- data/web/assets/stylesheets/layout.css +26 -0
- data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
- data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
- data/web/views/_paging.slim +15 -0
- data/web/views/_summary.slim +9 -0
- data/web/views/_workers.slim +14 -0
- data/web/views/index.slim +10 -0
- data/web/views/layout.slim +37 -0
- data/web/views/poll.slim +3 -0
- data/web/views/queue.slim +15 -0
- data/web/views/queues.slim +19 -0
- data/web/views/retries.slim +31 -0
- data/web/views/retry.slim +52 -0
- data/web/views/scheduled.slim +27 -0
- metadata +341 -23
- data/lib/creeper/celluloid_ext.rb +0 -42
- data/lib/creeper/creep.rb +0 -25
- data/lib/creeper/err_logger.rb +0 -37
- data/lib/creeper/launcher.rb +0 -44
- data/lib/creeper/out_logger.rb +0 -39
- data/spec/lib/creeper/session_spec.rb +0 -15
- data/spec/lib/creeper/worker_spec.rb +0 -21
data/lib/creeper/web.rb
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
require 'slim'
|
|
3
|
+
require 'sprockets'
|
|
4
|
+
require 'creeper/paginator'
|
|
5
|
+
|
|
6
|
+
module Creeper
|
|
7
|
+
class SprocketsMiddleware
|
|
8
|
+
def initialize(app, options={})
|
|
9
|
+
@app = app
|
|
10
|
+
@root = options[:root]
|
|
11
|
+
path = options[:path] || 'assets'
|
|
12
|
+
@matcher = /^\/#{path}\/*/
|
|
13
|
+
@environment = ::Sprockets::Environment.new(@root)
|
|
14
|
+
@environment.append_path 'assets/javascripts'
|
|
15
|
+
@environment.append_path 'assets/javascripts/vendor'
|
|
16
|
+
@environment.append_path 'assets/stylesheets'
|
|
17
|
+
@environment.append_path 'assets/stylesheets/vendor'
|
|
18
|
+
@environment.append_path 'assets/images'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(env)
|
|
22
|
+
# Solve the problem of people requesting /creeper when they need to request /creeper/ so
|
|
23
|
+
# that relative links in templates resolve correctly.
|
|
24
|
+
return [301, { 'Location' => "#{env['SCRIPT_NAME']}/", 'Content-Type' => 'text/html' }, ['redirecting']] if env['SCRIPT_NAME'] == env['REQUEST_PATH']
|
|
25
|
+
|
|
26
|
+
return @app.call(env) unless @matcher =~ env["PATH_INFO"]
|
|
27
|
+
env['PATH_INFO'].sub!(@matcher,'')
|
|
28
|
+
@environment.call(env)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Web < Sinatra::Base
|
|
33
|
+
include Creeper::Paginator
|
|
34
|
+
|
|
35
|
+
dir = File.expand_path(File.dirname(__FILE__) + "/../../web")
|
|
36
|
+
set :views, "#{dir}/views"
|
|
37
|
+
set :root, "#{dir}/public"
|
|
38
|
+
set :slim, :pretty => true
|
|
39
|
+
use SprocketsMiddleware, :root => dir
|
|
40
|
+
|
|
41
|
+
helpers do
|
|
42
|
+
|
|
43
|
+
def reset_worker_list
|
|
44
|
+
Creeper.redis do |conn|
|
|
45
|
+
workers = conn.smembers('workers')
|
|
46
|
+
workers.each do |name|
|
|
47
|
+
conn.srem('workers', name)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def workers
|
|
53
|
+
@workers ||= begin
|
|
54
|
+
Creeper.redis do |conn|
|
|
55
|
+
conn.smembers('workers').map do |w|
|
|
56
|
+
msg = conn.get("worker:#{w}")
|
|
57
|
+
msg ? [w, Creeper.load_json(msg)] : nil
|
|
58
|
+
end.compact.sort { |x| x[1] ? -1 : 1 }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def processed
|
|
64
|
+
Creeper.redis { |conn| conn.get('stat:processed') } || 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def failed
|
|
68
|
+
Creeper.redis { |conn| conn.get('stat:failed') } || 0
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def zcard(name)
|
|
72
|
+
Creeper.redis { |conn| conn.zcard(name) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def queues
|
|
76
|
+
@queues ||= Creeper.redis do |conn|
|
|
77
|
+
conn.smembers('queues').map do |q|
|
|
78
|
+
[q, conn.llen("queue:#{q}") || 0]
|
|
79
|
+
end.sort { |x,y| x[1] <=> y[1] }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def backlog
|
|
84
|
+
# queues.map {|name, size| size }.inject(0) {|memo, val| memo + val }
|
|
85
|
+
beanstalk_stats['current-jobs-ready']
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def buried
|
|
89
|
+
beanstalk_stats['current-jobs-buried']
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def scheduled
|
|
93
|
+
beanstalk_stats['current-jobs-delayed']
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def retries_with_score(score)
|
|
97
|
+
Creeper.redis do |conn|
|
|
98
|
+
results = conn.zrangebyscore('retry', score, score)
|
|
99
|
+
results.map { |msg| Creeper.load_json(msg) }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def beanstalk_stats
|
|
104
|
+
@beanstalk_stats ||= Creeper.beanstalk do |conn|
|
|
105
|
+
conn.stats
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def beanstalk_location
|
|
110
|
+
Creeper.beanstalk { |conn| conn.instance_variable_get(:@addrs) }.join(', ')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def location
|
|
114
|
+
Creeper.redis { |conn| conn.client.location }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def root_path
|
|
118
|
+
"#{env['SCRIPT_NAME']}/"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def current_status
|
|
122
|
+
return 'idle' if workers.size == 0
|
|
123
|
+
return 'active'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def relative_time(time)
|
|
127
|
+
%{<time datetime="#{time.getutc.iso8601}">#{time}</time>}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def display_args(args, count=100)
|
|
131
|
+
args.map { |arg| a = arg.inspect; a.size > count ? "#{a[0..count]}..." : a }.join(", ")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
get "/" do
|
|
136
|
+
slim :index
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
get "/poll" do
|
|
140
|
+
slim :poll, layout: false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
get "/queues" do
|
|
144
|
+
@queues = queues
|
|
145
|
+
slim :queues
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
get "/queues/:name" do
|
|
149
|
+
halt 404 unless params[:name]
|
|
150
|
+
@count = (params[:count] || 25).to_i
|
|
151
|
+
@name = params[:name]
|
|
152
|
+
(@current_page, @total_size, @messages) = page("queue:#{@name}", params[:page], @count)
|
|
153
|
+
@messages = @messages.map {|msg| Creeper.load_json(msg) }
|
|
154
|
+
slim :queue
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
post "/reset" do
|
|
158
|
+
reset_worker_list
|
|
159
|
+
redirect root_path
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
post "/queues/:name" do
|
|
163
|
+
Creeper.redis do |conn|
|
|
164
|
+
conn.del("queue:#{params[:name]}")
|
|
165
|
+
conn.srem("queues", params[:name])
|
|
166
|
+
end
|
|
167
|
+
redirect "#{root_path}queues"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
get "/retries/:score" do
|
|
171
|
+
halt 404 unless params[:score]
|
|
172
|
+
@score = params[:score].to_f
|
|
173
|
+
@retries = retries_with_score(@score)
|
|
174
|
+
redirect "#{root_path}retries" if @retries.empty?
|
|
175
|
+
slim :retry
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
get '/retries' do
|
|
179
|
+
@count = (params[:count] || 25).to_i
|
|
180
|
+
(@current_page, @total_size, @retries) = page("retry", params[:page], @count)
|
|
181
|
+
@retries = @retries.map {|msg, score| [Creeper.load_json(msg), score] }
|
|
182
|
+
slim :retries
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
get '/scheduled' do
|
|
186
|
+
@count = (params[:count] || 25).to_i
|
|
187
|
+
(@current_page, @total_size, @scheduled) = page("schedule", params[:page], @count)
|
|
188
|
+
@scheduled = @scheduled.map {|msg, score| [Creeper.load_json(msg), score] }
|
|
189
|
+
slim :scheduled
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
post '/scheduled' do
|
|
193
|
+
halt 404 unless params[:score]
|
|
194
|
+
halt 404 unless params['delete']
|
|
195
|
+
params[:score].each do |score|
|
|
196
|
+
s = score.to_f
|
|
197
|
+
process_score('schedule', s, :delete)
|
|
198
|
+
end
|
|
199
|
+
redirect "#{root_path}scheduled"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
post '/retries' do
|
|
203
|
+
halt 404 unless params[:score]
|
|
204
|
+
params[:score].each do |score|
|
|
205
|
+
s = score.to_f
|
|
206
|
+
if params['retry']
|
|
207
|
+
process_score('retry', s, :retry)
|
|
208
|
+
elsif params['delete']
|
|
209
|
+
process_score('retry', s, :delete)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
redirect "#{root_path}retries"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
post "/retries/:score" do
|
|
216
|
+
halt 404 unless params[:score]
|
|
217
|
+
score = params[:score].to_f
|
|
218
|
+
if params['retry']
|
|
219
|
+
process_score('retry', score, :retry)
|
|
220
|
+
elsif params['delete']
|
|
221
|
+
process_score('retry', score, :delete)
|
|
222
|
+
end
|
|
223
|
+
redirect "#{root_path}retries"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def process_score(set, score, operation)
|
|
227
|
+
case operation
|
|
228
|
+
when :retry
|
|
229
|
+
raise "Not Implemented"
|
|
230
|
+
Creeper.redis do |conn|
|
|
231
|
+
results = conn.zrangebyscore(set, score, score)
|
|
232
|
+
conn.zremrangebyscore(set, score, score)
|
|
233
|
+
results.map do |message|
|
|
234
|
+
msg = Creeper.load_json(message)
|
|
235
|
+
msg['retry_count'] = msg['retry_count'] - 1
|
|
236
|
+
conn.rpush("queue:#{msg['queue']}", Creeper.dump_json(msg))
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
when :delete
|
|
240
|
+
Creeper.redis do |conn|
|
|
241
|
+
conn.zremrangebyscore(set, score, score)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
end
|
data/lib/creeper/worker.rb
CHANGED
|
@@ -1,331 +1,80 @@
|
|
|
1
|
-
require 'creeper'
|
|
2
|
-
|
|
3
|
-
require 'celluloid'
|
|
4
|
-
require 'creeper/celluloid_ext'
|
|
5
|
-
# require 'em-jack'
|
|
1
|
+
require 'creeper/client'
|
|
2
|
+
require 'creeper/core_ext'
|
|
6
3
|
|
|
7
4
|
module Creeper
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# Creeper.worker_pool = Creeper::Worker.pool(options)
|
|
44
|
-
|
|
45
|
-
# sleep 1
|
|
46
|
-
|
|
47
|
-
# EM.run do
|
|
48
|
-
# trap(:INT) { Creeper.shutdown = true; EM.stop }
|
|
49
|
-
# trap(:TERM) { Creeper.shutdown = true; EM.stop }
|
|
50
|
-
# trap(:QUIT) { Creeper.shutdown = true; EM.stop }
|
|
51
|
-
|
|
52
|
-
# jack = EMJack::Connection.new(Creeper.beanstalk_url)
|
|
53
|
-
|
|
54
|
-
# reserve_loop = ->(timeout) do
|
|
55
|
-
# r = jack.reserve(timeout)
|
|
56
|
-
# r.callback do |job|
|
|
57
|
-
# Creeper.worker_pool.work! job
|
|
58
|
-
# EM.next_tick { reserve_loop.call(timeout) }
|
|
59
|
-
# end
|
|
60
|
-
# r.errback do
|
|
61
|
-
# EM.next_tick { reserve_loop.call(timeout) }
|
|
62
|
-
# end
|
|
63
|
-
# end
|
|
64
|
-
|
|
65
|
-
# watch_tubes = ->(list) do
|
|
66
|
-
# if list.empty?
|
|
67
|
-
# reserve_loop.call(Creeper.reserve_timeout)
|
|
68
|
-
# else
|
|
69
|
-
# w = jack.watch(list.shift)
|
|
70
|
-
# w.callback do
|
|
71
|
-
# watch_tubes.call(list)
|
|
72
|
-
# end
|
|
73
|
-
# end
|
|
74
|
-
# end
|
|
75
|
-
|
|
76
|
-
# watch_tubes.call(tubes)
|
|
77
|
-
|
|
78
|
-
# end
|
|
79
|
-
|
|
80
|
-
# end
|
|
81
|
-
|
|
82
|
-
def self.jobs_for(jobs = nil)
|
|
83
|
-
case jobs
|
|
84
|
-
when :all, nil
|
|
85
|
-
Creeper.all_jobs
|
|
86
|
-
else
|
|
87
|
-
Array(jobs)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
include Celluloid
|
|
92
|
-
|
|
93
|
-
attr_accessor :number
|
|
94
|
-
attr_reader :jobs
|
|
95
|
-
|
|
96
|
-
def initialize(jobs = nil)
|
|
97
|
-
@jobs = self.class.jobs_for(jobs)
|
|
98
|
-
|
|
99
|
-
Creeper.register_worker(self)
|
|
100
|
-
OutLogger.info "#{prefix} Working #{self.jobs.size} jobs: [ #{self.jobs.join(' ')} ]"
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def dump(job, name = nil, data = nil)
|
|
104
|
-
"#{name.inspect rescue nil} { data: #{(data.inspect rescue nil)}, job: #{(job.inspect rescue nil)} }"
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def prefix
|
|
108
|
-
"[#{number_format % number} - #{'%x' % Thread.current.object_id}]"
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def number_format
|
|
112
|
-
"%#{Creeper.pool_size.to_s.length}d"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def time_in_milliseconds
|
|
116
|
-
((stopped_at - started_at).to_f * 1000).to_i
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def started_at
|
|
120
|
-
Thread.current[:creeper_started_at]
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def started_at=(started_at)
|
|
124
|
-
Thread.current[:creeper_started_at] = started_at
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def stopped_at
|
|
128
|
-
Thread.current[:creeper_stopped_at]
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def stopped_at=(stopped_at)
|
|
132
|
-
Thread.current[:creeper_stopped_at] = stopped_at
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
## beanstalk ##
|
|
136
|
-
|
|
137
|
-
def beanstalk
|
|
138
|
-
Creeper.beanstalk
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def ignore(tube)
|
|
142
|
-
beanstalk.ignore(tube)
|
|
143
|
-
rescue Beanstalk::NotConnected => e
|
|
144
|
-
disconnected(self, :ignore, tube) || raise
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def list_tubes_watched(cached = false)
|
|
148
|
-
beanstalk.list_tubes_watched
|
|
149
|
-
rescue Beanstalk::NotConnected => e
|
|
150
|
-
disconnected(self, :list_tubes_watched, cached) || raise
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def reserve(timeout = nil)
|
|
154
|
-
beanstalk.reserve(timeout)
|
|
155
|
-
rescue Beanstalk::NotConnected => e
|
|
156
|
-
disconnected(self, :reserve, timeout) || raise
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def watch(tube)
|
|
160
|
-
beanstalk.watch(tube)
|
|
161
|
-
rescue Beanstalk::NotConnected => e
|
|
162
|
-
disconnected(self, :watch, tube) || raise
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
##
|
|
166
|
-
|
|
167
|
-
## work ##
|
|
168
|
-
|
|
169
|
-
def start(short_circuit = false)
|
|
170
|
-
return false if short_circuit
|
|
171
|
-
exit if stopped?
|
|
172
|
-
return true if working?
|
|
173
|
-
prepare if not prepared?
|
|
174
|
-
|
|
175
|
-
begin
|
|
176
|
-
job = reserve Creeper.reserve_timeout
|
|
177
|
-
rescue Beanstalk::TimedOut
|
|
178
|
-
OutLogger.debug "#{prefix} Back to the unemployment line" if $DEBUG
|
|
179
|
-
return false
|
|
6
|
+
##
|
|
7
|
+
# Include this module in your worker class and you can easily create
|
|
8
|
+
# asynchronous jobs:
|
|
9
|
+
#
|
|
10
|
+
# class HardWorker
|
|
11
|
+
# include Creeper::Worker
|
|
12
|
+
#
|
|
13
|
+
# def perform(*args)
|
|
14
|
+
# # do some work
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# Then in your Rails app, you can do this:
|
|
19
|
+
#
|
|
20
|
+
# HardWorker.perform_async(1, 2, 3)
|
|
21
|
+
#
|
|
22
|
+
# Note that perform_async is a class method, perform is an instance method.
|
|
23
|
+
module Worker
|
|
24
|
+
def self.included(base)
|
|
25
|
+
base.extend(ClassMethods)
|
|
26
|
+
base.class_attribute :creeper_options_hash
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def logger
|
|
30
|
+
Creeper.logger
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ClassMethods
|
|
34
|
+
|
|
35
|
+
include Creeper::Legacy::WorkerMethods
|
|
36
|
+
|
|
37
|
+
def perform_async(*args)
|
|
38
|
+
client_push('class' => self, 'args' => args)
|
|
180
39
|
end
|
|
181
40
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
OutLogger.debug "#{prefix} Got #{job.inspect}" if $DEBUG
|
|
187
|
-
|
|
188
|
-
work! job # asynchronously go to work
|
|
189
|
-
rescue SystemExit => e
|
|
190
|
-
job.release rescue nil
|
|
191
|
-
Creeper.unregister_worker(self)
|
|
192
|
-
rescue => e
|
|
193
|
-
job.release rescue nil
|
|
194
|
-
Creeper.unregister_worker(self, "start loop error")
|
|
195
|
-
raise
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def finalize
|
|
199
|
-
Creeper.finalizers.each do |finalizer|
|
|
200
|
-
begin
|
|
201
|
-
case finalizer.arity
|
|
202
|
-
when 1
|
|
203
|
-
finalizer.call(self)
|
|
204
|
-
else
|
|
205
|
-
finalizer.call
|
|
206
|
-
end
|
|
207
|
-
rescue => e
|
|
208
|
-
OutLogger.crash "#{prefix} finalizer error", e
|
|
209
|
-
end
|
|
41
|
+
def perform_in(interval, *args)
|
|
42
|
+
int = interval.to_f
|
|
43
|
+
ts = (int < 1_000_000_000 ? Time.now.to_f + int : int)
|
|
44
|
+
client_push('class' => self, 'args' => args, 'at' => ts)
|
|
210
45
|
end
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
name, data = JSON.parse(job.body)
|
|
225
|
-
|
|
226
|
-
Creeper.start_work(self, data, name, job)
|
|
227
|
-
|
|
228
|
-
begin
|
|
229
|
-
Creeper.before_handlers_for(name).each do |handler|
|
|
230
|
-
process(handler, data, name, job)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
Creeper.handler_for(name).tap do |handler|
|
|
234
|
-
process(handler, data, name, job)
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
Creeper.after_handlers_for(name).each do |handler|
|
|
238
|
-
process(handler, data, name, job)
|
|
239
|
-
end
|
|
46
|
+
alias_method :perform_at, :perform_in
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Allows customization for this type of Worker.
|
|
50
|
+
# Legal options:
|
|
51
|
+
#
|
|
52
|
+
# :queue - use a named queue for this Worker, default 'default'
|
|
53
|
+
# :retry - enable the RetryJobs middleware for this Worker, default *true*
|
|
54
|
+
# :timeout - timeout the perform method after N seconds, default *nil*
|
|
55
|
+
# :backtrace - whether to save any error backtrace in the retry payload to display in web UI,
|
|
56
|
+
# can be true, false or an integer number of lines to save, default *false*
|
|
57
|
+
def creeper_options(opts={})
|
|
58
|
+
self.creeper_options_hash = get_creeper_options.merge(stringify_keys(opts || {}))
|
|
240
59
|
end
|
|
241
60
|
|
|
242
|
-
|
|
61
|
+
DEFAULT_OPTIONS = { 'retry' => true, 'queue' => 'default' }
|
|
243
62
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
# start! unless stopped? or EM.reactor_running?
|
|
247
|
-
start! unless stopped? # continue processing, even when end of links is reached
|
|
248
|
-
|
|
249
|
-
rescue Beanstalk::NotConnected => e
|
|
250
|
-
disconnected(self, :work, job) || begin
|
|
251
|
-
job.release rescue nil
|
|
252
|
-
Creeper.unregister_worker(self)
|
|
253
|
-
raise
|
|
63
|
+
def get_creeper_options # :nodoc:
|
|
64
|
+
self.creeper_options_hash ||= DEFAULT_OPTIONS
|
|
254
65
|
end
|
|
255
|
-
rescue SystemExit => e
|
|
256
|
-
job.release rescue nil
|
|
257
|
-
Creeper.unregister_worker(self)
|
|
258
|
-
rescue => e
|
|
259
|
-
|
|
260
|
-
job.bury rescue nil
|
|
261
|
-
|
|
262
|
-
Creeper.error_work(self, data, name, job)
|
|
263
66
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
67
|
+
def stringify_keys(hash) # :nodoc:
|
|
68
|
+
hash.keys.each do |key|
|
|
69
|
+
hash[key.to_s] = hash.delete(key)
|
|
267
70
|
end
|
|
71
|
+
hash
|
|
268
72
|
end
|
|
269
73
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
raise
|
|
273
|
-
ensure
|
|
274
|
-
Thread.current[:creeper_started_at] = nil
|
|
275
|
-
Thread.current[:creeper_stopped_at] = nil
|
|
276
|
-
Thread.current[:creeper_working] = false
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def process(handler, data, name, job)
|
|
280
|
-
case handler.arity
|
|
281
|
-
when 3
|
|
282
|
-
handler.call(data, name, job)
|
|
283
|
-
when 2
|
|
284
|
-
handler.call(data, name)
|
|
285
|
-
when 1
|
|
286
|
-
handler.call(data)
|
|
287
|
-
else
|
|
288
|
-
handler.call
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
##
|
|
293
|
-
|
|
294
|
-
## flags ##
|
|
295
|
-
|
|
296
|
-
def prepared?
|
|
297
|
-
Thread.current[:creeper_prepared] == true
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
def stopped?
|
|
301
|
-
Thread.current[:creeper_stopped] == true || Creeper.shutdown?
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def working?
|
|
305
|
-
Thread.current[:creeper_working] == true
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
##
|
|
309
|
-
|
|
310
|
-
protected
|
|
311
|
-
|
|
312
|
-
def disconnected(target, method, *args, &block)
|
|
313
|
-
Creeper.send(:disconnected, target, method, *args, &block)
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def prepare
|
|
317
|
-
jobs.each do |job|
|
|
318
|
-
watch(job)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
list_tubes_watched.each do |server, tubes|
|
|
322
|
-
tubes.each do |tube|
|
|
323
|
-
ignore(tube) unless jobs.include?(tube)
|
|
324
|
-
end
|
|
74
|
+
def client_push(*args) # :nodoc:
|
|
75
|
+
Creeper::Client.push(*args)
|
|
325
76
|
end
|
|
326
77
|
|
|
327
|
-
Thread.current[:creeper_prepared] = true
|
|
328
78
|
end
|
|
329
|
-
|
|
330
79
|
end
|
|
331
|
-
end
|
|
80
|
+
end
|