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 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