sidekiq 2.9.0 → 2.10.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.

data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .rvmrc
2
+ .ruby-version
2
3
  tags
3
4
  Gemfile.lock
4
5
  *.swp
data/Changes.md CHANGED
@@ -1,3 +1,15 @@
1
+ 2.10.0
2
+ -----------
3
+
4
+ - Refactor algorithm for putting scheduled jobs onto the queue [#843]
5
+ - Fix scheduler thread dying due to incorrect error handling [#839]
6
+ - Fix issue which left stale workers if Sidekiq wasn't shutdown while
7
+ quiet. [#840]
8
+ - I18n for web UI. Please submit translations of `web/locales/en.yml` for
9
+ your own language. [#811]
10
+ - 'sinatra', 'slim' and 'i18n' are now gem dependencies for Sidekiq.
11
+
12
+
1
13
  2.9.0
2
14
  -----------
3
15
 
data/README.md CHANGED
@@ -1,10 +1,7 @@
1
1
  Sidekiq
2
2
  ==============
3
3
 
4
- - [![Gem Version](https://badge.fury.io/rb/sidekiq.png)](https://rubygems.org/gems/sidekiq)
5
- - [![Code Climate](https://codeclimate.com/github/mperham/sidekiq.png)](https://codeclimate.com/github/mperham/sidekiq)
6
- - [![Build Status](https://travis-ci.org/mperham/sidekiq.png)](https://travis-ci.org/mperham/sidekiq)
7
- - [![Coverage Status](https://coveralls.io/repos/mperham/sidekiq/badge.png?branch=master)](https://coveralls.io/r/mperham/sidekiq)
4
+ [![Gem Version](https://badge.fury.io/rb/sidekiq.png)](https://rubygems.org/gems/sidekiq) [![Code Climate](https://codeclimate.com/github/mperham/sidekiq.png)](https://codeclimate.com/github/mperham/sidekiq) [![Build Status](https://travis-ci.org/mperham/sidekiq.png)](https://travis-ci.org/mperham/sidekiq) [![Coverage Status](https://coveralls.io/repos/mperham/sidekiq/badge.png?branch=master)](https://coveralls.io/r/mperham/sidekiq)
8
5
 
9
6
 
10
7
  Simple, efficient message processing for Ruby.
@@ -21,16 +18,13 @@ use the Resque client to enqueue messages in Redis to be processed by Sidekiq.
21
18
  At the same time, Sidekiq uses multithreading so it is much more memory efficient than
22
19
  Resque (which forks a new process for every job). You'll find that you might need
23
20
  50 200MB resque processes to peg your CPU whereas one 300MB Sidekiq process will peg
24
- the same CPU and perform the same amount of work. Please see [my blog post on Resque's memory
25
- efficiency](http://blog.carbonfive.com/2011/09/16/improving-resques-memory-efficiency/)
26
- and how I was able to shrink a Carbon Five client's resque processing farm
27
- from 9 machines to 1 machine.
21
+ the same CPU and perform the same amount of work.
28
22
 
29
23
 
30
24
  Requirements
31
25
  -----------------
32
26
 
33
- I test on Ruby 1.9.3 and JRuby 1.6.x in 1.9 mode. Other versions/VMs are
27
+ I test on Ruby 1.9.3 and JRuby 1.7.x. Other versions/VMs are
34
28
  untested but I will do my best to support them. Ruby 1.8 is not supported.
35
29
 
36
30
  Redis 2.0 or greater is required.
@@ -45,12 +39,20 @@ Installation
45
39
  Getting Started
46
40
  -----------------
47
41
 
48
- See the [sidekiq home page](http://mperham.github.com/sidekiq) for the simple 4-step process.
42
+ See the [sidekiq home page](http://mperham.github.com/sidekiq) for the simple 3-step process.
49
43
  You can watch [Railscast #366](http://railscasts.com/episodes/366-sidekiq) to see Sidekiq in action. If you do everything right, you should see this:
50
44
 
51
45
  ![Web UI](https://github.com/mperham/sidekiq/raw/master/examples/web-ui.png)
52
46
 
53
47
 
48
+ Want to Upgrade?
49
+ -------------------
50
+
51
+ I also sell Sidekiq Pro, an extension to Sidekiq which provides more
52
+ features, a commercial-friendly license and allows you to support high
53
+ quality open source development all at the same time. Please see the
54
+ [Sidekiq Pro](http://sidekiq.org/pro) homepage for more detail.
55
+
54
56
 
55
57
  More Information
56
58
  -----------------
@@ -42,8 +42,9 @@ module Sidekiq
42
42
  @ready.each { |x| x.terminate if x.alive? }
43
43
  @ready.clear
44
44
 
45
+ clear_worker_set
46
+
45
47
  return after(0) { signal(:shutdown) } if @busy.empty?
46
- logger.info { "Pausing up to #{timeout} seconds to allow workers to finish..." }
47
48
  hard_shutdown_in timeout if shutdown
48
49
  end
49
50
  end
@@ -108,7 +109,24 @@ module Sidekiq
108
109
 
109
110
  private
110
111
 
112
+ def clear_worker_set
113
+ # Clearing workers in Redis
114
+ # NOTE: we do this before terminating worker threads because the
115
+ # process will likely receive a hard shutdown soon anyway, which
116
+ # means the threads will killed.
117
+ logger.debug { "Clearing workers in redis" }
118
+ Sidekiq.redis do |conn|
119
+ workers = conn.smembers('workers')
120
+ workers_to_remove = workers.select do |worker_name|
121
+ worker_name =~ /:#{process_id}-/
122
+ end
123
+ conn.srem('workers', workers_to_remove) if !workers_to_remove.empty?
124
+ end
125
+ end
126
+
111
127
  def hard_shutdown_in(delay)
128
+ logger.info { "Pausing up to #{delay} seconds to allow workers to finish..." }
129
+
112
130
  after(delay) do
113
131
  watchdog("Manager#hard_shutdown_in died") do
114
132
  # We've reached the timeout and we still have busy workers.
@@ -123,19 +141,6 @@ module Sidekiq
123
141
  # it is worse to lose a job than to run it twice.
124
142
  Sidekiq::Fetcher.strategy.bulk_requeue(@in_progress.values)
125
143
 
126
- # Clearing workers in Redis
127
- # NOTE: we do this before terminating worker threads because the
128
- # process will likely receive a hard shutdown soon anyway, which
129
- # means the threads will killed.
130
- logger.debug { "Clearing workers in redis" }
131
- Sidekiq.redis do |conn|
132
- workers = conn.smembers('workers')
133
- workers_to_remove = workers.select do |worker_name|
134
- worker_name =~ /:#{process_id}-/
135
- end
136
- conn.srem('workers', workers_to_remove) if !workers_to_remove.empty?
137
- end
138
-
139
144
  logger.debug { "Terminating worker threads" }
140
145
  @busy.each do |processor|
141
146
  t = processor.bare_object.actual_work_thread
@@ -28,25 +28,30 @@ module Sidekiq
28
28
  now = Time.now.to_f.to_s
29
29
  Sidekiq.redis do |conn|
30
30
  SETS.each do |sorted_set|
31
- (messages, _) = conn.multi do
32
- conn.zrangebyscore(sorted_set, '-inf', now)
33
- conn.zremrangebyscore(sorted_set, '-inf', now)
34
- end
35
-
36
- messages.each do |message|
37
- logger.debug { "enqueued #{sorted_set}: #{message}" }
31
+ # Get the next item in the queue if it's score (time to execute) is <= now.
32
+ # We need to go through the list one at a time to reduce the risk of something
33
+ # going wrong between the time jobs are popped from the scheduled queue and when
34
+ # they are pushed onto a work queue and losing the jobs.
35
+ while message = conn.zrangebyscore(sorted_set, '-inf', now, :limit => [0, 1]).first do
38
36
  msg = Sidekiq.load_json(message)
39
- conn.multi do
40
- conn.sadd('queues', msg['queue'])
41
- conn.rpush("queue:#{msg['queue']}", message)
37
+ # Pop item off the queue and add it to the work queue. If the job can't be popped from
38
+ # the queue, it's because another process already popped it so we can move on to the
39
+ # next one.
40
+ if conn.zrem(sorted_set, message)
41
+ conn.multi do
42
+ conn.sadd('queues', msg['queue'])
43
+ conn.rpush("queue:#{msg['queue']}", message)
44
+ end
45
+ logger.debug("enqueued #{sorted_set}: #{message}") if logger.debug?
42
46
  end
43
47
  end
44
48
  end
45
49
  end
46
- rescue SystemCallError, Redis::TimeoutError, Redis::ConnectionError => ex
47
- # ECONNREFUSED, etc. Most likely a problem with
48
- # redis networking. Punt and try again at the next interval
49
- logger.warn ex.message
50
+ rescue => ex
51
+ # Most likely a problem with redis networking.
52
+ # Punt and try again at the next interval
53
+ logger.error ex.message
54
+ logger.error(ex.backtrace.first)
50
55
  end
51
56
 
52
57
  after(poll_interval) { poll }
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "2.9.0"
2
+ VERSION = "2.10.0"
3
3
  end
@@ -1,18 +1,28 @@
1
1
  require 'sinatra/base'
2
2
  require 'slim'
3
3
  require 'sidekiq/paginator'
4
+ require 'i18n'
4
5
 
5
6
  module Sidekiq
6
7
  class Web < Sinatra::Base
7
8
  include Sidekiq::Paginator
8
9
 
9
10
  dir = File.expand_path(File.dirname(__FILE__) + "/../../web")
11
+ I18n.load_path += Dir[File.join(dir, 'locales', '*.yml').to_s]
12
+
10
13
  set :public_folder, "#{dir}/assets"
11
14
  set :views, "#{dir}/views"
12
15
  set :root, "#{dir}/public"
13
16
  set :slim, :pretty => true
14
17
 
15
18
  helpers do
19
+ def get_locale
20
+ (request.env["HTTP_ACCEPT_LANGUAGE"] || 'en')[0,2]
21
+ end
22
+
23
+ def t(msg, options={})
24
+ I18n.t(msg, options.merge(:locale => get_locale))
25
+ end
16
26
 
17
27
  def reset_worker_list
18
28
  Sidekiq.redis do |conn|
@@ -19,9 +19,10 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency 'connection_pool', '~> 1.0'
20
20
  gem.add_dependency 'celluloid', '~> 0.12.0'
21
21
  gem.add_dependency 'multi_json', '~> 1'
22
+ gem.add_dependency 'sinatra'
23
+ gem.add_dependency 'slim'
24
+ gem.add_dependency 'i18n'
22
25
  gem.add_development_dependency 'minitest', '~> 4'
23
- gem.add_development_dependency 'sinatra'
24
- gem.add_development_dependency 'slim'
25
26
  gem.add_development_dependency 'rake'
26
27
  gem.add_development_dependency 'actionmailer', '~> 3'
27
28
  gem.add_development_dependency 'activerecord', '~> 3'
@@ -199,26 +199,4 @@ class TestRetry < MiniTest::Unit::TestCase
199
199
  end
200
200
  end
201
201
 
202
- describe 'poller' do
203
- before do
204
- @redis = MiniTest::Mock.new
205
- Sidekiq.instance_variable_set(:@redis, @redis)
206
-
207
- def @redis.with; yield self; end
208
- end
209
-
210
- it 'should poll like a bad mother...SHUT YO MOUTH' do
211
- fake_msg = Sidekiq.dump_json({ 'class' => 'Bob', 'args' => [1,2], 'queue' => 'someq' })
212
- @redis.expect :multi, [[fake_msg], 1], []
213
- @redis.expect :multi, [[], nil], []
214
- @redis.expect :multi, [[], nil], []
215
- @redis.expect :multi, [[], nil], []
216
-
217
- inst = Sidekiq::Scheduled::Poller.new
218
- inst.poll
219
-
220
- @redis.verify
221
- end
222
- end
223
-
224
202
  end
@@ -0,0 +1,48 @@
1
+ require 'helper'
2
+ require 'sidekiq/scheduled'
3
+
4
+ class TestScheduled < MiniTest::Unit::TestCase
5
+ class ScheduledWorker
6
+ include Sidekiq::Worker
7
+ def perform(x)
8
+ end
9
+ end
10
+
11
+ describe 'poller' do
12
+ before do
13
+ Sidekiq.redis = REDIS
14
+ Sidekiq.redis do |conn|
15
+ conn.flushdb
16
+ end
17
+ end
18
+
19
+ it 'should empty the retry and scheduled queues up to the current time' do
20
+ Sidekiq.redis do |conn|
21
+ error_1 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["error_1"], 'queue' => 'queue_1')
22
+ error_2 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["error_2"], 'queue' => 'queue_2')
23
+ error_3 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["error_3"], 'queue' => 'queue_3')
24
+ future_1 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["future_1"], 'queue' => 'queue_4')
25
+ future_2 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["future_2"], 'queue' => 'queue_5')
26
+ future_3 = Sidekiq.dump_json('class' => ScheduledWorker.name, 'args' => ["future_3"], 'queue' => 'queue_6')
27
+
28
+ conn.zadd("retry", (Time.now - 60).to_f.to_s, error_1)
29
+ conn.zadd("retry", (Time.now - 50).to_f.to_s, error_2)
30
+ conn.zadd("retry", (Time.now + 60).to_f.to_s, error_3)
31
+ conn.zadd("schedule", (Time.now - 60).to_f.to_s, future_1)
32
+ conn.zadd("schedule", (Time.now - 50).to_f.to_s, future_2)
33
+ conn.zadd("schedule", (Time.now + 60).to_f.to_s, future_3)
34
+
35
+ poller = Sidekiq::Scheduled::Poller.new
36
+ poller.poll
37
+ poller.terminate
38
+
39
+ assert_equal [error_1], conn.lrange("queue:queue_1", 0, -1)
40
+ assert_equal [error_2], conn.lrange("queue:queue_2", 0, -1)
41
+ assert_equal [error_3], conn.zrange("retry", 0, -1)
42
+ assert_equal [future_1], conn.lrange("queue:queue_4", 0, -1)
43
+ assert_equal [future_2], conn.lrange("queue:queue_5", 0, -1)
44
+ assert_equal [future_3], conn.zrange("schedule", 0, -1)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ # elements like %{queue} are variables and should not be translated
2
+ da:
3
+ Dashboard: Instrumentbræt
4
+ Status: Status
5
+ Time: Tid
6
+ Namespace: Namespace
7
+ Realtime: Real-time
8
+ History: Historik
9
+ Busy: Travl
10
+ Processed: Processeret
11
+ Failed: Fejlet
12
+ Scheduled: Planlagt
13
+ Retries: Forsøg
14
+ Enqueued: I kø
15
+ Worker: Arbejder
16
+ Workers: Arbejdere
17
+ LivePoll: Live Poll
18
+ StopPolling: Stop Polling
19
+ Queue: Kø
20
+ Class: Klasse
21
+ Job: Job
22
+ Arguments: Argumenter
23
+ Started: Startet
24
+ ShowAll: Vis alle
25
+ CurrentMessagesInQueue: Nuværende beskeder i <span class='title'>%{queue}</span>
26
+ Delete: Slet
27
+ AreYouSureDeleteJob: Er du sikker på at du vil slette dette job?
28
+ AreYouSureDeleteQueue: Er du sikker på at du vil slette %{queue} køen?
29
+ Queues: Køer
30
+ Size: Størrelse
31
+ Actions: Actions
32
+ NextRetry: Næste forsøg
33
+ RetryCount: Antal forsøg
34
+ RetryNow: Prøv igen nu
35
+ LastRetry: Sidste forsøg
36
+ OriginallyFailed: Oprindeligt fejlet
37
+ AreYouSure: Er du sikker?
38
+ DeleteAll: Slet alle
39
+ RetryAll: Forsøg alle
40
+ NoRetriesFound: Ingen gen-forsøg var fundet
41
+ Error: Fejl
42
+ ErrorClass: Fejl klasse
43
+ ErrorMessage: Fejl besked
44
+ ErrorBacktrace: Fejl backtrace
45
+ GoBack: ← Tilbage
46
+ NoScheduledFound: Ingen jobs i kø fundet
47
+ When: Når
48
+ ScheduledJobs: Jobs i kø
49
+ idle: idle
50
+ active: aktiv
51
+ Version: Version
52
+ Connections: Forbindelser
53
+ MemoryUsage: RAM forbrug
54
+ PeakMemoryUsage: Peak RAM forbrug
55
+ Uptime: Oppetid (dage)
56
+ OneWeek: 1 uge
57
+ OneMonth: 1 måned
58
+ ThreeMonths: 3 måneder
59
+ SixMonths: 6 måneder
@@ -0,0 +1,59 @@
1
+ # elements like %{queue} are variables and should not be translated
2
+ de:
3
+ Dashboard: Dashboard
4
+ Status: Status
5
+ Time: Zeit
6
+ Namespace: Namensraum
7
+ Realtime: Echtzeit
8
+ History: Verlauf
9
+ Busy: Beschäftigt
10
+ Processed: Verarbeitet
11
+ Failed: Fehlgeschlagen
12
+ Scheduled: Geplant
13
+ Retries: Versuche
14
+ Enqueued: In der Warteschlange
15
+ Worker: Arbeiter
16
+ Workers: Arbeiter
17
+ LivePoll: Live Poll
18
+ StopPolling: Abfrage stoppen
19
+ Queue: Warteschlange
20
+ Class: Klasse
21
+ Job: Job
22
+ Arguments: Argumente
23
+ Started: Startet
24
+ ShowAll: Alle anzeigen
25
+ CurrentMessagesInQueue: Aktuelle Nachrichten in <span class='title'>%{queue}</span>
26
+ Delete: Löschen
27
+ AreYouSureDeleteJob: Möchtest du diesen Job wirklich löschen?
28
+ AreYouSureDeleteQueue: Möchtest du %{queue} wirklich löschen?
29
+ Queues: Warteschlangen
30
+ Size: Größe
31
+ Actions: Aktionen
32
+ NextRetry: Nächster Versuch
33
+ RetryCount: Anzahl der Versuche
34
+ RetryNow: Jetzt erneut versuchen
35
+ LastRetry: Letzter Versuch
36
+ OriginallyFailed: Ursprünglich Fehlgeschlagen
37
+ AreYouSure: Bist du sicher?
38
+ DeleteAll: Alle löschen
39
+ RetryAll: Alle erneut versuchen
40
+ NoRetriesFound: Keine erneuten Versuche gefunden
41
+ Error: Fehler
42
+ ErrorClass: Fehler klasse
43
+ ErrorMessage: Fehler-Nachricht
44
+ ErrorBacktrace: Fehler-Backtrace
45
+ GoBack: ← Zurück
46
+ NoScheduledFound: Keine geplanten Jobs gefunden
47
+ When: Wann
48
+ ScheduledJobs: Jobs in Wartschlange
49
+ idle: inaktiv
50
+ active: aktiv
51
+ Version: Version
52
+ Connections: Verbindungen
53
+ MemoryUsage: RAM-Nutzung
54
+ PeakMemoryUsage: Maximale RAM-Nutzung
55
+ Uptime: Laufzeit
56
+ OneWeek: 1 Woche
57
+ OneMonth: 1 Monat
58
+ ThreeMonths: 3 Monate
59
+ SixMonths: 6 Monate
@@ -0,0 +1,61 @@
1
+ # elements like %{queue} are variables and should not be translated
2
+ en:
3
+ Dashboard: Dashboard
4
+ Status: Status
5
+ Time: Time
6
+ Namespace: Namespace
7
+ Realtime: Real-time
8
+ History: History
9
+ Busy: Busy
10
+ Processed: Processed
11
+ Failed: Failed
12
+ Scheduled: Scheduled
13
+ Retries: Retries
14
+ Enqueued: Enqueued
15
+ ClearWorkerList: Clear workers list
16
+ Worker: Worker
17
+ Workers: Workers
18
+ LivePoll: Live Poll
19
+ StopPolling: Stop Polling
20
+ Queue: Queue
21
+ Class: Class
22
+ Job: Job
23
+ Arguments: Arguments
24
+ Started: Started
25
+ ShowAll: Show All
26
+ CurrentMessagesInQueue: Current messages in <span class='title'>%{queue}</span>
27
+ Delete: Delete
28
+ AreYouSureDeleteJob: Are you sure you want to delete this job?
29
+ AreYouSureDeleteQueue: Are you sure you want to delete the %{queue} queue?
30
+ Queues: Queues
31
+ Size: Size
32
+ Actions: Actions
33
+ NextRetry: Next Retry
34
+ RetryCount: Retry Count
35
+ RetryNow: Retry Now
36
+ LastRetry: Last Retry
37
+ OriginallyFailed: Originally Failed
38
+ AreYouSure: Are you sure?
39
+ DeleteAll: Delete All
40
+ RetryAll: Retry All
41
+ NoRetriesFound: No retries were found
42
+ Error: Error
43
+ ErrorClass: Error Class
44
+ ErrorMessage: Error Message
45
+ ErrorBacktrace: Error Backtrace
46
+ GoBack: ← Back
47
+ NoScheduledFound: No scheduled jobs were found
48
+ When: When
49
+ ScheduledJobs: Scheduled Jobs
50
+ idle: idle
51
+ active: active
52
+ Version: Version
53
+ Connections: Connections
54
+ MemoryUsage: Memory Usage
55
+ PeakMemoryUsage: Peak Memory Usage
56
+ Uptime: Uptime (days)
57
+ OneWeek: 1 week
58
+ OneMonth: 1 month
59
+ ThreeMonths: 3 months
60
+ SixMonths: 6 months
61
+ Batches: Batches