sidekiq 2.6.4 → 2.6.5

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.

data/.rvmrc CHANGED
@@ -1,3 +1,3 @@
1
1
  export RUBYOPT="-Ilib:test"
2
- rvm use 1.9.3@sidekiq --create
2
+ rvm use 1.9.3-p327@sidekiq --create
3
3
  #rvm use jruby@sidekiq --create
data/Changes.md CHANGED
@@ -1,3 +1,14 @@
1
+ 2.6.5
2
+ -----------
3
+
4
+ - Several reliability fixes for job requeueing upon termination [apinstein, #622, #624]
5
+ - Fix typo in capistrano recipe
6
+ - Add `retry_queue` option so retries can be given lower priority [ryanlower, #620]
7
+
8
+ ```ruby
9
+ sidekiq_options queue: 'high', retry_queue: 'low'
10
+ ```
11
+
1
12
  2.6.4
2
13
  -----------
3
14
 
@@ -0,0 +1,92 @@
1
+ #!/bin/bash
2
+ # sidekiq Init script for Sidekiq
3
+ # chkconfig: 345 100 75
4
+ #
5
+ # Description: Starts and Stops Sidekiq message processor for Stratus application.
6
+ #
7
+ # User-specified exit parameters used in this script:
8
+ #
9
+ # Exit Code 5 - Incorrect User ID
10
+ # Exit Code 6 - Directory not found
11
+
12
+
13
+ #Variable Set
14
+ APP="stratus"
15
+ APP_DIR="/opt/railsapps/${APP}"
16
+ APP_CONFIG="${APP_DIR}/config"
17
+ LOG_FILE="$APP_DIR/log/sidekiq.log"
18
+ LOCK_FILE="$APP_DIR/${APP}-lock"
19
+ PID_FILE="$APP_DIR/${APP}.pid"
20
+ GEMFILE="$APP_DIR/Gemfile"
21
+ SIDEKIQ="sidekiq"
22
+ APP_ENV="production"
23
+ BUNDLE="bundle"
24
+
25
+ START_CMD="$BUNDLE exec $SIDEKIQ -e $APP_ENV -P $PID_FILE"
26
+ RETVAL=0
27
+
28
+
29
+ start() {
30
+
31
+ status
32
+ if [ $? -eq 1 ]; then
33
+
34
+ [ `id -u` == '0' ] || (echo "$SIDEKIQ runs as root only .."; exit 5)
35
+ [ -d $APP_DIR ] || (echo "$APP_DIR not found!.. Exiting"; exit 6)
36
+ cd $APP_DIR
37
+ echo "Starting $SIDEKIQ message processor .. "
38
+ $START_CMD >> $LOG_FILE 2>&1 &
39
+ RETVAL=$?
40
+ #Sleeping for 8 seconds for process to be precisely visible in process table - See status ()
41
+ sleep 8
42
+ [ $RETVAL -eq 0 ] && touch $LOCK_FILE
43
+ return $RETVAL
44
+ else
45
+ echo "$SIDEKIQ message processor is already running .. "
46
+ fi
47
+
48
+
49
+ }
50
+
51
+ stop() {
52
+
53
+ echo "Stopping $SIDEKIQ message processor .."
54
+ SIG="INT"
55
+ kill -$SIG `cat $PID_FILE`
56
+ RETVAL=$?
57
+ [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
58
+ return $RETVAL
59
+ }
60
+
61
+
62
+ status() {
63
+
64
+ ps -ef | grep 'sidekiq [0-9].[0-9].[0-9]' | grep -v grep
65
+ return $?
66
+ }
67
+
68
+
69
+ case "$1" in
70
+ start)
71
+ start
72
+ ;;
73
+ stop)
74
+ stop
75
+ ;;
76
+ status)
77
+ status
78
+
79
+ if [ $? -eq 0 ]; then
80
+ echo "$SIDEKIQ message processor is running .."
81
+ RETVAL=0
82
+ else
83
+ echo "$SIDEKIQ message processor is stopped .."
84
+ RETVAL=1
85
+ fi
86
+ ;;
87
+ *)
88
+ echo "Usage: $0 {start|stop|status}"
89
+ exit 0
90
+ ;;
91
+ esac
92
+ exit $RETVAL
@@ -13,21 +13,21 @@ Capistrano::Configuration.instance.load do
13
13
 
14
14
  namespace :sidekiq do
15
15
  def for_each_process(&block)
