Rails 7.0 Added Batch Processing Of Records For destroy_all

July 03, 2021

In Ruby on Rails, we use destroy_all when we want to delete a collection of ActiveRecord::Relation records.


Article.where(published: false).destroy_all # Logs # Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."published" = #? [["published", 0]] # TRANSACTION (0.1ms) begin transaction # Article Destroy (0.5ms) DELETE FROM "articles" WHERE "articles"."id" = ? [["id", 1]] # TRANSACTION (1.7ms) commit transaction # TRANSACTION (0.1ms) begin transaction # Article Destroy (0.5ms) DELETE FROM "articles" WHERE "articles"."id" = ? [["id", 2]] # TRANSACTION (1.9ms) commit transaction # Output #=> [#<Article id: 1, title: "#0 Title", published: false>, #<Article id: 2, title: "#1 Title", published: false">]

As we notice above in the logs, all the records were loaded into the memory at a time and then DELETE transaction was initiated for each record one by one.

Mark that destroy_all always returns the destroyed records as an array.

The initial load of all records at a time consumes memory unnecessarily which can be processed in batches.

Before Ruby on Rails 7.0, to destroy records in batches, we had to use in_batches and destroy_all methods defined in ActiveRecord::Batches::BatchEnumerator.

Let's refactor above example to process it in batches:

# Ruby on Rails 6.1 Article.where(published: false).in_batches(of: 100).destroy_all

With Ruby on Rails 7.0, we do not have to use in_batches explicitly.
destroy_all uses in_batches under the hood and allows us to process records in batches.

Hence, we can update the above example as follows:

# Ruby on Rails 7.0 Article.where(published: false).destroy_all(batch_size: 100) # Output #=> nil


Mark that destroy_all in Ruby on Rails 7.0 returns nil as output.

Since this change will break many existing applications,
we can add the following configuration to existing applications to keep using the old destroy_all behaviour:

Rails.application.config.active_record.destroy_all_in_batches = false


Applications migrating from older versions to Ruby on Rails 7.0 will find this configuration option in config/initializers/new_framework_defaults_7_0.rb.

However, this will give a deprecation warning as destroy_all batch processing will become the default behaviour in Ruby on Rails 7.1 .

DEPRECATION WARNING: As of Rails 7.1, destroy_all will no longer return the collection of objects that were destroyed. To transition to the new behaviour set the following in an initializer: Rails.application.config.active_record.destroy_all_in_batches = true

destroy_all with batch processing optionally accepts the following arguments:

  • batch_size : If we do not provide any batch_size, then 1000 is the default batch_size.
  • start, finish: We can pass our targeted primary key range in start and finish arguments.
  • order: We can destroy records by ordering them using primary key either in asc or desc order,
    the default order is asc.
  • error_on_ignore: We can override the application level error_on_ignore configuration.

Here is the link to the pull request.

Got something to share with us ? Please inbox


© All rights reserved 2021