activerecord-mysql-reconnect 0.4.0 → 0.4.1.beta
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.
- 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
|