Working on a rails application and a spring server is started with DATABASE_URL
environment variable set, both development and test environments connect to that database. My test setup has a database wipe between tests.
I was using guard to run my tests, which made the initial diagnosis challenging.
-
DATABASE_URL has special meaning for Active Record - it overrides any other configuration. This is tacitly understood - heroku will setup DATABASE_URL as an environment variable - but it is explicitly mentioned in the edge guides (describing v6).
-
A few people have my problem (rails/spring#558)
-
A spring server instance is started as a consequence of a rails server start if there isn't one already running, but you can start spring directly with
spring server
; this has the benefits of exposing the logs. -
The environment variables set on the spring server process are not inherited by child processes forked from that server. To see this in action, start a server with a variable of your choice, then
bin/spring rspec
with a pry early in 'rails_helper' to inspect the variable. -
In spite of 4., the effects of having
DATABASE_URL
set when the spring server starts are inherited by child processes. -
Before I started this exploration, we hadn't bin-stubbed rspec. This meant if you ran
bin/rspec
, you'd be booting your application from the ground up everytime. For this reason, I was preferring our guard setup for incremental test runs, which was using spring, and I'd never bothered to check how. Our project's guardfile explicitly ran rspect through spring withbin/spring rspec
. So a new thing I learned - you can run spring directly. -
But spring supports binstubbing! Run
spring binstub rspec
generatedbin/rspec
which runs rspec using spring (i.e. the previous effect ofbin/spring rspec
). So now I can run faster tests outside of guard (if I have some reason to, and my reason to today comes from limitations of my knowledge of guard).
if defined?(Spring)
database_from_config = YAML.load(File.read(Rails.root.join('config', 'database.yml')))['test']['database']
active_database = ActiveRecord::Base.connection.execute('SELECT current_database();')[0]['current_database's]
if database_from_config != active_database
puts "Current database #{active_database} doesn't match database in config (#{database_from_config}); resetting ..."
# Attempt #1
#
# > Spring::Application.disconnect_database
# > Spring::Application.connect_database
#
# doesn't work - these are instance methods, not class methods
# I can't figure out how to get the instance of Spring::Application ...
# Attempt #2
#
# > ActiveRecord::Base.clear_all_connections!
# > ActiveRecord::Base.establish_connection
#
# Code runs successfully but doesn't do what we want
#
ActiveRecord::Base.connection.reconnect!
active_database =
ActiveRecord::Base.connection.execute('SELECT current_database();')[0]
puts "We're now connected to #{active_database}"
end
end
puts ActiveRecord::Base.connection_config
puts ActiveRecord::Base.connection.execute('SELECT current_database();')[0]
exit(0)
This should be workable. For all my variations above, I could not convince ActiveRecord to reset its connection.
This also is certainly inefficient, as this code is running after the spring fork. But I was trying to get anything to work before worrying about performance.
Use a different environment variable, like DEV_DATABASE_NAME
, and directly inject that in database.yml
. This just sidesteps the problem, because ActiveRecord won't be doing special things for you behind your back.
Note that this does mean that you need to set DEV_DATABASE_NAME
at each rails invocation (rails server
, rails db:migrate
, rails console
), as the environment variables set when a spring server is started are not inherited by subsequent child processes.