I have a task that checks for campaigns that are supposed to be finished by checking the end_at date. If finished, then set the status of the campaign to finished, and send the owner of the campaign an email.The way I see it, is approach 1 and 2 may have the worker queued more than once in the worker pool.Approach 3, always makes sure that there is only one instance running at a time.Approach 3 also means making sure the worker is run once on every deploy.I have 3 scenarios to consider implementing this:APPROACH #1 - Run the cron job. Cron job determines which campaigns the worker should process# CRON via whenever gem that runs every 3 minutescampaigns = Campaign.where(properties -> 'end_at' <= ? AND status = ?, Time.now.utc, 'running')campaigns.each do |campaign|  FinishCampaignWorker.perform_async(campaign.id)end# FinishCampaignWorker classclass FinishCampaignWorker  include Sidekiq::Worker  sidekiq_options({    retry: false,    backtrace: true  })  def perform(campaign_id)    begin      campaign = Campaign.unscoped.find(campaign_id)      # Do not update campaign if deleted or deactivated      if campaign.present? && !%w(deleted deactivated).include?(campaign.status)        campaign.status = 'finished'        UserMailer.post_campaign_notification(campaign.id, campaign.user.id).deliver        campaign.save      end    rescue      Airbrake.notify(        error_class:      'Finish campaign worker',        error_message:    #{$!},        environment_name: Rails.env      )    end  endendAPPROACH #2 - Cron triggers the worker to run every 3 minutes. Let the worker handle most of the logic# CRON via whenever gem that runs every 3 minutesFinishCampaignWorker.perform_async# FinishCampaignWorker classclass FinishCampaignWorker  include Sidekiq::Worker  sidekiq_options({    retry: false,    backtrace: true  })  def perform    begin      campaigns = Campaign.where(properties -> 'end_at' <= ? AND status = ?, Time.now.utc, 'running')      campaigns.each do |campaign|        campaign = Campaign.unscoped.find(campaign_id)        # Do not update campaign if deleted or deactivated        if !%w(deleted deactivated).include?(campaign.status)          campaign.status = 'finished'          UserMailer.post_campaign_notification(campaign.id, campaign.user.id).deliver          campaign.save        end      end    rescue      Airbrake.notify(        error_class:      'Finish campaign worker',        error_message:    #{$!},        environment_name: Rails.env      )    end  endendAPPROACH #3 - Run the worker from console/chef/other on first deployment, let the worker handle most of the logic, and then reschedule the worker 3 minutes after completion# FinishCampaignWorker classclass FinishCampaignWorker  include Sidekiq::Worker  sidekiq_options({    retry: false,    backtrace: true  })  def perform    begin      campaigns = Campaign.where(properties -> 'end_at' <= ? AND status = ?, Time.now.utc, 'running')      campaigns.each do |campaign|        campaign = Campaign.unscoped.find(campaign_id)        # Do not update campaign if deleted or deactivated        if !%w(deleted deactivated).include?(campaign.status)          campaign.status = 'finished'          UserMailer.post_campaign_notification(campaign.id, campaign.user.id).deliver          campaign.save        end      end    rescue      Airbrake.notify(        error_class:      'Finish campaign worker',        error_message:    #{$!},        environment_name: Rails.env      )    end    FinishCampaignWorker.perform_at(3.minutes.from_now)  endend
Triggering the execution of a job at the end of a timed campaign
ruby;ruby on rails;comparative review;sidekiq