-
-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract Scheduler polling behavior to its own object
- Loading branch information
1 parent
9b187e9
commit 75bc5e1
Showing
11 changed files
with
161 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
require 'concurrent/atomic/atomic_boolean' | ||
|
||
module GoodJob # :nodoc: | ||
# | ||
# Pollers regularly wake up execution threads to check for new work. | ||
# | ||
class Poller | ||
# Default poll interval. | ||
DEFAULT_POLL_INTERVAL = 1 | ||
|
||
# Defaults for instance of Concurrent::TimerTask. | ||
# The timer controls how and when sleeping threads check for new work. | ||
DEFAULT_TIMER_OPTIONS = { | ||
execution_interval: DEFAULT_POLL_INTERVAL, | ||
timeout_interval: 1, | ||
run_now: true, | ||
}.freeze | ||
|
||
# @!attribute [r] instances | ||
# @!scope class | ||
# List of all instantiated Pollers in the current process. | ||
# @return [array<GoodJob:Poller>] | ||
cattr_reader :instances, default: [], instance_reader: false | ||
|
||
def self.from_configuration(configuration) | ||
GoodJob::Poller.new(poll_interval: configuration.poll_interval) | ||
end | ||
|
||
# List of recipients that will receive notifications. | ||
# @return [Array<#call, Array(Object, Symbol)>] | ||
attr_reader :recipients | ||
|
||
# @param recipients [Array<#call, Array(Object, Symbol)>] | ||
# @param timer_options [Hash] Options to instantiate a Concurrent::TimerTask | ||
def initialize(*recipients, poll_interval: DEFAULT_POLL_INTERVAL) | ||
@recipients = Concurrent::Array.new(recipients) | ||
@timer_options = DEFAULT_TIMER_OPTIONS.merge(execution_interval: poll_interval) | ||
|
||
self.class.instances << self | ||
|
||
create_pool | ||
end | ||
|
||
# Shut down the poller. | ||
# If +wait+ is +true+, the poller will wait for background thread to shutdown. | ||
# If +wait+ is +false+, this method will return immediately even though threads may still be running. | ||
# Use {#shutdown?} to determine whether threads have stopped. | ||
# @param wait [Boolean] Wait for actively executing threads to finish | ||
# @return [void] | ||
def shutdown(wait: true) | ||
return unless @timer&.running? | ||
|
||
@timer.shutdown | ||
@timer.wait_for_termination if wait | ||
end | ||
|
||
# Tests whether the poller is shutdown. | ||
# @return [true, false, nil] | ||
def shutdown? | ||
!@timer&.running? | ||
end | ||
|
||
# Restart the poller. | ||
# When shutdown, start; or shutdown and start. | ||
# @param wait [Boolean] Wait for background thread to finish | ||
# @return [void] | ||
def restart(wait: true) | ||
shutdown(wait: wait) | ||
create_pool | ||
end | ||
|
||
# Invoked on completion of TimerTask task. | ||
# @!visibility private | ||
# @return [void] | ||
def timer_observer(time, executed_task, thread_error) | ||
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call) | ||
instrument("finished_timer_task", { result: executed_task, error: thread_error, time: time }) | ||
end | ||
|
||
private | ||
|
||
def create_pool | ||
return if @timer_options[:execution_interval] <= 0 | ||
|
||
@timer = Concurrent::TimerTask.new(@timer_options) do | ||
recipients.each do |recipient| | ||
target, method_name = recipient.is_a?(Array) ? recipient : [recipient, :call] | ||
target.send(method_name) | ||
end | ||
end | ||
@timer.add_observer(self, :timer_observer) | ||
@timer.execute | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe GoodJob::Poller do | ||
describe '.instances' do | ||
it 'contains all registered instances' do | ||
poller = nil | ||
expect do | ||
poller = described_class.new | ||
end.to change { described_class.instances.size }.by(1) | ||
|
||
expect(described_class.instances).to include poller | ||
end | ||
end | ||
|
||
describe '#initialize' do | ||
it 'accepts a zero or negative poll_interval to disable TimerTask' do | ||
poller = described_class.new(poll_interval: 0) | ||
|
||
expect(poller.instance_variable_get(:@task)).to be_nil | ||
end | ||
end | ||
|
||
describe '#recipients' do | ||
it 'polls recipients method' do | ||
stub_const 'POLL_COUNT', Concurrent::AtomicFixnum.new(0) | ||
|
||
recipient = proc { |_payload| POLL_COUNT.increment } | ||
|
||
poller = described_class.new(recipient) | ||
sleep_until(max: 5, increments_of: 0.5) { POLL_COUNT.value > 2 } | ||
poller.shutdown | ||
|
||
expect(POLL_COUNT.value).to be > 2 | ||
end | ||
end | ||
end |
Oops, something went wrong.