-
Notifications
You must be signed in to change notification settings - Fork 116
Description
Hi! It might not actually be a problem to have multiple schedulers running. I'm just looking through the code to debug why I'm missing check-in events when using .cron(...) { block }, and spotted this.
TLDR: it's possible for there to be multiple Appsignal::CheckIn::Scheduler instances running, and it keeps itself alive.
appsignal-ruby/lib/appsignal/check_in.rb
Lines 93 to 95 in 5697de3
| def scheduler | |
| @scheduler ||= Scheduler.new | |
| end |
It's possible to spawn multiple schedulers if e.g. you call Appsignal::CheckIn.cron(...) from separate threads, if there's a context-switch during the execution of the ||= operator.
Here's an example script to highlight the behaviour:
example script
require "appsignal"
Event = Data.define(:thread, :event, :scheduler)
module Example
# Simulate a delay in the initialization of the scheduler.
module DelayInitialization
def initialize(...)
sleep(0.5)
super
end
end
Appsignal::CheckIn::Scheduler.prepend(DelayInitialization)
@events = Thread::Queue.new
class << self
attr_reader :events
def mark!(event)
events << Event.new(Thread.current.name, event, Appsignal::CheckIn.scheduler)
end
def count_schedulers(id)
schedulers = ObjectSpace.each_object(Appsignal::CheckIn::Scheduler)
$stdout.puts "#{id}: #{schedulers.count}"
end
end
end
Example.count_schedulers("Before.")
Appsignal.start
threads = 2.times.map { |index|
Thread.new do
Thread.current.name = "Thread #{index}"
Example.mark!("start")
Appsignal::CheckIn.cron("thr1") do
Example.mark!("checking in")
sleep 1
end
Example.mark!("done")
end
}
threads.each(&:join)
Example.events.close
Example.count_schedulers("After.")
$stdout.puts "Events:"
Example.events.size.times do
event = Example.events.pop
$stdout.puts "#{event.thread}: #{event.event} (#{event.scheduler&.__id__})"
endExample output:
Before.: 0
After.: 2
Events:
Thread 0: start (680)
Thread 0: checking in (680)
Thread 1: start (700)
Thread 1: checking in (700)
Thread 0: done (700)
Thread 1: done (700)
Workaround
Calling Appsignal::CheckIn.scheduler during application boot should be sufficient.
### Tasks