All lessons

Spot the Tax · Card 14 of 20

User.all.each will eventually run out of memory

Why a digest sender that works at launch becomes an outage trigger as the table grows.

The code

What will this cost you in six months?

class WeeklyDigestSender
  def call
    User.all.each do |user|
      next unless user.subscribed?
      WeeklyDigestMailer.with(user: user).digest.deliver_later
    end
  end
end

The problem

User.all.each loads every row from the users table into memory before iterating. At 5,000 users that's fine. At 50,000 it's slow. At 2,000,000 the worker process runs out of memory long before it sends a single email. The code that worked perfectly at launch becomes an outage trigger the day your table grows past whatever Sidekiq's memory limit happens to be.

Take a moment. Before revealing, think about how you'd iterate over a table that doesn't fit in memory. What does Active Record give you for that?