kulesa-sidekiq 1.2.2
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 +6 -0
- data/.rvmrc +4 -0
- data/COMM-LICENSE +83 -0
- data/Changes.md +207 -0
- data/Gemfile +12 -0
- data/LICENSE +22 -0
- data/README.md +61 -0
- data/Rakefile +9 -0
- data/bin/client +7 -0
- data/bin/sidekiq +14 -0
- data/bin/sidekiqctl +74 -0
- data/config.ru +18 -0
- data/examples/chef/cookbooks/sidekiq/README.rdoc +11 -0
- data/examples/chef/cookbooks/sidekiq/recipes/default.rb +55 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/monitrc.conf.erb +8 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.erb +219 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.yml.erb +22 -0
- data/examples/clockwork.rb +44 -0
- data/examples/config.yml +10 -0
- data/examples/monitrc.conf +6 -0
- data/examples/por.rb +27 -0
- data/examples/scheduling.rb +37 -0
- data/examples/sinkiq.rb +59 -0
- data/examples/web-ui.png +0 -0
- data/lib/sidekiq.rb +98 -0
- data/lib/sidekiq/capistrano.rb +35 -0
- data/lib/sidekiq/cli.rb +214 -0
- data/lib/sidekiq/client.rb +72 -0
- data/lib/sidekiq/extensions/action_mailer.rb +26 -0
- data/lib/sidekiq/extensions/active_record.rb +27 -0
- data/lib/sidekiq/extensions/generic_proxy.rb +21 -0
- data/lib/sidekiq/fetch.rb +76 -0
- data/lib/sidekiq/logging.rb +46 -0
- data/lib/sidekiq/manager.rb +163 -0
- data/lib/sidekiq/middleware/chain.rb +96 -0
- data/lib/sidekiq/middleware/client/unique_jobs.rb +36 -0
- data/lib/sidekiq/middleware/server/active_record.rb +13 -0
- data/lib/sidekiq/middleware/server/exception_handler.rb +38 -0
- data/lib/sidekiq/middleware/server/failure_jobs.rb +25 -0
- data/lib/sidekiq/middleware/server/logging.rb +31 -0
- data/lib/sidekiq/middleware/server/retry_jobs.rb +69 -0
- data/lib/sidekiq/middleware/server/timeout.rb +21 -0
- data/lib/sidekiq/middleware/server/unique_jobs.rb +17 -0
- data/lib/sidekiq/processor.rb +92 -0
- data/lib/sidekiq/rails.rb +21 -0
- data/lib/sidekiq/redis_connection.rb +27 -0
- data/lib/sidekiq/retry.rb +59 -0
- data/lib/sidekiq/testing.rb +44 -0
- data/lib/sidekiq/testing/inline.rb +37 -0
- data/lib/sidekiq/util.rb +40 -0
- data/lib/sidekiq/version.rb +3 -0
- data/lib/sidekiq/web.rb +185 -0
- data/lib/sidekiq/worker.rb +62 -0
- data/lib/sidekiq/yaml_patch.rb +21 -0
- data/myapp/.gitignore +15 -0
- data/myapp/Capfile +5 -0
- data/myapp/Gemfile +19 -0
- data/myapp/Rakefile +7 -0
- data/myapp/app/controllers/application_controller.rb +3 -0
- data/myapp/app/controllers/work_controller.rb +38 -0
- data/myapp/app/helpers/application_helper.rb +2 -0
- data/myapp/app/mailers/.gitkeep +0 -0
- data/myapp/app/mailers/user_mailer.rb +9 -0
- data/myapp/app/models/.gitkeep +0 -0
- data/myapp/app/models/post.rb +5 -0
- data/myapp/app/views/layouts/application.html.erb +14 -0
- data/myapp/app/views/user_mailer/greetings.html.erb +3 -0
- data/myapp/app/views/work/index.html.erb +1 -0
- data/myapp/app/workers/hard_worker.rb +10 -0
- data/myapp/config.ru +4 -0
- data/myapp/config/application.rb +59 -0
- data/myapp/config/boot.rb +6 -0
- data/myapp/config/database.yml +25 -0
- data/myapp/config/deploy.rb +15 -0
- data/myapp/config/environment.rb +5 -0
- data/myapp/config/environments/development.rb +38 -0
- data/myapp/config/environments/production.rb +67 -0
- data/myapp/config/environments/test.rb +37 -0
- data/myapp/config/initializers/backtrace_silencers.rb +7 -0
- data/myapp/config/initializers/inflections.rb +15 -0
- data/myapp/config/initializers/mime_types.rb +5 -0
- data/myapp/config/initializers/secret_token.rb +7 -0
- data/myapp/config/initializers/session_store.rb +8 -0
- data/myapp/config/initializers/sidekiq.rb +6 -0
- data/myapp/config/initializers/wrap_parameters.rb +14 -0
- data/myapp/config/locales/en.yml +5 -0
- data/myapp/config/routes.rb +10 -0
- data/myapp/db/migrate/20120123214055_create_posts.rb +10 -0
- data/myapp/db/seeds.rb +7 -0
- data/myapp/lib/assets/.gitkeep +0 -0
- data/myapp/lib/tasks/.gitkeep +0 -0
- data/myapp/log/.gitkeep +0 -0
- data/myapp/script/rails +6 -0
- data/sidekiq.gemspec +27 -0
- data/test/config.yml +9 -0
- data/test/fake_env.rb +0 -0
- data/test/helper.rb +16 -0
- data/test/test_cli.rb +168 -0
- data/test/test_client.rb +119 -0
- data/test/test_extensions.rb +69 -0
- data/test/test_manager.rb +51 -0
- data/test/test_middleware.rb +92 -0
- data/test/test_processor.rb +32 -0
- data/test/test_retry.rb +125 -0
- data/test/test_stats.rb +68 -0
- data/test/test_testing.rb +97 -0
- data/test/test_testing_inline.rb +75 -0
- data/test/test_web.rb +122 -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 +20 -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 +27 -0
- data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
- data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
- data/web/views/index.slim +48 -0
- data/web/views/layout.slim +26 -0
- data/web/views/queue.slim +11 -0
- data/web/views/retries.slim +29 -0
- data/web/views/retry.slim +52 -0
- metadata +371 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'multi_json'
|
|
3
|
+
|
|
4
|
+
module Sidekiq
|
|
5
|
+
module Middleware
|
|
6
|
+
module Client
|
|
7
|
+
class UniqueJobs
|
|
8
|
+
HASH_KEY_EXPIRATION = 30 * 60
|
|
9
|
+
|
|
10
|
+
def call(worker_class, item, queue)
|
|
11
|
+
enabled = worker_class.get_sidekiq_options['unique']
|
|
12
|
+
if enabled
|
|
13
|
+
payload_hash = Digest::MD5.hexdigest(Sidekiq.dump_json(item))
|
|
14
|
+
unique = false
|
|
15
|
+
|
|
16
|
+
Sidekiq.redis do |conn|
|
|
17
|
+
conn.watch(payload_hash)
|
|
18
|
+
|
|
19
|
+
if conn.get(payload_hash)
|
|
20
|
+
conn.unwatch
|
|
21
|
+
else
|
|
22
|
+
unique = conn.multi do
|
|
23
|
+
conn.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
yield if unique
|
|
28
|
+
else
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'sidekiq/util'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Middleware
|
|
5
|
+
module Server
|
|
6
|
+
class ExceptionHandler
|
|
7
|
+
include Util
|
|
8
|
+
def call(*args)
|
|
9
|
+
yield
|
|
10
|
+
rescue => ex
|
|
11
|
+
logger.warn ex
|
|
12
|
+
logger.warn ex.backtrace.join("\n")
|
|
13
|
+
send_to_airbrake(args[1], ex) if defined?(::Airbrake)
|
|
14
|
+
send_to_exceptional(args[1], ex) if defined?(::Exceptional)
|
|
15
|
+
send_to_exception_notifier(args[1], ex) if defined?(::ExceptionNotifier)
|
|
16
|
+
raise
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def send_to_airbrake(msg, ex)
|
|
22
|
+
::Airbrake.notify(ex, :parameters => msg)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_to_exceptional(msg, ex)
|
|
26
|
+
if ::Exceptional::Config.should_send_to_api?
|
|
27
|
+
::Exceptional.context(msg)
|
|
28
|
+
::Exceptional::Remote.error(::Exceptional::ExceptionData.new(ex))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def send_to_exception_notifier(msg, ex)
|
|
33
|
+
::ExceptionNotifier::Notifier.background_exception_notification(ex, :data => { :message => msg })
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Middleware
|
|
5
|
+
module Server
|
|
6
|
+
class FailureJobs
|
|
7
|
+
def call(*args)
|
|
8
|
+
yield
|
|
9
|
+
rescue => e
|
|
10
|
+
data = {
|
|
11
|
+
:failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
|
|
12
|
+
:payload => args[1],
|
|
13
|
+
:exception => e.class.to_s,
|
|
14
|
+
:error => e.to_s,
|
|
15
|
+
:backtrace => e.backtrace,
|
|
16
|
+
:worker => args[1]['class'],
|
|
17
|
+
:queue => args[2]
|
|
18
|
+
}
|
|
19
|
+
Sidekiq.redis {|conn| conn.rpush(:failed, Sidekiq.dump_json(data)) }
|
|
20
|
+
raise
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
module Middleware
|
|
3
|
+
module Server
|
|
4
|
+
class Logging
|
|
5
|
+
|
|
6
|
+
def call(*args)
|
|
7
|
+
Sidekiq::Logging.with_context("#{args[0].class.to_s} MSG-#{args[0].object_id.to_s(36)}") do
|
|
8
|
+
begin
|
|
9
|
+
start = Time.now
|
|
10
|
+
logger.info { "start" }
|
|
11
|
+
yield
|
|
12
|
+
logger.info { "done: #{elapsed(start)} sec" }
|
|
13
|
+
rescue
|
|
14
|
+
logger.info { "fail: #{elapsed(start)} sec" }
|
|
15
|
+
raise
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def elapsed(start)
|
|
21
|
+
(Time.now - start).to_f.round(3)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def logger
|
|
25
|
+
Sidekiq.logger
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
|
|
3
|
+
require 'sidekiq/retry'
|
|
4
|
+
|
|
5
|
+
module Sidekiq
|
|
6
|
+
module Middleware
|
|
7
|
+
module Server
|
|
8
|
+
##
|
|
9
|
+
# Automatically retry jobs that fail in Sidekiq.
|
|
10
|
+
# A message looks like:
|
|
11
|
+
#
|
|
12
|
+
# { 'class' => 'HardWorker', 'args' => [1, 2, 'foo'] }
|
|
13
|
+
#
|
|
14
|
+
# We'll add a bit more data to the message to support retries:
|
|
15
|
+
#
|
|
16
|
+
# * 'queue' - the queue to use
|
|
17
|
+
# * 'retry_count' - number of times we've retried so far.
|
|
18
|
+
# * 'error_message' - the message from the exception
|
|
19
|
+
# * 'error_class' - the exception class
|
|
20
|
+
# * 'failed_at' - the first time it failed
|
|
21
|
+
# * 'retried_at' - the last time it was retried
|
|
22
|
+
#
|
|
23
|
+
# We don't store the backtrace as that can add a lot of overhead
|
|
24
|
+
# to the message and everyone is using Airbrake, right?
|
|
25
|
+
class RetryJobs
|
|
26
|
+
include Sidekiq::Util
|
|
27
|
+
include Sidekiq::Retry
|
|
28
|
+
|
|
29
|
+
def call(worker, msg, queue)
|
|
30
|
+
yield
|
|
31
|
+
rescue => e
|
|
32
|
+
raise unless msg['retry']
|
|
33
|
+
|
|
34
|
+
msg['queue'] = queue
|
|
35
|
+
msg['error_message'] = e.message
|
|
36
|
+
msg['error_class'] = e.class.name
|
|
37
|
+
count = if msg['retry_count']
|
|
38
|
+
msg['retried_at'] = Time.now.utc
|
|
39
|
+
msg['retry_count'] += 1
|
|
40
|
+
else
|
|
41
|
+
msg['failed_at'] = Time.now.utc
|
|
42
|
+
msg['retry_count'] = 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if msg['backtrace'] == true
|
|
46
|
+
msg['error_backtrace'] = e.backtrace
|
|
47
|
+
elsif msg['backtrace'].to_i != 0
|
|
48
|
+
msg['error_backtrace'] = e.backtrace[0..msg['backtrace'].to_i]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if count <= MAX_COUNT
|
|
52
|
+
delay = DELAY.call(count)
|
|
53
|
+
logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
|
54
|
+
retry_at = Time.now.to_f + delay
|
|
55
|
+
payload = Sidekiq.dump_json(msg)
|
|
56
|
+
Sidekiq.redis do |conn|
|
|
57
|
+
conn.zadd('retry', retry_at.to_s, payload)
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
# Goodbye dear message, you (re)tried your best I'm sure.
|
|
61
|
+
logger.debug { "Dropping message after hitting the retry maximum: #{msg}" }
|
|
62
|
+
end
|
|
63
|
+
raise
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'timeout'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Middleware
|
|
5
|
+
module Server
|
|
6
|
+
class Timeout
|
|
7
|
+
|
|
8
|
+
def call(worker, msg, queue)
|
|
9
|
+
if msg['timeout'] && msg['timeout'].to_i != 0
|
|
10
|
+
::Timeout.timeout(msg['timeout'].to_i) do
|
|
11
|
+
yield
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
yield
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Middleware
|
|
5
|
+
module Server
|
|
6
|
+
class UniqueJobs
|
|
7
|
+
def call(*args)
|
|
8
|
+
yield
|
|
9
|
+
ensure
|
|
10
|
+
json = Sidekiq.dump_json(args[1])
|
|
11
|
+
hash = Digest::MD5.hexdigest(json)
|
|
12
|
+
Sidekiq.redis {|conn| conn.del(hash) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'celluloid'
|
|
2
|
+
require 'multi_json'
|
|
3
|
+
require 'sidekiq/util'
|
|
4
|
+
|
|
5
|
+
require 'sidekiq/middleware/server/active_record'
|
|
6
|
+
require 'sidekiq/middleware/server/exception_handler'
|
|
7
|
+
require 'sidekiq/middleware/server/retry_jobs'
|
|
8
|
+
require 'sidekiq/middleware/server/logging'
|
|
9
|
+
require 'sidekiq/middleware/server/timeout'
|
|
10
|
+
|
|
11
|
+
module Sidekiq
|
|
12
|
+
class Processor
|
|
13
|
+
include Util
|
|
14
|
+
include Celluloid
|
|
15
|
+
|
|
16
|
+
def self.default_middleware
|
|
17
|
+
Middleware::Chain.new do |m|
|
|
18
|
+
m.add Middleware::Server::ExceptionHandler
|
|
19
|
+
m.add Middleware::Server::Logging
|
|
20
|
+
m.add Middleware::Server::RetryJobs
|
|
21
|
+
m.add Middleware::Server::ActiveRecord
|
|
22
|
+
m.add Middleware::Server::Timeout
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(boss)
|
|
27
|
+
@boss = boss
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def process(msg, queue)
|
|
31
|
+
klass = constantize(msg['class'])
|
|
32
|
+
worker = klass.new
|
|
33
|
+
defer do
|
|
34
|
+
stats(worker, msg, queue) do
|
|
35
|
+
Sidekiq.server_middleware.invoke(worker, msg, queue) do
|
|
36
|
+
worker.perform(*msg['args'])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
@boss.processor_done!(current_actor)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# See http://github.com/tarcieri/celluloid/issues/22
|
|
44
|
+
def inspect
|
|
45
|
+
"#<Processor #{to_s}>"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_s
|
|
49
|
+
@str ||= "#{hostname}:#{process_id}-#{Thread.current.object_id}:default"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def stats(worker, msg, queue)
|
|
55
|
+
redis do |conn|
|
|
56
|
+
conn.multi do
|
|
57
|
+
conn.sadd('workers', self)
|
|
58
|
+
conn.setex("worker:#{self}:started", EXPIRY, Time.now.to_s)
|
|
59
|
+
hash = {:queue => queue, :payload => msg, :run_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z")}
|
|
60
|
+
conn.setex("worker:#{self}", EXPIRY, Sidekiq.dump_json(hash))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
dying = false
|
|
65
|
+
begin
|
|
66
|
+
yield
|
|
67
|
+
rescue Exception
|
|
68
|
+
dying = true
|
|
69
|
+
redis do |conn|
|
|
70
|
+
conn.multi do
|
|
71
|
+
conn.incrby("stat:failed", 1)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
raise
|
|
75
|
+
ensure
|
|
76
|
+
redis do |conn|
|
|
77
|
+
conn.multi do
|
|
78
|
+
conn.srem("workers", self)
|
|
79
|
+
conn.del("worker:#{self}")
|
|
80
|
+
conn.del("worker:#{self}:started")
|
|
81
|
+
conn.incrby("stat:processed", 1)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def hostname
|
|
89
|
+
@h ||= `hostname`.strip
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
def self.hook_rails!
|
|
3
|
+
return unless Sidekiq.options[:enable_rails_extensions]
|
|
4
|
+
if defined?(ActiveRecord)
|
|
5
|
+
ActiveRecord::Base.extend(Sidekiq::Extensions::ActiveRecord)
|
|
6
|
+
ActiveRecord::Base.send(:include, Sidekiq::Extensions::ActiveRecord)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if defined?(ActionMailer)
|
|
10
|
+
ActionMailer::Base.extend(Sidekiq::Extensions::ActionMailer)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Rails < ::Rails::Engine
|
|
15
|
+
config.autoload_paths << File.expand_path("#{config.root}/app/workers") if File.exist?("#{config.root}/app/workers")
|
|
16
|
+
|
|
17
|
+
initializer 'sidekiq' do
|
|
18
|
+
Sidekiq.hook_rails!
|
|
19
|
+
end
|
|
20
|
+
end if defined?(::Rails)
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'connection_pool'
|
|
2
|
+
require 'redis'
|
|
3
|
+
require 'redis/namespace'
|
|
4
|
+
|
|
5
|
+
module Sidekiq
|
|
6
|
+
class RedisConnection
|
|
7
|
+
def self.create(options={})
|
|
8
|
+
url = options[:url] || ENV['REDISTOGO_URL'] || 'redis://localhost:6379/0'
|
|
9
|
+
# need a connection for Fetcher and Retry
|
|
10
|
+
size = options[:size] || (Sidekiq.server? ? (Sidekiq.options[:concurrency] + 2) : 5)
|
|
11
|
+
|
|
12
|
+
ConnectionPool.new(:timeout => 1, :size => size) do
|
|
13
|
+
build_client(url, options[:namespace])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.build_client(url, namespace)
|
|
18
|
+
client = Redis.connect(:url => url)
|
|
19
|
+
if namespace
|
|
20
|
+
Redis::Namespace.new(namespace, :redis => client)
|
|
21
|
+
else
|
|
22
|
+
client
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
private_class_method :build_client
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'sidekiq'
|
|
2
|
+
require 'sidekiq/util'
|
|
3
|
+
require 'celluloid'
|
|
4
|
+
require 'multi_json'
|
|
5
|
+
|
|
6
|
+
module Sidekiq
|
|
7
|
+
##
|
|
8
|
+
# Sidekiq's retry support assumes a typical development lifecycle:
|
|
9
|
+
# 0. push some code changes with a bug in it
|
|
10
|
+
# 1. bug causes message processing to fail, sidekiq's middleware captures
|
|
11
|
+
# the message and pushes it onto a retry queue
|
|
12
|
+
# 2. sidekiq retries messages in the retry queue multiple times with
|
|
13
|
+
# an exponential delay, the message continues to fail
|
|
14
|
+
# 3. after a few days, a developer deploys a fix. the message is
|
|
15
|
+
# reprocessed successfully.
|
|
16
|
+
# 4. if 3 never happens, sidekiq will eventually give up and throw the
|
|
17
|
+
# message away.
|
|
18
|
+
module Retry
|
|
19
|
+
|
|
20
|
+
# delayed_job uses the same basic formula
|
|
21
|
+
MAX_COUNT = 25
|
|
22
|
+
DELAY = proc { |count| (count ** 4) + 15 }
|
|
23
|
+
POLL_INTERVAL = 15
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# The Poller checks Redis every N seconds for messages in the retry
|
|
27
|
+
# set have passed their retry timestamp and should be retried. If so, it
|
|
28
|
+
# just pops the message back onto its original queue so the
|
|
29
|
+
# workers can pick it up like any other message.
|
|
30
|
+
class Poller
|
|
31
|
+
include Celluloid
|
|
32
|
+
include Sidekiq::Util
|
|
33
|
+
|
|
34
|
+
def poll
|
|
35
|
+
watchdog('retry poller thread died!') do
|
|
36
|
+
|
|
37
|
+
Sidekiq.redis do |conn|
|
|
38
|
+
# A message's "score" in Redis is the time at which it should be retried.
|
|
39
|
+
# Just check Redis for the set of messages with a timestamp before now.
|
|
40
|
+
messages = nil
|
|
41
|
+
now = Time.now.to_f.to_s
|
|
42
|
+
(messages, _) = conn.multi do
|
|
43
|
+
conn.zrangebyscore('retry', '-inf', now)
|
|
44
|
+
conn.zremrangebyscore('retry', '-inf', now)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
messages.each do |message|
|
|
48
|
+
logger.debug { "Retrying #{message}" }
|
|
49
|
+
msg = Sidekiq.load_json(message)
|
|
50
|
+
conn.rpush("queue:#{msg['queue']}", message)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
after(POLL_INTERVAL) { poll }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|