2

I'm considering paying the $50/ mo for a production database so I can use PostGIS with my rails 4 app. My issue is that I would like to continue developing with a 'staging' environment, but can't yet justify paying for two production databases. Staging is obviously just a clone of production, so just pointing both apps to the same DB url (post) would likely cause some major headaches.

Is there any way to partition the database, or another strategy you'd recommend?

Initial answer from heroku support is no, but I'm hoping there's a scrappy workaround.

Thanks!

Community
  • 1
  • 1
Jay
  • 3,857
  • 2
  • 23
  • 25
  • 1
    On first glance, I think you can use Postgres schemas to achieve this, though it might be messy. I'll put together an example in a few minutes. If you have Railscasts Pro, check out [#389 Multitenancy with PostgreSQL](http://railscasts.com/episodes/389-multitenancy-with-postgresql). – Benjamin Manns Aug 20 '13 at 19:39
  • Awesome. I should tape that on my monitor, 'Always Check for a Railscast' – Jay Aug 20 '13 at 21:22

3 Answers3

2

First off, I would highly suggest just paying the extra $50/mo. For that, you get all kinds of cool stuff like forking and pipelines, as well as the fact that this is kind of hacky. I honestly don't know if this might end up wiping your production data when you clear the staging database. Please make backups before trying this.

I'll view this answer as an answer to a technical challenge rather than a business decision. Without further ado,

Setting up multiple environments on the same database with Heroku Postgres schemas

First, I deleted the existing dev database and added a production database to my production app.

heroku addons:add heroku-postgresql:crane

> Adding heroku-postgresql:crane on test-shared-app... done, v# ($50/mo)
> Attached as HEROKU_POSTGRESQL_RED_URL

This attached a database under the RED color, so replace HEROKU_POSTGRESQL_RED_URL with the appropriate color for your app.

This attaches a database to the production app, but we need to connect to the same app for staging. First, create the staging schema

heroku run "bundle exec rails runner 'ActiveRecord::Base.connection.execute(%q{CREATE SCHEMA staging})'"

Next, create the staging app. See Managing Multiple Environments for an App for more information.

heroku create --remote staging test-shared-app-staging
heroku config:set RACK_ENV=staging RAILS_ENV=staging --remote staging

Then, copy the environment data from your existing app. Add ?schema_search_path=staging to the end of the URL.

heroku config --remote heroku --shell
# make note of your database URLs
heroku config:set --remote staging \
  DATABASE_URL=postgres://...?schema_search_path=staging \
  HEROKU_POSTGRESQL_RED_URL=postgres://...?schema_search_path=staging

And push to staging

git push staging master

Now, run migrations on staging

heroku run --remote staging bundle exec rake db:migrate

And try it out.

My "app" is running at http://test-shared-app.herokuapp.com/posts and http://test-shared-app-staging.herokuapp.com/posts. You can see the source at https://github.com/benmanns/heroku-shared-app.

Benjamin Manns
  • 9,028
  • 4
  • 37
  • 48
  • Agreed, probably doesn't make sense to be maximally 'lean' in this case. I'll give it a shot though, thanks for the quick and thorough answer. – Jay Aug 20 '13 at 22:24
  • To follow up on this: you want to also 'namespace' your production tables under a 'schema' (unfortunately that's what a Postgres namespace is called) other than the default 'public'. Then have a rake task that resets only your staging namespaced tables. [Here's an article](http://blog.jerodsanto.net/2011/07/building-multi-tenant-rails-apps-with-postgresql-schemas/) on schemas, may be newer ones but this helped me wrap my head around things. – Jay Aug 21 '13 at 16:08
1

If I were you, I would use production DB until the right moment (start making money or getting more users, etc). I would replicate all the tables in production DB and give the new tables new names by prepending something like "stag_{original_table_name}. So, you would have two different sets of the same tables. In your models, make them use the new table on staging environment:

class Foo < ActiveRecord::Base
  self.table_name = "staging_#{self.class.name}" if Rails.env.staging?

I am pretty cheap... and this may be an ugly solution in the eyes of the true Rails masters.

Yosep Kim
  • 2,931
  • 22
  • 23
  • Nice! Just the sort of solution I was looking for. I guess I'd have to do something similar with migrations-- I wonder if there are similar ways to keep things DRY? – Jay Aug 20 '13 at 21:13
  • I would create a rake task that would loop through your models and create the tables for you. I wouldn't use DB migration here, if I were you, because we are not really using the Rails default conventions. Fixing migrations can be very painful and time-consuming. And when you are ready to get a full DB for staging, then, just "undo" your changes. That is, drop the tables using a script (rake task) and delete out the line in the models. – Yosep Kim Aug 21 '13 at 12:54
1

Heroku Schemas is another approach to this; it's a Heroku plugin that basically lets you run a single command to apply Benjamin Manns's solution of multiple schemas.

Tom
  • 1,007
  • 12
  • 13