13

My Rails 3.1 application connects to 2 databases, one is the default, the other is an Amazon RDS MYSQL instance.

The current database.yml contains two production database connections. The models that need to pull from the second database simply use

establish_connection "production_on_amazon"

Unfortunately Heroku overwrites your database.yml, and only seems to inlcude one database connection. Does anyone know how I can add or configure my second?

Running "heroku config" I can see there are 2 DB's listed but cant seem to configure to connect to both. Perhaps somehow set my default to the SHARED_DATABASE_URL db on Heroku and set the alternate to the DATABASE_URL which points to Amazon...

3 Answers 3

7

Working off of the previous responses, but incorporating some Rails 3 advantages with the configuration and simplifying the parsing...

# config/application.rb
module MyApp
  class Application < Rails::Application
    ... other configs

    config.secondary_database_url = ENV['SECONDARY_DB_URL']
  end
end

We may want to override this in development / test

# config/environments/development.rb

module MyApp
  class Application < Rails::Application
    ... other configs

    config.secondary_database_url = 'SOME_CONNECTION_STRING'
  end
end    

Now to setup the class we'll have our models inherit from...

# lib/active_record/secondary.rb 
module ActiveRecord
  class Secondary < ActiveRecord::Base
    self.abstract_class = true

    # prior to AR 3.2.1
    url = URI.parse( MyApp::Application.config.secondary_database_url )
    establish_connection(
      :adapter  => 'mysql',
      :host     => url.host,
      :username => url.userinfo.split(':')[0],
      :password => url.userinfo.split(':')[1],
      :database => url.path[1..-1],
      :port     => url.port || 3306
    )

    # as of AR 3.2.1
    establish_connection(MyApp::Application.config.secondary_database_url)

  end

  class SecondaryMigration < ActiveRecord::Migration
    def connection
      ActiveRecord::Secondary.connection 
    end
  end

end
Sign up to request clarification or add additional context in comments.

3 Comments

Note: Since 3.2.1, you don't have to parse the database url at all. You can just pass it directly to establish_connection. apidock.com/rails/v3.2.1/ActiveRecord/Base/establish_connection/…
Fyi, making ActiveRecord::Secondary inherit from ActiveRecord::Base can cause problems: ActiveRecord::StatementInvalid: Could not find table 'secondaries'
@Ajedi32 not if you set abstract class to true which I forgot to include
2

Heroku will always connect your app to the production DB that they create for you. If you want to make an additional connection you'll need to do this in your code manually, and create a ENV var that the code can use as a connection string.

Anything in the production segment of database.yml is binned by Heroku and replaced.

1 Comment

For those wondering how they might do this, the way I settled on was to extend ActiveRecord::Base in a module in the lib folder, and then extend my new class in my models, that way I could shard across several kinds of DB is a somewhat DRY way.
2

Regarding Neil's answer, here is a way to do it. Not an out-of-box solution, but might give you an idea... /lib/active_record_extensions.rb

module ActiveRecordExtensions
  class Shard < ActiveRecord::Base
    #need to switch to the shard database connection from heroku config 
    primary_database_url = ENV['PRIMARY_DATABASE_URL']

    if(!primary_database_url.nil?)
      parsed_connection_string = primary_database_url.split("://")
      adapter = parsed_connection_string[0]
      parsed_connection_string = parsed_connection_string[1].split(":")
      username = parsed_connection_string[0]
      parsed_connection_string = parsed_connection_string[1].split("@")
      password = parsed_connection_string[0]
      parsed_connection_string = parsed_connection_string[1].split("/")  
      host = parsed_connection_string[0]
      database = parsed_connection_string[1]

      establish_connection(
        :adapter  => adapter,
        :host     => host,
        :username => username,
        :password => password,
        :database => database,
        :port     => 3306,
        :pool     => 5,
        :timeout  => 5000
      )
    else
      self.establish_connection "shard_#{Rails.env}"
    end
  end

  class ShardMigration < ActiveRecord::Migration
    def connection
      ActiveRecord::Shard.connection 
    end
  end
end

So your model should just extend ActiveRecord::Shard instead of Base

1 Comment

Instead of doing all this manual parsing, you could just use Ruby's URI, eg: parsed_uri = URI(ENV['PRIMARY_DATABASE_URL'])

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.