16
- 0.upto(fetch(:sidekiq_processes) - 1) do |process|
17
- yield process == 0 ? "#{fetch(:sidekiq_pid)}" : "#{fetch(:sidekiq_pid)}-#{process}"
16
+ fetch(:sidekiq_processes).times do |idx|
17
+ yield((idx == 0 ? "#{fetch(:sidekiq_pid)}" : "#{fetch(:sidekiq_pid)}-#{idx}"), idx)
18
18
  end
19
19
  end
20
20
 
21
21
  desc "Quiet sidekiq (stop accepting new work)"
22
22
  task :quiet, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
23
- for_each_process do |pid_file|
23
+ for_each_process do |pid_file, idx|
24
24
  run "if [ -d #{current_path} ] && [ -f #{pid_file} ]; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} quiet #{pid_file} ; fi"
25
25
  end
26
26
  end
27
27
 
28
28
  desc "Stop sidekiq"
29
29
  task :stop, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
30
- for_each_process do |pid_file|
30
+ for_each_process do |pid_file, idx|
31
31
  run "if [ -d #{current_path} ] && [ -f #{pid_file} ]; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} stop #{pid_file} #{fetch :sidekiq_timeout} ; fi"
32
32
  end
33
33
  end
@@ -35,8 +35,8 @@ Capistrano::Configuration.instance.load do
35
35
  desc "Start sidekiq"
36
36
  task :start, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
37
37
  rails_env = fetch(:rails_env, "production")
38
- for_each_process do |pid_file|
39
- run "cd #{current_path} ; nohup #{fetch(:sidekiq_cmd)} -e #{rails_env} -C #{current_path}/config/sidekiq.yml -P #{pid_file} >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
38
+ for_each_process do |pid_file, idx|
39
+ run "cd #{current_path} ; nohup #{fetch(:sidekiq_cmd)} -e #{rails_env} -C #{current_path}/config/sidekiq.yml -i #{idx} -P #{pid_file} >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
40
40
  end
41
41
  end
42
42
 
@@ -71,6 +71,22 @@ module Sidekiq
71
71
  UnitOfWork.new(*work) if work
72
72
  end
73
73
 
74
+ def self.bulk_requeue(inprogress)
75
+ Sidekiq.logger.debug { "Re-queueing terminated jobs" }
76
+ jobs_to_requeue = {}
77
+ inprogress.each do |unit_of_work|
78
+ jobs_to_requeue[unit_of_work.queue] ||= []
79
+ jobs_to_requeue[unit_of_work.queue] << unit_of_work.message
80
+ end
81
+
82
+ Sidekiq.redis do |conn|
83
+ jobs_to_requeue.each do |queue, jobs|
84
+ conn.rpush(queue, jobs)
85
+ end
86
+ end
87
+ Sidekiq.logger.info("Pushed #{inprogress.size} messages back to Redis")
88
+ end
89
+
74
90
  UnitOfWork = Struct.new(:queue, :message) do
75
91
  def acknowledge
76
92
  # nothing to do
@@ -93,9 +109,7 @@ module Sidekiq
93
109
  # recreate the queue command each time we invoke Redis#blpop
94
110
  # to honor weights and avoid queue starvation.
95
111
  def queues_cmd
96
- return @unique_queues.dup << Sidekiq::Fetcher::TIMEOUT if @strictly_ordered_queues
97
- queues = @queues.sample(@unique_queues.size).uniq
98
- queues.concat(@unique_queues - queues)
112
+ queues = @strictly_ordered_queues ? @unique_queues.dup : @queues.shuffle.uniq
99
113
  queues << Sidekiq::Fetcher::TIMEOUT
100
114
  end
101
115
  end
@@ -112,23 +112,31 @@ module Sidekiq
112
112
  # They must die but their messages shall live on.
113
113
  logger.info("Still waiting for #{@busy.size} busy workers")
114
114
 
115
+ # Re-enqueue terminated jobs
116
+ # NOTE: You may notice that we may push a job back to redis before
117
+ # the worker thread is terminated. This is ok because Sidekiq's
118
+ # contract says that jobs are run AT LEAST once. Process termination
119
+ # is delayed until we're certain the jobs are back in Redis because
120
+ # it is worse to lose a job than to run it twice.
121
+ Sidekiq.options[:fetch].bulk_requeue(@in_progress.values)
122
+
123
+ # Clearing workers in Redis
124
+ # NOTE: we do this before terminating worker threads because the
125
+ # process will likely receive a hard shutdown soon anyway, which
126
+ # means the threads will killed.
127
+ logger.debug { "Clearing workers in redis" }
115
128
  Sidekiq.redis do |conn|
116
- logger.debug { "Clearing workers in redis" }
117
129
  workers = conn.smembers('workers')
