sidekiq 5.0.0 → 5.0.1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2a9cd7270e53981aaf7b3f2639596ff042f5ae3
4
- data.tar.gz: 7b6e4fbfee794de34d9b6b15772cbf085e05a65b
3
+ metadata.gz: 04414e0337f9a41c13f324892ee670396e99ff89
4
+ data.tar.gz: ca4f4c23c886511caf5699b6e4dffba02c5c721a
5
5
  SHA512:
6
- metadata.gz: dfaecc27ce4071e36b5ab994c27f59d85b4cf7f16ca05a5b786c79125c311f58fbb38b94f7e1c71e4dbf90e6645cb358102e8940a94881981f7bbc2578fd18ff
7
- data.tar.gz: a98e9b87e5f4abe2580ea5fcb6f20ef1d28623eae9be4ca758d5f5b52c65dbef7a6b5df74f673449177f36494b9075573315c7ef6cd6b757ba6a59e825737833
6
+ metadata.gz: d55706e0ffb62246ba056607a7b3ce6c35cad7d18fcc0bff277d4de89f87cecd64e81a373c9dacc9add59604036fed55c0e66ebf9fa62ca2c8b6da553db60e13
7
+ data.tar.gz: c66998d5104c7c5fa0c22034b9c7b97ad685a3bc14f65b72812ef3c59fbf01799b81304da6e39806bd49661129629ed8ad2619fae29b4b3d04b7f9225403fbfd
@@ -38,7 +38,7 @@ under the terms of the GNU Lesser General Public License versions 3.0
38
38
 
39
39
  5. Fees and Payment. The Software license fees will be due and payable in full as set forth in the applicable invoice or at the time of purchase. If the Software does not function properly within two weeks of purchase, please contact us within those two weeks for a refund. You shall be responsible for all taxes, withholdings, duties and levies arising from the order (excluding taxes based on the net income of Contributed Systems).
40
40
 
