Rails fixtures and PostgreSQL referencial integrity
Recently, on a Rails project, I had the need to create a disposable Heroku app to show features in progress and get quick feedback from the stakeholders. Not a long time ago, I’d read about the Heroku Review App functionality and I though this would be a good opportunity to try it out.
Following the Heroku documentation I configured the Review apps for the project and, in no time, I had the ability to spin up a disposable Heroku app per pull request. The next step was populating these apps with demo data.
In this project we have a QA/UAT Heroku app with a database containing demo data. My first thought was cloning the UAT db in the review boxes, but Heroku is clear about this approach: “Copying full database contents from parent to Review Apps (similar to heroku fork) is not currently supported”. Instead, they recommend seeding the database with a post deploy script.
As this is a Rails project, I created some fixtures with demo data and loaded them using the command rake db:fixtures:load
. Fixtures loaded fine in my local machine but in the review boxes I was getting a referential integrity violation error. What was happening?
We use PostgreSQL as database and PostgreSQL does integrity checks, like foreign key constraints. Rails fixtures are loaded in alphabetical order, so it’s common to find cases where a table that is being created has a reference to another table which hasn’t been created yet.
If we take a look at ActiveRecord code base we can see how it calls the method disable_referential_integrity
from create_fixtures
method. This method contains a warning which says “Rails needs superuser privileges to disable referential integrity”. That was the reason why it worked in my machine, I had super user privileges for the local database.
As expected, in Heroku it’s not possible to change PostreSQL database privileges, so I had to search for a different approach. After a bit of researching about PostgreSQL and referencial integrity, I found a post which shows how to drop and recreate PostgreSQL constraints.
Using the scripts in the mentioned post I created a new Rake task called populate_sample_data
. This task resets the database, drops the PostgreSQL constraints, loads the fixtures and then recreates the previous constraints.
Finally, I added the populate_sample_data
rake task to the Review Apps post deploy script and everything started to work as expected.
Rails contributors are working on a solution for this problem, but for now it’s a work in progress and it doesn’t cover PostgreSQL versions older than 9.4.
Hopefully this post will help other people facing the same problem, or someone can come up with a better solution.