Make small, atomic Sidekiq jobs

Problem ๐Ÿ”—

When Sidekiq jobs are big with too much functionality:

Solution ๐Ÿ”—

Bad ๐Ÿ”—

class StartStopPauseJob
  include Sidekiq::Job

  def perform
    LeadBundleSubscription.find_each do |lead_bundle_subscription|
      # โš ๏ธ This will make a huge database query, only to discard 90% of the records!
      next unless lead_bundle_subscription.pause_start_date

      # โš ๏ธ If any of these lines cause a crash, it aborts the whole job and valid subscriptions aren't updated
      if lead_bundle_subscription.pause_end_date.past?
        lead_bundle_subscription.unpause!
      else
        lead_bundle_subscription.pause!
      end
    end
  end
end

Better ๐Ÿ”—

class StartStopPauseJob
  include Sidekiq::Job

  def perform
    LeadBundleSubscription.with_pause_start_date.find_each do |lead_bundle_subscription|
      StartStopPauseSubscriptionJob.perform_async(lead_bundle_subscription.id)
    end
  end
end

class StartStopPauseSubscriptionJob
  include Sidekiq::Job

  def perform(lead_bundle_subscription_id)
    lead_bundle_subscription = LeadBundleSubscription.find(lead_bundle_subscription_id)
    if lead_bundle_subscription.pause_end_date.past?
      lead_bundle_subscription.unpause!
    else
      lead_bundle_subscription.pause!
    end
  end
end

Best ๐Ÿ”—

class ActivateOrPauseJob
  include Sidekiq::Job

  def perform
    # Use #in_batches and #perform_bulk for performant queries and minimise Redis round trips
    LeadBundleSubscription.with_pause_start_date.in_the_past.in_batches do |subscriptions_batch|
      ActivateJob.perform_bulk(subscriptions_batch.ids.zip)
    end

    LeadBundleSubscription.with_pause_start_date.in_the_future.in_batches do |subscriptions_batch|
      PauseJob.perform_bulk(subscriptions_batch.ids.zip)
    end
  end
end

# Small atomic jobs
class PauseJob
  include Sidekiq::Job

  def perform(lead_bundle_subscription_id)
    LeadBundleSubscription.find(lead_bundle_subscription_id).pause!
  end
end

class ActivateJob
  include Sidekiq::Job

  def perform(lead_bundle_subscription_id)
    LeadBundleSubscription.find(lead_bundle_subscription_id).activate!
  end
end