41
- 6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, and as set forth on the Sidekiq Pro support page (https://github.com/mperham/sidekiq/wiki/Pro-Support), support and maintenance services may be included with the purchase of your license subscription.
41
+ 6. Support, Maintenance and Services. Subject to the terms and conditions of this Agreement, as set forth in your invoice, and as set forth on the Sidekiq Pro support page (https://github.com/mperham/sidekiq/wiki/Commercial-Support), support and maintenance services may be included with the purchase of your license subscription.
42
42
 
43
43
  7. Term of Agreement.
44
44
 
data/Changes.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  [Sidekiq Changes](https://github.com/mperham/sidekiq/blob/master/Changes.md) | [Sidekiq Pro Changes](https://github.com/mperham/sidekiq/blob/master/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/mperham/sidekiq/blob/master/Ent-Changes.md)
4
4
 
5
+ HEAD
6
+ -----------
7
+
8
+ - Fix incorrect server identity when daemonizing [jwilm, #3496]
9
+ - Work around error running Web UI against Redis Cluster [#3492]
10
+ - Remove core extensions, Sidekiq is now monkeypatch-free! [#3474]
11
+ - Reimplement Web UI's HTTP\_ACCEPT\_LANGUAGE parsing because the spec is utterly
12
+ incomprehensible for various edge cases. [johanlunds, natematykiewicz, #3449]
13
+ - Update `class_attribute` core extension to avoid warnings
14
+ - Expose `job_hash_context` from `Sidekiq::Logging` to support log customization
15
+
5
16
  5.0.0
6
17
  -----------
7
18
 
@@ -11,7 +22,7 @@
11
22
  commonly used public APIs so this shouldn't impact most users.
12
23
  ```
13
24
  Sidekiq::Middleware::Server::RetryJobs -> Sidekiq::JobRetry
14
- Sidekiq::Middleware::Server::Logging -> Sidekiq::JobLogging
25
+ Sidekiq::Middleware::Server::Logging -> Sidekiq::JobLogger
15
26
  ```
16
27
  - Quieting Sidekiq is now done via the TSTP signal, the USR1 signal is deprecated.
17
28
  - The `delay` extension APIs are no longer available by default, you
@@ -4,6 +4,27 @@
4
4
 
5
5
  Please see [http://sidekiq.org/](http://sidekiq.org/) for more details and how to buy.
6
6
 
7
+ HEAD
8
+ -------------
9
+
10
+ - Show process "leader" tag on Busy page, requires Sidekiq 5.0.1 [#2867]
11
+ - Capture custom metrics with the `save_history` API. [#2815]
12
+ - Implement new `unique_util: 'start'` policy option. [#3471]
13
+
14
+ 1.5.4
15
+ -------------
16
+
17
+ - Fix broken Cron page in Web UI [#3458]
18
+
19
+ 1.5.3
20
+ -------------
21
+
22
+ - Remove dependency on the algorithms gem [#3446]
23
+ - Allow user to specify max memory in megabytes with SIDEKIQ\_MAXMEM\_MB [#3451]
24
+ - Implement logic to detect app startup failure, sidekiqswarm will exit
25
+ rather than try to restart the app forever [#3450]
26
+ - Another fix for doubly-encrypted arguments [#3368]
27
+
7
28
  1.5.2
8
29
  -------------
9
30
 
@@ -4,9 +4,11 @@
4
4
 
5
5
  Please see [http://sidekiq.org/](http://sidekiq.org/) for more details and how to buy.
6
6
 
7
- HEAD
7
+ 3.5.0
8
8
  ---------
9
9
 
10
+ - Add queue pause/unpause endpoints for scripting via curl [#3445]
11
+ - Change how super\_fetch names private queues to avoid hostname/queue clashes. [#3443]
10
12
  - Re-implement `Sidekiq::Queue#delete_job` to avoid O(n) runtime [#3408]
11
13
  - Batch page displays Pending JIDs if less than 10 [#3130]
12
14
  - Batch page has a Search button to find associated Retries [#3130]
data/README.md CHANGED
@@ -28,6 +28,7 @@ Sidekiq 3.5.1 | 22ms | 1257 MB | 125 sec | 800 jobs/sec
28
28
  Resque 1.25.2 | - | - | 420 sec | 240 jobs/sec
29
29
  DelayedJob 4.1.1 | - | - | 465 sec | 215 jobs/sec
30
30
 
31
+ <small>This benchmark can be found in `bin/sidekiqload`.</small>
31
32
 
32
33
  Requirements
33
34
  -----------------
@@ -80,7 +81,6 @@ Useful resources:
80
81
 
81
82
  * Product documentation is in the [wiki](https://github.com/mperham/sidekiq/wiki).
82
83
  * Release announcements are made to the [@sidekiq](https://twitter.com/sidekiq) Twitter account.
83
- * Here's a [Reddit forum](https://reddit.com/r/sidekiq) dedicated to Sidekiq discussion
84
84
  * The [Sidekiq tag](https://stackoverflow.com/questions/tagged/sidekiq) on Stack Overflow has lots of useful Q &amp; A.
85
85
 
86
86
  **No support via Twitter, 140 characters is not enough.**
@@ -1,6 +1,6 @@
1
1
  require 'rails_helper'
2
2
  <% module_namespacing do -%>
3
3
  RSpec.describe <%= class_name %>Worker, type: :worker do
4
- pending "add some examples to (or delete) #{__FILE__}"
4
+ pending "add some examples to (or delete) #{__FILE__}"
5
5
  end
6
6
  <% end -%>
@@ -149,7 +149,8 @@ module Sidekiq
149
149
  end
150
150
 
151
151
  def self.default_worker_options=(hash)
152
- @default_worker_options = default_worker_options.merge(hash.stringify_keys)
152
+ # stringify
153
+ @default_worker_options = default_worker_options.merge(Hash[hash.map{|k, v| [k.to_s, v]}])
153
154
  end
154
155
  def self.default_worker_options
155
156
  defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
@@ -222,7 +223,6 @@ module Sidekiq
222
223
  # otherwise Ruby's Thread#kill will commit. See #377.
223
224
  # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
224
225
  class Shutdown < Interrupt; end
225
-
226
226
  end
227
227
 
228
228
  require 'sidekiq/rails' if defined?(::Rails::Engine)
@@ -146,11 +146,11 @@ module Sidekiq
146
146
  end
147
147
 
148
148
  def processed
149
- date_stat_hash("processed")
149
+ @processed ||= date_stat_hash("processed")
150
150
  end
151
151
 
152
152
  def failed
153
- date_stat_hash("failed")
153
+ @failed ||= date_stat_hash("failed")
154
154
  end
155
155
 
156
156
  private
@@ -169,10 +169,15 @@ module Sidekiq
169
169
  i += 1
170
170
  end
171
171
 
172
- Sidekiq.redis do |conn|
173
- conn.mget(keys).each_with_index do |value, idx|
174
- stat_hash[dates[idx]] = value ? value.to_i : 0
172
+ begin
173
+ Sidekiq.redis do |conn|
174
+ conn.mget(keys).each_with_index do |value, idx|
175
+ stat_hash[dates[idx]] = value ? value.to_i : 0
176
+ end
175
177
  end
178
+ rescue Redis::CommandError
179
+ # mget will trigger a CROSSSLOT error when run against a Cluster
180
+ # TODO Someone want to add Cluster support?
176
181
  end
177
182
 
178
183
  stat_hash
@@ -725,6 +730,11 @@ module Sidekiq
725
730
  end
726
731
 
727
732
  result.each do |info, busy, at_s, quiet|
733
+ # If a process is stopped between when we query Redis for `procs` and
734
+ # when we query for `result`, we will have an item in `result` that is
735
+ # composed of `nil` values.
736
+ next if info.nil?
737
+
728
738
  hash = Sidekiq.load_json(info)
729
739
  yield Process.new(hash.merge('busy' => busy.to_i, 'beat' => at_s.to_f, 'quiet' => quiet))
730
740
  end
@@ -740,6 +750,18 @@ module Sidekiq
740
750
  def size
741
751
  Sidekiq.redis { |conn| conn.scard('processes') }
742
752
  end
753
+
754
+ # Returns the identity of the current cluster leader or "" if no leader.
755
+ # This is a Sidekiq Enterprise feature, will always return "" in Sidekiq
756
+ # or Sidekiq Pro.
757
+ def leader
758
+ @leader ||= begin
759
+ x = Sidekiq.redis {|c| c.get("dear-leader") }
760
+ # need a non-falsy value so we can memoize
761
+ x = "" unless x
762
+ x
763
+ end
764
+ end
743
765
  end
744
766
 
745
767
  #
@@ -774,6 +796,10 @@ module Sidekiq
774
796
  @attribs[key]
775
797
  end
776
798
 
799
+ def identity
800
+ self['identity']
801
+ end
802
+
777
803
  def quiet!
778
804
  signal('TSTP')
779
805
  end
@@ -802,9 +828,6 @@ module Sidekiq
802
828
  end
803
829
  end
804
830
 
805
- def identity
806
- self['identity']
807
- end
808
831
  end
809
832
 
810
833
  ##
@@ -81,6 +81,9 @@ module Sidekiq
81
81
  ver = Sidekiq.redis_info['redis_version']
82
82
  raise "You are using Redis v#{ver}, Sidekiq requires Redis v2.8.0 or greater" if ver < '2.8'
83
83
 
84
+ # cache process identity
85
+ opts[:identity] = identity
86
+
84
87
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
85
88
  Sidekiq.server_middleware
86
89
 
@@ -223,7 +226,6 @@ module Sidekiq
223
226
 
224
227
  opts[:strict] = true if opts[:strict].nil?
225
228
  opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if !opts[:concurrency] && ENV["RAILS_MAX_THREADS"]
226
- opts[:identity] = identity
227
229
 
228
230
  options.merge!(opts)
229
231
  end
@@ -51,6 +51,8 @@ module Sidekiq
51
51
  # at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
52
52
  # retry - whether to retry this job if it fails, default true or an integer number of retries
53
53
  # backtrace - whether to save any error backtrace, default false
54
+ #
55
+ # Any options valid for a worker class's sidekiq_options are also available here.
54
56
  #
55
57
  # All options must be strings, not symbols. NB: because we are serializing to JSON, all
56
58
  # symbols in 'args' will be converted to strings. Note that +backtrace: true+ can take quite a bit of
@@ -1,119 +1 @@
1
- # frozen_string_literal: true
2
- begin
3
- require 'active_support/core_ext/class/attribute'
4
- rescue LoadError
5
-
6
- # A dumbed down version of ActiveSupport's
7
- # Class#class_attribute helper.
8
- class Class
9
- def class_attribute(*attrs)
10
- instance_writer = true
11
-
12
- attrs.each do |name|
13
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
- def self.#{name}() nil end
15
- def self.#{name}?() !!#{name} end
16
-
17
- def self.#{name}=(val)
18
- singleton_class.class_eval do
19
- define_method(:#{name}) { val }
20
- end
21
-
22
- if singleton_class?
23
- class_eval do
24
- def #{name}
25
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
26
- end
27
- end
28
- end
29
- val
30
- end
31
-
32
- def #{name}
33
- defined?(@#{name}) ? @#{name} : self.class.#{name}
34
- end
35
-
36
- def #{name}?
37
- !!#{name}
38
- end
39
- RUBY
40
-
41
- attr_writer name if instance_writer
42
- end
43
- end
44
-
45
- private
46
- def singleton_class?
47
- ancestors.first != self
48
- end
49
- end
50
- end
51
-
52
- begin
53
- require 'active_support/core_ext/hash/keys'
54
- require 'active_support/core_ext/hash/deep_merge'
55
- rescue LoadError
56
- class Hash
57
- def stringify_keys
58
- keys.each do |key|
59
- self[key.to_s] = delete(key)
60
- end
61
- self
62
- end if !{}.respond_to?(:stringify_keys)
63
-
64
- def symbolize_keys
65
- keys.each do |key|
66
- self[(key.to_sym rescue key) || key] = delete(key)
67
- end
68
- self
69
- end if !{}.respond_to?(:symbolize_keys)
70
-
71
- def deep_merge(other_hash, &block)
72
- dup.deep_merge!(other_hash, &block)
73
- end if !{}.respond_to?(:deep_merge)
74
-
75
- def deep_merge!(other_hash, &block)
76
- other_hash.each_pair do |k,v|
77
- tv = self[k]
78
- if tv.is_a?(Hash) && v.is_a?(Hash)
79
- self[k] = tv.deep_merge(v, &block)
80
- else
81
- self[k] = block && tv ? block.call(k, tv, v) : v
82
- end
83
- end
84
- self
85
- end if !{}.respond_to?(:deep_merge!)
86
- end
87
- end
88
-
89
- begin
90
- require 'active_support/core_ext/string/inflections'
91
- rescue LoadError
92
- class String
93
- def constantize
94
- names = self.split('::')
95
- names.shift if names.empty? || names.first.empty?
96
-
97
- constant = Object
98
- names.each do |name|
99
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
100
- end
101
- constant
102
- end
103
- end if !"".respond_to?(:constantize)
104
- end
105
-
106
-
107
- begin
108
- require 'active_support/core_ext/kernel/reporting'
109
- rescue LoadError
110
- module Kernel
111
- module_function
112
- def silence_warnings
113
- old_verbose, $VERBOSE = $VERBOSE, nil
114
- yield
115
- ensure
116
- $VERBOSE = old_verbose
117
- end
118
- end
119
- end
1
+ raise "no longer used, will be removed in 5.1"
@@ -2,15 +2,13 @@ module Sidekiq
2
2
  class JobLogger
3
3
 
4
4
  def call(item, queue)
5
- begin
6
- start = Time.now
7
- logger.info("start".freeze)
8
- yield
9
- logger.info("done: #{elapsed(start)} sec")
10
- rescue Exception
11
- logger.info("fail: #{elapsed(start)} sec")
12
- raise
13
- end
5
+ start = Time.now
6
+ logger.info("start".freeze)
7
+ yield
8
+ logger.info("done: #{elapsed(start)} sec")
9
+ rescue Exception
10
+ logger.info("fail: #{elapsed(start)} sec")
11
+ raise
14
12
  end
15
13
 
16
14
  private
@@ -24,4 +22,3 @@ module Sidekiq
24
22
  end
25
23
  end
26
24
  end
27
-
@@ -203,7 +203,7 @@ module Sidekiq
203
203
  end
204
204
 
205
205
  def delay_for(worker, count, exception)
206
- worker && worker.sidekiq_retry_in_block? && retry_in(worker, count, exception) || seconds_to_delay(count)
206
+ worker && worker.sidekiq_retry_in_block && retry_in(worker, count, exception) || seconds_to_delay(count)
207
207
  end
208
208
 
209
209
  # delayed_job uses the same basic formula
@@ -26,6 +26,18 @@ module Sidekiq
26
26
  end
27
27
  end
28
28
 
29
+ def self.job_hash_context(job_hash)
30
+ # If we're using a wrapper class, like ActiveJob, use the "wrapped"
31
+ # attribute to expose the underlying thing.
32
+ klass = job_hash['wrapped'.freeze] || job_hash["class".freeze]
33
+ bid = job_hash['bid'.freeze]
34
+ "#{klass} JID-#{job_hash['jid'.freeze]}#{" BID-#{bid}" if bid}"
35
+ end
36
+
37
+ def self.with_job_hash_context(job_hash, &block)
38
+ with_context(job_hash_context(job_hash), &block)
39
+ end
40
+
29
41
  def self.with_context(msg)
30
42
  Thread.current[:sidekiq_context] ||= []
31
43
  Thread.current[:sidekiq_context] << msg
@@ -125,12 +125,7 @@ module Sidekiq
125
125
  # job structure to the Web UI
126
126
  pristine = cloned(job_hash)
127
127
 
128
- # If we're using a wrapper class, like ActiveJob, use the "wrapped"
129
- # attribute to expose the underlying thing.
130
- klass = job_hash['wrapped'.freeze] || job_hash["class".freeze]
131
- ctx = "#{klass} JID-#{job_hash['jid'.freeze]}#{" BID-#{job_hash['bid'.freeze]}" if job_hash['bid'.freeze]}"
132
-
133
- Sidekiq::Logging.with_context(ctx) do
128
+ Sidekiq::Logging.with_job_hash_context(job_hash) do
134
129
  @retrier.global(job_hash, queue) do
135
130
  @logging.call(job_hash, queue) do
136
131
  stats(pristine, queue) do
@@ -139,7 +134,7 @@ module Sidekiq
139
134
  # the Reloader. It handles code loading, db connection management, etc.
140
135
  # Effectively this block denotes a "unit of work" to Rails.
141
136
  @reloader.call do
142
- klass = job_hash['class'.freeze].constantize
137
+ klass = constantize(job_hash['class'.freeze])
143
138
  worker = klass.new
144
139
  worker.jid = job_hash['jid'.freeze]
145
140
  @retrier.local(worker, job_hash, queue) do
@@ -234,5 +229,14 @@ module Sidekiq
234
229
  Marshal.load(Marshal.dump(thing))
235
230
  end
236
231
 
232
+ def constantize(str)
233
+ names = str.split('::')
234
+ names.shift if names.empty? || names.first.empty?
235
+
236
+ names.inject(Object) do |constant, name|
237
+ constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
238
+ end
239
+ end
240
+
237
241
  end
238
242
  end
@@ -28,7 +28,6 @@ module Sidekiq
28
28
  Sidekiq.configure_server do |_|
29
29
  if ::Rails::VERSION::MAJOR >= 5
30
30
  Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
31
- Psych::Visitors::ToRuby.prepend(Sidekiq::Rails::PsychAutoload)
32
31
  end
33
32
  end
34
33
  end
@@ -48,13 +47,5 @@ module Sidekiq
48
47
  "#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
49
48
  end
50
49
  end
51
-
52
- module PsychAutoload
53
- def resolve_class(klass_name)
54
- klass_name && klass_name.constantize
55
- rescue NameError
56
- super
57
- end
58
- end
59
50
  end if defined?(::Rails)
60
51
  end
@@ -8,7 +8,9 @@ module Sidekiq
8
8
  class << self
9
9
 
10
10
  def create(options={})
11
- options = options.symbolize_keys
11
+ options.keys.each do |key|
12
+ options[key.to_sym] = options.delete(key)
13
+ end
12
14
 
13
15
  options[:url] ||= determine_redis_provider
14
16
 
@@ -55,6 +55,15 @@ module Sidekiq
55
55
  yield @server_chain if block_given?
56
56
  @server_chain
57
57
  end
58
+
59
+ def constantize(str)
60
+ names = str.split('::')
61
+ names.shift if names.empty? || names.first.empty?
62
+
63
+ names.inject(Object) do |constant, name|
64
+ constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
65
+ end
66
+ end
58
67
  end
59
68
  end
60
69
 
@@ -76,7 +85,7 @@ module Sidekiq
76
85
  true
77
86
  elsif Sidekiq::Testing.inline?
78
87
  payloads.each do |job|
79
- klass = job['class'].constantize
88
+ klass = Sidekiq::Testing.constantize(job['class'])
80
89
  job['id'] ||= SecureRandom.hex(12)
81
90
  job_hash = Sidekiq.load_json(Sidekiq.dump_json(job))
82
91
  klass.process_job(job_hash)
@@ -309,7 +318,7 @@ module Sidekiq
309
318
  worker_classes = jobs.map { |job| job["class"] }.uniq
310
319
 
311
320
  worker_classes.each do |worker_class|
312
- worker_class.constantize.drain
321
+ Sidekiq::Testing.constantize(worker_class).drain
313
322
  end
314
323
  end
315
324
  end
@@ -2,7 +2,6 @@
2
2
  require 'socket'
3
3
  require 'securerandom'
4
4
  require 'sidekiq/exception_handler'
5
- require 'sidekiq/core_ext'
6
5
 
7
6
  module Sidekiq
8
7
  ##
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sidekiq
3
- VERSION = "5.0.0"
3
+ VERSION = "5.0.1"
4
4
  end
@@ -234,7 +234,6 @@ module Sidekiq
234
234
  get '/stats' do
235
235
  sidekiq_stats = Sidekiq::Stats.new
236
236
  redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
237
-
238
237
  json(
239
238
  sidekiq: {
240
239
  processed: sidekiq_stats.processed,
@@ -247,7 +246,8 @@ module Sidekiq
247
246
  dead: sidekiq_stats.dead_size,
248
247
  default_latency: sidekiq_stats.default_queue_latency
249
248
  },
250
- redis: redis_stats
249
+ redis: redis_stats,
250
+ server_utc_time: server_utc_time
251
251
  )
252
252
  end
253
253
 
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  settings.locales.each_with_object({}) do |path, global|
16
16
  find_locale_files(lang).each do |file|
17
17
  strs = YAML.load(File.open(file))
18
- global.deep_merge!(strs[lang])
18
+ global.merge!(strs[lang])
19
19
  end
20
20
  end
21
21
  end
@@ -24,6 +24,7 @@ module Sidekiq
24
24
  def clear_caches
25
25
  @@strings = nil
26
26
  @@locale_files = nil
27
+ @@available_locales = nil
27
28
  end
28
29
 
29
30
  def locale_files
@@ -32,6 +33,10 @@ module Sidekiq
32
33
  end
33
34
  end
34
35
 
36
+ def available_locales
37
+ @@available_locales ||= locale_files.map { |path| File.basename(path, '.yml') }.uniq
38
+ end
39
+
35
40
  def find_locale_files(lang)
36
41
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
37
42
  end
@@ -73,20 +78,36 @@ module Sidekiq
73
78
  text_direction == 'rtl'
74
79
  end
75
80
 
76
- # Given a browser request Accept-Language header like
77
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
78
- # will return "fr" since that's the first code with a matching
79
- # locale in web/locales
81
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
82
+ def user_preferred_languages
83
+ languages = env['HTTP_ACCEPT_LANGUAGE'.freeze]
84
+ languages.to_s.downcase.gsub(/\s+/, '').split(',').map do |language|
85
+ locale, quality = language.split(';q=', 2)
86
+ locale = nil if locale == '*' # Ignore wildcards
87
+ quality = quality ? quality.to_f : 1.0
88
+ [locale, quality]
89
+ end.sort do |(_, left), (_, right)|
90
+ right <=> left
91
+ end.map(&:first).compact
92
+ end
93
+
94
+ # Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
95
+ # this method will try to best match the available locales to the user's preferred languages.
96
+ #
97
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
80
98
  def locale
81
99
  @locale ||= begin
82
- locale = 'en'.freeze
83
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
84
- languages.downcase.split(','.freeze).each do |lang|
85
- next if lang == '*'.freeze
86
- lang = lang.split(';'.freeze)[0]
87
- break locale = lang if find_locale_files(lang).any?
88
- end
89
- locale
100
+ matched_locale = user_preferred_languages.map do |preferred|
101
+ preferred_language = preferred.split('-', 2).first
102
+
103
+ lang_group = available_locales.select do |available|
104
+ preferred_language == available.split('-', 2).first
105
+ end
106
+
107
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
108
+ end.compact.first
109
+
110
+ matched_locale || 'en'
90
111
  end
91
112
  end
92
113
 
@@ -168,7 +189,11 @@ module Sidekiq
168
189
 
169
190
  # Merge options with current params, filter safe params, and stringify to query string
170
191
  def qparams(options)
171
- options = options.stringify_keys
192
+ # stringify
193
+ options.keys.each do |key|
194
+ options[key.to_s] = options.delete(key)
195
+ end
196
+
172
197
  params.merge(options).map do |key, value|
173
198
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
174
199
  end.compact.join("&")
@@ -258,6 +283,10 @@ module Sidekiq
258
283
  "Sidekiq v#{Sidekiq::VERSION}"
259
284
  end
260
285
 
286
+ def server_utc_time
287
+ Time.now.utc.strftime('%H:%M:%S UTC')
288
+ end
289
+
261
290
  def redis_connection_and_namespace
262
291
  @redis_connection_and_namespace ||= begin
263
292
  namespace_suffix = namespace == nil ? '' : "##{namespace}"
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  require 'sidekiq/client'
3
- require 'sidekiq/core_ext'
4
3
 
5
4
  module Sidekiq
6
5
 
7
-
8
6
  ##
9
7
  # Include this module in your worker class and you can easily create
10
8
  # asynchronous jobs:
@@ -118,7 +116,8 @@ module Sidekiq
118
116
  # In practice, any option is allowed. This is the main mechanism to configure the
119
117
  # options for a specific job.
120
118
  def sidekiq_options(opts={})
121
- self.sidekiq_options_hash = get_sidekiq_options.merge(opts.stringify_keys)
119
+ # stringify
120
+ self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map{|k, v| [k.to_s, v]}])
122
121
  end
123
122
 
124
123
  def sidekiq_retry_in(&block)
@@ -135,8 +134,68 @@ module Sidekiq
135
134
 
136
135
  def client_push(item) # :nodoc:
137
136
  pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'.freeze] || Sidekiq.redis_pool
138
- hash = item.stringify_keys
139
- Sidekiq::Client.new(pool).push(hash)
137
+ # stringify
138
+ item.keys.each do |key|
139
+ item[key.to_s] = item.delete(key)
140
+ end
141
+
142
+ Sidekiq::Client.new(pool).push(item)
143
+ end
144
+
145
+ def class_attribute(*attrs)
146
+ instance_reader = true
147
+ instance_writer = true
148
+
149
+ attrs.each do |name|
150
+ singleton_class.instance_eval do
151
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
152
+ end
153
+ define_singleton_method(name) { nil }
154
+
155
+ ivar = "@#{name}"
156
+
157
+ singleton_class.instance_eval do
158
+ m = "#{name}="
159
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
160
+ end
161
+ define_singleton_method("#{name}=") do |val|
162
+ singleton_class.class_eval do
163
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
164
+ define_method(name) { val }
165
+ end
166
+
167
+ if singleton_class?
168
+ class_eval do
169
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
170
+ define_method(name) do
171
+ if instance_variable_defined? ivar
172
+ instance_variable_get ivar
173
+ else
174
+ singleton_class.send name
175
+ end
176
+ end
177
+ end
178
+ end
179
+ val
180
+ end
181
+
182
+ if instance_reader
183
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
184
+ define_method(name) do
185
+ if instance_variable_defined?(ivar)
186
+ instance_variable_get ivar
187
+ else
188
+ self.class.public_send name
189
+ end
190
+ end
191
+ end
192
+
193
+ if instance_writer
194
+ m = "#{name}="
195
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
196
+ attr_writer name
197
+ end
198
+ end
140
199
  end
141
200
 
142
201
  end
@@ -111,8 +111,11 @@ var realtimeGraph = function(updatePath) {
111
111
 
112
112
  updateStatsSummary(data.sidekiq);
113
113
  updateRedisStats(data.redis);
114
+ updateFooterUTCTime(data.server_utc_time)
115
+
114
116
  pulseBeacon();
115
117
  });
118
+
116
119
  i++;
117
120
  }, timeInterval);
118
121
  }
@@ -224,6 +227,10 @@ var updateRedisStats = function(data) {
224
227
  $('.stat h3.used_memory_peak_human').html(data.used_memory_peak_human)
225
228
  }
226
229
 
230
+ var updateFooterUTCTime = function(time) {
231
+ $('.navbar-fixed-bottom .navbar-inner .server-utc-time').html(time)
232
+ }
233
+
227
234
  var pulseBeacon = function(){
228
235
  $('.beacon').addClass('pulse').delay(1000).queue(function(){
229
236
  $(this).removeClass('pulse').dequeue();
@@ -9,7 +9,7 @@
9
9
  <p class="navbar-text redis-url" title="<%= redis_connection_and_namespace %>"><%= redis_connection_and_namespace %></p>
10
10
  </li>
11
11
  <li>
12
- <p class="navbar-text"><%= Time.now.utc.strftime('%H:%M:%S UTC') %></p>
12
+ <p class="navbar-text server-utc-time"><%= server_utc_time %></p>
13
13
  </li>
14
14
  </ul>
15
15
  </div>
@@ -22,6 +22,7 @@
22
22
  <th><%= t('Busy') %></th>
23
23
  <th>&nbsp;</th>
24
24
  </thead>
25
+ <% lead = processes.leader %>
25
26
  <% processes.each do |process| %>
26
27
  <tr>
27
28
  <td class="box">
@@ -31,7 +32,10 @@
31
32
  <span class="label label-info"><%= label %></span>
32
33
  <% end %>
33
34
  <% if process.stopping? %>
34
- <span class="label label-danger">Quiet</span>
35
+ <span class="label label-danger">quiet</span>
36
+ <% end %>
37
+ <% if process.identity == lead %>
38
+ <span class="label label-warning">leader</span>
35
39
  <% end %>
36
40
  <br>
37
41
  <b><%= "#{t('Queues')}: " %></b>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-25 00:00:00.000000000 Z
11
+ date: 2017-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis