sidekiq 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (66) hide show
  1. data/Changes.md +11 -0
  2. data/Gemfile +7 -1
  3. data/LICENSE +7 -1
  4. data/README.md +2 -0
  5. data/TODO.md +1 -2
  6. data/bin/sidekiq +1 -1
  7. data/config.ru +8 -0
  8. data/examples/config.yml +0 -2
  9. data/examples/scheduling.rb +37 -0
  10. data/lib/sidekiq.rb +25 -11
  11. data/lib/sidekiq/capistrano.rb +8 -5
  12. data/lib/sidekiq/cli.rb +20 -21
  13. data/lib/sidekiq/client.rb +9 -6
  14. data/lib/sidekiq/extensions/action_mailer.rb +1 -2
  15. data/lib/sidekiq/extensions/active_record.rb +1 -3
  16. data/lib/sidekiq/extensions/generic_proxy.rb +1 -1
  17. data/lib/sidekiq/manager.rb +28 -22
  18. data/lib/sidekiq/middleware/client/unique_jobs.rb +3 -3
  19. data/lib/sidekiq/middleware/server/failure_jobs.rb +1 -1
  20. data/lib/sidekiq/middleware/server/unique_jobs.rb +1 -1
  21. data/lib/sidekiq/processor.rb +4 -6
  22. data/lib/sidekiq/rails.rb +16 -1
  23. data/lib/sidekiq/redis_connection.rb +3 -3
  24. data/lib/sidekiq/util.rb +3 -15
  25. data/lib/sidekiq/version.rb +1 -1
  26. data/lib/sidekiq/web.rb +93 -0
  27. data/lib/sidekiq/worker.rb +0 -4
  28. data/myapp/Gemfile +15 -3
  29. data/myapp/app/controllers/work_controller.rb +1 -1
  30. data/myapp/config/initializers/sidekiq.rb +2 -2
  31. data/myapp/config/routes.rb +2 -2
  32. data/sidekiq.gemspec +3 -1
  33. data/test/helper.rb +4 -0
  34. data/test/test_cli.rb +26 -14
  35. data/test/test_client.rb +2 -2
  36. data/test/test_extensions.rb +11 -7
  37. data/test/test_manager.rb +2 -3
  38. data/test/test_middleware.rb +1 -1
  39. data/test/test_stats.rb +28 -29
  40. data/test/test_testing.rb +47 -14
  41. data/test/test_web.rb +51 -0
  42. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  43. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  44. data/web/assets/javascripts/application.js +3 -0
  45. data/web/assets/javascripts/vendor/bootstrap.js +12 -0
  46. data/web/assets/javascripts/vendor/bootstrap/bootstrap-alert.js +91 -0
  47. data/web/assets/javascripts/vendor/bootstrap/bootstrap-button.js +98 -0
  48. data/web/assets/javascripts/vendor/bootstrap/bootstrap-carousel.js +154 -0
  49. data/web/assets/javascripts/vendor/bootstrap/bootstrap-collapse.js +136 -0
  50. data/web/assets/javascripts/vendor/bootstrap/bootstrap-dropdown.js +92 -0
  51. data/web/assets/javascripts/vendor/bootstrap/bootstrap-modal.js +210 -0
  52. data/web/assets/javascripts/vendor/bootstrap/bootstrap-popover.js +95 -0
  53. data/web/assets/javascripts/vendor/bootstrap/bootstrap-scrollspy.js +125 -0
  54. data/web/assets/javascripts/vendor/bootstrap/bootstrap-tab.js +130 -0
  55. data/web/assets/javascripts/vendor/bootstrap/bootstrap-tooltip.js +270 -0
  56. data/web/assets/javascripts/vendor/bootstrap/bootstrap-transition.js +51 -0
  57. data/web/assets/javascripts/vendor/bootstrap/bootstrap-typeahead.js +271 -0
  58. data/web/assets/javascripts/vendor/jquery.js +9266 -0
  59. data/web/assets/stylesheets/application.css +11 -0
  60. data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
  61. data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
  62. data/web/views/index.slim +42 -0
  63. data/web/views/layout.slim +24 -0
  64. data/web/views/queue.slim +11 -0
  65. metadata +71 -22
  66. data/lib/sidekiq/middleware/client/resque_web_compatibility.rb +0 -14
