master_slave_adapter 1.0.0.beta1 → 1.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|