sidekiq 0.6.0 → 0.7.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 (45) hide show
  1. data/.travis.yml +3 -0
  2. data/Changes.md +15 -0
  3. data/README.md +1 -1
  4. data/TODO.md +0 -2
  5. data/examples/chef/cookbooks/sidekiq/README.rdoc +7 -0
  6. data/examples/chef/cookbooks/sidekiq/recipes/default.rb +54 -0
  7. data/examples/chef/cookbooks/sidekiq/templates/default/monitrc.conf.erb +8 -0
  8. data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.erb +219 -0
  9. data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.yml.erb +28 -0
  10. data/examples/monitrc.conf +6 -0
  11. data/examples/por.rb +10 -0
  12. data/lib/sidekiq.rb +70 -0
  13. data/lib/sidekiq/cli.rb +72 -30
  14. data/lib/sidekiq/client.rb +16 -16
  15. data/lib/sidekiq/extensions/action_mailer.rb +27 -0
  16. data/lib/sidekiq/extensions/active_record.rb +29 -0
  17. data/lib/sidekiq/extensions/generic_proxy.rb +21 -0
  18. data/lib/sidekiq/manager.rb +16 -12
  19. data/lib/sidekiq/middleware/chain.rb +23 -23
  20. data/lib/sidekiq/middleware/client/resque_web_compatibility.rb +1 -4
  21. data/lib/sidekiq/middleware/client/unique_jobs.rb +5 -13
  22. data/lib/sidekiq/middleware/server/failure_jobs.rb +24 -0
  23. data/lib/sidekiq/middleware/server/unique_jobs.rb +1 -5
  24. data/lib/sidekiq/processor.rb +11 -13
  25. data/lib/sidekiq/redis_connection.rb +1 -1
  26. data/lib/sidekiq/util.rb +20 -1
  27. data/lib/sidekiq/version.rb +1 -1
  28. data/lib/sidekiq/worker.rb +1 -1
  29. data/myapp/Gemfile +0 -2
  30. data/myapp/app/controllers/work_controller.rb +25 -1
  31. data/myapp/app/mailers/user_mailer.rb +9 -0
  32. data/myapp/app/models/post.rb +3 -0
  33. data/myapp/app/views/user_mailer/greetings.html.erb +3 -0
  34. data/myapp/app/workers/hard_worker.rb +2 -2
  35. data/myapp/config/initializers/sidekiq.rb +6 -1
  36. data/myapp/config/routes.rb +3 -0
  37. data/sidekiq.gemspec +2 -0
  38. data/test/config.yml +11 -0
  39. data/test/test_cli.rb +119 -3
  40. data/test/test_client.rb +21 -10
  41. data/test/test_extensions.rb +45 -0
  42. data/test/test_manager.rb +2 -2
  43. data/test/test_middleware.rb +7 -12
  44. data/test/test_stats.rb +1 -2
  45. metadata +55 -16
@@ -2,12 +2,9 @@ module Sidekiq
2
2
  module Middleware
3
3
  module Client
4
4
  class ResqueWebCompatibility
5
- def initialize(redis)
6
- @redis = redis
7
- end
8
5
 
9
6
  def call(item, queue)
10
- @redis.sadd('queues', queue)
7
+ Sidekiq.redis.sadd('queues', queue)
11
8
  yield
12
9
  end
13
10
 
@@ -1,3 +1,4 @@
1
+ require 'multi_json'
1
2
  require 'digest'
2
3
 
3
4
  module Sidekiq
@@ -6,24 +7,15 @@ module Sidekiq
6
7
  class UniqueJobs
7
8
  HASH_KEY_EXPIRATION = 30 * 60
8
9
 
9
- def initialize(redis)
10
- @redis = redis
11
- end
12
-
13
10
  def call(item, queue)
14
11
  payload_hash = Digest::MD5.hexdigest(MultiJson.encode(item))
15
- return if already_scheduled?(payload_hash)
16
-
17
- @redis.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
12
+ Sidekiq.redis.with_connection do |redis|
13
+ return if redis.get(payload_hash)
14
+ redis.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
15
+ end
18
16
 
19
17
  yield
20
18
  end
21
-
22
- private
23
-
24
- def already_scheduled?(payload_hash)
25
- !!@redis.get(payload_hash)
26
- end
27
19
  end
28
20
  end
29
21
  end
@@ -0,0 +1,24 @@
1
+ module Sidekiq
2
+ module Middleware
3
+ module Server
4
+ class FailureJobs
5
+ def call(*args)
6
+ yield
7
+ rescue => e
8
+ data = {
9
+ :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
10
+ :payload => args[1],
11
+ :exception => e.class.to_s,
12
+ :error => e.to_s,
13
+ :backtrace => e.backtrace,
14
+ :worker => args[1]['class'],
15
+ :queue => args[2]
16
+ }
17
+
18
+ Sidekiq.redis.rpush(:failed, MultiJson.encode(data))
19
+ raise
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,14 +2,10 @@ module Sidekiq
2
2
  module Middleware
3
3
  module Server
4
4
  class UniqueJobs
5
- def initialize(redis)
6
- @redis = redis
7
- end
8
-
9
5
  def call(*args)
10
6
  yield
11
7
  ensure
12
- @redis.del(Digest::MD5.hexdigest(MultiJson.encode(args[1])))
8
+ Sidekiq.redis.del(Digest::MD5.hexdigest(MultiJson.encode(args[1])))
13
9
  end
14
10
  end
15
11
  end
@@ -1,10 +1,10 @@
1
1
  require 'celluloid'
2
-
3
2
  require 'sidekiq/util'
4
- require 'sidekiq/middleware/chain'
3
+
5
4
  require 'sidekiq/middleware/server/active_record'
6
5
  require 'sidekiq/middleware/server/airbrake'
7
6
  require 'sidekiq/middleware/server/unique_jobs'
7
+ require 'sidekiq/middleware/server/failure_jobs'
8
8
 
9
9
  module Sidekiq
10
10
  class Processor
@@ -12,16 +12,14 @@ module Sidekiq
12
12
  include Celluloid
13
13
 
14
14
  def self.middleware
15
- @middleware ||= begin
16
- chain = Middleware::Chain.new
15
+ raise "Sidekiq::Processor.middleware is now Sidekiq.server_middleware"
16
+ end
17
17
 
18
- # default middleware
19
- chain.register do
20
- use Middleware::Server::Airbrake
21
- use Middleware::Server::UniqueJobs, Sidekiq::Manager.redis
22
- use Middleware::Server::ActiveRecord
23
- end
24
- chain
18
+ def self.default_middleware
19
+ Middleware::Chain.new do |m|
20
+ m.add Middleware::Server::Airbrake
21
+ m.add Middleware::Server::UniqueJobs
22
+ m.add Middleware::Server::ActiveRecord
25
23
  end
26
24
  end
27
25
 
@@ -34,7 +32,7 @@ module Sidekiq
34
32
  klass = constantize(msg['class'])
35
33
  worker = klass.new
36
34
  stats(worker, msg, queue) do
37
- self.class.middleware.invoke(worker, msg, queue) do
35
+ Sidekiq.server_middleware.invoke(worker, msg, queue) do
38
36
  worker.perform(*msg['args'])
39
37
  end
40
38
  end
@@ -47,7 +45,7 @@ module Sidekiq
47
45
  end
48
46
 
49
47
  def to_s
50
- @str ||= "#{hostname}:#{Process.pid}-#{Thread.current.object_id}:default"
48
+ @str ||= "#{hostname}:#{process_id}-#{Thread.current.object_id}:default"
51
49
  end
52
50
 
53
51
  private
@@ -6,7 +6,7 @@ module Sidekiq
6
6
  def self.create(options={})
7
7
  url = options[:url] || ENV['REDISTOGO_URL'] || 'redis://localhost:6379/0'
8
8
  client = build_client(url, options[:namespace])
9
- return ConnectionPool.new(:timeout => 1, :size => 25) { client } unless options[:use_pool] == false
9
+ return ConnectionPool.new(:timeout => 1, :size => (options[:size] || Sidekiq.options[:concurrency] || 25)) { client } unless options[:use_pool] == false
10
10
  client
11
11
  end
12
12
 
data/lib/sidekiq/util.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'logger'
2
2
 
3
3
  module Sidekiq
4
+ ##
5
+ # This module is part of Sidekiq core and not intended for extensions.
6
+ #
4
7
  module Util
5
8
 
6
9
  def self.logger
@@ -39,7 +42,23 @@ module Sidekiq
39
42
  end
40
43
 
41
44
  def redis
42
- Sidekiq::Manager.redis
45
+ Sidekiq.redis
46
+ end
47
+
48
+ def process_id
49
+ Sidekiq::Util.process_id
50
+ end
51
+
52
+ def self.process_id
53
+ @pid ||= begin
54
+ if Process.pid == 1
55
+ # Heroku does not expose pids.
56
+ require 'securerandom'
57
+ (SecureRandom.random_number * 4_000_000_000).floor.to_s(16)
58
+ else
59
+ Process.pid
60
+ end
61
+ end
43
62
  end
44
63
  end
45
64
  end
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -34,7 +34,7 @@ module Sidekiq
34
34
  end
35
35
 
36
36
  def queue(name)
37
- Sidekiq::Client.queues[self.name] = name.to_s
37
+ Sidekiq::Client.queue_mappings[self.name] = name.to_s
38
38
  end
39
39
  end
40
40
  end
