Skip to content

Instantly share code, notes, and snippets.

@wxgeorge
Last active January 25, 2019 22:31
Show Gist options
  • Save wxgeorge/ed5be6abab69b2eb33044f2264068d6b to your computer and use it in GitHub Desktop.
Save wxgeorge/ed5be6abab69b2eb33044f2264068d6b to your computer and use it in GitHub Desktop.
debug log: some troubles involving DATABASE_URL, spring, guard and a constantly nuked dev database

The problem

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.

Things I learned

  1. 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).

  2. A few people have my problem (rails/spring#558)

  3. 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.

  4. 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.

  5. In spite of 4., the effects of having DATABASE_URL set when the spring server starts are inherited by child processes.

  6. 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 with bin/spring rspec. So a new thing I learned - you can run spring directly.

  7. But spring supports binstubbing! Run spring binstub rspec generated bin/rspec which runs rspec using spring (i.e. the previous effect of bin/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).

Things I tried

Resetting the ActiveRecord connection at the top of spec/rails_helper.rb

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.

My solution

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment