fix Rails 4 multisite so connection handlers don't keep spinning up new pools

This commit is contained in:
Sam 2013-11-06 11:42:00 +11:00
parent dee38dad9d
commit 0327f6e02a
7 changed files with 113 additions and 16 deletions

View file

@ -1,11 +1,14 @@
source 'https://rubygems.org' source 'https://rubygems.org'
group :test do group :test do
gem 'rails' gem 'rails'
gem 'rspec' gem 'rspec'
gem 'activerecord' gem 'activerecord'
gem 'sqlite3' gem 'sqlite3'
end end
gem 'guard'
gem 'guard-rspec'
# Specify your gem's dependencies in rails_multisite.gemspec # Specify your gem's dependencies in rails_multisite.gemspec
gemspec gemspec

9
vendor/gems/rails_multisite/Guardfile vendored Normal file
View file

@ -0,0 +1,9 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard :rspec do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/rails_multisite/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end

View file

@ -16,16 +16,23 @@ module RailsMultisite
handler = @@connection_handlers[spec] handler = @@connection_handlers[spec]
unless handler unless handler
handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
if rails4?
handler.establish_connection(ActiveRecord::Base, spec)
end
@@connection_handlers[spec] = handler @@connection_handlers[spec] = handler
end end
else else
handler = @@default_connection_handler handler = @@default_connection_handler
if rails4? && !@@established_default
handler.establish_connection(ActiveRecord::Base, spec)
@@established_default = true
end
end end
ActiveRecord::Base.connection_handler = handler ActiveRecord::Base.connection_handler = handler
if rails4?
ActiveRecord::Base.connection_handler.establish_connection(ActiveRecord::Base, spec) unless rails4?
else handler.establish_connection("ActiveRecord::Base", spec)
ActiveRecord::Base.connection_handler.establish_connection("ActiveRecord::Base", spec)
end end
end end
end end
@ -100,17 +107,19 @@ module RailsMultisite
@@host_spec_cache[host] = @@default_spec @@host_spec_cache[host] = @@default_spec
end end
@@default_connection_handler = ActiveRecord::Base.connection_handler
# inject our connection_handler pool # inject our connection_handler pool
# WARNING MONKEY PATCH # WARNING MONKEY PATCH
# #
# see: https://github.com/rails/rails/issues/8344#issuecomment-10800848 # see: https://github.com/rails/rails/issues/8344#issuecomment-10800848
# if ActiveRecord::VERSION::MAJOR == 3
@@default_connection_handler = ActiveRecord::Base.connection_handler ActiveRecord::Base.send :include, NewConnectionHandler
ActiveRecord::Base.send :include, NewConnectionHandler if ActiveRecord::VERSION::MAJOR == 3 ActiveRecord::Base.connection_handler = @@default_connection_handler
end
ActiveRecord::Base.connection_handler = @@default_connection_handler
@@connection_handlers = {} @@connection_handlers = {}
@@established_default = false
end end
module NewConnectionHandler module NewConnectionHandler

View file

@ -1,16 +1,26 @@
require 'spec_helper' require 'spec_helper'
require 'rails_multisite' require 'rails_multisite'
class Person < ActiveRecord::Base; end
describe RailsMultisite::ConnectionManagement do describe RailsMultisite::ConnectionManagement do
subject { RailsMultisite::ConnectionManagement } subject { RailsMultisite::ConnectionManagement }
def with_connection(db)
subject.establish_connection(db: db)
yield ActiveRecord::Base.connection.raw_connection
ensure
ActiveRecord::Base.connection_handler.clear_active_connections!
end
context 'default' do context 'default' do
its(:all_dbs) { should == ['default']} its(:all_dbs) { should == ['default']}
context 'current' do context 'current' do
before do before do
subject.establish_connection(db: 'default') subject.establish_connection(db: 'default')
ActiveRecord::Base.establish_connection
end end
its(:current_db) { should == 'default' } its(:current_db) { should == 'default' }
@ -25,6 +35,7 @@ describe RailsMultisite::ConnectionManagement do
subject.config_filename = "spec/fixtures/two_dbs.yml" subject.config_filename = "spec/fixtures/two_dbs.yml"
subject.load_settings! subject.load_settings!
end end
its(:all_dbs) { should == ['default', 'second']} its(:all_dbs) { should == ['default', 'second']}
context 'second db' do context 'second db' do
@ -36,6 +47,53 @@ describe RailsMultisite::ConnectionManagement do
its(:current_hostname) { should == "second.localhost" } its(:current_hostname) { should == "second.localhost" }
end end
context 'data partitioning' do
after do
['default','second'].each do |db|
with_connection(db) do |cnn|
cnn.execute("drop table people") rescue nil
end
end
end
it 'partitions data correctly' do
col1 = []
col2 = []
['default','second'].map do |db|
with_connection(db) do |cnn|
cnn.execute("create table if not exists people(id INTEGER PRIMARY KEY AUTOINCREMENT, db)")
end
end
SQLite3::Database.query_log.clear
5.times do
['default','second'].map do |db|
Thread.new do
with_connection(db) do |cnn|
Person.create!(db: db)
end
end
end.map(&:join)
end
lists = []
['default', 'second'].each do |db|
with_connection(db) do |cnn|
lists << Person.order(:id).to_a.map{|p| [p.id, p.db]}
end
end
lists[1].should == (1..5).map{|id| [id, "second"]}
lists[0].should == (1..5).map{|id| [id, "default"]}
# puts SQLite3::Database.query_log.map{|args, caller, oid| "#{oid} #{args.join.inspect}"}.join("\n")
end
end
end end
end end

View file

@ -1,6 +1,6 @@
test: test:
adapter: sqlite3 adapter: sqlite3
database: rails_multisite database: "db.test"
timeout: 5000 timeout: 5000
host_names: host_names:
- default.localhost - default.localhost

View file

@ -1,9 +1,6 @@
second: second:
adapter: sqlite3 adapter: sqlite3
database: second_db database: "db1.test"
username: username
password: password
db_id: 1
host_names: host_names:
- second.localhost - second.localhost
- 2nd.localhost - 2nd.localhost

View file

@ -6,6 +6,27 @@ require 'active_record'
ENV["RAILS_ENV"] ||= 'test' ENV["RAILS_ENV"] ||= 'test'
RSpec.configure do |config| RSpec.configure do |config|
require 'sqlite3'
class SQLite3::Database
def self.query_log
@@query_log ||= []
end
alias_method :old_execute, :execute
alias_method :old_prepare, :prepare
def execute(*args,&blk)
self.class.query_log << [args, caller, Thread.current.object_id]
old_execute(*args,&blk)
end
def prepare(*args,&blk)
self.class.query_log << [args, caller, Thread.current.object_id]
old_prepare(*args,&blk)
end
end
config.color_enabled = true config.color_enabled = true
config.before(:suite) do config.before(:suite) do