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.
Files changed (145) hide show
  1. data/.gitignore +1 -0
  2. data/.rvmrc +48 -0
  3. data/Gemfile +17 -1
  4. data/Guardfile +32 -0
  5. data/Rakefile +9 -1
  6. data/bin/creeper +10 -58
  7. data/bin/creeperctl +74 -0
  8. data/config.ru +18 -0
  9. data/creeper.gemspec +19 -9
  10. data/lib/creeper.rb +108 -413
  11. data/lib/creeper/beanstalk_connection.rb +35 -0
  12. data/lib/creeper/cli.rb +225 -0
  13. data/lib/creeper/client.rb +93 -0
  14. data/lib/creeper/core_ext.rb +54 -0
  15. data/lib/creeper/exception_handler.rb +30 -0
  16. data/lib/creeper/extensions/action_mailer.rb +33 -0
  17. data/lib/creeper/extensions/active_record.rb +30 -0
  18. data/lib/creeper/extensions/generic_proxy.rb +26 -0
  19. data/lib/creeper/fetch.rb +94 -0
  20. data/lib/creeper/legacy.rb +46 -0
  21. data/lib/creeper/logging.rb +46 -0
  22. data/lib/creeper/manager.rb +164 -0
  23. data/lib/creeper/middleware/chain.rb +100 -0
  24. data/lib/creeper/middleware/server/active_record.rb +13 -0
  25. data/lib/creeper/middleware/server/logging.rb +31 -0
  26. data/lib/creeper/middleware/server/retry_jobs.rb +79 -0
  27. data/lib/creeper/middleware/server/timeout.rb +21 -0
  28. data/lib/creeper/paginator.rb +31 -0
  29. data/lib/creeper/processor.rb +116 -0
  30. data/lib/creeper/rails.rb +21 -0
  31. data/lib/creeper/redis_connection.rb +28 -0
  32. data/lib/creeper/testing.rb +44 -0
  33. data/lib/creeper/util.rb +45 -0
  34. data/lib/creeper/version.rb +1 -1
  35. data/lib/creeper/web.rb +248 -0
  36. data/lib/creeper/worker.rb +62 -313
  37. data/spec/dummy/.gitignore +15 -0
  38. data/spec/dummy/Gemfile +51 -0
  39. data/spec/dummy/README.rdoc +261 -0
  40. data/spec/dummy/Rakefile +7 -0
  41. data/spec/dummy/app/assets/images/rails.png +0 -0
  42. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  43. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  44. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  45. data/spec/dummy/app/controllers/work_controller.rb +71 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/mailers/.gitkeep +0 -0
  48. data/spec/dummy/app/mailers/user_mailer.rb +9 -0
  49. data/spec/dummy/app/models/.gitkeep +0 -0
  50. data/spec/dummy/app/models/post.rb +8 -0
  51. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  52. data/spec/dummy/app/views/user_mailer/greetings.html.erb +3 -0
  53. data/spec/dummy/app/views/work/index.html.erb +1 -0
  54. data/spec/dummy/app/workers/fast_worker.rb +10 -0
  55. data/spec/dummy/app/workers/hard_worker.rb +11 -0
  56. data/spec/dummy/app/workers/lazy_worker.rb +12 -0
  57. data/spec/dummy/app/workers/suicidal_worker.rb +33 -0
  58. data/spec/dummy/config.ru +4 -0
  59. data/spec/dummy/config/application.rb +68 -0
  60. data/spec/dummy/config/boot.rb +6 -0
  61. data/spec/dummy/config/creeper.yml +9 -0
  62. data/spec/dummy/config/database.yml +25 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +37 -0
  65. data/spec/dummy/config/environments/production.rb +67 -0
  66. data/spec/dummy/config/environments/test.rb +37 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/creeper.rb +8 -0
  69. data/spec/dummy/config/initializers/inflections.rb +15 -0
  70. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  71. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  72. data/spec/dummy/config/initializers/session_store.rb +8 -0
  73. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/spec/dummy/config/locales/en.yml +5 -0
  75. data/spec/dummy/config/routes.rb +13 -0
  76. data/spec/dummy/db/migrate/20120123214055_create_posts.rb +10 -0
  77. data/spec/dummy/db/schema.rb +23 -0
  78. data/spec/dummy/db/seeds.rb +7 -0
  79. data/spec/dummy/lib/assets/.gitkeep +0 -0
  80. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  81. data/spec/dummy/log/.gitkeep +0 -0
  82. data/spec/dummy/public/404.html +26 -0
  83. data/spec/dummy/public/422.html +26 -0
  84. data/spec/dummy/public/500.html +25 -0
  85. data/spec/dummy/public/favicon.ico +0 -0
  86. data/spec/dummy/public/index.html +241 -0
  87. data/spec/dummy/public/robots.txt +5 -0
  88. data/spec/dummy/script/rails +6 -0
  89. data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  90. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  91. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  92. data/spec/lib/creeper/cli_spec.rb +208 -0
  93. data/spec/lib/creeper/client_spec.rb +110 -0
  94. data/spec/lib/creeper/exception_handler_spec.rb +110 -0
  95. data/spec/lib/creeper/processor_spec.rb +92 -0
  96. data/spec/lib/creeper/testing_spec.rb +105 -0
  97. data/spec/lib/creeper_spec.rb +54 -120
  98. data/spec/spec_helper.rb +81 -7
  99. data/spec/support/config.yml +9 -0
  100. data/spec/support/fake_env.rb +0 -0
  101. data/spec/support/workers/base_worker.rb +11 -0
  102. data/spec/support/workers/my_worker.rb +4 -0
  103. data/spec/support/workers/queued_worker.rb +5 -0
  104. data/spec/support/workers/real_worker.rb +10 -0
  105. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  106. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  107. data/web/assets/javascripts/application.js +49 -0
  108. data/web/assets/javascripts/vendor/bootstrap.js +12 -0
  109. data/web/assets/javascripts/vendor/bootstrap/bootstrap-alert.js +91 -0
  110. data/web/assets/javascripts/vendor/bootstrap/bootstrap-button.js +98 -0
  111. data/web/assets/javascripts/vendor/bootstrap/bootstrap-carousel.js +154 -0
  112. data/web/assets/javascripts/vendor/bootstrap/bootstrap-collapse.js +136 -0
  113. data/web/assets/javascripts/vendor/bootstrap/bootstrap-dropdown.js +92 -0
  114. data/web/assets/javascripts/vendor/bootstrap/bootstrap-modal.js +210 -0
  115. data/web/assets/javascripts/vendor/bootstrap/bootstrap-popover.js +95 -0
  116. data/web/assets/javascripts/vendor/bootstrap/bootstrap-scrollspy.js +125 -0
  117. data/web/assets/javascripts/vendor/bootstrap/bootstrap-tab.js +130 -0
  118. data/web/assets/javascripts/vendor/bootstrap/bootstrap-tooltip.js +270 -0
  119. data/web/assets/javascripts/vendor/bootstrap/bootstrap-transition.js +51 -0
  120. data/web/assets/javascripts/vendor/bootstrap/bootstrap-typeahead.js +271 -0
  121. data/web/assets/javascripts/vendor/jquery.js +9266 -0
  122. data/web/assets/javascripts/vendor/jquery.timeago.js +148 -0
  123. data/web/assets/stylesheets/application.css +6 -0
  124. data/web/assets/stylesheets/layout.css +26 -0
  125. data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
  126. data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
  127. data/web/views/_paging.slim +15 -0
  128. data/web/views/_summary.slim +9 -0
  129. data/web/views/_workers.slim +14 -0
  130. data/web/views/index.slim +10 -0
  131. data/web/views/layout.slim +37 -0
  132. data/web/views/poll.slim +3 -0
  133. data/web/views/queue.slim +15 -0
  134. data/web/views/queues.slim +19 -0
  135. data/web/views/retries.slim +31 -0
  136. data/web/views/retry.slim +52 -0
  137. data/web/views/scheduled.slim +27 -0
  138. metadata +341 -23
  139. data/lib/creeper/celluloid_ext.rb +0 -42
  140. data/lib/creeper/creep.rb +0 -25
  141. data/lib/creeper/err_logger.rb +0 -37
  142. data/lib/creeper/launcher.rb +0 -44
  143. data/lib/creeper/out_logger.rb +0 -39
  144. data/spec/lib/creeper/session_spec.rb +0 -15
  145. data/spec/lib/creeper/worker_spec.rb +0 -21
@@ -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
@@ -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
- at_exit { shutdown_workers }
10
-
11
- class Worker
12
-
13
- def self.work(jobs = nil, size = nil)
14
-
15
- size ||= Creeper.pool_size
16
-
17
- options = {
18
- size: size,
19
- args: [jobs]
20
- }
21
-
22
- Creeper.worker_pool = Creeper::Worker.pool(options)
23
-
24
- begin
25
- trap(:INT) { Creeper.shutdown = true }
26
- trap(:TERM) { Creeper.shutdown = true }
27
- trap(:QUIT) { Creeper.shutdown = true }
28
- Creeper.worker_pool.start
29
- end until Creeper.shutdown?
30
-
31
- exit
32
- end
33
-
34
- # def self.em_work(jobs = nil, size = 2)
35
-
36
- # options = {
37
- # size: size,
38
- # args: [jobs]
39
- # }
40
-
41
- # tubes = jobs_for(jobs)
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
- exit if stopped?
183
-
184
- Thread.current[:creeper_working] = true
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
- ensure
213
- Creeper.disconnect
214
- end
215
-
216
- def stop
217
- Thread.current[:creeper_stopped] = true
218
- end
219
-
220
- def work(job)
221
-
222
- exit if stopped?
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
- job.delete
61
+ DEFAULT_OPTIONS = { 'retry' => true, 'queue' => 'default' }
243
62
 
244
- Creeper.stop_work(self, data, name, job)
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
- begin
265
- Creeper.error_handlers_for(name).each do |handler|
266
- process(handler, data, name, job)
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
- Creeper.unregister_worker(self, "work loop error, burying #{dump(job, name, data)}")
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