118
- workers.each do |name|
119
- conn.srem('workers', name) if name =~ /:#{process_id}-/
130
+ workers_to_remove = workers.select do |worker_name|
131
+ worker_name =~ /:#{process_id}-/
120
132
  end
133
+ conn.srem('workers', workers_to_remove)
134
+ end
121
135
 
122
- @busy.each do |processor|
123
- # processor is an actor proxy and we can't call any methods
124
- # that would go to the actor (since it's busy). Instead
125
- # we'll use the object_id to track the worker's data here.
126
- processor.terminate if processor.alive?
127
- msg, queue = @in_progress[processor.object_id]
128
- conn.lpush("queue:#{queue}", msg)
129
- end
136
+ logger.debug { "Terminating worker threads" }
137
+ @busy.each do |processor|
138
+ processor.terminate if processor.alive?
130
139
  end
131
- logger.info("Pushed #{@busy.size} messages back to Redis")
132
140
 
133
141
  after(0) { signal(:shutdown) }
134
142
  end
@@ -51,7 +51,11 @@ module Sidekiq
51
51
  raise e unless msg['retry']
52
52
  max_retry_attempts = retry_attempts_from(msg['retry'], DEFAULT_MAX_RETRY_ATTEMPTS)
53
53
 
54
- msg['queue'] = queue
54
+ msg['queue'] = if msg['retry_queue']
55
+ msg['retry_queue']
56
+ else
57
+ queue
58
+ end
55
59
  msg['error_message'] = e.message
56
60
  msg['error_class'] = e.class.name
57
61
  count = if msg['retry_count']
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "2.6.4"
2
+ VERSION = "2.6.5"
3
3
  end
@@ -5,7 +5,7 @@ class TestFetcher < MiniTest::Unit::TestCase
5
5
 
6
6
  def setup
7
7
  Sidekiq.redis do |conn|
8
- conn.del('queue:basic')
8
+ conn.flushdb
9
9
  conn.rpush('queue:basic', 'msg')
10
10
  end
11
11
  end
@@ -28,4 +28,15 @@ class TestFetcher < MiniTest::Unit::TestCase
28
28
  cmd = fetch.queues_cmd
29
29
  assert_equal cmd, ['queue:basic', 'queue:bar', 1]
30
30
  end
31
+
32
+ def test_basic_fetch_bulk_requeue
33
+ q1 = Sidekiq::Queue.new('foo')
34
+ q2 = Sidekiq::Queue.new('bar')
35
+ assert_equal 0, q1.size
36
+ assert_equal 0, q2.size
37
+ uow = Sidekiq::BasicFetch::UnitOfWork
38
+ Sidekiq::BasicFetch.bulk_requeue([uow.new('queue:foo', 'bob'), uow.new('queue:foo', 'bar'), uow.new('queue:bar', 'widget')])
39
+ assert_equal 2, q1.size
40
+ assert_equal 1, q2.size
41
+ end
31
42
  end
@@ -86,6 +86,24 @@ class TestRetry < MiniTest::Unit::TestCase
86
86
  @redis.verify
87
87
  end
88
88
 
89
+ it 'allows a retry queue' do
90
+ @redis.expect :zadd, 1, ['retry', String, String]
91
+ msg = { 'class' => 'Bob', 'args' => [1,2,'foo'], 'retry' => true, 'retry_queue' => 'retry' }
92
+ handler = Sidekiq::Middleware::Server::RetryJobs.new
93
+ assert_raises RuntimeError do
94
+ handler.call('', msg, 'default') do
95
+ raise "kerblammo!"
96
+ end
97
+ end
98
+ assert_equal 'retry', msg["queue"]
99
+ assert_equal 'kerblammo!', msg["error_message"]
100
+ assert_equal 'RuntimeError', msg["error_class"]
101
+ assert_equal 0, msg["retry_count"]
102
+ refute msg["error_backtrace"]
103
+ assert msg["failed_at"]
104
+ @redis.verify
105
+ end
106
+
89
107
  it 'handles a recurring failed message' do
90
108
  @redis.expect :zadd, 1, ['retry', String, String]
91
109
  now = Time.now.utc
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.4
4
+ version: 2.6.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-08 00:00:00.000000000 Z
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -219,6 +219,7 @@ files:
219
219
  - examples/monitrc.conf
220
220
  - examples/por.rb
221
221
  - examples/scheduling.rb
222
+ - examples/sidekiq
222
223
  - examples/sinkiq.rb
223
224
  - examples/web-ui.png
224
225
  - lib/sidekiq.rb