Define ActiveRecord connection for given Resque queue - activerecord

Problem Domain
Heroku Cedar stack, with multiple databases. RDS for main database, and Postgres for a second Analytics database. Server runs using the read/write RDS and Postgres databases. Nightly rake task, which are run in a different environment, needs to run a specific Resque queue in a read-only slave of the RDS database.
Postgres DB connection
For the record, all models in the Postgres database include:
module Trove::PostgresConnection
def self.included(base)
base.class_eval do
…set up Postgres database
end
end
end
This works fine, and, being a module injected into each class, does not get squashed by any changes to ActiveRecord::Base.connection
MySQL Connection
Defined using the Heroku RDS plugin. Connection is made to the read/write production database. Unfortunately, this connection is used regardless of environment. Thus, running a rake task on Heroku using RAILS_ENV=analytics rake some:task does not use this connection for ActiveRecord::Base:
analytics:
adapter: mysql2
encoding: utf8
database: dbase
username: uname
password: pword
host: read-only.uri.on.amazonaws.com
reconnect: true
port: 3306
Rather, it uses the connection string provided in the RDS connection:
puts Rails.env
-> 'analytics'
puts SomeModel.connection_config[:host]
-> read-write.uri.on.amazonaws.com
Took me a while to figure that out. Note to self: don't just look at environment, look at database host.
Current Workaround
# Perform an operation using another database connection in ActiveRecord
module SwapConnection
def with_connection_to(config, &block)
ActiveRecord::Base.connection.disconnect!
ActiveRecord::Base.establish_connection(config)
yield
end
end
require 'swap_connection'
class TroveCalculations
#queue = :trove_queue
def self.perform(class_name, id)
SwapConnection.with_connection_to(Rails.env) do
Do something in a given queue
end
end
end
Desired ability
Have something like this in the Procfile
troveworker: env RAILS_ENV=analytics QUEUE=trove_queue bundle exec rake resque:work
which actually uses the analytics database.yml config for that worker. We currently run our server with this Procfile, but it still uses the RDS database.

To expand on my comment on the question, I meant adding a config for your DB the "Heroku way" and then referencing it in your Procfile for the one worker that will process jobs on that queue.
Add a config/environment variable with the DB config you need using a new name:
heroku config:add ANALYTICS_DB=postgres://some_url
And in your Procfile, based on your example of what you want:
troveworker: env DATABASE_URL=$(ANALYTICS_DB) QUEUE=trove_queue \
bundle exec rake resque:work
You'll have to use separate workers for each queue with a different config this way, but the config will be out of the code at least.

I've only played with Heroku, but I thought the database connection info was overridden by the Heroku tools based on environment variables specified by the heroku toolbelt.

The issue here is Heroku generates its own database.yml file: https://devcenter.heroku.com/articles/ruby-support#build-behavior
By using the Amazon RDS addon, Heroku sets a DATABASE_URL environment variable. You can see its contents by running the following from the root of your applications directory:
heroku config
Also, as of Rails 3.2, it will use a DATABASE_URL env var (if set) instead of a database.yml file:
https://github.com/mperham/sidekiq/issues/503#issuecomment-11862427
The simplest workaround might be to:
create an env var called DATABASE_URL_ANALYTICS w/ the Postgres connection string:
heroku config:add DATABASE_URL_ANALYTICS=postgres://xxxxxxxxxxxx
At the beginning of your rake file (before any rails initialization might occur), add:
ENV['DATABASE_URL'] = ENV['DATABASE_URL_ANALYTICS'] if Rails.env.analytics?

Update: Not working. (original answer kept for documentation)
This is how we've solved it:
Procfile:
troveworker: env RAILS_ENV=analytics QUEUE=trove_queue rake trove:worker
lib/tasks/trove.rake:
desc 'Start the Resque workers in the proper environment'
task :worker do
SwapConnection.with_connection_to Rails.env do
Rake::Task['resque:work'].invoke
end
end
This solution solves some other issues for us as well, and works quite nicely. Thanks everyone.

Related

How can I get Heroku's database information in a ruby script?

I created a rails app that is hosted on Heroku. Now I want to upload a ruby script that will populate my database.
This script already exists and is using
PGconn.connect( :hostaddr=>"127.0.0.1", :port=>5432, :dbname=>"myDB", :user=>"MyUser", :password=>'MyPass');
This will obviously not work on Heroku.
I tried to get it using:
Rails.configuration.database_configuration
But I get
uninitialized constant Rails
How can I get the information so I can connect to my Heroku DB? Is there any way to make the same code wotk both on my localhost and on Heroku?
(ps: I am using foreman and the .ENV file, if it helps)
Edit:
The solution I took:
require 'yaml'
require 'erb'
config = YAML.load(ERB.new(File.read(File.join("config","database.yml"))).result)
In config I have all the information I need to perform the DB access.
Why would you not just access ENV['DATABASE_URL'] and break it up into the constituent parts? To see how this is done, if you do heroku run bash and do cat config\database.yml you will see how Heroku does this itself.
To ensure that locally it works as well if you execute via foreman run <Scriptname> then it will be executed and the contents of your .env will be loaded.

Heroku Rails 4 could not connect to server: connection refused

Using postgres.
Haven't been able to push.
Tried this without any luck:
config.assets.initialize_on_precompile = false
-----> Preparing app for Rails asset pipeline
Running: rake assets:precompile
rake aborted!
could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 5432?
The accepted answer did not fully resolve this. I tried to find a solution for 2-3 hours without success, then this worked:
In your app directory.
heroku labs:enable user-env-compile
it is still failing?
heroku labs:disable user-env-compile
heroku labs:enable user-env-compile
Then it worked for me, just had to remove and do again.
The following config is no longer needed in Rails 4. Compiling assets must work without it.
config.assets.initialize_on_precompile = false
config.assets.initialize_on_precompile = false
Include that in application.rb, ABOVE module APPNAME
I had originally included it inside
class Application < Rails::Application
Edit: actually, the above didn't fix it.
I had to do this
https://devcenter.heroku.com/articles/labs-user-env-compile
Try executing locally
bundle exec rake assets:precompile RAILS_ENV=production
It might be because of devise gem as in my case. You might be missing secret key in devise initializer. try adding
config.secret_key = "PROVIDE-KEY"
The heroku dev center talks about troubleshooting this. Basically your rails4 app should not rely on having config vars present during runtime.
https://devcenter.heroku.com/articles/rails-asset-pipeline#troubleshooting
If you're using Rolify pre version 3.5 then it might be that:
https://github.com/EppO/rolify/issues/221

Heroku database migrations failing with "unable to connect"

I'm having a heck of a time getting a Postgres database on a heroku instance to allow me to run database migrations.
I'm also incredibly new to Heroku, so apologies in advance if this is a silly question :)
So my app is on Heroku and can successfully connect to its database - it just can't do anything because the tables aren't set up. My connect block in the code looks like this:
if ENV['DATABASE_URL']
ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
else
ActiveRecord::Base.establish_connection(dbconfig['development'])
end
The environment variable is set by Heroku at deploy time - this also works. If I jump in after everything is initialized with Pry or something, connection works great. The dbconfig hash is populated from my db/config.yml which works fine locally.
I can rake db:migrate on my local system just fine. However, trying this via heroku run rake db:migrate gives me an error dump beginning with:
rake aborted!
could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
The only other odd thing I can think of here is that I'm using a gem called standalone migrations (declared in Gemfile) which should allow me to do this without pulling in the rest of Rails.
So, my question, why aren't my migrations running?

No such file 'config/database.yml' on Heroku

My app is a Ruby rack app. When my Heroku app starts it breaks because
/app/config.ru:8:in `read': No such file or directory - config/database.yml (Errno::ENOENT)
Why does this happen? I understood Heroku is meant to create this file https://devcenter.heroku.com/articles/cedar-migration
The database credentials will still be configured automatically: at slug compile time, a config/database.yml that parses the DATABASE_URL from the environment will be written into the app.
Frustratingly the doc at https://devcenter.heroku.com/articles/ruby doesn't explain about database.yml
Ok, the first thing - heroku does not use database.yml file. By default rails app loading it from config/ directory automatically. And it's no need to load it manually in config.ru. If you want to use in heroku PosgreSQL - just add add-on. Heroku wil do all other things to link your app and db. If you want to use external MySQL server you should use Amazon RDS add-on
heroku addons:add amazon_rds url=mysql2://user:pass#dbhost/dbname
By this you can use any db. I use GoDaddy mysql bases through the Amazon RDS add-on.
Any way, the problem in your config.ru 8th line something like
read 'config/database.yml'
Delete it and look other ways that not conflicted with heroku
Good luck
It appears Heroku only creates its config/database.yml if you have a folder config under source control. Not explained in docs.

When or where in a custom Heroku buildpack are config vars available in the environment?

I understand that running a rake task that expects the environment to fully work requires that the DB connection can actually be stablished. Basically, that's the reason why Heroku needs asset pipeline precompilations to run before the environment can be loaded.
That's why this line is needed to deploy rails 3.X apps to the Cedar:
config.assets.initialize_on_precompile = false
Now, I'm trying to build a custom build pack that needs a rake task to do other assets compilations to make the app work as expected. This is because I have internationalized JS asset files and use https://github.com/fnando/i18n-js
I need to run rake i18n:js:export and that requires the environment. *Is there any point in the buildpack where I can add this call and be sure that the connection to the DB of the Rails app will work? * Loading the environment is as a requirement of this task and I don't know how to work it out and it gives the typical error:
rake aborted!
could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 5432?
Naturally, I can compile locally and then deploy. But the buildpack seems like the right tool to solve this matter in an automated way. Thanks for the help!
Check out the user env Heroku labs feature: https://devcenter.heroku.com/articles/labs-user-env-compile

Resources