Ruby on Rails and Phoenix Ecto Sharing A Database
I found a curious problem recently where I built what for the sake of argument we’ll call an “admin control panel” application in Ruby on Rails. The application was originally designed as an internal tool for a company. Eventually the company decided that they wanted to incorporate some of the existing data, as well as data from additional website-specific tables, into a new website.
I decided to build the website with Ember.js on top of a Phoenix API. The site accesses the same database, in otherwords, as the internal Rails application. The Phoenix API is a GET-only API, existing solely to provide content for the site.
That all worked splendidly until I started building building out the suite of tests. I ran rake RAILSENV=test db:drop, rake RAILSENV=test db:create and rake RAILSENV=test db:migrate on my Rails application’s test database. At this point I believe that a recent version of Rails may have changed the design of the schemamigrations table? Because I started getting the following error when I ran mix test:
** (ArgumentError) cannot load `"20110909131101"` as type :integer
(ecto) lib/ecto/repo/queryable.ex:132: Ecto.Repo.Queryable.load!/3
(ecto) lib/ecto/adapters/sql.ex:499: anonymous fn/3 in Ecto.Adapters.SQL.process_row/3
(elixir) lib/enum.ex:1036: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(ecto) lib/ecto/adapters/sql.ex:497: Ecto.Adapters.SQL.process_row/3
(elixir) lib/enum.ex:977: anonymous fn/3 in Enum.map/2
(elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:977: Enum.map/2
(ecto) lib/ecto/adapters/sql.ex:267: Ecto.Adapters.SQL.decode/2
As it turns out, Ecto and ActiveRecord use the same table name for tracking migrations: schema_migrations, but Ecto expects the version column to be an integer rather than the string that ActiveRecord prefers.
I did some digging and it seems that customizing this in Ecto and ActiveRecord is not currently an option, but the solution that I landed at was changing the table name that Rails uses in config/initializers/activerecordextensions.rb:
class ActiveRecord::SchemaMigration
def self.table_name
"schema_versions"
end
end
This does necessitate manually renaming the table in test, development and production databases.
I first wrote a rake task to rename the table in lib/schemamigrationsrename.rake:
namespace :schema_migrations_rename do
desc "do"
task :do => :environment do
puts "Renaming schema_migrations table to schema_versions"
ActiveRecord::Base.connection.execute('ALTER TABLE schema_migrations RENAME TO schema_versions;')
puts "Job complete..."
end
end
and ran it with:
$ rake RAILS_ENV=production schema_migrations_rename:do
I ran into a spot of trouble when I hit production with capistrano… Something funky happened with my capistrano deploys, which does automated migrations. It must have responded to the changed ActiveRecord::SchemaMigration table_name declaration by adding a new table while automatically running migrations as part of the deploy process. So I then ssh’d to the machine and added the records from the schema migrations table to the schema versions table.
$ bundle exec rails console
a.each do |item|
ActiveRecord::Base.connection..connection.execute("INSERT INTO schema_versions (version) VALUES ('#{item['version']}')")
end
Deploys ran smoothly after that.
I am available for Rails and Elixir/Phoenix consulting work – get in touch to learn more.