-
Notifications
You must be signed in to change notification settings - Fork 484
Description
I have two database, one with a model call Device, and the other has a model call User.
When I set up database_cleaner according to readme describe:
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner[:active_record,{:connection => :test}].strategy = :transaction
DatabaseCleaner[:active_record,{:connection => :secondary_test}].strategy = :transaction
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
endI found that in my test, device model's connection will point to secondary_test database. And I will have following error as my secondary_test database don't have a table call devices:
ActiveRecord::StatementInvalid:
PG::UndefinedTable: ERROR: relation "devices" does not exist
LINE 5: WHERE a.attrelid = '"devices"'::regclass
^
: SELECT a.attname, format_type(a.atttypid, a.atttypmod),
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '"devices"'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum
I dig in the code, and found that it is cause by #connection_class
def connection_class
@connection_class ||= if db && !db.is_a?(Symbol)
db
elsif connection_hash
lookup_from_connection_pool || establish_connection
else
::ActiveRecord::Base
end
end
private
def lookup_from_connection_pool
if ::ActiveRecord::Base.respond_to?(:descendants)
database_name = connection_hash["database"] || connection_hash[:database]
models = ::ActiveRecord::Base.descendants
models.detect { |m| m.connection_pool.spec.config[:database] == database_name }
end
end
def establish_connection
::ActiveRecord::Base.establish_connection(connection_hash)
endHowever, since ::ActiveRecord::Base.descendants is blank when lookup_from_connection_pool is called, lookup_from_connection_pool would return nil, and establish_connection will be called. Unfortunately, ActiveRecord::Base.establish_connection(connection_hash) has a huge side effect in Rails 4+. It will set connection to specify one for ALL models that inherit from ActiveRecord::Base class. This can be reproduced by following code:
ActiveRecord::Base.establish_connection('test')
Device.connection
In my case, following code:
DatabaseCleaner[:active_record,{:connection => :test}].start
DatabaseCleaner[:active_record,{:connection => :secondary_test}].startwill first set ALL ActiveRecord::Base descendants' connections to test db, then it will set ALL their connection to secondary_test. Therefore, when I try to use Device in my spec, I will have PG::UndefinedTable: ERROR
Here is the similar issues that cause by the side effect for establish_connection: