← til

Faster database resets

December 18, 2018
rails

After I've made a significant change to the relationships of the models on the application I'm working on; I wanted to update the seeds file to accommodate new relationships.

Rails' default command to reset the database rails db:reset drops the database and then recreates all the tables from the schema. Dropping and recreating like this adds some overhead to the command's run time. I wanted to mitigate that since I didn't do any schema updates and I just wanted to clear the tables.

It turns out there is a SQL command made precisely for this scenario: TRUNCATE. The only trick is to use the TRUNCATE command with CASCADE to also truncate the tables with foreign key references to the truncated table and RESTART IDENTITY to reset the auto sequences. I also needed to avoid truncating the schema_migrations table which is used in Rails to store which migrations have been run and ar_internal_metadata which stores Active Record's internal metadata.

Here is the Rake task I've ended up with. It usually shaves off ~10s when reseeding the database.

namespace :db do
  desc 'Clears the database and then seeds it'
  task reseed: :environment do 
    Rake::Task["db:truncate"].invoke
    Rake::Task["db:seed"].invoke
  end

  desc 'Clears the database'
  task truncate: :environment do 
    puts "Truncating database"
    ActiveRecord::Base.connection.
      tables.
      reject {|t| t =~ /ar_internal_metadata|schema_migrations/}.
      map do |t|
        putc "."
        command = "TRUNCATE TABLE #{t} RESTART IDENTITY CASCADE"
        ActiveRecord::Base.connection.execute(command)
      end
    puts
  end
end