Slow rebake script to avoid overwhelming your server

So I had to go live without waiting for whole rebake to finish. After going live I could not run rebake as it would kill the server. I could not run rebake on the default queue as it would delay all other processes like notifications.

So I used this script to enqueue all the rebake tasks into the ultralow priority sidekiq queue. Sharing here in case anyone else needs it.

# slow_rebake_resumer_ultralow.rb
# Resumes enqueuing rebake jobs from a specific point and sends them
# directly to the :ultra_low priority queue.
# execute inside the container with -> rails runner slow_rebake_resumer_ultralow.rb

# --- Configuration ---
BATCH_SIZE = 500
SLEEP_DURATION = 5
# --- Starting point from your last update ---
last_processed_id = 0 
# --------------------

start_time = Time.now
total_posts = Post.count

puts "Resuming and enqueuing rebake jobs for #{total_posts} posts to the :ultra_low queue..."
puts "Starting after post ID #{last_processed_id}."
puts "---"

loop do
  # Efficiently find the next batch of posts to process
  posts_to_enqueue = Post.where("id > ?", last_processed_id).order(id: :asc).limit(BATCH_SIZE)

  # Exit the loop if there are no more posts
  break if posts_to_enqueue.empty?

  posts_to_enqueue.each do |post|
    begin
      # Define all options in a single hash, including the target queue
      options = {
        post_id: post.id,
        cook: true,
        bypass_bump: true,
        queue: :ultra_low
      }

      # Enqueue the job with the correct, verified syntax
      Jobs.enqueue(:process_post, options)

    rescue => e
      puts "!!! ERROR enqueuing job for post ID #{post.id}: #{e.message}"
    end
  end

  # Update our position to the last post ID in the current batch
  last_processed_id = posts_to_enqueue.last.id
  
  # Get a more accurate count for the progress report
  enqueued_count = Post.where("id <= ?", last_processed_id).count

  # --- Progress Reporting ---
  percentage = (enqueued_count.to_f / total_posts * 100).round(2)
  elapsed_minutes = ((Time.now - start_time) / 60).round(2)

  puts "Enqueued up to post ID #{last_processed_id}. Total enqueued: ~#{enqueued_count} / #{total_posts} (#{percentage}%)"
  puts "Elapsed time (this session): #{elapsed_minutes} minutes."
  puts "Jobs are being sent to the :ultra_low queue."

  # Pause between batches to keep the load low
  if enqueued_count < total_posts
    puts "Sleeping for #{SLEEP_DURATION} seconds..."
    sleep(SLEEP_DURATION)
    puts "---"
  end
end

puts "\nAll remaining rebake jobs have been enqueued successfully to the :ultra_low queue!"

3 Likes

I’m pretty sure that there’s already a process running all the time that does pretty much the same thing and that it’s mentioned by the restore script, but maybe you ran the migration on the live server and didn’t see it. . .

Yes there is. I did not do the standard migration script import. Will post more about it later, once the users have settled down from complaining about infinite scroll and overall bad cluttered user experience on Discourse. :upside_down_face:

But in general also once might need a full rebake like changing the domain, doing some find and replace etc.