Skip to content

Instantly share code, notes, and snippets.

@kokorolx
Created August 6, 2023 15:08
Show Gist options
  • Save kokorolx/b64df3c20d9f93adf278ac57e16fa594 to your computer and use it in GitHub Desktop.
Save kokorolx/b64df3c20d9f93adf278ac57e16fa594 to your computer and use it in GitHub Desktop.
Recurring Workers with Rails, Sidekiq and ice_cube
app/sidekiq/recurring_workers/base_job.rb
require 'sidekiq/api'
class RecurringWorkers::BaseJob
include Sidekiq::Job
sidekiq_options queue: "recurring_workers", retry: 3, backtrace: true
def self.schedule_all
# Pre-load all recurring workers
Dir[Rails.root.join("app/workers/recurring_workers/*.rb")].sort.each { |file| require file }
RecurringWorkers.constants.each do |class_name|
next if class_name == :BaseJob
RecurringWorkers.module_eval(class_name.to_s).schedule
end
end
# When a clas that inherits from RecurringWorkers::Base defines its #perform
# method, redefine it to wrap it in a with_rescheduling block
def self.method_added(method_name)
super
return unless method_name == :perform && !@redefining_method
@redefining_method = true
original = instance_method(:perform)
define_method(:perform_with_rescheduling) do |*args, &block|
with_rescheduling do
original.bind(self).call(*args, &block)
end
rescue StandardError => e
logger.error("#{self.class}##{__method__}: #{e.message}\n #{e.backtrace.join("\n")}")
end
alias_method :perform, :perform_with_rescheduling
@redefining_method = false
end
module ClassMethods
def schedule
if scheduled?
puts "#{self.name} already scheduled"
return false
end
perform_at(perform_at_timestamp)
puts "Scheduled #{self.name} to perform at #{perform_at_timestamp}"
end
# Schedule to run daily at midnight. See
# https://github.com/seejohnrun/ice_cube
def schedule_rules
IceCube::Schedule.new(Time.zone.now.midnight) do |schedule|
schedule.add_recurrence_rule IceCube::Rule.daily
end
end
private
def perform_at_timestamp
schedule_rules.next_occurrence
end
def scheduled_set
Sidekiq::ScheduledSet.new
end
def scheduled_jobs_for_class
scheduled_set.scan(self.name).select { |job| job.klass == self.name }
end
def scheduled?
scheduled_jobs_for_class.present?
end
end
module InstanceMethods
private
def re_schedule
self.class.schedule
end
def with_rescheduling
yield
ensure
re_schedule
end
def log_filename
"#{self.class.name.underscore.gsub('/', '_')}_#{Rails.env}"
end
def logger
filename = Rails.root.join("log/#{log_filename}.log").to_s
@logger ||= ::Logger.new(filename)
end
end
extend ClassMethods
include InstanceMethods
end
# app/sidekiq/recurring_workers/expiry_notification_job.rb
class RecurringWorkers::ExpiryNotificationJob < RecurringWorkers::BaseJob
def perform
ExpiryNotificationService.process
end
def self.schedule_rules
# change to anytime that you want to run
# check more details here: https://github.com/kokorolx/ice_cube
IceCube::Schedule.new(Time.zone.now.change(hour: 15, min: 0, sec: 0)) do |schedule|
schedule.add_recurrence_rule IceCube::Rule.daily
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment