sidekiq 0.5.1 → 0.6.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 (47) hide show
  1. data/.gitignore +5 -0
  2. data/.rvmrc +2 -1
  3. data/Changes.md +11 -0
  4. data/LICENSE +11 -4
  5. data/README.md +1 -7
  6. data/TODO.md +0 -1
  7. data/bin/sidekiq +2 -0
  8. data/examples/por.rb +17 -0
  9. data/examples/sinkiq.rb +57 -0
  10. data/lib/sidekiq.rb +1 -1
  11. data/lib/sidekiq/cli.rb +72 -34
  12. data/lib/sidekiq/client.rb +33 -16
  13. data/lib/sidekiq/manager.rb +37 -47
  14. data/lib/sidekiq/middleware/chain.rb +92 -0
  15. data/lib/sidekiq/middleware/client/resque_web_compatibility.rb +17 -0
  16. data/lib/sidekiq/middleware/client/unique_jobs.rb +30 -0
  17. data/lib/sidekiq/middleware/server/active_record.rb +13 -0
  18. data/lib/sidekiq/middleware/server/airbrake.rb +30 -0
  19. data/lib/sidekiq/middleware/server/unique_jobs.rb +17 -0
  20. data/lib/sidekiq/processor.rb +74 -16
  21. data/lib/sidekiq/redis_connection.rb +23 -0
  22. data/lib/sidekiq/testing.rb +34 -0
  23. data/lib/sidekiq/util.rb +21 -12
  24. data/lib/sidekiq/version.rb +1 -1
  25. data/lib/sidekiq/worker.rb +6 -7
  26. data/myapp/Gemfile +4 -1
  27. data/myapp/Gemfile.lock +29 -6
  28. data/myapp/app/controllers/work_controller.rb +9 -0
  29. data/myapp/app/views/work/index.html.erb +1 -0
  30. data/myapp/app/workers/hard_worker.rb +4 -2
  31. data/myapp/config/environments/development.rb +1 -0
  32. data/myapp/config/initializers/sidekiq.rb +1 -0
  33. data/myapp/config/routes.rb +4 -56
  34. data/sidekiq.gemspec +1 -0
  35. data/test/fake_env.rb +0 -0
  36. data/test/helper.rb +3 -0
  37. data/test/test_cli.rb +49 -0
  38. data/test/test_client.rb +52 -10
  39. data/test/test_manager.rb +11 -6
  40. data/test/test_middleware.rb +39 -20
  41. data/test/test_processor.rb +3 -2
  42. data/test/test_stats.rb +79 -0
  43. data/test/test_testing.rb +32 -0
  44. metadata +47 -18
  45. data/Gemfile.lock +0 -32
  46. data/lib/sidekiq/middleware.rb +0 -89
  47. data/test/timed_queue.rb +0 -42
@@ -1,5 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'jruby-openssl' if defined?(JRUBY_VERSION)
4
+
3
5
  gem 'rails', '3.2.1'
4
6
  gem 'sqlite3'
5
- gem 'sidekiq'#, :path => '..'
7
+ gem 'sidekiq', :path => '..'
8
+ gem 'resque'
@@ -1,3 +1,13 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ sidekiq (0.6.0)
5
+ celluloid
6
+ connection_pool
7
+ multi_json
8
+ redis
9
+ redis-namespace
10
+
1
11
  GEM
2
12
  remote: https://rubygems.org/
3
13
  specs:
@@ -37,6 +47,7 @@ GEM
37
47
  i18n (0.6.0)
38
48
  journey (1.0.1)
39
49
  json (1.6.5)
50
+ json (1.6.5-java)
40
51
  mail (2.4.1)
41
52
  i18n (>= 0.4.0)
42
53
  mime-types (~> 1.16)
@@ -47,6 +58,8 @@ GEM
47
58
  rack (1.4.1)
48
59
  rack-cache (1.1)
49
60
  rack (>= 0.4)
61
+ rack-protection (1.2.0)
62
+ rack
50
63
  rack-ssl (1.3.2)
51
64
  rack
52
65
  rack-test (0.6.1)
@@ -70,11 +83,17 @@ GEM
70
83
  rdoc (3.12)
71
84
  json (~> 1.4)
72
85
  redis (2.2.2)
73
- sidekiq (0.5.1)
74
- celluloid
75
- connection_pool
76
- multi_json
77
- redis
86
+ redis-namespace (1.0.3)
87
+ redis (< 3.0.0)
88
+ resque (1.19.0)
89
+ multi_json (~> 1.0)
90
+ redis-namespace (~> 1.0.2)
91
+ sinatra (>= 0.9.2)
92
+ vegas (~> 0.1.2)
93
+ sinatra (1.3.2)
94
+ rack (~> 1.3, >= 1.3.6)
95
+ rack-protection (~> 1.2)
96
+ tilt (~> 1.3, >= 1.3.3)
78
97
  sprockets (2.1.2)
79
98
  hike (~> 1.2)
80
99
  rack (~> 1.0)
@@ -86,11 +105,15 @@ GEM
86
105
  polyglot
87
106
  polyglot (>= 0.3.1)
88
107
  tzinfo (0.3.31)
108
+ vegas (0.1.11)
109
+ rack (>= 1.0.0)
89
110
 
90
111
  PLATFORMS
112
+ java
91
113
  ruby
92
114
 
93
115
  DEPENDENCIES
94
116
  rails (= 3.2.1)
95
- sidekiq
117
+ resque
118
+ sidekiq!
96
119
  sqlite3
@@ -0,0 +1,9 @@
1
+ class WorkController < ApplicationController
2
+ def index
3
+ @count = rand(100)
4
+ puts "Adding #{@count} jobs"
5
+ @count.times do |x|
6
+ HardWorker.perform_async('bubba', x)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ Added <%= @count %> jobs!
@@ -1,6 +1,8 @@
1
1
  class HardWorker
2
+ include Sidekiq::Worker
3
+
2
4
  def perform(name, count)
3
- sleep 0.01
4
- puts 'done'
5
+ sleep 1
6
+ print "#{Time.now}\n"
5
7
  end
6
8
  end
@@ -34,4 +34,5 @@ Myapp::Application.configure do
34
34
 
35
35
  # Expands the lines which load the assets
36
36
  config.assets.debug = true
37
+ config.assets.logger = nil
37
38
  end
@@ -0,0 +1 @@
1
+ Sidekiq::Client.redis = Sidekiq::RedisConnection.create(:namespace => 'resque')
@@ -1,58 +1,6 @@
1
- Myapp::Application.routes.draw do
2
- # The priority is based upon order of creation:
3
- # first created -> highest priority.
4
-
5
- # Sample of regular route:
6
- # match 'products/:id' => 'catalog#view'
7
- # Keep in mind you can assign values other than :controller and :action
8
-
9
- # Sample of named route:
10
- # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
11
- # This route can be invoked with purchase_url(:id => product.id)
12
-
13
- # Sample resource route (maps HTTP verbs to controller actions automatically):
14
- # resources :products
15
-
16
- # Sample resource route with options:
17
- # resources :products do
18
- # member do
19
- # get 'short'
20
- # post 'toggle'
21
- # end
22
- #
23
- # collection do
24
- # get 'sold'
25
- # end
26
- # end
27
-
28
- # Sample resource route with sub-resources:
29
- # resources :products do
30
- # resources :comments, :sales
31
- # resource :seller
32
- # end
1
+ require 'resque/server'
33
2
 
34
- # Sample resource route with more complex sub-resources
35
- # resources :products do
36
- # resources :comments
37
- # resources :sales do
38
- # get 'recent', :on => :collection
39
- # end
40
- # end
41
-
42
- # Sample resource route within a namespace:
43
- # namespace :admin do
44
- # # Directs /admin/products/* to Admin::ProductsController
45
- # # (app/controllers/admin/products_controller.rb)
46
- # resources :products
47
- # end
48
-
49
- # You can have the root of your site routed with "root"
50
- # just remember to delete public/index.html.
51
- # root :to => 'welcome#index'
52
-
53
- # See how all your routes lay out with "rake routes"
54
-
55
- # This is a legacy wild controller route that's not recommended for RESTful applications.
56
- # Note: This route will make all actions in every controller accessible via GET requests.
57
- # match ':controller(/:action(/:id))(.:format)'
3
+ Myapp::Application.routes.draw do
4
+ mount Resque::Server.new, :at => '/resque'
5
+ get "work" => "work#index"
58
6
  end
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.require_paths = ["lib"]
15
15
  gem.version = Sidekiq::VERSION
