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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4739211c7d68c950f02a389a199ac953edb9346c
4
- data.tar.gz: aa3824a24006c4f756e8888776b1be254a6b9d92
3
+ metadata.gz: c37f85305063d95208c4a30ace243a62709d0d25
4
+ data.tar.gz: 55d1bf38531ede94d81d977e33f3def41719f09d
5
5
  SHA512:
6
- metadata.gz: 088a4a7e0b756eff50878720a35c27ad96c1e93a002dc3c7cabdff0c9220f4b057b636b3693bc305ec95167c6234cdb51929972d61acef5df7954acb14b56558
7
- data.tar.gz: 86a1698ce6854e3b10bae489f8e6178a6472c50fd94fa5fd16ef73894c350ef0a2dd6496abe35497d0be5316d1d6cb5682b942bbda3264bdf1047fb38b43ce58
6
+ metadata.gz: a83fe523cc08085211d963711f06264d20728cf8d7e3d3c9458cbd57f77faee1a2e597bd8487bc804bc81d83a23cdec747732a4d28c1b670ebe12de6f9376e2d
7
+ data.tar.gz: 17ce95bde54d2b35ef1a9f92dd9a6546dd1e6f02a85fd8bdc276c6d49133e95e960c1f14730e44d8c5f82e0e984772a0f12cd7763111a7c5ded6e6dac5d8c388
data/.rspec CHANGED
@@ -1,4 +1,3 @@
1
1
  --require spec_helper
2
2
  --colour
3
- --require rspec/instafail
4
- --format RSpec::Instafail
3
+ --format documentation
@@ -2,27 +2,31 @@ dist: trusty
2
2
  sudo: required
3
3
  language: ruby
4
4
  rvm:
5
- - 2.0.0
6
- - 2.1.8
7
- - 2.2.4
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
- - sudo apt-get -y install sysvbanner
12
- - if [ -n "$ACTIVERECORD_MYSQL_RECONNECT_AR_VERSION" ]; then sed -i.bak "s/spec.add_dependency 'activerecord'.*/spec.add_dependency 'activerecord', '~> $ACTIVERECORD_MYSQL_RECONNECT_AR_VERSION'/" activerecord-mysql-reconnect.gemspec; fi
13
- - bundle install
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
- - ACTIVERECORD_MYSQL_RECONNECT_ENGINE=InnoDB ACTIVERECORD_MYSQL_RECONNECT_AR_VERSION=4.2.6
22
- - ACTIVERECORD_MYSQL_RECONNECT_ENGINE=MyISAM ACTIVERECORD_MYSQL_RECONNECT_AR_VERSION=4.2.6
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
- - mysql-server-5.6
27
- - mysql-client-core-5.6
28
- - mysql-client-5.6
31
+ - mysql-client-core-5.6
32
+ - mysql-client-5.6
@@ -0,0 +1,7 @@
1
+ appraise "activerecord-4.2" do
2
+ gem "activerecord", "~> 4.2.0"
3
+ end
4
+
5
+ appraise "activerecord-5.0" do
6
+ gem "activerecord", "~> 5.0.0"
7
+ end
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ activerecord-mysql-reconnect 0.4.1 (Aug 8, 2016)
2
+
3
+ * Support AR 5.0 (RP#5 @ssig33)
4
+ * Fix test (use docker-compose)
5
+
1
6
  activerecord-mysql-reconnect 0.4.0 (Mar 22, 2016)
2
7
 
3
8
  * Remove `retryable_transaction`
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 on local
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 rake
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 'rspec-instafail'
27
+ spec.add_development_dependency "appraisal"
28
28
  end
@@ -0,0 +1,8 @@
1
+ mysql_for_ar_mysql_reconn:
2
+ image: "mysql:5.6"
3
+ ports:
4
+ - "3306:3306"
5
+ environment:
6
+ MYSQL_ROOT_PASSWORD: password
7
+ volumes:
8
+ - /tmp/mysql_for_ar_mysql_reconn:/var/lib/mysql
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0"
6
+
7
+ gemspec :path => "../"
@@ -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,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.0.0"
6
+
7
+ gemspec :path => "../"
@@ -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
- class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter
2
- def execute_with_reconnect(sql, name = nil)
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 = execute_without_reconnect(s, n)
7
+ retval = super(s, n)
8
8
  end
9
9
 
10
10
  retval
11
11
  end
12
12
  end
13
+ end
13
14
 
14
- alias_method_chain :execute, :reconnect
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 {
@@ -28,11 +28,5 @@ class ActiveRecord::Base
28
28
  yield
29
29
  end
30
30
  end
31
-
32
- def retryable_transaction
33
- Activerecord::Mysql::Reconnect.retryable_transaction do
34
- yield
35
- end
36
- end
37
31
  end
38
32
  end
@@ -1,12 +1,11 @@
1
- class ActiveRecord::ConnectionAdapters::ConnectionPool
2
- def new_connection_with_retry
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
- alias_method_chain :new_connection, :retry
8
+ end
9
+ class ActiveRecord::ConnectionAdapters::ConnectionPool
10
+ prepend Activerecord::Mysql::Reconnect::NewConnectionWithRetry
12
11
  end
@@ -1,12 +1,13 @@
1
- class ActiveRecord::ConnectionAdapters::Mysql2Adapter
2
- def reconnect_with_retry!
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
- alias_method_chain :reconnect!, :retry
10
+ class ActiveRecord::ConnectionAdapters::Mysql2Adapter
11
+ alias_method :reconnect_without_retry!, :reconnect!
12
+ prepend Activerecord::Mysql::Reconnect::ReconnectWithRetry
12
13
  end
@@ -0,0 +1,9 @@
1
+ module Activerecord::Mysql::Reconnect::NullTransactionExt
2
+ def state
3
+ nil
4
+ end
5
+ end
6
+
7
+ class ActiveRecord::ConnectionAdapters::NullTransaction
8
+ prepend Activerecord::Mysql::Reconnect::NullTransactionExt
9
+ end
@@ -1,7 +1,7 @@
1
1
  module Activerecord
2
2
  module Mysql
3
3
  module Reconnect
4
- VERSION = '0.4.0'
4
+ VERSION = '0.4.1.beta'
5
5
  end
6
6
  end
7
7
  end
@@ -1,57 +1,96 @@
1
1
  describe 'activerecord-mysql-reconnect' do
2
- it 'select all' do
3
- expect {
4
- expect(Employee.all.length).to eq(300024)
5
- mysql_restart
6
- expect(Employee.all.length).to eq(300024)
7
- }.to_not raise_error
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
- it 'count' do
11
- expect {
12
- expect(Employee.count).to eq(300024)
13
- mysql_restart
14
- expect(Employee.count).to eq(300024)
15
- }.to_not raise_error
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
- it 'on select' do
19
- expect {
20
- th = thread_run {|do_stop|
21
- expect(Employee.where(:id => 1).pluck('sleep(10) * 0')).to eq([0])
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
- mysql_restart
25
- expect(Employee.count).to be >= 300024
26
- th.join
27
- }.to_not raise_error
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
- it 'on insert' do
31
- expect {
32
- th = thread_run {|do_stop|
33
- emp = nil
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
- mysql2_error('MySQL server has gone away') do
36
- emp = Employee.create(
37
- :emp_no => 1,
38
- :birth_date => Time.now,
39
- :first_name => "' + sleep(10) + '",
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
- do_stop.call
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
- expect(emp.id).to eq(300025)
48
- expect(emp.emp_no).to eq(1)
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
- mysql_restart
52
- expect(Employee.count).to be >= 300024
91
+ MysqlServer.restart
53
92
  th.join
54
- }.to_not raise_error
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
- it "on error: #{errmsg}" do
70
- expect {
71
- th = thread_run {|do_stop|
72
- emp = nil
73
-
74
- mysql2_error("x#{errmsg}x") do
75
- emp = Employee.create(
76
- :emp_no => 1,
77
- :birth_date => Time.now,
78
- :first_name => "' + sleep(10) + '",
79
- :last_name => 'Tiger',
80
- :hire_date => Time.now
81
- )
82
- end
83
-
84
- do_stop.call
85
-
86
- expect(emp.id).to eq(300025)
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
- mysql_restart
91
- expect(Employee.count).to be >= 300024
128
+ MysqlServer.restart
92
129
  th.join
93
- }.to_not raise_error
130
+ end
94
131
  end
95
132
  end
96
133
 
97
- it "on unhandled error" do
98
- expect {
99
- th = thread_run {|do_stop|
100
- emp = nil
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
- mysql2_error("unhandled error") do
139
+ specify do
140
+ th = thread_start {
141
+ expect {
103
142
  emp = Employee.create(
104
- :emp_no => 1,
105
- :birth_date => Time.now,
106
- :first_name => "' + sleep(10) + '",
107
- :last_name => 'Tiger',
108
- :hire_date => Time.now
109
- )
110
- end
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
- mysql_restart
119
- expect(Employee.count).to be >= 300024
153
+ MysqlServer.restart
120
154
  th.join
121
- }.to raise_error(/unhandled error/)
155
+ end
122
156
  end
123
157
 
124
- it 'op update' do
125
- expect {
126
- th = thread_run {|do_stop|
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('ZapZapZap')
172
+ expect(emp.last_name).to eq 'ZapZapZap'
139
173
  }
140
174
 
141
- mysql_restart
142
- expect(Employee.count).to eq(300024)
175
+ MysqlServer.restart
143
176
  th.join
144
- }.to_not raise_error
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
- it 'transaction' do
158
- unless /MyISAM/i =~ ENV['ACTIVERECORD_MYSQL_RECONNECT_ENGINE']
180
+ context 'when use #without_retry' do
181
+ specify do
159
182
  expect {
160
- expect(Employee.count).to eq(300024)
161
-
162
- mysql2_error('MySQL server has gone away') do
163
- ActiveRecord::Base.transaction do
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
- expect(Employee.count).to eq(300025)
187
- }.to_not raise_error
229
+ expect(Employee.count).to eq 1001
188
230
  end
189
231
  end
190
232
 
191
- it 'retry new connection' do
192
- expect {
233
+ context 'when new connection' do
234
+ specify do
193
235
  ActiveRecord::Base.clear_all_connections!
194
- mysql_restart
195
- expect(Employee.count).to eq(300024)
196
- }.to_not raise_error
236
+ MysqlServer.restart
237
+ expect(Employee.count).to eq 1000
238
+ end
197
239
  end
198
240
 
199
- it 'retry verify' do
200
- expect {
201
- th = thread_run {|do_stop|
202
- mysql_stop
241
+ context 'when connection verify' do
242
+ specify do
243
+ th = thread_start {
244
+ MysqlServer.stop
203
245
  sleep 10
204
- mysql_start
246
+ MysqlServer.start
205
247
  }
206
248
 
249
+ sleep 5
207
250
  ActiveRecord::Base.connection.verify!
208
251
  th.join
209
- }.to_not raise_error
252
+ end
210
253
  end
211
254
 
212
- it 'retry reconnect' do
213
- expect {
214
- th = thread_run {|do_stop|
215
- mysql_stop
255
+ context 'when connection reconnect' do
256
+ specify do
257
+ th = thread_start {
258
+ MysqlServer.stop
216
259
  sleep 10
217
- mysql_start
260
+ MysqlServer.start
218
261
  }
219
262
 
263
+ sleep 5
220
264
  ActiveRecord::Base.connection.reconnect!
221
265
  th.join
222
- }.to_not raise_error
266
+ end
223
267
  end
224
268
 
225
- it 'disable reconnect' do
226
- disable_retry do
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(300024)
229
- mysql_restart
230
- expect(Employee.all.length).to eq(300024)
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
- expect {
235
- expect(Employee.all.length).to eq(300024)
236
- mysql_restart
237
- expect(Employee.all.length).to eq(300024)
238
- }.to_not raise_error
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
- it 'read only (read)' do
242
- enable_read_only do
243
- expect {
244
- expect(Employee.all.length).to eq(300024)
245
- mysql_restart
246
- expect(Employee.all.length).to eq(300024)
247
- }.to_not raise_error
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
- it 'read only (write)' do
252
- enable_read_only do
253
- expect {
254
- lock_table
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
- mysql_restart
269
- th.join
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
- it 'lost connection' do
275
- sql = "INSERT INTO `employees` (`birth_date`, `emp_no`, `first_name`, `hire_date`, `last_name`) VALUES ('2014-01-09 03:22:25', SLEEP(10), 'Scott', '2014-01-09 03:22:25', 'Tiger')"
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
- expect {
278
- ActiveRecord::Base.connection.execute(sql)
279
- }.to_not raise_error
328
+ specify do
329
+ expect(Employee.all.length).to eq 1000
280
330
 
281
- lock_table
331
+ MysqlServer.restart
282
332
 
283
- mysql2_error('Lost connection to MySQL server during query') do
284
333
  expect {
285
- th = thread_run {|do_stop|
286
- ActiveRecord::Base.connection.execute(sql)
287
- }
288
-
289
- sleep 3
290
- mysql_restart
291
- th.join
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
- it 'force retry' do
297
- sql = "INSERT INTO `employees` (`birth_date`, `emp_no`, `first_name`, `hire_date`, `last_name`) VALUES ('2014-01-09 03:22:25', 1, 'Scott', '2014-01-09 03:22:25', 'Tiger')"
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
- expect {
300
- ActiveRecord::Base.connection.execute(sql)
301
- }.to_not raise_error
352
+ specify do
353
+ expect(Employee.all.length).to eq 1000
302
354
 
303
- lock_table
355
+ MysqlServer.restart
304
356
 
305
- mysql2_error('Lost connection to MySQL server during query') do
306
- expect {
307
- th = thread_run {|do_stop|
308
- force_retry do
309
- ActiveRecord::Base.connection.execute(sql)
310
- end
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
- mysql_restart
314
- th.join
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
- it 'read-only=1' do
320
- mysql2_error('The MySQL server is running with the --read-only option so it cannot execute this statement:') do
321
- expect {
322
- expect(Employee.all.length).to eq(300024)
323
- mysql_restart
324
- expect(Employee.all.length).to eq(300024)
325
- }.to_not raise_error
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
- it "retry specific database: #{db}" do
335
- retry_databases(db) do
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(300024)
338
- mysql_restart
339
- expect(Employee.all.length).to eq(300024)
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
- expect {
344
- expect(Employee.all.length).to eq(300024)
345
- mysql_restart
346
- expect(Employee.all.length).to eq(300024)
347
- }.to_not raise_error
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
- it "retry specific database: #{db}" do
357
- retry_databases(db) do
358
- expect {
359
- expect(Employee.all.length).to eq(300024)
360
- mysql_restart
361
- expect(Employee.all.length).to eq(300024)
362
- }.to_not raise_error
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