@@ -9,9 +9,9 @@ module Sidekiq
9
9
 
10
10
  def call(item, queue)
11
11
  payload_hash = Digest::MD5.hexdigest(MultiJson.encode(item))
12
- Sidekiq.redis.with_connection do |redis|
13
- return if redis.get(payload_hash)
14
- redis.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
12
+ Sidekiq.redis do |conn|
13
+ return if conn.get(payload_hash)
14
+ conn.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
15
15
  end
16
16
 
17
17
  yield
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  :queue => args[2]
16
16
  }
17
17
 
18
- Sidekiq.redis.rpush(:failed, MultiJson.encode(data))
18
+ Sidekiq.redis {|conn| conn.rpush(:failed, MultiJson.encode(data)) }
19
19
  raise
20
20
  end
21
21
  end
@@ -5,7 +5,7 @@ module Sidekiq
5
5
  def call(*args)
6
6
  yield
7
7
  ensure
8
- Sidekiq.redis.del(Digest::MD5.hexdigest(MultiJson.encode(args[1])))
8
+ Sidekiq.redis {|conn| conn.del(Digest::MD5.hexdigest(MultiJson.encode(args[1]))) }
9
9
  end
10
10
  end
11
11
  end
@@ -25,11 +25,9 @@ module Sidekiq
25
25
  end
26
26
  end
27
27
 
28
- attr_accessor :msg, :queue
29
-
30
28
  def initialize(boss)
31
29
  @boss = boss
32
- redis.sadd('workers', self)
30
+ redis {|x| x.sadd('workers', self) }
33
31
  end
34
32
 
35
33
  def process(msg, queue)
@@ -55,7 +53,7 @@ module Sidekiq
55
53
  private
56
54
 
57
55
  def stats(worker, msg, queue)
58
- redis.with_connection do |conn|
56
+ redis do |conn|
59
57
  conn.multi do
60
58
  conn.set("worker:#{self}:started", Time.now.to_s)