data/myapp/Gemfile CHANGED
@@ -1,7 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'jruby-openssl' if defined?(JRUBY_VERSION)
4
-
5
3
  gem 'rails', '3.2.1'
6
4
  gem 'sqlite3'
7
5
  gem 'sidekiq', :path => '..'
@@ -3,7 +3,31 @@ class WorkController < ApplicationController
3
3
  @count = rand(100)
4
4
  puts "Adding #{@count} jobs"
5
5
  @count.times do |x|
6
- HardWorker.perform_async('bubba', x)
6
+ HardWorker.perform_async('bubba', 0.01, x)
7
7
  end
8
8
  end
9
+
10
+ def email
11
+ UserMailer.delay.greetings(Time.now)
12
+ render :nothing => true
13
+ end
14
+
15
+ def long
16
+ 10.times do |x|
17
+ HardWorker.perform_async('bob', 10, x)
18
+ end
19
+ render :text => 'enqueued'
20
+ end
21
+
22
+ def delayed_post
23
+ p = Post.first
24
+ unless p
25
+ p = Post.create!(:title => "Title!", :body => 'Body!')
26
+ p2 = Post.create!(:title => "Other!", :body => 'Second Body!')
27
+ else
28
+ p2 = Post.second
29
+ end
30
+ p.delay.long_method(p2)
31
+ render :nothing => true
32
+ end
9
33
  end
@@ -0,0 +1,9 @@
1
+ class UserMailer < ActionMailer::Base
2
+ default from: "sidekiq@example.com"
3
+
4
+ def greetings(now)
5
+ @now = now
6
+ @hostname = `hostname`.strip
7
+ mail(:to => 'mperham@gmail.com', :subject => 'Ahoy Matey!')
8
+ end
9
+ end
@@ -1,2 +1,5 @@
1
1
  class Post < ActiveRecord::Base
2
+ def long_method(other_post)
3
+ puts "Running long method with #{self.id} and #{other_post.id}"
4
+ end
2
5
  end
@@ -0,0 +1,3 @@
1
+ <p>
2
+ Hi Mike, it's <%= @now %> and I'm at <%= @hostname %>.
3
+ </p>
@@ -1,8 +1,8 @@
1
1
  class HardWorker
2
2
  include Sidekiq::Worker
3
3
 
4
- def perform(name, count)
5
- sleep 1
4
+ def perform(name, count, salt)
6
5
  print "#{Time.now}\n"
6
+ sleep count
7
7
  end
8
8
  end
@@ -1 +1,6 @@
1
- Sidekiq::Client.redis = Sidekiq::RedisConnection.create(:namespace => 'resque')
1
+ Sidekiq.configure_client do |config|
2
+ config.redis = Sidekiq::RedisConnection.create(:namespace => 'resque', :size => 2)
3
+ end
4
+ Sidekiq.configure_server do |config|
5
+ config.redis = Sidekiq::RedisConnection.create(:namespace => 'resque', :size => 25)
6
+ end
@@ -3,4 +3,7 @@ require 'resque/server'
3
3
  Myapp::Application.routes.draw do
4
4
  mount Resque::Server.new, :at => '/resque'
5
5
  get "work" => "work#index"
6
+ get "work/email" => "work#email"
7
+ get "work/post" => "work#delayed_post"
8
+ get "work/long" => "work#long"
6
9
  end
data/sidekiq.gemspec CHANGED
@@ -20,4 +20,6 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency 'multi_json'
21
21
  gem.add_development_dependency 'minitest'
22
22
  gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'actionmailer'
24
+ gem.add_development_dependency 'activerecord'
23
25
  end
data/test/config.yml ADDED
@@ -0,0 +1,11 @@
1
+ ---
2
+ :verbose: false
3
+ :namespace: test_namespace
4
+ :server: 127.0.0.1:1234
5
+ :environment: xzibit
6
+ :require: ./test/fake_env.rb
7
+ :pidfile: /tmp/sidekiq-config-test.pid
8
+ :concurrency: 50
9
+ :queues:
10
+ - [often, 2]
11
+ - [seldom, 1]
data/test/test_cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'helper'
2
2
  require 'sidekiq/cli'
3
+ require 'tempfile'
3
4
 
4
5
  class TestCli < MiniTest::Unit::TestCase
5
6
  describe 'with cli' do
@@ -21,17 +22,132 @@ class TestCli < MiniTest::Unit::TestCase
21
22
 
22
23
  it 'changes concurrency' do
23
24
  @cli.parse(['sidekiq', '-c', '60', '-r', './test/fake_env.rb'])
24
- assert_equal 60, @cli.options[:processor_count]
25
+ assert_equal 60, Sidekiq.options[:concurrency]
25
26
  end
26
27
 
27
28
  it 'changes queues' do
