mirror of
https://github.com/endofunky/sidetiq.git
synced 2022-11-09 13:53:30 -05:00
Store schedules on the workers instead of a global, mutable Hash.
This commit is contained in:
parent
fdedc078f9
commit
f5c1b91587
17 changed files with 89 additions and 59 deletions
|
@ -2,6 +2,7 @@
|
|||
require 'ostruct'
|
||||
require 'singleton'
|
||||
require 'socket'
|
||||
require 'time'
|
||||
|
||||
# gems
|
||||
require 'ice_cube'
|
||||
|
@ -12,6 +13,7 @@ require 'celluloid'
|
|||
require 'sidetiq/config'
|
||||
require 'sidetiq/logging'
|
||||
require 'sidetiq/api'
|
||||
require 'sidetiq/subclass_tracking'
|
||||
require 'sidetiq/clock'
|
||||
require 'sidetiq/handler'
|
||||
require 'sidetiq/lock/meta_data'
|
||||
|
@ -58,3 +60,7 @@ module Sidetiq
|
|||
Sidetiq::Supervisor.handler
|
||||
end
|
||||
end
|
||||
|
||||
if Sidekiq.server?
|
||||
Sidetiq::Supervisor.run!
|
||||
end
|
||||
|
|
|
@ -3,12 +3,12 @@ module Sidetiq
|
|||
module API
|
||||
# Public: Returns an Array of workers including Sidetiq::Schedulable.
|
||||
def workers
|
||||
schedules.keys
|
||||
Sidetiq::Schedulable.subclasses(true)
|
||||
end
|
||||
|
||||
# Public: Returns a Hash of Sidetiq::Schedule instances.
|
||||
def schedules
|
||||
clock.schedules.dup
|
||||
workers.map(&:schedule)
|
||||
end
|
||||
|
||||
# Public: Currently scheduled recurring jobs.
|
||||
|
|
|
@ -8,7 +8,6 @@ module Sidetiq
|
|||
|
||||
def initialize # :nodoc:
|
||||
super
|
||||
@schedules = {}
|
||||
end
|
||||
|
||||
# Public: Get the schedule for `worker`.
|
||||
|
@ -22,7 +21,9 @@ module Sidetiq
|
|||
#
|
||||
# Returns a Sidetiq::Schedule instances.
|
||||
def schedule_for(worker)
|
||||
schedules[worker] ||= Sidetiq::Schedule.new
|
||||
if worker.respond_to?(:schedule)
|
||||
worker.schedule
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Issue a single clock tick.
|
||||
|
@ -35,8 +36,8 @@ module Sidetiq
|
|||
# Returns a hash of Sidetiq::Schedule instances.
|
||||
def tick
|
||||
tick = gettime
|
||||
schedules.each do |worker, sched|
|
||||
Sidetiq.handler.dispatch(worker,sched, tick)
|
||||
Sidetiq.workers.each do |worker|
|
||||
Sidetiq.handler.dispatch(worker, tick)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,18 +3,20 @@ module Sidetiq
|
|||
include Logging
|
||||
include Sidekiq::ExceptionHandler
|
||||
|
||||
def dispatch(worker, sched, tick)
|
||||
return unless sched.schedule_next?(tick)
|
||||
def dispatch(worker, tick)
|
||||
schedule = worker.schedule
|
||||
|
||||
return unless schedule.schedule_next?(tick)
|
||||
|
||||
Lock::Redis.new(worker).synchronize do |redis|
|
||||
if sched.backfill? && (last = worker.last_scheduled_occurrence) > 0
|
||||
if schedule.backfill? && (last = worker.last_scheduled_occurrence) > 0
|
||||
last = Sidetiq.config.utc ? Time.at(last).utc : Time.at(last)
|
||||
sched.occurrences_between(last + 1, tick).each do |past_t|
|
||||
schedule.occurrences_between(last + 1, tick).each do |past_t|
|
||||
enqueue(worker, past_t, redis)
|
||||
end
|
||||
end
|
||||
|
||||
enqueue(worker, sched.next_occurrence(tick), redis)
|
||||
enqueue(worker, schedule.next_occurrence(tick), redis)
|
||||
end
|
||||
rescue StandardError => e
|
||||
handle_exception(e, context: "Sidetiq::Handler#dispatch")
|
||||
|
|
|
@ -39,12 +39,10 @@ module Sidetiq
|
|||
|
||||
def save_entry_for_worker(entry, worker)
|
||||
Sidekiq.redis do |redis|
|
||||
redis.pipelined do |pipe|
|
||||
list_name = "sidetiq:#{worker.class.name}:history"
|
||||
|
||||
pipe.lpush(list_name, JSON.dump(entry))
|
||||
pipe.ltrim(list_name, 0, Sidetiq.config.worker_history - 1)
|
||||
end
|
||||
redis.lpush(list_name, JSON.dump(entry))
|
||||
redis.ltrim(list_name, 0, Sidetiq.config.worker_history - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,14 +11,24 @@ module Sidetiq
|
|||
# recurrence { daily }
|
||||
# end
|
||||
module Schedulable
|
||||
extend SubclassTracking
|
||||
|
||||
module ClassMethods
|
||||
include Logging
|
||||
include SubclassTracking
|
||||
|
||||
attr_writer :schedule
|
||||
|
||||
# Public: Returns a Float timestamp of the last scheduled run.
|
||||
def last_scheduled_occurrence
|
||||
get_timestamp "last"
|
||||
end
|
||||
|
||||
# Public: Returns the Sidetiq::Schedule for this worker.
|
||||
def schedule
|
||||
@schedule ||= Sidetiq::Schedule.new
|
||||
end
|
||||
|
||||
# Public: Returns a Float timestamp of the next scheduled run.
|
||||
def next_scheduled_occurrence
|
||||
get_timestamp "next"
|
||||
|
@ -32,7 +42,6 @@ module Sidetiq
|
|||
end
|
||||
|
||||
def recurrence(options = {}, &block) # :nodoc:
|
||||
schedule = Sidetiq.clock.schedule_for(self)
|
||||
schedule.instance_eval(&block)
|
||||
schedule.set_options(options)
|
||||
end
|
||||
|
@ -47,7 +56,11 @@ module Sidetiq
|
|||
end
|
||||
|
||||
def self.included(klass) # :nodoc:
|
||||
super
|
||||
|
||||
klass.extend(Sidetiq::Schedulable::ClassMethods)
|
||||
klass.extend(Sidetiq::SubclassTracking)
|
||||
subclasses << klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<%= erb File.read(File.join(File.dirname(__FILE__), 'views', '_home_nav.erb')) %>
|
||||
|
||||
<div class="span9">
|
||||
<% if @schedules.length > 0 %>
|
||||
<% if @workers.length > 0 %>
|
||||
<table class="table table-striped table-bordered table-white" style="width: 100%; margin: 0; table-layout:fixed;">
|
||||
<thead>
|
||||
<th style="width: 50%">Worker</th>
|
||||
|
@ -19,7 +19,8 @@
|
|||
<th style="width: 30%">Next Run</th>
|
||||
<th style="width: 10%">Actions</th>
|
||||
</thead>
|
||||
<% @schedules.each do |worker, schedule| %>
|
||||
<% @workers.each do |worker| %>
|
||||
<% schedule = worker.schedule %>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="<%= "#{root_path}sidetiq/#{worker.name}/schedule" %>"><%= worker.name %></a>
|
||||
|
|
|
@ -6,7 +6,7 @@ module Sidetiq
|
|||
|
||||
def self.registered(app)
|
||||
app.get "/sidetiq" do
|
||||
@schedules = Sidetiq.schedules
|
||||
@workers = Sidetiq.workers
|
||||
@time = Sidetiq.clock.gettime
|
||||
erb File.read(File.join(VIEWS, 'sidetiq.erb'))
|
||||
end
|
||||
|
@ -22,9 +22,11 @@ module Sidetiq
|
|||
|
||||
@time = Sidetiq.clock.gettime
|
||||
|
||||
@worker, @schedule = Sidetiq.schedules.select do |worker, _|
|
||||
@worker = Sidetiq.workers.detect do |worker|
|
||||
worker.name == name
|
||||
end.flatten
|
||||
end
|
||||
|
||||
@schedule = @worker.schedule
|
||||
|
||||
erb File.read(File.join(VIEWS, 'schedule.erb'))
|
||||
end
|
||||
|
@ -34,12 +36,12 @@ module Sidetiq
|
|||
|
||||
@time = Sidetiq.clock.gettime
|
||||
|
||||
@worker, @schedule = Sidetiq.schedules.select do |worker, _|
|
||||
@worker = Sidetiq.workers.detect do |worker|
|
||||
worker.name == name
|
||||
end.flatten
|
||||
end
|
||||
|
||||
@history = Sidekiq.redis do |redis|
|
||||
redis.lrange("sidetiq:#{@worker.name}:history", 0, -1)
|
||||
redis.lrange("sidetiq:#{name}:history", 0, -1)
|
||||
end
|
||||
|
||||
erb File.read(File.join(VIEWS, 'history.erb'))
|
||||
|
@ -48,9 +50,9 @@ module Sidetiq
|
|||
app.post "/sidetiq/:name/trigger" do
|
||||
halt 404 unless (name = params[:name])
|
||||
|
||||
worker, _ = Sidetiq.schedules.select do |w, _|
|
||||
w.name == name
|
||||
end.flatten
|
||||
worker = Sidetiq.workers.detect do |worker|
|
||||
worker.name == name
|
||||
end
|
||||
|
||||
worker.perform_async
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
class LastAndScheduledTicksWorker
|
||||
include Sidekiq::Worker
|
||||
include Sidetiq::Schedulable
|
||||
|
||||
recurrence { hourly }
|
||||
|
||||
def perform(last_tick, scheduled_tick)
|
||||
end
|
||||
end
|
||||
|
|
5
test/fixtures/last_tick_worker.rb
vendored
5
test/fixtures/last_tick_worker.rb
vendored
|
@ -1,4 +1,9 @@
|
|||
class LastTickWorker
|
||||
include Sidekiq::Worker
|
||||
include Sidetiq::Schedulable
|
||||
|
||||
recurrence { hourly }
|
||||
|
||||
def perform(last_tick)
|
||||
end
|
||||
end
|
||||
|
|
2
test/fixtures/optional_arguments_worker.rb
vendored
2
test/fixtures/optional_arguments_worker.rb
vendored
|
@ -2,6 +2,8 @@ class OptionalArgumentWorker
|
|||
include Sidekiq::Worker
|
||||
include Sidetiq::Schedulable
|
||||
|
||||
recurrence { hourly }
|
||||
|
||||
def perform(last_tick = nil)
|
||||
end
|
||||
end
|
||||
|
|
2
test/fixtures/simple_worker.rb
vendored
2
test/fixtures/simple_worker.rb
vendored
|
@ -2,6 +2,8 @@ class SimpleWorker
|
|||
include Sidekiq::Worker
|
||||
include Sidetiq::Schedulable
|
||||
|
||||
recurrence { daily }
|
||||
|
||||
def perform
|
||||
end
|
||||
end
|
||||
|
|
5
test/fixtures/splat_args_worker.rb
vendored
5
test/fixtures/splat_args_worker.rb
vendored
|
@ -1,4 +1,9 @@
|
|||
class SplatArgsWorker
|
||||
include Sidekiq::Worker
|
||||
include Sidetiq::Schedulable
|
||||
|
||||
recurrence { hourly }
|
||||
|
||||
def perform(arg1, *args)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@ class TestClock < Sidetiq::TestCase
|
|||
|
||||
def test_backfilling
|
||||
BackfillWorker.jobs.clear
|
||||
Sidetiq.stubs(:workers).returns([BackfillWorker])
|
||||
start = Sidetiq::Schedule::START_TIME
|
||||
|
||||
BackfillWorker.stubs(:last_scheduled_occurrence).returns(start.to_f)
|
||||
|
@ -32,10 +33,7 @@ class TestClock < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_enqueues_jobs_by_schedule
|
||||
schedule = Sidetiq::Schedule.new
|
||||
schedule.daily
|
||||
|
||||
clock.stubs(:schedules).returns(SimpleWorker => schedule)
|
||||
Sidetiq.stubs(:workers).returns([SimpleWorker])
|
||||
|
||||
SimpleWorker.expects(:perform_at).times(10)
|
||||
|
||||
|
@ -51,13 +49,11 @@ class TestClock < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_enqueues_jobs_with_default_last_tick_arg_on_first_run
|
||||
schedule = Sidetiq::Schedule.new
|
||||
schedule.hourly
|
||||
|
||||
time = Time.local(2011, 1, 1, 1, 30)
|
||||
|
||||
clock.stubs(:gettime).returns(time, time + 3600)
|
||||
clock.stubs(:schedules).returns(LastTickWorker => schedule)
|
||||
|
||||
Sidetiq.stubs(:workers).returns([LastTickWorker])
|
||||
|
||||
expected_first_tick = time + 1800
|
||||
expected_second_tick = expected_first_tick + 3600
|
||||
|
@ -71,13 +67,11 @@ class TestClock < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_enqueues_jobs_with_last_run_timestamp_and_next_run_timestamp
|
||||
schedule = Sidetiq::Schedule.new
|
||||
schedule.hourly
|
||||
|
||||
time = Time.local(2011, 1, 1, 1, 30)
|
||||
|
||||
clock.stubs(:gettime).returns(time, time + 3600)
|
||||
clock.stubs(:schedules).returns(LastAndScheduledTicksWorker => schedule)
|
||||
|
||||
Sidetiq.stubs(:workers).returns([LastAndScheduledTicksWorker])
|
||||
|
||||
expected_first_tick = time + 1800
|
||||
expected_second_tick = expected_first_tick + 3600
|
||||
|
@ -95,13 +89,11 @@ class TestClock < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_enqueues_jobs_with_last_run_timestamp_if_optional_argument
|
||||
schedule = Sidetiq::Schedule.new
|
||||
schedule.hourly
|
||||
|
||||
time = Time.local(2011, 1, 1, 1, 30)
|
||||
|
||||
clock.stubs(:gettime).returns(time, time + 3600)
|
||||
clock.stubs(:schedules).returns(OptionalArgumentWorker => schedule)
|
||||
|
||||
Sidetiq.stubs(:workers).returns([OptionalArgumentWorker])
|
||||
|
||||
expected_first_tick = time + 1800
|
||||
|
||||
|
@ -111,13 +103,11 @@ class TestClock < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_enqueues_jobs_correctly_for_splat_args_perform_methods
|
||||
schedule = Sidetiq::Schedule.new
|
||||
schedule.hourly
|
||||
|
||||
time = Time.local(2011, 1, 1, 1, 30)
|
||||
|
||||
clock.stubs(:gettime).returns(time, time + 3600)
|
||||
clock.stubs(:schedules).returns(SplatArgsWorker => schedule)
|
||||
|
||||
Sidetiq.stubs(:workers).returns([SplatArgsWorker])
|
||||
|
||||
expected_first_tick = time + 1800
|
||||
|
||||
|
|
|
@ -4,13 +4,11 @@ class TestSidetiq < Sidetiq::TestCase
|
|||
def test_schedules
|
||||
schedules = Sidetiq.schedules
|
||||
|
||||
assert_equal 2, schedules.length
|
||||
assert_includes schedules, ScheduledWorker.schedule
|
||||
assert_includes schedules, BackfillWorker.schedule
|
||||
|
||||
assert_includes schedules.keys, ScheduledWorker
|
||||
assert_includes schedules.keys, BackfillWorker
|
||||
|
||||
assert_kind_of Sidetiq::Schedule, schedules[ScheduledWorker]
|
||||
assert_kind_of Sidetiq::Schedule, schedules[BackfillWorker]
|
||||
assert_kind_of Sidetiq::Schedule, ScheduledWorker.schedule
|
||||
assert_kind_of Sidetiq::Schedule, BackfillWorker.schedule
|
||||
end
|
||||
|
||||
def test_workers
|
||||
|
@ -18,7 +16,6 @@ class TestSidetiq < Sidetiq::TestCase
|
|||
|
||||
assert_includes workers, ScheduledWorker
|
||||
assert_includes workers, BackfillWorker
|
||||
assert_equal 2, workers.length
|
||||
end
|
||||
|
||||
def test_scheduled
|
||||
|
|
|
@ -14,6 +14,7 @@ class TestWeb < Sidetiq::TestCase
|
|||
def setup
|
||||
super
|
||||
ScheduledWorker.jobs.clear
|
||||
Sidetiq.stubs(:workers).returns([ScheduledWorker])
|
||||
end
|
||||
|
||||
def test_home_tab
|
||||
|
@ -27,7 +28,7 @@ class TestWeb < Sidetiq::TestCase
|
|||
get '/sidetiq'
|
||||
assert_equal 200, last_response.status
|
||||
|
||||
clock.schedules.each do |worker, schedule|
|
||||
Sidetiq.workers.each do |worker|
|
||||
assert_match /#{worker.name}/, last_response.body
|
||||
assert_match /#{worker.get_sidekiq_options['queue']}/, last_response.body
|
||||
end
|
||||
|
@ -46,7 +47,7 @@ class TestWeb < Sidetiq::TestCase
|
|||
def test_schedule_page
|
||||
get "/sidetiq/ScheduledWorker/schedule"
|
||||
assert_equal 200, last_response.status
|
||||
schedule = clock.schedules[ScheduledWorker]
|
||||
schedule = ScheduledWorker.schedule
|
||||
|
||||
schedule.recurrence_rules.each do |rule|
|
||||
assert_match /#{rule.to_s}/, last_response.body
|
||||
|
|
|
@ -24,6 +24,6 @@ class TestWorker < Sidetiq::TestCase
|
|||
end
|
||||
|
||||
def test_options
|
||||
assert Sidetiq.schedules[BackfillWorker].backfill?
|
||||
assert BackfillWorker.schedule.backfill?
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue