master_slave_adapter 1.0.0.beta1 → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +4 -1
- data/LICENSE +2 -0
- data/Rakefile +51 -2
- data/Readme.md +55 -37
- data/lib/active_record/connection_adapters/master_slave_adapter/clock.rb +0 -1
- data/lib/active_record/connection_adapters/master_slave_adapter/shared_mysql_adapter_behavior.rb +43 -0
- data/lib/active_record/connection_adapters/master_slave_adapter/version.rb +1 -1
- data/lib/active_record/connection_adapters/master_slave_adapter.rb +347 -354
- data/lib/active_record/connection_adapters/mysql2_master_slave_adapter.rb +48 -0
- data/lib/active_record/connection_adapters/mysql_master_slave_adapter.rb +22 -41
- data/master_slave_adapter.gemspec +3 -3
- data/spec/all.sh +15 -0
- data/spec/gemfiles/activerecord2.3 +5 -0
- data/spec/gemfiles/activerecord3.0 +5 -0
- data/spec/gemfiles/activerecord3.2 +6 -0
- data/spec/integration/helpers/mysql_helper.rb +174 -0
- data/spec/integration/helpers/shared_mysql_examples.rb +212 -0
- data/spec/integration/mysql2_master_slave_adapter_spec.rb +11 -0
- data/spec/integration/mysql_master_slave_adapter_spec.rb +11 -0
- data/spec/master_slave_adapter_spec.rb +75 -21
- data/spec/mysql2_master_slave_adapter_spec.rb +372 -0
- data/spec/mysql_master_slave_adapter_spec.rb +116 -72
- metadata +40 -15
- data/Gemfile +0 -3
- data/TODO.txt +0 -18
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_record/connection_adapters/master_slave_adapter'
|
2
|
+
require 'active_record/connection_adapters/master_slave_adapter/clock'
|
3
|
+
require 'active_record/connection_adapters/master_slave_adapter/shared_mysql_adapter_behavior'
|
4
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
5
|
+
require 'mysql2'
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
class Base
|
9
|
+
def self.mysql2_master_slave_connection(config)
|
10
|
+
ConnectionAdapters::Mysql2MasterSlaveAdapter.new(config, logger)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ConnectionAdapters
|
15
|
+
class Mysql2MasterSlaveAdapter < AbstractAdapter
|
16
|
+
include MasterSlaveAdapter
|
17
|
+
include SharedMysqlAdapterBehavior
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def select_hash(conn, sql)
|
22
|
+
conn.select_one(sql)
|
23
|
+
end
|
24
|
+
|
25
|
+
CONNECTION_ERRORS = {
|
26
|
+
2002 => "query: not connected", # CR_CONNECTION_ERROR
|
27
|
+
2003 => "Can't connect to MySQL server on", # CR_CONN_HOST_ERROR
|
28
|
+
2006 => "MySQL server has gone away", # CR_SERVER_GONE_ERROR
|
29
|
+
2013 => "Lost connection to MySQL server during query", # CR_SERVER_LOST
|
30
|
+
-1 => "closed MySQL connection", # defined by Mysql2
|
31
|
+
}
|
32
|
+
|
33
|
+
def connection_error?(exception)
|
34
|
+
case exception
|
35
|
+
when ActiveRecord::StatementInvalid
|
36
|
+
CONNECTION_ERRORS.values.any? do |description|
|
37
|
+
exception.message.start_with?("Mysql2::Error: #{description}")
|
38
|
+
end
|
39
|
+
when Mysql2::Error
|
40
|
+
CONNECTION_ERRORS.keys.include?(exception.errno)
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require 'active_record/connection_adapters/master_slave_adapter'
|
3
|
-
require 'active_record/connection_adapters/master_slave_adapter/
|
2
|
+
require 'active_record/connection_adapters/master_slave_adapter/shared_mysql_adapter_behavior'
|
4
3
|
require 'active_record/connection_adapters/mysql_adapter'
|
4
|
+
require 'mysql'
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
class Base
|
@@ -11,51 +11,31 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ConnectionAdapters
|
14
|
-
class MysqlMasterSlaveAdapter <
|
15
|
-
|
16
|
-
|
17
|
-
Mysql::Error::CR_CONN_HOST_ERROR, # Can't connect to MySQL server on '%s' (%d)
|
18
|
-
Mysql::Error::CR_SERVER_GONE_ERROR, # MySQL server has gone away
|
19
|
-
Mysql::Error::CR_SERVER_LOST, # Lost connection to MySQL server during query
|
20
|
-
]
|
14
|
+
class MysqlMasterSlaveAdapter < AbstractAdapter
|
15
|
+
include MasterSlaveAdapter
|
16
|
+
include SharedMysqlAdapterBehavior
|
21
17
|
|
22
|
-
|
23
|
-
clock =
|
24
|
-
case clock
|
25
|
-
when Clock then clock
|
26
|
-
when String then Clock.parse(clock)
|
27
|
-
when nil then Clock.zero
|
28
|
-
end
|
29
|
-
|
30
|
-
super(clock)
|
31
|
-
end
|
18
|
+
private
|
32
19
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Clock.infinity
|
20
|
+
if MysqlAdapter.instance_methods.map(&:to_sym).include?(:exec_without_stmt)
|
21
|
+
# The MysqlAdapter in ActiveRecord > v3.1 uses prepared statements which
|
22
|
+
# don't return any results for queries like "SHOW MASTER/SLAVE STATUS",
|
23
|
+
# so we have to use normal queries here.
|
24
|
+
def select_hash(conn, sql)
|
25
|
+
conn.exec_without_stmt(sql).first
|
40
26
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Clock.infinity
|
45
|
-
end
|
46
|
-
|
47
|
-
# TODO: only do the actual conenction specific things here
|
48
|
-
def slave_clock(conn)
|
49
|
-
if status = conn.uncached { conn.select_one("SHOW SLAVE STATUS") }
|
50
|
-
Clock.new(status['Relay_Master_Log_File'], status['Exec_Master_Log_Pos'])
|
51
|
-
else
|
52
|
-
Clock.zero
|
27
|
+
else
|
28
|
+
def select_hash(conn, sql)
|
29
|
+
conn.select_one(sql)
|
53
30
|
end
|
54
|
-
rescue
|
55
|
-
Clock.zero
|
56
31
|
end
|
57
32
|
|
58
|
-
|
33
|
+
CONNECTION_ERRORS = [
|
34
|
+
Mysql::Error::CR_CONNECTION_ERROR, # query: not connected
|
35
|
+
Mysql::Error::CR_CONN_HOST_ERROR, # Can't connect to MySQL server on '%s' (%d)
|
36
|
+
Mysql::Error::CR_SERVER_GONE_ERROR, # MySQL server has gone away
|
37
|
+
Mysql::Error::CR_SERVER_LOST, # Lost connection to MySQL server during query
|
38
|
+
]
|
59
39
|
|
60
40
|
def connection_error?(exception)
|
61
41
|
case exception
|
@@ -67,6 +47,7 @@ module ActiveRecord
|
|
67
47
|
false
|
68
48
|
end
|
69
49
|
end
|
50
|
+
|
70
51
|
end
|
71
52
|
end
|
72
53
|
end
|
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.required_ruby_version = '>= 1.8.7'
|
21
21
|
s.required_rubygems_version = '>= 1.3.7'
|
22
22
|
|
23
|
-
s.
|
24
|
-
s.add_development_dependency 'rake'
|
23
|
+
s.add_dependency 'activerecord', ['>= 2.3.9', '<= 4.0']
|
25
24
|
|
26
|
-
s.
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'rspec'
|
27
27
|
end
|
data/spec/all.sh
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
source ~/.rvm/scripts/rvm
|
4
|
+
|
5
|
+
for ruby in 1.8.7 1.9.2 1.9.3; do
|
6
|
+
rvm use $ruby
|
7
|
+
for gemfile in spec/gemfiles/*; do
|
8
|
+
if [[ "$gemfile" =~ \.lock ]]; then
|
9
|
+
continue
|
10
|
+
fi
|
11
|
+
|
12
|
+
BUNDLE_GEMFILE=$gemfile bundle install --quiet
|
13
|
+
BUNDLE_GEMFILE=$gemfile bundle exec rake spec
|
14
|
+
done
|
15
|
+
done
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module MysqlHelper
|
5
|
+
MASTER_ID = "1"
|
6
|
+
MASTER_PORT = 3310
|
7
|
+
SLAVE_ID = "2"
|
8
|
+
SLAVE_PORT = 3311
|
9
|
+
TEST_TABLE = "master_slave_adapter.master_slave_test"
|
10
|
+
|
11
|
+
def port(identifier)
|
12
|
+
case identifier
|
13
|
+
when :master then MASTER_PORT
|
14
|
+
when :slave then SLAVE_PORT
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def server_id(identifier)
|
19
|
+
case identifier
|
20
|
+
when :master then MASTER_ID
|
21
|
+
when :slave then SLAVE_ID
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_replication
|
26
|
+
execute(:slave, "start slave")
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_replication
|
30
|
+
execute(:slave, "stop slave")
|
31
|
+
end
|
32
|
+
|
33
|
+
def move_master_clock
|
34
|
+
execute(:master, "insert into #{TEST_TABLE} (message) VALUES ('test')")
|
35
|
+
end
|
36
|
+
|
37
|
+
def wait_for_replication_sync
|
38
|
+
Timeout.timeout(5) do
|
39
|
+
until slave_status == master_status; end
|
40
|
+
end
|
41
|
+
rescue Timeout::Error
|
42
|
+
raise "Replication synchronization failed"
|
43
|
+
end
|
44
|
+
|
45
|
+
def configure
|
46
|
+
execute(:master, <<-EOS)
|
47
|
+
SET sql_log_bin = 0;
|
48
|
+
create user 'slave'@'localhost' identified by 'slave';
|
49
|
+
grant replication slave on *.* to 'slave'@'localhost';
|
50
|
+
create database master_slave_adapter;
|
51
|
+
SET sql_log_bin = 1;
|
52
|
+
EOS
|
53
|
+
|
54
|
+
execute(:slave, <<-EOS)
|
55
|
+
change master to master_user = 'slave',
|
56
|
+
master_password = 'slave',
|
57
|
+
master_port = #{port(:master)},
|
58
|
+
master_host = 'localhost';
|
59
|
+
create database master_slave_adapter;
|
60
|
+
EOS
|
61
|
+
|
62
|
+
execute(:master, <<-EOS)
|
63
|
+
CREATE TABLE #{TEST_TABLE} (
|
64
|
+
id int(11) NOT NULL AUTO_INCREMENT,
|
65
|
+
message text COLLATE utf8_unicode_ci,
|
66
|
+
created_at datetime DEFAULT NULL,
|
67
|
+
PRIMARY KEY (id)
|
68
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
69
|
+
EOS
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup
|
73
|
+
[:master, :slave].each do |name|
|
74
|
+
path = location(name)
|
75
|
+
config_path = File.join(path, "my.cnf")
|
76
|
+
data_path = File.join(path, "data")
|
77
|
+
base_dir = File.dirname(File.dirname(`which mysql_install_db`))
|
78
|
+
|
79
|
+
FileUtils.rm_rf(path)
|
80
|
+
FileUtils.mkdir_p(path)
|
81
|
+
File.open(config_path, "w") { |file| file << config(name) }
|
82
|
+
|
83
|
+
`mysql_install_db --basedir='#{base_dir}' --datadir='#{data_path}'`
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def start_master
|
88
|
+
start(:master)
|
89
|
+
end
|
90
|
+
|
91
|
+
def stop_master
|
92
|
+
stop(:master)
|
93
|
+
end
|
94
|
+
|
95
|
+
def start_slave
|
96
|
+
start(:slave)
|
97
|
+
end
|
98
|
+
|
99
|
+
def stop_slave
|
100
|
+
stop(:slave)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def slave_status
|
106
|
+
status(:slave).values_at(9, 21)
|
107
|
+
end
|
108
|
+
|
109
|
+
def master_status
|
110
|
+
status(:master).values_at(0, 1)
|
111
|
+
end
|
112
|
+
|
113
|
+
def status(name)
|
114
|
+
`mysql --protocol=TCP -P#{port(name)} -uroot -N -s -e 'show #{name} status'`.strip.split("\t")
|
115
|
+
end
|
116
|
+
|
117
|
+
def execute(host, statement = '')
|
118
|
+
system(%{mysql --protocol=TCP -P#{port(host)} -uroot -e "#{statement}"})
|
119
|
+
end
|
120
|
+
|
121
|
+
def start(name)
|
122
|
+
$pipes ||= {}
|
123
|
+
$pipes[name] = IO.popen("mysqld --defaults-file='#{location(name)}/my.cnf'")
|
124
|
+
wait_for_database_boot(name)
|
125
|
+
end
|
126
|
+
|
127
|
+
def stop(name)
|
128
|
+
pipe = $pipes[name]
|
129
|
+
Process.kill("KILL", pipe.pid)
|
130
|
+
Process.wait(pipe.pid, Process::WNOHANG)
|
131
|
+
|
132
|
+
# Ruby 1.8.7 doesn't support IO.popen([cmd, [arg, ]]) syntax, and passing
|
133
|
+
# the command line as string wraps the process in a shell. The IO#pid method
|
134
|
+
# will then only return the pid of the wrapping shell process, which is not
|
135
|
+
# what we need here.
|
136
|
+
mysqld_pid = `ps a | grep 'mysqld.*#{location(name)}/my.cnf' | grep -v grep | awk '{print $1}'`.to_i
|
137
|
+
Process.kill("KILL", mysqld_pid) unless mysqld_pid.zero?
|
138
|
+
ensure
|
139
|
+
pipe.close unless pipe.closed?
|
140
|
+
end
|
141
|
+
|
142
|
+
def started?(host)
|
143
|
+
system(%{mysql --protocol=TCP -P#{port(host)} -uroot -e '' 2> /dev/null})
|
144
|
+
end
|
145
|
+
|
146
|
+
def wait_for_database_boot(host)
|
147
|
+
Timeout.timeout(5) do
|
148
|
+
until started?(host); sleep(0.1); end
|
149
|
+
end
|
150
|
+
rescue Timeout::Error
|
151
|
+
raise "Couldn't connect to MySQL in time"
|
152
|
+
end
|
153
|
+
|
154
|
+
def location(name)
|
155
|
+
File.expand_path(File.join("..", "mysql", name.to_s), File.dirname(__FILE__))
|
156
|
+
end
|
157
|
+
|
158
|
+
def config(name)
|
159
|
+
path = location(name)
|
160
|
+
|
161
|
+
<<-EOS
|
162
|
+
[mysqld]
|
163
|
+
pid-file = #{path}/mysqld.pid
|
164
|
+
socket = #{path}/mysqld.sock
|
165
|
+
port = #{port(name)}
|
166
|
+
log-error = #{path}/error.log
|
167
|
+
datadir = #{path}/data
|
168
|
+
log-bin = #{name}-bin
|
169
|
+
log-bin-index = #{name}-bin.index
|
170
|
+
server-id = #{server_id(name)}
|
171
|
+
lower_case_table_names = 1
|
172
|
+
EOS
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'integration/helpers/mysql_helper'
|
2
|
+
|
3
|
+
shared_examples_for "a MySQL MasterSlaveAdapter" do
|
4
|
+
include MysqlHelper
|
5
|
+
|
6
|
+
let(:configuration) do
|
7
|
+
{
|
8
|
+
:adapter => 'master_slave',
|
9
|
+
:connection_adapter => connection_adapter,
|
10
|
+
:username => 'root',
|
11
|
+
:database => 'master_slave_adapter',
|
12
|
+
:master => {
|
13
|
+
:host => '127.0.0.1',
|
14
|
+
:port => port(:master),
|
15
|
+
},
|
16
|
+
:slaves => [{
|
17
|
+
:host => '127.0.0.1',
|
18
|
+
:port => port(:slave),
|
19
|
+
}],
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:test_table) { MysqlHelper::TEST_TABLE }
|
24
|
+
|
25
|
+
def connection
|
26
|
+
ActiveRecord::Base.connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def should_read_from(host)
|
30
|
+
server = server_id(host)
|
31
|
+
query = "SELECT @@Server_id as Value"
|
32
|
+
|
33
|
+
connection.select_all(query).first["Value"].to_s.should == server
|
34
|
+
connection.select_one(query)["Value"].to_s.should == server
|
35
|
+
connection.select_rows(query).first.first.to_s.should == server
|
36
|
+
connection.select_value(query).to_s.should == server
|
37
|
+
connection.select_values(query).first.to_s.should == server
|
38
|
+
end
|
39
|
+
|
40
|
+
before(:all) do
|
41
|
+
setup
|
42
|
+
start_master
|
43
|
+
start_slave
|
44
|
+
configure
|
45
|
+
start_replication
|
46
|
+
end
|
47
|
+
|
48
|
+
after(:all) do
|
49
|
+
stop_master
|
50
|
+
stop_slave
|
51
|
+
end
|
52
|
+
|
53
|
+
before do
|
54
|
+
ActiveRecord::Base.establish_connection(configuration)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "connects to the database" do
|
58
|
+
expect { ActiveRecord::Base.connection }.to_not raise_error
|
59
|
+
end
|
60
|
+
|
61
|
+
context "given a debug logger" do
|
62
|
+
let(:debug_logger) do
|
63
|
+
logger = []
|
64
|
+
def logger.debug(*args)
|
65
|
+
push(args.join)
|
66
|
+
end
|
67
|
+
def logger.debug?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
logger
|
72
|
+
end
|
73
|
+
|
74
|
+
before do
|
75
|
+
ActiveRecord::Base.logger = debug_logger
|
76
|
+
end
|
77
|
+
|
78
|
+
after do
|
79
|
+
ActiveRecord::Base.logger = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it "logs the connection info" do
|
83
|
+
ActiveRecord::Base.connection.select_value("SELECT 42")
|
84
|
+
|
85
|
+
debug_logger.last.should =~ /\[slave:127.0.0.1:3311\] SQL .*SELECT 42/
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when asked for master" do
|
90
|
+
it "reads from master" do
|
91
|
+
ActiveRecord::Base.with_master do
|
92
|
+
should_read_from :master
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when asked for slave" do
|
98
|
+
it "reads from slave" do
|
99
|
+
ActiveRecord::Base.with_slave do
|
100
|
+
should_read_from :slave
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when asked for consistency" do
|
106
|
+
context "given slave is fully synced" do
|
107
|
+
before do
|
108
|
+
wait_for_replication_sync
|
109
|
+
end
|
110
|
+
|
111
|
+
it "reads from slave" do
|
112
|
+
ActiveRecord::Base.with_consistency(connection.master_clock) do
|
113
|
+
should_read_from :slave
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "given slave lags behind" do
|
119
|
+
before do
|
120
|
+
stop_replication
|
121
|
+
move_master_clock
|
122
|
+
end
|
123
|
+
|
124
|
+
after do
|
125
|
+
start_replication
|
126
|
+
end
|
127
|
+
|
128
|
+
it "reads from master" do
|
129
|
+
ActiveRecord::Base.with_consistency(connection.master_clock) do
|
130
|
+
should_read_from :master
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "and slave catches up" do
|
135
|
+
before do
|
136
|
+
start_replication
|
137
|
+
wait_for_replication_sync
|
138
|
+
end
|
139
|
+
|
140
|
+
it "reads from slave" do
|
141
|
+
ActiveRecord::Base.with_consistency(connection.master_clock) do
|
142
|
+
should_read_from :slave
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "given we always wait for slave to catch up and be consistent" do
|
149
|
+
before do
|
150
|
+
start_replication
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should always read from slave" do
|
154
|
+
wait_for_replication_sync
|
155
|
+
ActiveRecord::Base.with_consistency(connection.master_clock) do
|
156
|
+
should_read_from :slave
|
157
|
+
end
|
158
|
+
move_master_clock
|
159
|
+
wait_for_replication_sync
|
160
|
+
ActiveRecord::Base.with_consistency(connection.master_clock) do
|
161
|
+
should_read_from :slave
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "given master goes away in between queries" do
|
168
|
+
let(:query) { "INSERT INTO #{test_table} (message) VALUES ('test')" }
|
169
|
+
|
170
|
+
after do
|
171
|
+
start_master
|
172
|
+
end
|
173
|
+
|
174
|
+
it "raises a MasterUnavailable exception" do
|
175
|
+
expect do
|
176
|
+
ActiveRecord::Base.connection.insert(query)
|
177
|
+
end.to_not raise_error
|
178
|
+
|
179
|
+
stop_master
|
180
|
+
|
181
|
+
expect do
|
182
|
+
ActiveRecord::Base.connection.insert(query)
|
183
|
+
end.to raise_error(ActiveRecord::MasterUnavailable)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "given master is not available" do
|
188
|
+
before(:all) do
|
189
|
+
stop_master
|
190
|
+
end
|
191
|
+
|
192
|
+
after(:all) do
|
193
|
+
start_master
|
194
|
+
end
|
195
|
+
|
196
|
+
context "when asked for master" do
|
197
|
+
it "fails" do
|
198
|
+
expect do
|
199
|
+
ActiveRecord::Base.with_master { should_read_from :master }
|
200
|
+
end.to raise_error(ActiveRecord::MasterUnavailable)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "when asked for slave" do
|
205
|
+
it "reads from slave" do
|
206
|
+
ActiveRecord::Base.with_slave do
|
207
|
+
should_read_from :slave
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'master_slave_adapter'
|
5
|
+
require 'integration/helpers/shared_mysql_examples'
|
6
|
+
|
7
|
+
describe "ActiveRecord::ConnectionAdapters::Mysql2MasterSlaveAdapter" do
|
8
|
+
let(:connection_adapter) { 'mysql2' }
|
9
|
+
|
10
|
+
it_should_behave_like "a MySQL MasterSlaveAdapter"
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'master_slave_adapter'
|
5
|
+
require 'integration/helpers/shared_mysql_examples'
|
6
|
+
|
7
|
+
describe "ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter" do
|
8
|
+
let(:connection_adapter) { 'mysql' }
|
9
|
+
|
10
|
+
it_should_behave_like "a MySQL MasterSlaveAdapter"
|
11
|
+
end
|