activerecord-mysql-reconnect 0.4.0 → 0.4.1.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -2
- data/.travis.yml +21 -17
- data/Appraisals +7 -0
- data/ChangeLog +5 -0
- data/README.md +9 -6
- data/activerecord-mysql-reconnect.gemspec +1 -1
- data/docker-compose.yml +8 -0
- data/gemfiles/activerecord_4.2.gemfile +7 -0
- data/gemfiles/activerecord_4.2.gemfile.lock +66 -0
- data/gemfiles/activerecord_5.0.gemfile +7 -0
- data/gemfiles/activerecord_5.0.gemfile.lock +63 -0
- data/lib/activerecord/mysql/reconnect.rb +2 -0
- data/lib/activerecord/mysql/reconnect/abstract_mysql_adapter_ext.rb +11 -4
- data/lib/activerecord/mysql/reconnect/base_ext.rb +0 -6
- data/lib/activerecord/mysql/reconnect/connection_pool_ext.rb +6 -7
- data/lib/activerecord/mysql/reconnect/mysql2_adapter_ext.rb +7 -6
- data/lib/activerecord/mysql/reconnect/null_transaction_ext.rb +9 -0
- data/lib/activerecord/mysql/reconnect/version.rb +1 -1
- data/spec/activerecord-mysql-reconnect_spec.rb +322 -241
- data/spec/data.sql +1001 -0
- data/spec/employee_model.rb +1 -0
- data/spec/mysql2_ext.rb +5 -0
- data/spec/mysql_helper.rb +94 -0
- data/spec/spec_helper.rb +29 -127
- metadata +20 -7
- data/spec/employees.sql +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c37f85305063d95208c4a30ace243a62709d0d25
|
4
|
+
data.tar.gz: 55d1bf38531ede94d81d977e33f3def41719f09d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a83fe523cc08085211d963711f06264d20728cf8d7e3d3c9458cbd57f77faee1a2e597bd8487bc804bc81d83a23cdec747732a4d28c1b670ebe12de6f9376e2d
|
7
|
+
data.tar.gz: 17ce95bde54d2b35ef1a9f92dd9a6546dd1e6f02a85fd8bdc276c6d49133e95e960c1f14730e44d8c5f82e0e984772a0f12cd7763111a7c5ded6e6dac5d8c388
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
@@ -2,27 +2,31 @@ dist: trusty
|
|
2
2
|
sudo: required
|
3
3
|
language: ruby
|
4
4
|
rvm:
|
5
|
-
- 2.
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
- 2.3.0
|
5
|
+
- 2.1.9
|
6
|
+
- 2.2.5
|
7
|
+
- 2.3.1
|
9
8
|
before_install:
|
9
|
+
- docker-compose up -d
|
10
|
+
- function mysql_ping { mysqladmin -u root -h 127.0.0.1 -ppassword ping > /dev/null 2> /dev/null; }
|
11
|
+
- for i in {1..60}; do mysql_ping && break; sleep 1; done
|
10
12
|
- gem install bundler
|
11
|
-
-
|
12
|
-
-
|
13
|
-
|
14
|
-
before_scriot:
|
15
|
-
- banner `mysqld --version`
|
16
|
-
- banner $ACTIVERECORD_MYSQL_RECONNECT_ENGINE
|
17
|
-
- banner `bundle exec ruby -e 'require "active_record"; puts ActiveRecord::VERSION::STRING'`
|
18
|
-
scriot:
|
13
|
+
- docker-compose stop
|
14
|
+
- sudo rm -rf /tmp/mysql_for_ar_mysql_reconn
|
15
|
+
script:
|
19
16
|
- bundle exec rake
|
17
|
+
gemfile:
|
18
|
+
- gemfiles/activerecord_4.2.gemfile
|
19
|
+
- gemfiles/activerecord_5.0.gemfile
|
20
20
|
env:
|
21
|
-
|
22
|
-
|
21
|
+
matrix:
|
22
|
+
- ACTIVERECORD_MYSQL_RECONNECT_ENGINE=InnoDB
|
23
|
+
- ACTIVERECORD_MYSQL_RECONNECT_ENGINE=MyISAM
|
24
|
+
matrix:
|
25
|
+
exclude:
|
26
|
+
- rvm: 2.1.9
|
27
|
+
gemfile: gemfiles/activerecord_5.0.gemfile
|
23
28
|
addons:
|
24
29
|
apt:
|
25
30
|
packages:
|
26
|
-
|
27
|
-
|
28
|
-
- mysql-client-5.6
|
31
|
+
- mysql-client-core-5.6
|
32
|
+
- mysql-client-5.6
|
data/Appraisals
ADDED
data/ChangeLog
CHANGED
data/README.md
CHANGED
@@ -100,13 +100,16 @@ ene
|
|
100
100
|
* `:rw` Retry in all SQL, but does not retry if `Lost connection` has happened in write SQL
|
101
101
|
* `:force` Retry in all SQL
|
102
102
|
|
103
|
-
## Running tests
|
103
|
+
## Running tests
|
104
|
+
|
105
|
+
It requires the following:
|
106
|
+
|
107
|
+
* Docker
|
108
|
+
* Docker Compose
|
104
109
|
|
105
110
|
```sh
|
106
|
-
mysql.server start
|
107
|
-
export ACTIVERECORD_MYSQL_RECONNECT_MYSQL_START='mysql.server start'
|
108
|
-
export ACTIVERECORD_MYSQL_RECONNECT_MYSQL_STOP='mysql.server stop'
|
109
|
-
export ACTIVERECORD_MYSQL_RECONNECT_MYSQL_RESTART='killall -9 mysqld; sleep 3; mysql.server restart; true'
|
110
111
|
bundle install
|
111
|
-
bundle exec
|
112
|
+
bundle exec appraisal install
|
113
|
+
bundle exec appraisal activerecord-4.2 rake
|
114
|
+
bundle exec appraisal activerecord-5.0 rake
|
112
115
|
```
|
@@ -24,5 +24,5 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'bundler'
|
25
25
|
spec.add_development_dependency 'rake'
|
26
26
|
spec.add_development_dependency 'rspec', '>= 3.0.0'
|
27
|
-
spec.add_development_dependency
|
27
|
+
spec.add_development_dependency "appraisal"
|
28
28
|
end
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
activerecord-mysql-reconnect (0.4.0)
|
5
|
+
activerecord
|
6
|
+
mysql2
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (4.2.6)
|
12
|
+
activesupport (= 4.2.6)
|
13
|
+
builder (~> 3.1)
|
14
|
+
activerecord (4.2.6)
|
15
|
+
activemodel (= 4.2.6)
|
16
|
+
activesupport (= 4.2.6)
|
17
|
+
arel (~> 6.0)
|
18
|
+
activesupport (4.2.6)
|
19
|
+
i18n (~> 0.7)
|
20
|
+
json (~> 1.7, >= 1.7.7)
|
21
|
+
minitest (~> 5.1)
|
22
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
23
|
+
tzinfo (~> 1.1)
|
24
|
+
appraisal (2.1.0)
|
25
|
+
bundler
|
26
|
+
rake
|
27
|
+
thor (>= 0.14.0)
|
28
|
+
arel (6.0.3)
|
29
|
+
builder (3.2.2)
|
30
|
+
diff-lcs (1.2.5)
|
31
|
+
i18n (0.7.0)
|
32
|
+
json (1.8.3)
|
33
|
+
minitest (5.8.4)
|
34
|
+
mysql2 (0.4.4)
|
35
|
+
rake (11.1.2)
|
36
|
+
rspec (3.4.0)
|
37
|
+
rspec-core (~> 3.4.0)
|
38
|
+
rspec-expectations (~> 3.4.0)
|
39
|
+
rspec-mocks (~> 3.4.0)
|
40
|
+
rspec-core (3.4.4)
|
41
|
+
rspec-support (~> 3.4.0)
|
42
|
+
rspec-expectations (3.4.0)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.4.0)
|
45
|
+
rspec-mocks (3.4.1)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.4.0)
|
48
|
+
rspec-support (3.4.1)
|
49
|
+
thor (0.19.1)
|
50
|
+
thread_safe (0.3.5)
|
51
|
+
tzinfo (1.2.2)
|
52
|
+
thread_safe (~> 0.1)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
activerecord (~> 4.2.0)
|
59
|
+
activerecord-mysql-reconnect!
|
60
|
+
appraisal
|
61
|
+
bundler
|
62
|
+
rake
|
63
|
+
rspec (>= 3.0.0)
|
64
|
+
|
65
|
+
BUNDLED WITH
|
66
|
+
1.11.2
|
@@ -0,0 +1,63 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
activerecord-mysql-reconnect (0.4.0)
|
5
|
+
activerecord
|
6
|
+
mysql2
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (5.0.0)
|
12
|
+
activesupport (= 5.0.0)
|
13
|
+
activerecord (5.0.0)
|
14
|
+
activemodel (= 5.0.0)
|
15
|
+
activesupport (= 5.0.0)
|
16
|
+
arel (~> 7.0)
|
17
|
+
activesupport (5.0.0)
|
18
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
19
|
+
i18n (~> 0.7)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
tzinfo (~> 1.1)
|
22
|
+
appraisal (2.1.0)
|
23
|
+
bundler
|
24
|
+
rake
|
25
|
+
thor (>= 0.14.0)
|
26
|
+
arel (7.1.1)
|
27
|
+
concurrent-ruby (1.0.2)
|
28
|
+
diff-lcs (1.2.5)
|
29
|
+
i18n (0.7.0)
|
30
|
+
minitest (5.9.0)
|
31
|
+
mysql2 (0.4.4)
|
32
|
+
rake (11.2.2)
|
33
|
+
rspec (3.5.0)
|
34
|
+
rspec-core (~> 3.5.0)
|
35
|
+
rspec-expectations (~> 3.5.0)
|
36
|
+
rspec-mocks (~> 3.5.0)
|
37
|
+
rspec-core (3.5.2)
|
38
|
+
rspec-support (~> 3.5.0)
|
39
|
+
rspec-expectations (3.5.0)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.5.0)
|
42
|
+
rspec-mocks (3.5.0)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.5.0)
|
45
|
+
rspec-support (3.5.0)
|
46
|
+
thor (0.19.1)
|
47
|
+
thread_safe (0.3.5)
|
48
|
+
tzinfo (1.2.2)
|
49
|
+
thread_safe (~> 0.1)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
activerecord (~> 5.0.0)
|
56
|
+
activerecord-mysql-reconnect!
|
57
|
+
appraisal
|
58
|
+
bundler
|
59
|
+
rake
|
60
|
+
rspec (>= 3.0.0)
|
61
|
+
|
62
|
+
BUNDLED WITH
|
63
|
+
1.11.2
|
@@ -8,6 +8,7 @@ require 'active_record/connection_adapters/abstract_adapter'
|
|
8
8
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
9
9
|
require 'active_record/connection_adapters/mysql2_adapter'
|
10
10
|
require 'active_record/connection_adapters/abstract/connection_pool'
|
11
|
+
require 'active_record/connection_adapters/abstract/transaction'
|
11
12
|
|
12
13
|
require 'activerecord/mysql/reconnect/version'
|
13
14
|
require 'activerecord/mysql/reconnect/base_ext'
|
@@ -15,6 +16,7 @@ require 'activerecord/mysql/reconnect/base_ext'
|
|
15
16
|
require 'activerecord/mysql/reconnect/abstract_mysql_adapter_ext'
|
16
17
|
require 'activerecord/mysql/reconnect/mysql2_adapter_ext'
|
17
18
|
require 'activerecord/mysql/reconnect/connection_pool_ext'
|
19
|
+
require 'activerecord/mysql/reconnect/null_transaction_ext'
|
18
20
|
|
19
21
|
module Activerecord::Mysql::Reconnect
|
20
22
|
DEFAULT_EXECUTION_TRIES = 3
|
@@ -1,23 +1,30 @@
|
|
1
|
-
|
2
|
-
def
|
1
|
+
module Activerecord::Mysql::Reconnect::ExecuteWithReconnect
|
2
|
+
def execute(sql, name = nil)
|
3
3
|
retryable(sql, name) do |sql_names|
|
4
4
|
retval = nil
|
5
5
|
|
6
6
|
sql_names.each do |s, n|
|
7
|
-
retval =
|
7
|
+
retval = super(s, n)
|
8
8
|
end
|
9
9
|
|
10
10
|
retval
|
11
11
|
end
|
12
12
|
end
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
+
class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter
|
16
|
+
prepend Activerecord::Mysql::Reconnect::ExecuteWithReconnect
|
15
17
|
|
16
18
|
private
|
17
19
|
|
18
20
|
def retryable(sql, name, &block)
|
19
21
|
block_with_reconnect = nil
|
20
22
|
sql_names = [[sql, name]]
|
23
|
+
transaction = current_transaction
|
24
|
+
|
25
|
+
if sql =~ /\ABEGIN\z/i and transaction.is_a?(ActiveRecord::ConnectionAdapters::NullTransaction)
|
26
|
+
def transaction.state; nil; end
|
27
|
+
end
|
21
28
|
|
22
29
|
Activerecord::Mysql::Reconnect.retryable(
|
23
30
|
:proc => proc {
|
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
def
|
1
|
+
module Activerecord::Mysql::Reconnect::NewConnectionWithRetry
|
2
|
+
def new_connection
|
3
3
|
Activerecord::Mysql::Reconnect.retryable(
|
4
|
-
:proc => proc {
|
5
|
-
new_connection_without_retry
|
6
|
-
},
|
4
|
+
:proc => proc { super },
|
7
5
|
:connection => spec.config,
|
8
6
|
)
|
9
7
|
end
|
10
|
-
|
11
|
-
|
8
|
+
end
|
9
|
+
class ActiveRecord::ConnectionAdapters::ConnectionPool
|
10
|
+
prepend Activerecord::Mysql::Reconnect::NewConnectionWithRetry
|
12
11
|
end
|
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
def
|
1
|
+
module Activerecord::Mysql::Reconnect::ReconnectWithRetry
|
2
|
+
def reconnect!
|
3
3
|
Activerecord::Mysql::Reconnect.retryable(
|
4
|
-
:proc => proc {
|
5
|
-
reconnect_without_retry!
|
6
|
-
},
|
4
|
+
:proc => proc { super },
|
7
5
|
:connection => @connection
|
8
6
|
)
|
9
7
|
end
|
8
|
+
end
|
10
9
|
|
11
|
-
|
10
|
+
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
11
|
+
alias_method :reconnect_without_retry!, :reconnect!
|
12
|
+
prepend Activerecord::Mysql::Reconnect::ReconnectWithRetry
|
12
13
|
end
|
@@ -1,57 +1,96 @@
|
|
1
1
|
describe 'activerecord-mysql-reconnect' do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
before(:each) do
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => 'mysql2',
|
5
|
+
:host => '127.0.0.1',
|
6
|
+
:username => 'root',
|
7
|
+
:database => 'employees'
|
8
|
+
)
|
9
|
+
|
10
|
+
ActiveRecord::Base.logger = Logger.new($stdout)
|
11
|
+
ActiveRecord::Base.logger.formatter = proc {|_, _, _, message| "#{message}\n" }
|
12
|
+
|
13
|
+
if ENV['DEBUG'] == '1'
|
14
|
+
ActiveRecord::Base.logger.level = Logger::DEBUG
|
15
|
+
else
|
16
|
+
ActiveRecord::Base.logger.level = Logger::ERROR
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Base.enable_retry = true
|
20
|
+
ActiveRecord::Base.execution_tries = 10
|
21
|
+
ActiveRecord::Base.retry_mode = :rw
|
22
|
+
ActiveRecord::Base.retry_databases = []
|
8
23
|
end
|
9
24
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
25
|
+
let(:insert_with_sleep) do
|
26
|
+
<<-SQL
|
27
|
+
INSERT INTO `employees` (
|
28
|
+
`birth_date`,
|
29
|
+
`emp_no`,
|
30
|
+
`first_name`,
|
31
|
+
`hire_date`,
|
32
|
+
`last_name`
|
33
|
+
) VALUES (
|
34
|
+
'2014-01-09 03:22:25',
|
35
|
+
SLEEP(10),
|
36
|
+
'Scott',
|
37
|
+
'2014-01-09 03:22:25',
|
38
|
+
'Tiger'
|
39
|
+
)
|
40
|
+
SQL
|
16
41
|
end
|
17
42
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
43
|
+
context 'when select all on same thread' do
|
44
|
+
specify do
|
45
|
+
expect(Employee.all.length).to eq 1000
|
46
|
+
MysqlServer.restart
|
47
|
+
expect(Employee.all.length).to eq 1000
|
48
|
+
end
|
49
|
+
end
|
23
50
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
51
|
+
context 'when count on same thead' do
|
52
|
+
specify do
|
53
|
+
expect(Employee.count).to eq 1000
|
54
|
+
MysqlServer.restart
|
55
|
+
expect(Employee.count).to eq 1000
|
56
|
+
end
|
28
57
|
end
|
29
58
|
|
30
|
-
|
31
|
-
|
32
|
-
th =
|
33
|
-
|
59
|
+
context 'wehn select on other thread' do
|
60
|
+
specify do
|
61
|
+
th = thread_start {
|
62
|
+
expect(Employee.where(:id => 1).pluck('sleep(10) * 0 + 3')).to eq [3]
|
63
|
+
}
|
34
64
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
:last_name => 'Tiger',
|
41
|
-
:hire_date => Time.now
|
42
|
-
)
|
43
|
-
end
|
65
|
+
MysqlServer.restart
|
66
|
+
expect(Employee.count).to eq 1000
|
67
|
+
th.join
|
68
|
+
end
|
69
|
+
end
|
44
70
|
|
45
|
-
|
71
|
+
context 'when insert on other thread' do
|
72
|
+
before do
|
73
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('MySQL server has gone away')
|
74
|
+
end
|
46
75
|
|
47
|
-
|
48
|
-
|
76
|
+
specify do
|
77
|
+
th = thread_start {
|
78
|
+
emp = Employee.create(
|
79
|
+
:emp_no => 9999,
|
80
|
+
:birth_date => Time.now,
|
81
|
+
# wait 10 sec
|
82
|
+
:first_name => "' + sleep(10) + '",
|
83
|
+
:last_name => 'Tiger',
|
84
|
+
:hire_date => Time.now
|
85
|
+
)
|
86
|
+
|
87
|
+
expect(emp.id).to eq 1001
|
88
|
+
expect(emp.emp_no).to eq 9999
|
49
89
|
}
|
50
90
|
|
51
|
-
|
52
|
-
expect(Employee.count).to be >= 300024
|
91
|
+
MysqlServer.restart
|
53
92
|
th.join
|
54
|
-
|
93
|
+
end
|
55
94
|
end
|
56
95
|
|
57
96
|
[
|
@@ -66,263 +105,299 @@ describe 'activerecord-mysql-reconnect' do
|
|
66
105
|
'Unknown MySQL server host', # For DNS blips
|
67
106
|
"Lost connection to MySQL server at 'reading initial communication packet'",
|
68
107
|
].each do |errmsg|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
expect(emp.
|
87
|
-
expect(emp.emp_no).to eq(1)
|
108
|
+
context "when `#{errmsg}` is happened" do
|
109
|
+
before do
|
110
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return(errmsg)
|
111
|
+
end
|
112
|
+
|
113
|
+
specify do
|
114
|
+
th = thread_start {
|
115
|
+
emp = Employee.create(
|
116
|
+
:emp_no => 9999,
|
117
|
+
:birth_date => Time.now,
|
118
|
+
# wait 10 sec
|
119
|
+
:first_name => "' + sleep(10) + '",
|
120
|
+
:last_name => 'Tiger',
|
121
|
+
:hire_date => Time.now
|
122
|
+
)
|
123
|
+
|
124
|
+
expect(emp.id).to eq 1001
|
125
|
+
expect(emp.emp_no).to eq 9999
|
88
126
|
}
|
89
127
|
|
90
|
-
|
91
|
-
expect(Employee.count).to be >= 300024
|
128
|
+
MysqlServer.restart
|
92
129
|
th.join
|
93
|
-
|
130
|
+
end
|
94
131
|
end
|
95
132
|
end
|
96
133
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
134
|
+
context 'when unexpected error is happened' do
|
135
|
+
before do
|
136
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return("unexpected error")
|
137
|
+
end
|
101
138
|
|
102
|
-
|
139
|
+
specify do
|
140
|
+
th = thread_start {
|
141
|
+
expect {
|
103
142
|
emp = Employee.create(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
do_stop.call
|
113
|
-
|
114
|
-
expect(emp.id).to eq(300025)
|
115
|
-
expect(emp.emp_no).to eq(1)
|
143
|
+
:emp_no => 9999,
|
144
|
+
:birth_date => Time.now,
|
145
|
+
# wait 10 sec
|
146
|
+
:first_name => "' + sleep(10) + '",
|
147
|
+
:last_name => 'Tiger',
|
148
|
+
:hire_date => Time.now
|
149
|
+
)
|
150
|
+
}.to raise_error(/unexpected error/)
|
116
151
|
}
|
117
152
|
|
118
|
-
|
119
|
-
expect(Employee.count).to be >= 300024
|
153
|
+
MysqlServer.restart
|
120
154
|
th.join
|
121
|
-
|
155
|
+
end
|
122
156
|
end
|
123
157
|
|
124
|
-
|
125
|
-
|
126
|
-
|
158
|
+
context 'when update on other thread' do
|
159
|
+
before do
|
160
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('MySQL server has gone away')
|
161
|
+
end
|
162
|
+
|
163
|
+
specify do
|
164
|
+
th = thread_start {
|
127
165
|
emp = Employee.where(:id => 1).first
|
166
|
+
# wait 10 sec
|
128
167
|
emp.first_name = "' + sleep(10) + '"
|
129
168
|
emp.last_name = 'ZapZapZap'
|
130
|
-
|
131
|
-
mysql2_error('MySQL server has gone away') do
|
132
|
-
emp.save!
|
133
|
-
end
|
134
|
-
|
135
|
-
do_stop.call
|
169
|
+
emp.save!
|
136
170
|
|
137
171
|
emp = Employee.where(:id => 1).first
|
138
|
-
expect(emp.last_name).to eq
|
172
|
+
expect(emp.last_name).to eq 'ZapZapZap'
|
139
173
|
}
|
140
174
|
|
141
|
-
|
142
|
-
expect(Employee.count).to eq(300024)
|
175
|
+
MysqlServer.restart
|
143
176
|
th.join
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
it 'without_retry' do
|
148
|
-
expect {
|
149
|
-
ActiveRecord::Base.without_retry do
|
150
|
-
Employee.count
|
151
|
-
mysql_restart
|
152
|
-
Employee.count
|
153
|
-
end
|
154
|
-
}.to raise_error(ActiveRecord::StatementInvalid)
|
177
|
+
end
|
155
178
|
end
|
156
179
|
|
157
|
-
|
158
|
-
|
180
|
+
context 'when use #without_retry' do
|
181
|
+
specify do
|
159
182
|
expect {
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
emp = Employee.create(
|
165
|
-
:emp_no => 1,
|
166
|
-
:birth_date => Time.now,
|
167
|
-
:first_name => 'Scott',
|
168
|
-
:last_name => 'Tiger',
|
169
|
-
:hire_date => Time.now
|
170
|
-
)
|
171
|
-
expect(emp.id).to eq(300025)
|
172
|
-
expect(emp.emp_no).to eq(1)
|
173
|
-
mysql_restart
|
174
|
-
emp = Employee.create(
|
175
|
-
:emp_no => 2,
|
176
|
-
:birth_date => Time.now,
|
177
|
-
:first_name => 'Scott',
|
178
|
-
:last_name => 'Tiger',
|
179
|
-
:hire_date => Time.now
|
180
|
-
)
|
181
|
-
expect(emp.id).to eq(300025)
|
182
|
-
expect(emp.emp_no).to eq(2)
|
183
|
-
end
|
183
|
+
ActiveRecord::Base.without_retry do
|
184
|
+
Employee.count
|
185
|
+
MysqlServer.restart
|
186
|
+
Employee.count
|
184
187
|
end
|
188
|
+
}.to raise_error(ActiveRecord::StatementInvalid)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'with transaction' do
|
193
|
+
before do
|
194
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('MySQL server has gone away')
|
195
|
+
end
|
196
|
+
|
197
|
+
specify do
|
198
|
+
skip if myisam?
|
199
|
+
|
200
|
+
expect(Employee.count).to eq 1000
|
201
|
+
|
202
|
+
ActiveRecord::Base.transaction do
|
203
|
+
emp = Employee.create(
|
204
|
+
:emp_no => 9999,
|
205
|
+
:birth_date => Time.now,
|
206
|
+
:first_name => 'Scott',
|
207
|
+
:last_name => 'Tiger',
|
208
|
+
:hire_date => Time.now
|
209
|
+
)
|
210
|
+
|
211
|
+
expect(emp.id).to eq 1001
|
212
|
+
expect(emp.emp_no).to eq 9999
|
213
|
+
|
214
|
+
MysqlServer.restart
|
215
|
+
|
216
|
+
emp = Employee.create(
|
217
|
+
:emp_no => 9998,
|
218
|
+
:birth_date => Time.now,
|
219
|
+
:first_name => 'Scott',
|
220
|
+
:last_name => 'Tiger',
|
221
|
+
:hire_date => Time.now
|
222
|
+
)
|
223
|
+
|
224
|
+
# NOTE: Ignore the transaction on :rw mode
|
225
|
+
expect(emp.id).to eq 1001
|
226
|
+
expect(emp.emp_no).to eq 9998
|
227
|
+
end
|
185
228
|
|
186
|
-
|
187
|
-
}.to_not raise_error
|
229
|
+
expect(Employee.count).to eq 1001
|
188
230
|
end
|
189
231
|
end
|
190
232
|
|
191
|
-
|
192
|
-
|
233
|
+
context 'when new connection' do
|
234
|
+
specify do
|
193
235
|
ActiveRecord::Base.clear_all_connections!
|
194
|
-
|
195
|
-
expect(Employee.count).to eq
|
196
|
-
|
236
|
+
MysqlServer.restart
|
237
|
+
expect(Employee.count).to eq 1000
|
238
|
+
end
|
197
239
|
end
|
198
240
|
|
199
|
-
|
200
|
-
|
201
|
-
th =
|
202
|
-
|
241
|
+
context 'when connection verify' do
|
242
|
+
specify do
|
243
|
+
th = thread_start {
|
244
|
+
MysqlServer.stop
|
203
245
|
sleep 10
|
204
|
-
|
246
|
+
MysqlServer.start
|
205
247
|
}
|
206
248
|
|
249
|
+
sleep 5
|
207
250
|
ActiveRecord::Base.connection.verify!
|
208
251
|
th.join
|
209
|
-
|
252
|
+
end
|
210
253
|
end
|
211
254
|
|
212
|
-
|
213
|
-
|
214
|
-
th =
|
215
|
-
|
255
|
+
context 'when connection reconnect' do
|
256
|
+
specify do
|
257
|
+
th = thread_start {
|
258
|
+
MysqlServer.stop
|
216
259
|
sleep 10
|
217
|
-
|
260
|
+
MysqlServer.start
|
218
261
|
}
|
219
262
|
|
263
|
+
sleep 5
|
220
264
|
ActiveRecord::Base.connection.reconnect!
|
221
265
|
th.join
|
222
|
-
|
266
|
+
end
|
223
267
|
end
|
224
268
|
|
225
|
-
|
226
|
-
|
269
|
+
context 'when disable reconnect' do
|
270
|
+
specify do
|
271
|
+
ActiveRecord::Base.enable_retry = false
|
272
|
+
|
227
273
|
expect {
|
228
|
-
expect(Employee.all.length).to eq
|
229
|
-
|
230
|
-
expect(Employee.all.length).to eq
|
274
|
+
expect(Employee.all.length).to eq 1000
|
275
|
+
MysqlServer.restart
|
276
|
+
expect(Employee.all.length).to eq 1000
|
231
277
|
}.to raise_error(ActiveRecord::StatementInvalid)
|
232
|
-
end
|
233
278
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
279
|
+
ActiveRecord::Base.enable_retry = true
|
280
|
+
|
281
|
+
expect(Employee.all.length).to eq 1000
|
282
|
+
MysqlServer.restart
|
283
|
+
expect(Employee.all.length).to eq 1000
|
284
|
+
end
|
239
285
|
end
|
240
286
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
287
|
+
context 'when select on :r mode' do
|
288
|
+
before do
|
289
|
+
ActiveRecord::Base.retry_mode = :r
|
290
|
+
end
|
291
|
+
|
292
|
+
specify do
|
293
|
+
expect(Employee.all.length).to eq 1000
|
294
|
+
MysqlServer.restart
|
295
|
+
expect(Employee.all.length).to eq 1000
|
248
296
|
end
|
249
297
|
end
|
250
298
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
th = thread_run {|do_stop|
|
257
|
-
mysql2_error('MySQL server has gone away') do
|
258
|
-
emp = Employee.create(
|
259
|
-
:emp_no => 1,
|
260
|
-
:birth_date => Time.now,
|
261
|
-
:first_name => 'Scott',
|
262
|
-
:last_name => 'Tiger',
|
263
|
-
:hire_date => Time.now
|
264
|
-
)
|
265
|
-
end
|
266
|
-
}
|
299
|
+
context 'when insert on :r mode' do
|
300
|
+
before do
|
301
|
+
ActiveRecord::Base.retry_mode = :r
|
302
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('MySQL server has gone away')
|
303
|
+
end
|
267
304
|
|
268
|
-
|
269
|
-
|
305
|
+
specify do
|
306
|
+
expect(Employee.all.length).to eq 1000
|
307
|
+
|
308
|
+
MysqlServer.restart
|
309
|
+
|
310
|
+
expect {
|
311
|
+
Employee.create(
|
312
|
+
:emp_no => 9999,
|
313
|
+
:birth_date => Time.now,
|
314
|
+
# wait 10 sec
|
315
|
+
:first_name => "' + sleep(10) + '",
|
316
|
+
:last_name => 'Tiger',
|
317
|
+
:hire_date => Time.now
|
318
|
+
)
|
270
319
|
}.to raise_error(ActiveRecord::StatementInvalid)
|
271
320
|
end
|
272
321
|
end
|
273
322
|
|
274
|
-
|
275
|
-
|
323
|
+
context 'when `lost connection` is happened' do
|
324
|
+
before do
|
325
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('Lost connection to MySQL server during query')
|
326
|
+
end
|
276
327
|
|
277
|
-
|
278
|
-
|
279
|
-
}.to_not raise_error
|
328
|
+
specify do
|
329
|
+
expect(Employee.all.length).to eq 1000
|
280
330
|
|
281
|
-
|
331
|
+
MysqlServer.restart
|
282
332
|
|
283
|
-
mysql2_error('Lost connection to MySQL server during query') do
|
284
333
|
expect {
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
334
|
+
Employee.create(
|
335
|
+
:emp_no => 9999,
|
336
|
+
:birth_date => Time.now,
|
337
|
+
# wait 10 sec
|
338
|
+
:first_name => "' + sleep(10) + '",
|
339
|
+
:last_name => 'Tiger',
|
340
|
+
:hire_date => Time.now
|
341
|
+
)
|
292
342
|
}.to raise_error(ActiveRecord::StatementInvalid)
|
293
343
|
end
|
294
344
|
end
|
295
345
|
|
296
|
-
|
297
|
-
|
346
|
+
context 'when `lost connection` is happened on :force mode' do
|
347
|
+
before do
|
348
|
+
ActiveRecord::Base.retry_mode = :force
|
349
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('Lost connection to MySQL server during query')
|
350
|
+
end
|
298
351
|
|
299
|
-
|
300
|
-
|
301
|
-
}.to_not raise_error
|
352
|
+
specify do
|
353
|
+
expect(Employee.all.length).to eq 1000
|
302
354
|
|
303
|
-
|
355
|
+
MysqlServer.restart
|
304
356
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
357
|
+
emp = Employee.create(
|
358
|
+
:emp_no => 9999,
|
359
|
+
:birth_date => Time.now,
|
360
|
+
# wait 10 sec
|
361
|
+
:first_name => "' + sleep(10) + '",
|
362
|
+
:last_name => 'Tiger',
|
363
|
+
:hire_date => Time.now
|
364
|
+
)
|
312
365
|
|
313
|
-
|
314
|
-
|
315
|
-
}.to_not raise_error
|
366
|
+
expect(emp.id).to eq 1001
|
367
|
+
expect(emp.emp_no).to eq 9999
|
316
368
|
end
|
317
369
|
end
|
318
370
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
371
|
+
context 'when `lost connection` is happened on :force mode (2)' do
|
372
|
+
before do
|
373
|
+
ActiveRecord::Base.retry_mode = :force
|
374
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('Lost connection to MySQL server during query')
|
375
|
+
|
376
|
+
Thread.start do
|
377
|
+
MysqlServer.lock_tables
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
specify do
|
382
|
+
th = thread_start {
|
383
|
+
ActiveRecord::Base.connection.execute(insert_with_sleep)
|
384
|
+
}
|
385
|
+
|
386
|
+
sleep 3
|
387
|
+
MysqlServer.restart
|
388
|
+
th.join
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
context 'when read-only=1' do
|
393
|
+
before do
|
394
|
+
allow_any_instance_of(Mysql2::Error).to receive(:message).and_return('The MySQL server is running with the --read-only option so it cannot execute this statement:')
|
395
|
+
end
|
396
|
+
|
397
|
+
specify do
|
398
|
+
expect(Employee.all.length).to eq 1000
|
399
|
+
MysqlServer.restart
|
400
|
+
expect(Employee.all.length).to eq 1000
|
326
401
|
end
|
327
402
|
end
|
328
403
|
|
@@ -331,20 +406,24 @@ describe 'activerecord-mysql-reconnect' do
|
|
331
406
|
'127.0.0.2:employees',
|
332
407
|
'127.0.0.\_:employees',
|
333
408
|
].each do |db|
|
334
|
-
|
335
|
-
|
409
|
+
context "when retry specific database: #{db}" do
|
410
|
+
before do
|
411
|
+
ActiveRecord::Base.retry_databases = db
|
412
|
+
end
|
413
|
+
|
414
|
+
specify do
|
336
415
|
expect {
|
337
|
-
expect(Employee.all.length).to eq
|
338
|
-
|
339
|
-
expect(Employee.all.length).to eq
|
416
|
+
expect(Employee.all.length).to eq 1000
|
417
|
+
MysqlServer.restart
|
418
|
+
expect(Employee.all.length).to eq 1000
|
340
419
|
}.to raise_error(ActiveRecord::StatementInvalid)
|
341
|
-
end
|
342
420
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
421
|
+
ActiveRecord::Base.retry_databases = []
|
422
|
+
|
423
|
+
expect(Employee.all.length).to eq 1000
|
424
|
+
MysqlServer.restart
|
425
|
+
expect(Employee.all.length).to eq 1000
|
426
|
+
end
|
348
427
|
end
|
349
428
|
end
|
350
429
|
|
@@ -353,13 +432,15 @@ describe 'activerecord-mysql-reconnect' do
|
|
353
432
|
'127.0.0.1:employees',
|
354
433
|
'127.0.0._:e%',
|
355
434
|
].each do |db|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
435
|
+
context "when retry specific database: #{db}" do
|
436
|
+
before do
|
437
|
+
ActiveRecord::Base.retry_databases = db
|
438
|
+
end
|
439
|
+
|
440
|
+
specify do
|
441
|
+
expect(Employee.all.length).to eq 1000
|
442
|
+
MysqlServer.restart
|
443
|
+
expect(Employee.all.length).to eq 1000
|
363
444
|
end
|
364
445
|
end
|
365
446
|
end
|