-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic scheduled tasks #186
Comments
@wollistik, no, this is not supported yet, you didn't miss it 😅 This is something we can consider adding for sure. May I ask what's your use case for it? |
Hi @rosa,
Therefore the dynamic scheduling feature was really nice to implement these requirements. But after I thought about this the last two days, I might be completely redesign this feature and come up with a different solution, because it was rarely used and still required some quirks to get it working. |
Ahh got it! That makes sense 👍 Yes, I thought a bit more about it yesterday and this is something I want to implement eventually 😊 |
Maybe this helps, but the way I implemented dynamic cron schedules is by having one job run every minute and check if the cron schedule matches the one stored in my database table, with my table containing cron expressions. I use Fugit to check if the cron matches the current time. |
@abrunner94 Even I have done a similar thing. Mine is not exactly the cron like scheduling but I am having different schedules. |
SolidQueue is great. I’ve never embraced a new Rails addition more warmly than this one. Thanks to every contributor! I took at look into dynamic recurring jobs and I think SolidQueue is really close to supporting them, at least how I envision. There are just a couple of missing parts which I will try to detail below. TLDR; The The dirty details of exploration and a hacky workaroundI wanted to dive into SolidQueue in hope to uncover what it will take to run dynamic recurring tasks. First as a reminder, the README tells us that the
I will give an example of how to create a dynamic recurring task in my use case described way down below. But even if you created one now it will not run automatically. Remember, the
So here is one way to solve (1). In # in lib/solid_queue/configuration.rb
def recurring_tasks
@recurring_tasks ||= recurring_tasks_config.map do |id, options|
RecurringTask.from_configuration(id, **options)
end.select(&:valid?)
+ @recurring_tasks + RecurringTask.where(static: false)
end So now assuming that we have at least one static recurring job and a dynamic one then all we need to is restart our application for the I could not figure out if or how to reach the instance of the # after dynamic recurring task creation...
SolidQueue::Process.where(kind: "Scheduler").all.map(&:deregister) Assuming that To address that, first we can update # in lib/solid_queue/configuration.rb
+ def reload_recurring_tasks
+ recurring_tasks
+ end
private
... Then lets go to the # in lib/solid_queue/supervisor.rb
def replace_fork(pid, status)
SolidQueue.instrument(:replace_fork, supervisor_pid: ::Process.pid, pid: pid, status: status) do |payload|
if terminated_fork = forks.delete(pid)
payload[:fork] = terminated_fork
handle_claimed_jobs_by(terminated_fork, status)
+ if configured_processes[pid].kind == :scheduler
+ configured_processes[pid].attributes[:recurring_tasks] = configuration.reload_recurring_tasks
+ end
start_process(configured_processes.delete(pid))
end
end
end I tried to find a better way. This feels pretty hackish but it does get the job done. But now we seem to have everything in place for my use case. Final thoughts. I think what it boils down to is that it would be awesome if the Use CaseI have an # config/recurring.yml
development:
periodic_hello:
command: "puts 'Hello, static recurring world!'"
priority: 2
schedule: every 10 minutes
class Event < ApplicationRecord
after_create :create_event
validates :name, :start_time, :end_time, presence: true
private
def create_event
LiveEventDispatcherJob.set(wait_until: self.start_time).perform_later(event_id: self.id)
end
end class LiveEventDispatcherJob < ApplicationJob
queue_as :default
POLLING_SCHEDULE = "every 15 seconds"
def perform(event_id:)
puts "dispatching for the start of live event with id: #{event_id}"
event = Event.find(event_id)
# What I wish ActiveJob had (passing schedule to set):
# LiveEventPollingJob.set(schedule: POLLING_SCHEDULE).perform_later(event_id: event.id)
# What I am doing instead:
SolidQueue::RecurringTask.find_or_create_by(
static: false,
key: "LiveEventPollingEvent#{event.id}",
schedule: POLLING_SCHEDULE,
class_name: "LiveEventPollingJob",
arguments: [ { event_id: event.id } ]
)
# Restart the Scheduler to pick up dynamic recurring task changes
SolidQueue::Process.where(kind: "Scheduler").all.map(&:deregister)
# Schedule the cleanup job
LiveEventCleanupJob.set(wait_until: event.end_time).perform_later(event_id: event.id)
end
end class LiveEventPollingJob < ApplicationJob
queue_as :default
def perform(event_id:)
puts "polling for live event with id: #{event_id}"
sleep 5
end
end class LiveEventCleanupJob < ApplicationJob
queue_as :default
def perform(event_id:)
puts "cleaning up after the end of live event with id: #{event_id}"
SolidQueue::RecurringTask.where(key: "LiveEventPollingEvent#{event_id}").destroy_all
# ...
# Restart the Scheduler to pick up dynamic recurring task changes
SolidQueue::Process.where(kind: "Scheduler").all.map(&:deregister)
end
end So my hacks aside, I think SolidQueue is very close to supporting dynamic recurring tasks, at least for the my use case. Thank you SolidQueue team. |
Hi @rosa,
I really appreciate the work you have done here and I am eager to switch over to solid_queue. There is only one thing left, which is holding me back.
Other gems like resque-scheduler or sidekiq-scheduler offer the ability to dynamically add or remove tasks to the schedule (see https://github.com/resque/resque-scheduler?tab=readme-ov-file#dynamic-schedules). Since everything seems to be stored in the database for solid_queue, this should be quite easy to achieve.
Maybe it is only a documentation issue and it is already possible (this would be awesome 🤞 ).
Happy to hear from you!
The text was updated successfully, but these errors were encountered: