In the previous post, I wrote a couple of Rake tasks to manage Heroku db backups. Once I had this tasks in place I realised that it would be handy to have another one to load them into my local Postgres database, so I wrote this in my database.rake file:

namespace :db do
  desc 'Load remote database backup to local database'
  task :load_backup do
    fail 'latest.dump does not exist' unless File.exist?('latest.dump')
    Bundler.with_clean_env do
      `pg_restore --verbose --clean --no-acl --no-owner \
      -h localhost -U postgres_dev -d development latest.dump`
    end
    puts 'Load completed.'
  end
end

This worked fine, but if you take a look at the Rake documentation it’s easy to spot that Rake uses a dependency based style of computation rather than the usual imperative style.This means you can add dependent tasks and Rake will evaluate and run them first. Thanks to this Rake feature I rewrote my task to look like this:

namespace :db do
  desc 'Load remote database backup to local database'
  task :load_backup => 'heroku:download_backup' do
    Bundler.with_clean_env do
      `pg_restore --verbose --clean --no-acl --no-owner \
      -h localhost -U postgres_dev -d development latest.dump`
    end
    puts 'Load completed.'
  end
end

With these changes, the task heroku:download_backup, which was created in the previous post, runs before the load_backup task, so I can ensure that the lastest.dump file is going to be in place. As a side effect I don’t need to check for the dump file existence anymore.

It looks good, but after using these tasks a couple of times I felt the inconvenience of having to wait for the db dump to download. The solution to this problem was using the Rake file taks. It’s like a normal Rake task, but it checks for the file timestamps. This is how I rewrote my Heroku Rake task

desc 'Download database backup'
task :download_backup, [:app] =>  latest.dump

file 'latest.dump' do |task, args|
  args.with_defaults(app: DEFAULT_APP)
  Bundler.with_clean_env do
    url = `heroku pg:backups public-url --app #{args[:app]}`
    `curl -o latest.dump "#{url}"`
  end
  puts $?.exitstatus == 0 ? 'Database downloaded.' : 'Download failed.'
  end
end

The Rake file task only runs if the file doesn’t exist, so I can now run rake db:load_backup and it’ll only download the database dump file the first time.

It would also be nice if the heroku:download_backup ran when there is a new backup available in Heroku, but I’ll leave that for the future since this post is long enough.