28
29
  @cli.parse(['sidekiq', '-q', 'foo', '-r', './test/fake_env.rb'])
29
- assert_equal ['foo'], @cli.options[:queues]
30
+ assert_equal ['foo'], Sidekiq.options[:queues]
30
31
  end
31
32
 
32
33
  it 'handles weights' do
33
34
  @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
+ assert_equal %w(bar foo foo foo), Sidekiq.options[:queues].sort
36
+ end
37
+
38
+ describe 'with pidfile' do
39
+ before do
40
+ @tmp_file = Tempfile.new('sidekiq-test')
41
+ @tmp_path = @tmp_file.path
42
+ @tmp_file.close!
43
+
44
+ @cli.parse(['sidekiq', '-P', @tmp_path, '-r', './test/fake_env.rb'])
45
+ end
46
+
47
+ after do
48
+ File.unlink @tmp_path if File.exist? @tmp_path
49
+ end
50
+
51
+ it 'sets pidfile path' do
52
+ assert_equal @tmp_path, Sidekiq.options[:pidfile]
53
+ end
54
+
55
+ it 'writes pidfile' do
56
+ assert_equal File.read(@tmp_path).strip.to_i, Process.pid
57
+ end
58
+ end
59
+
60
+ describe 'with config file' do
61
+ before do
62
+ @cli.parse(['sidekiq', '-C', './test/config.yml'])
63
+ end
64
+
65
+ it 'takes a path' do
66
+ assert_equal './test/config.yml', Sidekiq.options[:config_file]
67
+ end
68
+
69
+ it 'sets verbose' do
70
+ refute Sidekiq.options[:verbose]
71
+ end
72
+
73
+ it 'sets namespace' do
74
+ assert_equal "test_namespace", Sidekiq.options[:namespace]
75
+ end
76
+
77
+ it 'sets require file' do
78
+ assert_equal './test/fake_env.rb', Sidekiq.options[:require]
79
+ end
80
+
81
+ it 'sets environment' do
82
+ assert_equal 'xzibit', Sidekiq.options[:environment]
83
+ end
84
+
85
+ it 'sets concurrency' do
86
+ assert_equal 50, Sidekiq.options[:concurrency]
87
+ end
88
+
89
+ it 'sets pid file' do
90
+ assert_equal '/tmp/sidekiq-config-test.pid', Sidekiq.options[:pidfile]
91
+ end
92
+
93
+ it 'sets queues' do
94
+ assert_equal 2, Sidekiq.options[:queues].select{ |q| q == 'often' }.length
95
+ assert_equal 1, Sidekiq.options[:queues].select{ |q| q == 'seldom' }.length
96
+ end
97
+ end
98
+
99
+ describe 'with config file and flags' do
100
+ before do
101
+ # We need an actual file here.
102
+ @tmp_lib_path = '/tmp/require-me.rb'
103
+ File.open(@tmp_lib_path, 'w') do |f|
104
+ f.puts "# do work"
105
+ end
106
+
107
+ @tmp_file = Tempfile.new('sidekiqr')
108
+ @tmp_path = @tmp_file.path
109
+ @tmp_file.close!
110
+
111
+ @cli.parse(['sidekiq',
112
+ '-C', './test/config.yml',
113
+ '-n', 'sweet_story_bro',
114
+ '-e', 'snoop',
115
+ '-c', '100',
116
+ '-r', @tmp_lib_path,
117
+ '-P', @tmp_path,
118
+ '-q', 'often,7',
119
+ '-q', 'seldom,3'])
120
+ end
121
+
122
+ after do
123
+ File.unlink @tmp_lib_path if File.exist? @tmp_lib_path
124
+ File.unlink @tmp_path if File.exist? @tmp_path
125
+ end
126
+
127
+ it 'uses concurrency flag' do
128
+ assert_equal 100, Sidekiq.options[:concurrency]
129
+ end
130
+
131
+ it 'uses namespace flag' do
132
+ assert_equal "sweet_story_bro", Sidekiq.options[:namespace]
133
+ end
134
+
135
+ it 'uses require file flag' do
136
+ assert_equal @tmp_lib_path, Sidekiq.options[:require]
137
+ end
138
+
139
+ it 'uses environment flag' do
140
+ assert_equal 'snoop', Sidekiq.options[:environment]
141
+ end
142
+
143
+ it 'uses pidfile flag' do
144
+ assert_equal @tmp_path, Sidekiq.options[:pidfile]
145
+ end
146
+
147
+ it 'sets queues' do
148
+ assert_equal 7, Sidekiq.options[:queues].select{ |q| q == 'often' }.length
149
+ assert_equal 3, Sidekiq.options[:queues].select{ |q| q == 'seldom' }.length
150
+ end
35
151
  end
36
152
 
37
153
  def new_cli