Import production Postgresql database into the development environment

rails + pg.png

Suppose you’ve configured both the development and production environments for your Rails application that uses Postgresql.

The production environment is hosted on a server whose IP is accessible from your development environment.

For example, let’s assume you’ve used the awesome Digital Ocean (yep, these are referral links <3)

digital ocean logo.png

And that you’ve configured your production environment to perform periodic backups, for example by calling the following script via cron:

#!/bin/bash

RAILS_APP_NAME="XXX"
DEPLOYER_USERNAME="YYY"
DATABASE_NAME="$RAILS_APP_NAME_production"

filename="/home/$DEPLOYER_USERNAME/backups/db/$(date --utc +"%Y%m%d%H%M%S")[$DATABASE_NAME].sql"
pg_dump $DATABASE_NAME > $filename
bzip2 $filename

Obviously, change XXX and YYY according to your setup.

Also, be sure to add your local ssh pub key to the production ~/.ssh/authorized_keys so that you won’t get asked to insert the ssh password every time.

Now, within the bin/ directory, contained in your Rails application structure, you can create the following script:

#/bin/bash

DEPLOYER_USERNAME="YYY"
PRODUCTION_PG_ROLE=$DEPLOYER_USERNAME
DEVELOPMENT_PG_ROLE="ZZZ"
RAILS_APP_NAME="XXX"
PRODUCTION_HOST="KKK"

echo "Make sure you're not running the server or the console (Spring should be stopped, too)."

date
echo "Fetching the freshest backup from the server."
filename=$(ssh $DEPLOYER_USERNAME@$PRODUCTION_HOST "ls ~/backups/db" | sort | tail -n 1)
scp "$DEPLOYER_USERNAME@$PRODUCTION_HOST:~/backups/db/$filename" tmp/
bzip2 -d -f "tmp/$filename"

echo "Recreating local database."
bundle exec rake db:drop
bundle exec rake db:create

extracted_dump_filename=$(ls "tmp/*.sql" | sort | tail -n 1)

echo "Setting local owner."
sed -i "s/OWNER TO $PRODUCTION_PG_ROLE/OWNER TO $DEVELOPMENT_PG_ROLE/g" $extracted_dump_filename

echo "Importing data into the local database."
psql --quiet --username="$DEVELOPMENT_PG_ROLE" --set ON_ERROR_STOP=on "$RAILS_APP_NAME"_development < $extracted_dump_filename > tmp/db_import.log

echo "Done."
date

The only trickier part is the string replacement:

sed -i "s/OWNER TO $PRODUCTION_PG_ROLE/OWNER TO $DEVELOPMENT_PG_ROLE/g" $extracted_dump_filename

and this is needed because when exporting the database with pg_dump you obtain a dump file containing the following commands:

ALTER TABLE public.JUSTATABLENAME OWNER TO PRODUCTION_DEPLOYER_ROLE;

And since PRODUCTION_DEPLOYER_ROLE can differ from production to development, you need to change every occurrence of this kind so that when we use psql to import the dump, it correctly sets the local role as the owner of the tables.

This is just a hack and I’d be more than happy to use a more clean approach ;-)

Finally do a chmod u+x bin/my_import_script and perform a ./bin/my_import_script every time you want to import the production database into your development environment.

 
2
Kudos
 
2
Kudos

Now read this

Autocriticism

Sometimes, during an interview, you get asked the most difficult question: What are your three main strengths and weaknesses? It’s difficult for two reasons: Maybe you can’t fully understand your top strengths, since you could’ve focused... Continue →