sidetiq 0.3.7 → 0.4.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE +2 -2
- data/README.md +9 -16
- data/Rakefile +2 -7
- data/examples/Procfile +2 -0
- data/examples/config.ru +15 -0
- data/examples/server.rb +17 -0
- data/examples/workers/failing.rb +13 -0
- data/examples/workers/simple.rb +10 -0
- data/lib/sidetiq/actor/clock.rb +31 -0
- data/lib/sidetiq/actor/handler.rb +7 -0
- data/lib/sidetiq/actor.rb +38 -0
- data/lib/sidetiq/api.rb +109 -0
- data/lib/sidetiq/clock.rb +3 -118
- data/lib/sidetiq/config.rb +8 -0
- data/lib/sidetiq/handler.rb +50 -0
- data/lib/sidetiq/lock/meta_data.rb +53 -0
- data/lib/sidetiq/lock/redis.rb +115 -0
- data/lib/sidetiq/lock/watcher.rb +41 -0
- data/lib/sidetiq/logging.rb +12 -0
- data/lib/sidetiq/middleware/history.rb +58 -0
- data/lib/sidetiq/schedulable.rb +6 -7
- data/lib/sidetiq/supervisor.rb +50 -0
- data/lib/sidetiq/version.rb +6 -3
- data/lib/sidetiq/views/_home_nav.erb +16 -0
- data/lib/sidetiq/views/_worker_nav.erb +22 -0
- data/lib/sidetiq/views/assets/styles.css +36 -0
- data/lib/sidetiq/views/history.erb +48 -0
- data/lib/sidetiq/views/locks.erb +48 -0
- data/lib/sidetiq/views/schedule.erb +59 -0
- data/lib/sidetiq/views/sidetiq.erb +39 -28
- data/lib/sidetiq/web.rb +36 -16
- data/lib/sidetiq.rb +34 -114
- data/sidetiq.gemspec +5 -2
- data/tasks/bundler.task +1 -0
- data/tasks/minitest.task +6 -0
- data/test/fixtures/optional_arguments_worker.rb +8 -0
- data/test/helper.rb +21 -6
- data/test/test_clock.rb +16 -24
- data/test/test_history.rb +60 -0
- data/test/test_lock_meta_data.rb +90 -0
- data/test/test_lock_redis.rb +63 -0
- data/test/test_version.rb +2 -1
- data/test/test_watcher.rb +24 -0
- data/test/test_web.rb +26 -2
- metadata +80 -12
- data/examples/simple.rb +0 -22
- data/lib/sidetiq/lock.rb +0 -63
- data/lib/sidetiq/middleware.rb +0 -23
- data/lib/sidetiq/views/sidetiq_details.erb +0 -49
- data/test/test_lock.rb +0 -30
- data/test/test_middleware.rb +0 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: afa72994a97058431570f5d871d813841987f3d3
|
|
4
|
+
data.tar.gz: 481d72a325c1c96c24cd0f0f38a346fc85077abf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea02330ee1cceb86a386b2d47bb2b825462e549fbf236f411bf08c9b55172ec4d7073e5eaab6b9ebbabff1c2280c330ad0247014bcbcc5625ed6d4651ab0430f
|
|
7
|
+
data.tar.gz: 5989c73b9f8e9c203b18ffa22dcedc2f92e06eeceafa88f6e301e08a0d8c631d6ceeb6b47fa747fc85c528711208a67e4a6d29b53993e0df04ca342271a269d3
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
0.4.0
|
|
2
|
+
-----
|
|
3
|
+
|
|
4
|
+
- Show job history in web extension.
|
|
5
|
+
- Integrate with Sidekiq's exception handling/reporting in critical parts.
|
|
6
|
+
- Store more detailed lock metadata.
|
|
7
|
+
- Remove stray 'thead' from ERB template.
|
|
8
|
+
- Store scheduled worker history in Redis.
|
|
9
|
+
- Use a Celluloid pool of scheduling handlers to run calculations in parallel.
|
|
10
|
+
- Use Celluloid actors instead of plain threads.
|
|
11
|
+
- Fix to work with workers with one optional argument. [nata79]
|
|
12
|
+
- Refactor top-level namespace methods into separate modules.
|
|
13
|
+
- Add Procfile-based example code to boot Sidekiq and the web frontend
|
|
14
|
+
simultaneuously.
|
|
15
|
+
- Experimental watcher worker to remove invalid locks.
|
|
16
|
+
|
|
1
17
|
0.3.7
|
|
2
18
|
-----
|
|
3
19
|
|
data/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2012-2013, Tobias Svensson <
|
|
1
|
+
Copyright (c) 2012-2013, Tobias Svensson <tob@tobiassvensson.co.uk>
|
|
2
2
|
All rights reserved.
|
|
3
3
|
|
|
4
4
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -21,4 +21,4 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
21
21
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
22
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
23
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
|
@@ -32,9 +32,11 @@ Overview
|
|
|
32
32
|
|
|
33
33
|
Sidetiq provides a simple API for defining recurring workers for Sidekiq.
|
|
34
34
|
|
|
35
|
+
- Cuncurrency using Celluloid actors.
|
|
36
|
+
|
|
35
37
|
- Flexible DSL based on [ice_cube](http://seejohnrun.github.com/ice_cube/)
|
|
36
38
|
|
|
37
|
-
- Sidetiq uses a locking mechanism (based on
|
|
39
|
+
- Sidetiq uses a locking mechanism (based on watch/multi/psetex/exec) internally
|
|
38
40
|
so Sidetiq clocks can run in each Sidekiq process without interfering with
|
|
39
41
|
each other (tested with sub-second polling of scheduled jobs by Sidekiq and
|
|
40
42
|
Sidetiq clock rates above 100hz).
|
|
@@ -46,6 +48,7 @@ Dependencies
|
|
|
46
48
|
------------
|
|
47
49
|
|
|
48
50
|
- [Sidekiq](http://mperham.github.com/sidekiq/)
|
|
51
|
+
- [Celluloid](http://celluloid.io/) (shared with Sidekiq)
|
|
49
52
|
- [ice_cube](http://seejohnrun.github.com/ice_cube/)
|
|
50
53
|
|
|
51
54
|
<a name='section_Installation'></a>
|
|
@@ -136,17 +139,8 @@ class MyWorker
|
|
|
136
139
|
end
|
|
137
140
|
```
|
|
138
141
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
```ruby
|
|
143
|
-
Sidekiq.configure_server do |config|
|
|
144
|
-
Sidetiq::Clock.start!
|
|
145
|
-
end
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Additionally, Sidetiq includes a middleware that will check if the clock
|
|
149
|
-
thread is still alive and restart it if necessary.
|
|
142
|
+
If Sidekiq is running in server-mode, Sidekiq will handle recurring
|
|
143
|
+
jobs automatically.
|
|
150
144
|
|
|
151
145
|
<a name='section_Backfills''></a>
|
|
152
146
|
Backfills
|
|
@@ -180,10 +174,6 @@ Configuration
|
|
|
180
174
|
|
|
181
175
|
```ruby
|
|
182
176
|
Sidetiq.configure do |config|
|
|
183
|
-
# Thread priority of the clock thread (default: Thread.main.priority as
|
|
184
|
-
# defined when Sidetiq is loaded).
|
|
185
|
-
config.priority = 2
|
|
186
|
-
|
|
187
177
|
# Clock tick resolution in seconds (default: 1).
|
|
188
178
|
config.resolution = 0.5
|
|
189
179
|
|
|
@@ -192,6 +182,9 @@ Sidetiq.configure do |config|
|
|
|
192
182
|
|
|
193
183
|
# When `true` uses UTC instead of local times (default: false)
|
|
194
184
|
config.utc = false
|
|
185
|
+
|
|
186
|
+
# Scheduling handler pool size (default: number of CPUs)
|
|
187
|
+
config.handler_pool_size = 5
|
|
195
188
|
end
|
|
196
189
|
```
|
|
197
190
|
<a name='section_Configuration_Logging'></a>
|
data/Rakefile
CHANGED
data/examples/Procfile
ADDED
data/examples/config.ru
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'sidekiq'
|
|
2
|
+
require 'sidetiq'
|
|
3
|
+
|
|
4
|
+
require 'sidekiq/web'
|
|
5
|
+
require 'sidetiq/web'
|
|
6
|
+
require 'sidetiq/lock/watcher'
|
|
7
|
+
|
|
8
|
+
require './workers/simple.rb'
|
|
9
|
+
require './workers/failing.rb'
|
|
10
|
+
|
|
11
|
+
Sidekiq.configure_client do |config|
|
|
12
|
+
config.redis = { :size => 1 }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
run Sidekiq::Web
|
data/examples/server.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Run with `sidekiq -r /path/to/simple.rb`
|
|
2
|
+
|
|
3
|
+
require 'sidekiq'
|
|
4
|
+
require 'sidetiq'
|
|
5
|
+
require 'sidetiq/lock/watcher'
|
|
6
|
+
|
|
7
|
+
require_relative 'workers/simple'
|
|
8
|
+
require_relative 'workers/failing'
|
|
9
|
+
|
|
10
|
+
Sidekiq.logger.level = Logger::DEBUG
|
|
11
|
+
|
|
12
|
+
Sidekiq.options[:poll_interval] = 1
|
|
13
|
+
|
|
14
|
+
Sidekiq.configure_server do |config|
|
|
15
|
+
Sidetiq.clock.start!
|
|
16
|
+
end
|
|
17
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Sidetiq
|
|
2
|
+
module Actor
|
|
3
|
+
class Clock < Sidetiq::Clock
|
|
4
|
+
include Sidetiq::Actor
|
|
5
|
+
include Sidekiq::ExceptionHandler
|
|
6
|
+
|
|
7
|
+
# Public: Starts the clock loop.
|
|
8
|
+
def start!
|
|
9
|
+
debug "Sidetiq::Clock looping ..."
|
|
10
|
+
loop!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def loop!
|
|
16
|
+
after([time { tick }, 0].max) do
|
|
17
|
+
loop!
|
|
18
|
+
end
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
handle_exception(e, context: 'Sidetiq::Clock#loop!')
|
|
21
|
+
retry
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def time
|
|
25
|
+
start = gettime
|
|
26
|
+
yield
|
|
27
|
+
Sidetiq.config.resolution - (gettime.to_f - start.to_f)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Sidetiq
|
|
2
|
+
module Actor
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.__send__(:include, Celluloid)
|
|
5
|
+
base.finalizer :sidetiq_finalizer
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize(*args, &block)
|
|
9
|
+
log_call "initialize"
|
|
10
|
+
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
# Link to Sidekiq::Manager when running in server-mode. In most
|
|
14
|
+
# cases the supervisor is booted before Sidekiq has launched
|
|
15
|
+
# fully, so defer this.
|
|
16
|
+
if Sidekiq.server?
|
|
17
|
+
after(0.1) { link_to_sidekiq_manager }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def sidetiq_finalizer
|
|
24
|
+
log_call "shutting down ..."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def link_to_sidekiq_manager
|
|
28
|
+
Sidekiq::CLI.instance.launcher.manager.link(current_actor)
|
|
29
|
+
rescue NoMethodError
|
|
30
|
+
warn "Can't link #{self.class.name}. Sidekiq::Manager not running. Retrying ..."
|
|
31
|
+
after(1) { link_to_sidekiq_manager }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def log_call(call)
|
|
35
|
+
info "#{self.class.name} id: #{object_id} #{call}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/sidetiq/api.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module Sidetiq
|
|
2
|
+
# Public: Sidetiq API methods.
|
|
3
|
+
module API
|
|
4
|
+
# Public: Returns an Array of workers including Sidetiq::Schedulable.
|
|
5
|
+
def workers
|
|
6
|
+
schedules.keys
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Public: Returns a Hash of Sidetiq::Schedule instances.
|
|
10
|
+
def schedules
|
|
11
|
+
clock.schedules.dup
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Public: Currently scheduled recurring jobs.
|
|
15
|
+
#
|
|
16
|
+
# worker - A Sidekiq::Worker class or String of the class name (optional)
|
|
17
|
+
# block - An optional block that can be given to which each
|
|
18
|
+
# Sidekiq::SortedEntry instance corresponding to a scheduled job will
|
|
19
|
+
# be yielded.
|
|
20
|
+
#
|
|
21
|
+
# Examples
|
|
22
|
+
#
|
|
23
|
+
# Sidetiq.scheduled
|
|
24
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
25
|
+
#
|
|
26
|
+
# Sidetiq.scheduled(MyWorker)
|
|
27
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
28
|
+
#
|
|
29
|
+
# Sidetiq.scheduled("MyWorker")
|
|
30
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
31
|
+
#
|
|
32
|
+
# Sidetiq.scheduled do |job|
|
|
33
|
+
# # do stuff ...
|
|
34
|
+
# end
|
|
35
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
36
|
+
#
|
|
37
|
+
# Sidetiq.scheduled(MyWorker) do |job|
|
|
38
|
+
# # do stuff ...
|
|
39
|
+
# end
|
|
40
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
41
|
+
#
|
|
42
|
+
# Sidetiq.scheduled("MyWorker") do |job|
|
|
43
|
+
# # do stuff ...
|
|
44
|
+
# end
|
|
45
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
46
|
+
#
|
|
47
|
+
# Yields each Sidekiq::SortedEntry instance.
|
|
48
|
+
# Returns an Array of Sidekiq::SortedEntry objects.
|
|
49
|
+
def scheduled(worker = nil, &block)
|
|
50
|
+
filter_set(Sidekiq::ScheduledSet.new, worker, &block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Public: Recurring jobs currently scheduled for retries.
|
|
54
|
+
#
|
|
55
|
+
# worker - A Sidekiq::Worker class or String of the class name (optional)
|
|
56
|
+
# block - An optional block that can be given to which each
|
|
57
|
+
# Sidekiq::SortedEntry instance corresponding to a scheduled job will
|
|
58
|
+
# be yielded.
|
|
59
|
+
#
|
|
60
|
+
# Examples
|
|
61
|
+
#
|
|
62
|
+
# Sidetiq.retries
|
|
63
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
64
|
+
#
|
|
65
|
+
# Sidetiq.retries(MyWorker)
|
|
66
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
67
|
+
#
|
|
68
|
+
# Sidetiq.retries("MyWorker")
|
|
69
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
70
|
+
#
|
|
71
|
+
# Sidetiq.retries do |job|
|
|
72
|
+
# # do stuff ...
|
|
73
|
+
# end
|
|
74
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
75
|
+
#
|
|
76
|
+
# Sidetiq.retries(MyWorker) do |job|
|
|
77
|
+
# # do stuff ...
|
|
78
|
+
# end
|
|
79
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
80
|
+
#
|
|
81
|
+
# Sidetiq.retries("MyWorker") do |job|
|
|
82
|
+
# # do stuff ...
|
|
83
|
+
# end
|
|
84
|
+
# # => [#<Sidekiq::SortedEntry>, ...]
|
|
85
|
+
#
|
|
86
|
+
# Yields each Sidekiq::SortedEntry instance.
|
|
87
|
+
# Returns an Array of Sidekiq::SortedEntry objects.
|
|
88
|
+
def retries(worker = nil, &block)
|
|
89
|
+
filter_set(Sidekiq::RetrySet.new, worker, &block)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def filter_set(set, worker, &block)
|
|
95
|
+
worker = worker.constantize if worker.kind_of?(String)
|
|
96
|
+
|
|
97
|
+
jobs = set.select do |job|
|
|
98
|
+
klass = job.klass.constantize
|
|
99
|
+
ret = klass.include?(Schedulable)
|
|
100
|
+
ret = ret && klass == worker if worker
|
|
101
|
+
ret
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
jobs.each(&block) if block_given?
|
|
105
|
+
|
|
106
|
+
jobs
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/sidetiq/clock.rb
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
module Sidetiq
|
|
2
|
-
configure do |config|
|
|
3
|
-
config.priority = Thread.main.priority
|
|
4
|
-
config.resolution = 1
|
|
5
|
-
config.lock_expire = 1000
|
|
6
|
-
config.utc = false
|
|
7
|
-
end
|
|
8
|
-
|
|
9
2
|
# Public: The Sidetiq clock.
|
|
10
3
|
class Clock
|
|
11
|
-
include
|
|
12
|
-
include MonitorMixin
|
|
4
|
+
include Logging
|
|
13
5
|
|
|
14
6
|
# Internal: Returns a hash of Sidetiq::Schedule instances.
|
|
15
7
|
attr_reader :schedules
|
|
16
8
|
|
|
17
|
-
# Internal: Returns the clock thread.
|
|
18
|
-
attr_reader :thread
|
|
19
|
-
|
|
20
|
-
def self.method_missing(meth, *args, &block) # :nodoc:
|
|
21
|
-
instance.__send__(meth, *args, &block)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
9
|
def initialize # :nodoc:
|
|
25
10
|
super
|
|
26
11
|
@schedules = {}
|
|
@@ -50,18 +35,8 @@ module Sidetiq
|
|
|
50
35
|
# Returns a hash of Sidetiq::Schedule instances.
|
|
51
36
|
def tick
|
|
52
37
|
tick = gettime
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Lock.new(worker).synchronize do |redis|
|
|
56
|
-
if sched.backfill? && (last = worker.last_scheduled_occurrence) > 0
|
|
57
|
-
last = Sidetiq.config.utc ? Time.at(last).utc : Time.at(last)
|
|
58
|
-
sched.occurrences_between(last + 1, tick).each do |past_t|
|
|
59
|
-
enqueue(worker, past_t, redis)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
enqueue(worker, sched.next_occurrence(tick), redis)
|
|
63
|
-
end if sched.schedule_next?(tick)
|
|
64
|
-
end
|
|
38
|
+
schedules.each do |worker, sched|
|
|
39
|
+
Sidetiq.handler.dispatch(worker,sched, tick)
|
|
65
40
|
end
|
|
66
41
|
end
|
|
67
42
|
|
|
@@ -76,96 +51,6 @@ module Sidetiq
|
|
|
76
51
|
def gettime
|
|
77
52
|
Sidetiq.config.utc ? Time.now.utc : Time.now
|
|
78
53
|
end
|
|
79
|
-
|
|
80
|
-
# Public: Starts the clock unless it is already running.
|
|
81
|
-
#
|
|
82
|
-
# Examples
|
|
83
|
-
#
|
|
84
|
-
# start!
|
|
85
|
-
# # => Thread
|
|
86
|
-
#
|
|
87
|
-
# Returns the Thread instance of the clock thread.
|
|
88
|
-
def start!
|
|
89
|
-
return if ticking?
|
|
90
|
-
|
|
91
|
-
Sidetiq.logger.info "Sidetiq::Clock start"
|
|
92
|
-
|
|
93
|
-
@thread = Thread.start { clock { tick } }
|
|
94
|
-
@thread.abort_on_exception = true
|
|
95
|
-
@thread.priority = Sidetiq.config.priority
|
|
96
|
-
@thread
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Public: Stops the clock if it is running.
|
|
100
|
-
#
|
|
101
|
-
# Examples
|
|
102
|
-
#
|
|
103
|
-
# stop!
|
|
104
|
-
# # => nil
|
|
105
|
-
#
|
|
106
|
-
# Returns nil.
|
|
107
|
-
def stop!
|
|
108
|
-
if ticking?
|
|
109
|
-
@thread.kill
|
|
110
|
-
Sidetiq.logger.info "Sidetiq::Clock stop"
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Public: Returns the status of the clock.
|
|
115
|
-
#
|
|
116
|
-
# Examples
|
|
117
|
-
#
|
|
118
|
-
# ticking?
|
|
119
|
-
# # => false
|
|
120
|
-
#
|
|
121
|
-
# start!
|
|
122
|
-
# ticking?
|
|
123
|
-
# # => true
|
|
124
|
-
#
|
|
125
|
-
# Returns true or false.
|
|
126
|
-
def ticking?
|
|
127
|
-
@thread && @thread.alive?
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
private
|
|
131
|
-
|
|
132
|
-
def enqueue(worker, time, redis)
|
|
133
|
-
key = "sidetiq:#{worker.name}"
|
|
134
|
-
time_f = time.to_f
|
|
135
|
-
next_run = (redis.get("#{key}:next") || -1).to_f
|
|
136
|
-
|
|
137
|
-
if next_run < time_f
|
|
138
|
-
Sidetiq.logger.info "Sidetiq::Clock enqueue #{worker.name} (at: #{time_f}) (last: #{next_run})"
|
|
139
|
-
|
|
140
|
-
redis.mset("#{key}:last", next_run, "#{key}:next", time_f)
|
|
141
|
-
|
|
142
|
-
case worker.instance_method(:perform).arity
|
|
143
|
-
when 0
|
|
144
|
-
worker.perform_at(time)
|
|
145
|
-
when 1
|
|
146
|
-
worker.perform_at(time, next_run)
|
|
147
|
-
else
|
|
148
|
-
worker.perform_at(time, next_run, time_f)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def clock
|
|
154
|
-
loop do
|
|
155
|
-
sleep_time = time { yield }
|
|
156
|
-
|
|
157
|
-
if sleep_time > 0
|
|
158
|
-
Thread.pass
|
|
159
|
-
sleep sleep_time
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def time
|
|
165
|
-
start = gettime
|
|
166
|
-
yield
|
|
167
|
-
Sidetiq.config.resolution - (gettime.to_f - start.to_f)
|
|
168
|
-
end
|
|
169
54
|
end
|
|
170
55
|
end
|
|
171
56
|
|
data/lib/sidetiq/config.rb
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Sidetiq
|
|
2
|
+
class Handler
|
|
3
|
+
include Logging
|
|
4
|
+
include Sidekiq::ExceptionHandler
|
|
5
|
+
|
|
6
|
+
def dispatch(worker, sched, tick)
|
|
7
|
+
return unless sched.schedule_next?(tick)
|
|
8
|
+
|
|
9
|
+
Lock::Redis.new(worker).synchronize do |redis|
|
|
10
|
+
if sched.backfill? && (last = worker.last_scheduled_occurrence) > 0
|
|
11
|
+
last = Sidetiq.config.utc ? Time.at(last).utc : Time.at(last)
|
|
12
|
+
sched.occurrences_between(last + 1, tick).each do |past_t|
|
|
13
|
+
enqueue(worker, past_t, redis)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
enqueue(worker, sched.next_occurrence(tick), redis)
|
|
18
|
+
end
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
handle_exception(e, context: "Sidetiq::Handler#dispatch")
|
|
21
|
+
raise e
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def enqueue(worker, time, redis)
|
|
27
|
+
key = "sidetiq:#{worker.name}"
|
|
28
|
+
time_f = time.to_f
|
|
29
|
+
next_run = (redis.get("#{key}:next") || -1).to_f
|
|
30
|
+
|
|
31
|
+
if next_run < time_f
|
|
32
|
+
info "Enqueue: #{worker.name} (at: #{time_f}) (last: #{next_run})"
|
|
33
|
+
|
|
34
|
+
redis.mset("#{key}:last", next_run, "#{key}:next", time_f)
|
|
35
|
+
|
|
36
|
+
case worker.instance_method(:perform).arity.abs
|
|
37
|
+
when 0
|
|
38
|
+
worker.perform_at(time)
|
|
39
|
+
when 1
|
|
40
|
+
worker.perform_at(time, next_run)
|
|
41
|
+
else
|
|
42
|
+
worker.perform_at(time, next_run, time_f)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
handle_exception(e, context: "Sidetiq::Handler#enqueue")
|
|
47
|
+
raise e
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Sidetiq
|
|
2
|
+
module Lock
|
|
3
|
+
class MetaData
|
|
4
|
+
OWNER = "#{Socket.gethostname}:#{Process.pid}"
|
|
5
|
+
|
|
6
|
+
attr_accessor :owner, :timestamp, :key
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
include Sidekiq::ExceptionHandler
|
|
10
|
+
|
|
11
|
+
def for_new_lock(key)
|
|
12
|
+
new(owner: OWNER, timestamp: Sidetiq.clock.gettime.to_f, key: key)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def from_json(json = "")
|
|
16
|
+
# Avoid TypeError when nil is passed to JSON.parse.
|
|
17
|
+
json = "" if json.nil?
|
|
18
|
+
|
|
19
|
+
hash = JSON.parse(json, symbolize_names: true)
|
|
20
|
+
new(hash)
|
|
21
|
+
rescue JSON::ParserError => e
|
|
22
|
+
if json != ""
|
|
23
|
+
# Looks like garbage lock metadata, so report it.
|
|
24
|
+
handle_exception(e, context: "Garbage lock meta data detected: #{json}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(hash = {})
|
|
32
|
+
@owner = hash[:owner]
|
|
33
|
+
@timestamp = hash[:timestamp]
|
|
34
|
+
@key = hash[:key]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pttl
|
|
38
|
+
Sidekiq.redis { |r| r.pttl(key) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_json
|
|
42
|
+
instance_variables.each_with_object({}) do |var, hash|
|
|
43
|
+
hash[var.to_s.delete("@")] = instance_variable_get(var)
|
|
44
|
+
end.to_json
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
"Sidetiq::Lock on #{key} set at #{timestamp} by #{owner}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|