61
59
  conn.set("worker:#{self}", MultiJson.encode(:queue => queue, :payload => msg,
@@ -69,7 +67,7 @@ module Sidekiq
69
67
  rescue
70
68
  dying = true
71
69
  # Uh oh, error. We will die so unregister as much as we can first.
72
- redis.with_connection do |conn|
70
+ redis do |conn|
73
71
  conn.multi do
74
72
  conn.incrby("stat:failed", 1)
75
73
  conn.del("stat:processed:#{self}")
@@ -78,7 +76,7 @@ module Sidekiq
78
76
  end
79
77
  raise
80
78
  ensure
81
- redis.with_connection do |conn|
79
+ redis do |conn|
82
80
  conn.multi do
83
81
  conn.del("worker:#{self}")
84
82
  conn.del("worker:#{self}:started")
@@ -1,5 +1,20 @@
1
1
  module Sidekiq
2
+ def self.hook_rails!
3
+ if defined?(ActiveRecord)
4
+ ActiveRecord::Base.extend(Sidekiq::Extensions::ActiveRecord)
5
+ ActiveRecord::Base.send(:include, Sidekiq::Extensions::ActiveRecord)
6
+ end
7
+
8
+ if defined?(ActionMailer)
9
+ ActionMailer::Base.extend(Sidekiq::Extensions::ActionMailer)
10
+ end
11
+ end
12
+
2
13
  class Rails < ::Rails::Engine
3
14
  config.autoload_paths << File.expand_path("#{config.root}/app/workers") if File.exist?("#{config.root}/app/workers")
4
- end
15
+
16
+ initializer 'sidekiq' do
17
+ Sidekiq.hook_rails!
18
+ end
19
+ end if defined?(::Rails)
5
20
  end
@@ -5,9 +5,9 @@ module Sidekiq
5
5
  class RedisConnection
6
6
  def self.create(options={})
7
7
  url = options[:url] || ENV['REDISTOGO_URL'] || 'redis://localhost:6379/0'
8
- client = build_client(url, options[:namespace])
9
- return ConnectionPool.new(:timeout => 1, :size => (options[:size] || Sidekiq.options[:concurrency] || 25)) { client } unless options[:use_pool] == false
10
- client
8
+ ConnectionPool::Wrapper.new(:timeout => 1, :size => (options[:size] || Sidekiq.options[:concurrency] || 25)) do
9
+ build_client(url, options[:namespace])
10
+ end
11
11
  end
12
12
 
13
13
  def self.build_client(url, namespace)
@@ -50,24 +50,12 @@ module Sidekiq
50
50
  Sidekiq::Util.logger
51
51
  end
52
52
 
53
- def redis
54
- Sidekiq.redis
53
+ def redis(&block)
54
+ Sidekiq.redis(&block)
55
55
  end
56
56
 
57
57
  def process_id
58
- Sidekiq::Util.process_id
59
- end
60
-
61
- def self.process_id
62
- @pid ||= begin
63
- if Process.pid == 1
64
- # Heroku does not expose pids.
65
- require 'securerandom'
66
- (SecureRandom.random_number * 4_000_000_000).floor.to_s(16)
67
- else
68
- Process.pid
69
- end
70
- end
58
+ Process.pid
71
59
  end
72
60
  end
73
61
  end
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -0,0 +1,93 @@
1
+ require 'sinatra/base'
2
+ require 'slim'
3
+ require 'sprockets'
4
+
5
+ module Sidekiq
6
+ class SprocketsMiddleware
7
+ def initialize(app, options={})
8
+ @app = app
9
+ @root = options[:root]
10
+ path = options[:path] || 'assets'
11
+ @matcher = /^\/#{path}\/*/
12
+ @environment = ::Sprockets::Environment.new(@root)
13
+ @environment.append_path 'assets/javascripts'
14
+ @environment.append_path 'assets/javascripts/vendor'
15
+ @environment.append_path 'assets/stylesheets'
16
+ @environment.append_path 'assets/stylesheets/vendor'
17
+ @environment.append_path 'assets/images'
18
+ end
19
+
20
+ def call(env)
21
+ # Solve the problem of people requesting /sidekiq when they need to request /sidekiq/ so
22
+ # that relative links in templates resolve correctly.
23
+ return [301, { 'Location' => "#{env['SCRIPT_NAME']}/" }, []] if env['SCRIPT_NAME'] == env['REQUEST_PATH']
24
+
25
+ return @app.call(env) unless @matcher =~ env["PATH_INFO"]
26
+ env['PATH_INFO'].sub!(@matcher,'')
27
+ @environment.call(env)
28
+ end
29
+ end
30
+
31
+ class Web < Sinatra::Base
32
+ dir = File.expand_path(File.dirname(__FILE__) + "/../../web")
33
+ set :views, "#{dir}/views"
34
+ set :root, "#{dir}/public"
35
+ set :slim, :pretty => true
36
+ use SprocketsMiddleware, :root => dir
37
+
38
+ helpers do
39
+ def workers
40
+ @workers ||= begin
41
+ Sidekiq.redis.with_connection do |conn|
42
+ conn.smembers('workers').map do |w|
43
+ msg = conn.get("worker:#{w}")
44
+ msg = MultiJson.decode(msg) if msg
45
+ [w, msg]
46
+ end.sort { |x| x[1] ? -1 : 1 }
47
+ end
48
+ end
49
+ end
50
+
51
+ def processed
52
+ Sidekiq.redis.get('stat:processed') || 0
53
+ end
54
+
55
+ def failed
56
+ Sidekiq.redis.get('stat:failed') || 0
57
+ end
58
+
59
+ def queues
60
+ Sidekiq.redis.with_connection do |conn|
61
+ conn.smembers('queues').map do |q|
62
+ [q, conn.llen("queue:#{q}") || 0]
63
+ end.sort { |x,y| x[1] <=> y[1] }
64
+ end
65
+ end
66
+
67
+ def location
68
+ Sidekiq.redis.client.location
69
+ end
70
+
71
+ def root_path
72
+ "#{env['SCRIPT_NAME']}/"
73
+ end
74
+
75
+ def status
76
+ return 'down' if workers.size == 0
77
+ return 'idle' if workers.size > 0 && workers.map { |x| x[1] }.compact.size == 0
78
+ return 'active'
79
+ end
80
+ end
81
+
82
+ get "/" do
83
+ slim :index
84
+ end
85
+
86
+ get "/queues/:name" do
87
+ @name = params[:name]
88
+ @messages = Sidekiq.redis.lrange("queue:#{@name}", 0, 10).map { |str| MultiJson.decode(str) }
89
+ slim :queue
90
+ end
91
+ end
92
+
93
+ end
@@ -24,10 +24,6 @@ module Sidekiq
24
24
  base.extend(ClassMethods)
25
25
  end
26
26
 
27
- def logger
28
- Sidekiq::Util.logger
29
- end
30
-
31
27
  module ClassMethods
32
28
  def perform_async(*args)
33
29
  Sidekiq::Client.push('class' => self.name, 'args' => args)
@@ -1,7 +1,19 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '3.2.1'
4
- gem 'sqlite3'
3
+ platforms :ruby do
4
+ gem 'sqlite3'
5
+ end
6
+
7
+ platforms :jruby do
8
+ gem 'jruby-openssl'
9
+ gem 'activerecord-jdbcsqlite3-adapter'
10
+ end
11
+
12
+ gem 'rails', '3.2.2'
5
13
  gem 'sidekiq', :path => '..'
6
- gem 'resque'
7
14
  gem 'capistrano'
15
+
16
+ # sidekiq-web dependencies
17
+ gem 'slim'
18
+ gem 'sinatra'
19
+ gem 'sprockets'
@@ -13,7 +13,7 @@ class WorkController < ApplicationController
13
13
  end
14
14
 
15
15
  def long
16
- 10.times do |x|
16
+ 50.times do |x|
17
17
  HardWorker.perform_async('bob', 10, x)
18
18
  end
19
19
  render :text => 'enqueued'
@@ -1,6 +1,6 @@
1
1
  Sidekiq.configure_client do |config|
2
- config.redis = { :namespace => 'resque', :size => 2 }
2
+ config.redis = { :size => 2 }
3
3
  end
4
4
  Sidekiq.configure_server do |config|
5
- config.redis = { :namespace => 'resque', :size => 25 }
5
+ config.redis = { :size => 25 }
6
6
  end
@@ -1,7 +1,7 @@
1
- require 'resque/server'
1
+ require 'sidekiq/web'
2
2
 
3
3
  Myapp::Application.routes.draw do
4
- mount Resque::Server.new, :at => '/resque'
4
+ mount Sidekiq::Web => '/sidekiq'
5
5
  get "work" => "work#index"
6
6
  get "work/email" => "work#email"
7
7
  get "work/post" => "work#delayed_post"
@@ -15,10 +15,12 @@ Gem::Specification.new do |gem|
15
15
  gem.version = Sidekiq::VERSION
16
16
  gem.add_dependency 'redis'
17
17
  gem.add_dependency 'redis-namespace'
18
- gem.add_dependency 'connection_pool'
18
+ gem.add_dependency 'connection_pool', '>= 0.9.0'
19
19
  gem.add_dependency 'celluloid'
20
20
  gem.add_dependency 'multi_json'
21
21
  gem.add_development_dependency 'minitest'
22
+ gem.add_development_dependency 'sinatra'
23
+ gem.add_development_dependency 'slim'
22
24
  gem.add_development_dependency 'rake'
23
25
  gem.add_development_dependency 'actionmailer'
24
26
  gem.add_development_dependency 'activerecord'
@@ -8,5 +8,9 @@ require 'minitest/unit'
8
8
  require 'minitest/pride'
9
9
  require 'minitest/autorun'
10
10
 
11
+ require 'sidekiq'
11
12
  require 'sidekiq/util'
12
13
  Sidekiq::Util.logger.level = Logger::ERROR
14
+
15
+ require 'sidekiq/redis_connection'
16
+ REDIS = Sidekiq::RedisConnection.create(:url => 'redis://localhost/sidekiq_test')
@@ -2,10 +2,20 @@ require 'helper'
2
2
  require 'sidekiq/cli'
3
3
  require 'tempfile'
4
4
 
5
+ cli = Sidekiq::CLI.instance
6
+ def cli.die(code)
7
+ @code = code
8
+ end
9
+
10
+ def cli.valid?
11
+ !@code
12
+ end
13
+
5
14
  class TestCli < MiniTest::Unit::TestCase
6
15
  describe 'with cli' do
16
+
7
17
  before do
8
- @cli = new_cli
18
+ @cli = Sidekiq::CLI.instance
9
19
  end
10
20
 
11
21
  it 'blows up with an invalid require' do
@@ -14,7 +24,7 @@ class TestCli < MiniTest::Unit::TestCase
14
24
  end
15
25
  end
16
26
 
17
- it 'blows up with invalid Ruby' do
27
+ it 'requires the specified Ruby code' do
18
28
  @cli.parse(['sidekiq', '-r', './test/fake_env.rb'])
19
29
  assert($LOADED_FEATURES.any? { |x| x =~ /fake_env/ })
20
30
  assert @cli.valid?
@@ -30,11 +40,24 @@ class TestCli < MiniTest::Unit::TestCase
30
40
  assert_equal ['foo'], Sidekiq.options[:queues]
31
41
  end
32
42
 
43
+ it 'changes timeout' do
44
+ @cli.parse(['sidekiq', '-t', '30', '-r', './test/fake_env.rb'])
45
+ assert_equal 30, Sidekiq.options[:timeout]
46
+ end
47
+
33
48
  it 'handles weights' do
34
49
  @cli.parse(['sidekiq', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
35
50
  assert_equal %w(bar foo foo foo), Sidekiq.options[:queues].sort
36
51
  end
37
52
 
53
+ it 'sets verbose' do
54
+ old = Sidekiq::Util.logger.level
55
+ @cli.parse(['sidekiq', '-v', '-r', './test/fake_env.rb'])
56
+ assert_equal Logger::DEBUG, Sidekiq::Util.logger.level
57
+ # If we leave the logger at DEBUG it'll add a lot of noise to the test output
58
+ Sidekiq::Util.logger.level = old
59
+ end
60
+
38
61
  describe 'with pidfile' do
39
62
  before do
40
63
  @tmp_file = Tempfile.new('sidekiq-test')
@@ -140,17 +163,6 @@ class TestCli < MiniTest::Unit::TestCase
140
163
  assert_equal 3, Sidekiq.options[:queues].select{ |q| q == 'seldom' }.length
141
164
  end
142
165
  end
143
-
144
- def new_cli
145
- cli = Sidekiq::CLI.new
146
- def cli.die(code)
147
- @code = code
148
- end
149
-
150
- def cli.valid?
151
- !@code
152
- end
153
- cli
154
- end
155
166
  end
167
+
156
168
  end
@@ -5,7 +5,7 @@ require 'sidekiq/worker'
5
5
  class TestClient < MiniTest::Unit::TestCase
6
6
  describe 'with real redis' do
7
7
  before do
8
- Sidekiq.redis = { :url => 'redis://localhost/sidekiq_test' }
8
+ Sidekiq.redis = REDIS
9
9
  Sidekiq.redis.flushdb
10
10
  end
11
11
 
@@ -13,7 +13,6 @@ class TestClient < MiniTest::Unit::TestCase
13
13
  Sidekiq.client_middleware.entries.clear
14
14
  Sidekiq.client_middleware do |chain|
15
15
  chain.add Sidekiq::Middleware::Client::UniqueJobs
16
- chain.add Sidekiq::Middleware::Client::ResqueWebCompatibility
17
16
  end
18
17
  10.times { Sidekiq::Client.push('customqueue', 'class' => 'Foo', 'args' => [1, 2]) }
19
18
  assert_equal 1, Sidekiq.redis.llen("queue:customqueue")
@@ -39,6 +38,7 @@ class TestClient < MiniTest::Unit::TestCase
39
38
  def @redis.setex(*); nil; end
40
39
  def @redis.expire(*); true; end
41
40
  def @redis.with_connection; yield self; end
41
+ def @redis.with; yield self; end
42
42
  Sidekiq.instance_variable_set(:@redis, @redis)
43
43
  end
44
44
 
@@ -5,13 +5,13 @@ require 'action_mailer'
5
5
  require 'sidekiq/extensions/action_mailer'
6
6
  require 'sidekiq/extensions/active_record'
7
7
 
8
+ Sidekiq.hook_rails!
8
9
 
9
10
  class TestExtensions < MiniTest::Unit::TestCase
10
11
  describe 'sidekiq extensions' do
11
12
  before do
12
- Sidekiq.client_middleware.entries.clear
13
- Sidekiq.instance_variable_set(:@redis, MiniTest::Mock.new)
14
- @redis = Sidekiq.redis
13
+ Sidekiq.redis = REDIS
14
+ Sidekiq.redis.flushdb
15
15
  end
16
16
 
17
17
  class MyModel < ActiveRecord::Base
@@ -21,9 +21,11 @@ class TestExtensions < MiniTest::Unit::TestCase
21
21
  end
22
22
 
23
23
  it 'allows delayed exection of ActiveRecord class methods' do
24
- @redis.expect(:rpush, @redis, ['queue:default', "{\"class\":\"Sidekiq::Extensions::DelayedModel\",\"args\":[\"---\\n- !ruby/class 'TestExtensions::MyModel'\\n- :long_class_method\\n- []\\n\"]}"])
24
+ assert_equal [], Sidekiq::Client.registered_queues
25
+ assert_equal 0, Sidekiq.redis.llen('queue:default')
25
26
  MyModel.delay.long_class_method
26
- @redis.verify
27
+ assert_equal ['default'], Sidekiq::Client.registered_queues
28
+ assert_equal 1, Sidekiq.redis.llen('queue:default')
27
29
  end
28
30
 
29
31
  it 'allows delayed exection of ActiveRecord instance methods' do
@@ -37,9 +39,11 @@ class TestExtensions < MiniTest::Unit::TestCase
37
39
  end
38
40
 
39
41
  it 'allows delayed delivery of ActionMailer mails' do
40
- @redis.expect(:rpush, @redis, ['queue:default', "{\"class\":\"Sidekiq::Extensions::DelayedMailer\",\"args\":[\"---\\n- !ruby/class 'TestExtensions::UserMailer'\\n- :greetings\\n- - 1\\n - 2\\n\"]}"])
42
+ assert_equal [], Sidekiq::Client.registered_queues
43
+ assert_equal 0, Sidekiq.redis.llen('queue:default')
41
44
  UserMailer.delay.greetings(1, 2)
42
- @redis.verify
45
+ assert_equal ['default'], Sidekiq::Client.registered_queues
46
+ assert_equal 1, Sidekiq.redis.llen('queue:default')
43
47
  end
44
48
 
45
49
  end