16
16
  gem.add_dependency 'redis'
17
+ gem.add_dependency 'redis-namespace'
17
18
  gem.add_dependency 'connection_pool'
18
19
  gem.add_dependency 'celluloid'
19
20
  gem.add_dependency 'multi_json'
File without changes
@@ -7,3 +7,6 @@ end
7
7
  require 'minitest/unit'
8
8
  require 'minitest/pride'
9
9
  require 'minitest/autorun'
10
+
11
+ require 'sidekiq/util'
12
+ Sidekiq::Util.logger.level = Logger::ERROR
@@ -0,0 +1,49 @@
1
+ require 'helper'
2
+ require 'sidekiq/cli'
3
+
4
+ class TestCli < MiniTest::Unit::TestCase
5
+ describe 'with cli' do
6
+ before do
7
+ @cli = new_cli
8
+ end
9
+
10
+ it 'blows up with an invalid require' do
11
+ assert_raises ArgumentError do
12
+ @cli.parse(['sidekiq', '-r', 'foobar'])
13
+ end
14
+ end
15
+
16
+ it 'blows up with invalid Ruby' do
17
+ @cli.parse(['sidekiq', '-r', './test/fake_env.rb'])
18
+ assert($LOADED_FEATURES.any? { |x| x =~ /fake_env/ })
19
+ assert @cli.valid?
20
+ end
21
+
22
+ it 'changes concurrency' do
23
+ @cli.parse(['sidekiq', '-c', '60', '-r', './test/fake_env.rb'])
24
+ assert_equal 60, @cli.options[:processor_count]
25
+ end
26
+
27
+ it 'changes queues' do
28
+ @cli.parse(['sidekiq', '-q', 'foo', '-r', './test/fake_env.rb'])
29
+ assert_equal ['foo'], @cli.options[:queues]
30
+ end
31
+
32
+ it 'handles weights' do
33
+ @cli.parse(['sidekiq', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
34
+ assert_equal %w(bar foo foo foo), @cli.options[:queues].sort
35
+ end
36
+
37
+ def new_cli
38
+ cli = Sidekiq::CLI.new
39
+ def cli.die(code)
40
+ @code = code
41
+ end
42
+
43
+ def cli.valid?
44
+ !@code
45
+ end
46
+ cli
47
+ end
48
+ end
49
+ end
@@ -3,9 +3,41 @@ require 'sidekiq/client'
3
3
  require 'sidekiq/worker'
4
4
 
5
5
  class TestClient < MiniTest::Unit::TestCase
6
+ describe 'with real redis' do
7
+ before do
8
+ Sidekiq::Client.redis = Redis.connect(:url => 'redis://localhost/sidekiq_test')
9
+ Sidekiq::Client.redis.flushdb
10
+ end
11
+
12
+ it 'does not push duplicate messages when configured for unique only' do
13
+ Sidekiq::Client.middleware.entries.clear
14
+ Sidekiq::Client.middleware.register do
15
+ use Sidekiq::Middleware::Client::UniqueJobs, Sidekiq::Client.redis
16
+ use Sidekiq::Middleware::Client::ResqueWebCompatibility, Sidekiq::Client.redis
17
+ end
18
+ 10.times { Sidekiq::Client.push('customqueue', 'class' => 'Foo', 'args' => [1, 2]) }
19
+ assert_equal 1, Sidekiq::Client.redis.llen("queue:customqueue")
20
+ end
21
+
22
+ it 'does push duplicate messages when not configured for unique only' do
23
+ Sidekiq::Client.middleware.unregister(Sidekiq::Middleware::Client::UniqueJobs)
24
+ 10.times { Sidekiq::Client.push('customqueue2', 'class' => 'Foo', 'args' => [1, 2]) }
25
+ assert_equal 10, Sidekiq::Client.redis.llen("queue:customqueue2")
26
+ end
27
+ end
28
+
6
29
  describe 'with mock redis' do
7
30
  before do
8
31
  @redis = MiniTest::Mock.new
32
+ def @redis.multi; yield; end
33
+ def @redis.set(*); true; end
34
+ def @redis.sadd(*); true; end
35
+ def @redis.srem(*); true; end
36
+ def @redis.get(*); nil; end
37
+ def @redis.del(*); nil; end
38
+ def @redis.incrby(*); nil; end
39
+ def @redis.setex(*); nil; end
40
+ def @redis.expire(*); true; end
9
41
  Sidekiq::Client.redis = @redis
10
42
  end
11
43
 
@@ -21,29 +53,39 @@ class TestClient < MiniTest::Unit::TestCase
21
53
 
22
54
  it 'pushes messages to redis' do
23
55
  @redis.expect :rpush, 1, ['queue:foo', String]
24
- count = Sidekiq::Client.push('foo', 'class' => 'Foo', 'args' => [1, 2])
25
- assert count > 0
56
+ pushed = Sidekiq::Client.push('foo', 'class' => 'Foo', 'args' => [1, 2])
57
+ assert pushed
26
58
  @redis.verify
27
59
  end
28
60
 
29
61
  class MyWorker
30
62
  include Sidekiq::Worker
31
- def self.queue
32
- 'foo'
33
- end
34
63
  end
35
64
 
36
65
  it 'handles perform_async' do
37
66
  @redis.expect :rpush, 1, ['queue:default', String]
38
- count = MyWorker.perform_async(1, 2)
39
- assert count > 0
67
+ pushed = MyWorker.perform_async(1, 2)
68
+ assert pushed
40
69
  @redis.verify
41
70
  end
42
71
 
43
72
  it 'enqueues messages to redis' do
44
- @redis.expect :rpush, 1, ['queue:foo', String]
45
- count = Sidekiq::Client.enqueue(MyWorker, 1, 2)
46
- assert count > 0
73
+ @redis.expect :rpush, 1, ['queue:default', String]
74
+ pushed = Sidekiq::Client.enqueue(MyWorker, 1, 2)
75
+ assert pushed
76
+ @redis.verify
77
+ end
78
+
79
+ class QueuedWorker
80
+ include Sidekiq::Worker
81
+
82
+ queue :flimflam
83
+ end
84
+
85
+ it 'enqueues to the named queue' do
86
+ @redis.expect :rpush, 1, ['queue:flimflam', String]
87
+ pushed = QueuedWorker.perform_async(1, 2)
88
+ assert pushed
47
89
  @redis.verify
48
90
  end
49
91
  end
@@ -1,36 +1,41 @@
1
1
  require 'helper'
2
2
  require 'sidekiq'
3
3
  require 'sidekiq/manager'
4
- require 'timed_queue'
4
+
5
+ # for TimedQueue
6
+ require 'connection_pool'
5
7
 
6
8
  class TestManager < MiniTest::Unit::TestCase
7
9
  describe 'with redis' do
8
10
  before do
9
- Sidekiq::Client.redis = @redis = Redis.connect(:url => 'redis://localhost/sidekiq_test')
11
+ Sidekiq::Manager.redis = Sidekiq::Client.redis = @redis = Sidekiq::RedisConnection.create(:url => 'redis://localhost/sidekiq_test')
10
12
  @redis.flushdb
11
13
  $processed = 0
14
+ $mutex = Mutex.new
12
15
  end
13
16
 
14
17
  class IntegrationWorker
15
18
  include Sidekiq::Worker
16
19
 
17
20
  def perform(a, b)
18
- $processed += 1
21
+ $mutex.synchronize do
22
+ $processed += 1
23
+ end
19
24
  a + b
20
25
  end
21
26
  end
22
27
 
23
28
  it 'processes messages' do
24
29
  Sidekiq::Client.push(:foo, 'class' => IntegrationWorker, 'args' => [1, 2])
25
- Sidekiq::Client.push(:foo, 'class' => IntegrationWorker, 'args' => [1, 2])
30
+ Sidekiq::Client.push(:foo, 'class' => IntegrationWorker, 'args' => [1, 3])
26
31
 
27
32
  q = TimedQueue.new
28
- mgr = Sidekiq::Manager.new("redis://localhost/sidekiq_test", :queues => [:foo])
33
+ mgr = Sidekiq::Manager.new(:queues => [:foo], :processor_count => 2)
29
34
  mgr.when_done do |_|
30
35
  q << 'done' if $processed == 2
31
36
  end
32
37
  mgr.start!
33
- result = q.timed_pop
38
+ result = q.timed_pop(1.0)
34
39
  assert_equal 'done', result
35
40
  mgr.stop
36
41
  end
@@ -1,12 +1,13 @@
1
1
  require 'helper'
2
- require 'sidekiq/middleware'
2
+ require 'sidekiq/middleware/chain'
3
+ require 'sidekiq/middleware/server/unique_jobs'
3
4
  require 'sidekiq/processor'
4
5
 
5
6
  class TestMiddleware < MiniTest::Unit::TestCase
6
7
  describe 'middleware chain' do
7
8
  before do
8
- @boss = MiniTest::Mock.new
9
- Celluloid.logger = nil
9
+ Sidekiq::Manager.redis = @redis = Sidekiq::RedisConnection.create(:url => 'redis://localhost/sidekiq_test')
10
+ Sidekiq::Client.redis = nil
10
11
  end
11
12
 
12
13
  class CustomMiddleware
@@ -15,48 +16,66 @@ class TestMiddleware < MiniTest::Unit::TestCase
15
16
  @recorder = recorder
16
17
  end
17
18
 
18
- def call(worker, msg)
19
+ def call(*args)
19
20
  @recorder << [@name, 'before']
20
21
  yield
21
22
  @recorder << [@name, 'after']
22
23
  end
23
24
  end
24
25
 
25
- it 'configures default middleware' do
26
- chain = Sidekiq::Middleware::Chain.chain
27
- assert_equal chain, Sidekiq::Middleware::Chain.default
28
- end
29
-
30
26
  it 'supports custom middleware' do
31
- Sidekiq::Middleware::Chain.register do
27
+ chain = Sidekiq::Middleware::Chain.new
28
+ chain.register do
32
29
  use CustomMiddleware, 1, []
33
30
  end
34
- chain = Sidekiq::Middleware::Chain.chain
35
- assert_equal chain.last.klass, CustomMiddleware
31
+
32
+ assert_equal CustomMiddleware, chain.entries.last.klass
36
33
  end
37
34
 
38
35
  class CustomWorker
36
+ include Sidekiq::Worker
39
37
  def perform(recorder)
40
38
  recorder << ['work_performed']
41
39
  end
42
40
  end
43
41
 
42
+ class NonYieldingMiddleware
43
+ def call(*args)
44
+ end
45
+ end
46
+
44
47
  it 'executes middleware in the proper order' do
48
+ Sidekiq::Middleware::Server::UniqueJobs.class_eval do
49
+ def call(*args); yield; end
50
+ end
51
+
45
52
  recorder = []
46
53
  msg = { 'class' => CustomWorker.to_s, 'args' => [recorder] }
47
54
 
48
- Sidekiq::Middleware::Chain.register do
55
+ Sidekiq::Processor.middleware.register do
49
56
  2.times { |i| use CustomMiddleware, i.to_s, recorder }
50
57
  end
51
58
 
52
- processor = Sidekiq::Processor.new(@boss)
53
- @boss.expect(:processor_done!, nil, [processor])
54
- processor.process(msg)
55
- assert_equal recorder.flatten, %w(0 before 1 before work_performed 1 after 0 after)
59
+ boss = MiniTest::Mock.new
60
+ processor = Sidekiq::Processor.new(boss)
61
+ boss.expect(:processor_done!, nil, [processor])
62
+ processor.process(msg, 'default')
63
+ assert_equal %w(0 before work_performed 0 after), recorder.flatten
56
64
  end
57
- end
58
- end
59
-
60
65
 
66
+ it 'allows middleware to abruptly stop processing rest of chain' do
67
+ recorder = []
68
+ chain = Sidekiq::Middleware::Chain.new
61
69
 
70
+ chain.register do
71
+ use NonYieldingMiddleware
72
+ use CustomMiddleware, 1, recorder
73
+ end
62
74
 
75
+ final_action = nil
76
+ chain.invoke { final_action = true }
77
+ assert_equal nil, final_action
78
+ assert_equal [], recorder
79
+ end
80
+ end
81
+ end