ghazel-deadlock_retry 1.1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.swp
data/CHANGELOG ADDED
@@ -0,0 +1,16 @@
1
+ deadlock_retry changes
2
+
3
+ == v1.1.1 (2011-05-13)
4
+
5
+ * Conditionally log INNODB STATUS only if user has permission. (osheroff)
6
+
7
+ == v1.1.0 (2011-04-20)
8
+
9
+ * Modernize.
10
+ * Drop support for Rails 2.1 and earlier.
11
+
12
+ == v1.0 - (2009-02-07)
13
+
14
+ * Add INNODB status logging for debugging deadlock issues.
15
+ * Clean up so the code will run as a gem plugin.
16
+ * Small fix for ActiveRecord 2.1.x compatibility.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ # Copyright (c) 2005 Jamis Buck
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,17 @@
1
+ = Deadlock Retry
2
+
3
+ Deadlock retry allows the database adapter (currently only tested with the
4
+ MySQLAdapter) to retry transactions that fall into deadlock. It will retry
5
+ such transactions three times before finally failing.
6
+
7
+ This capability is automatically added to ActiveRecord. No code changes or otherwise are required.
8
+
9
+ == Installation
10
+
11
+ Add it to your Rails application by installing the gem:
12
+
13
+ gem install deadlock_retry
14
+
15
+ and including a reference to it in your application's Gemfile:
16
+
17
+ gem 'deadlock_retry'
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ desc "Default task"
5
+ task :default => [ :test ]
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs = ['lib', 'test']
9
+ t.test_files = Dir["test/**/*_test.rb"]
10
+ t.verbose = true
11
+ t.warning = true
12
+ end
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ghazel-deadlock_retry}
5
+ s.version = "1.1.1.1"
6
+ s.authors = ["Jamis Buck", "Mike Perham"]
7
+ s.description = s.summary = %q{Provides automatic deadlock retry and logging functionality for ActiveRecord and MySQL}
8
+ s.email = %q{mperham@gmail.com}
9
+ s.files = `git ls-files`.split("\n")
10
+ s.homepage = %q{http://github.com/mperham/deadlock_retry}
11
+ s.require_paths = ["lib"]
12
+ end
@@ -0,0 +1,91 @@
1
+ module DeadlockRetry
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ base.class_eval do
5
+ class << self
6
+ alias_method_chain :transaction, :deadlock_handling
7
+ end
8
+ end
9
+ end
10
+
11
+ @@innodb_status_available = nil
12
+
13
+ def self.innodb_status_available?
14
+ @@innodb_status_available
15
+ end
16
+
17
+ def self.innodb_status_available=(bool)
18
+ @@innodb_status_available = bool
19
+ end
20
+
21
+ module ClassMethods
22
+ DEADLOCK_ERROR_MESSAGES = [
23
+ "Deadlock found when trying to get lock",
24
+ "Lock wait timeout exceeded"
25
+ ]
26
+
27
+ MAXIMUM_RETRIES_ON_DEADLOCK = 3
28
+
29
+
30
+ def transaction_with_deadlock_handling(*objects, &block)
31
+ retry_count = 0
32
+
33
+ check_innodb_status_available
34
+
35
+ begin
36
+ transaction_without_deadlock_handling(*objects, &block)
37
+ rescue ActiveRecord::StatementInvalid => error
38
+ raise if in_nested_transaction?
39
+ if DEADLOCK_ERROR_MESSAGES.any? { |msg| error.message =~ /#{Regexp.escape(msg)}/ }
40
+ raise if retry_count >= MAXIMUM_RETRIES_ON_DEADLOCK
41
+ retry_count += 1
42
+ logger.info "Deadlock detected on retry #{retry_count}, restarting transaction"
43
+ log_innodb_status if DeadlockRetry.innodb_status_available?
44
+ retry
45
+ else
46
+ raise
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def in_nested_transaction?
54
+ # open_transactions was added in 2.2's connection pooling changes.
55
+ connection.open_transactions != 0
56
+ end
57
+
58
+ def show_innodb_status
59
+ self.connection.select_value("show engine innodb status")
60
+ end
61
+
62
+ # Should we try to log innodb status -- if we don't have permission to,
63
+ # we actually break in-flight transactions, silently (!)
64
+ def check_innodb_status_available
65
+ return unless DeadlockRetry.innodb_status_available? == nil
66
+
67
+ begin
68
+ show_innodb_status
69
+ DeadlockRetry.innodb_status_available = true
70
+ rescue
71
+ DeadlockRetry.innodb_status_available = false
72
+ end
73
+ end
74
+
75
+ def log_innodb_status
76
+ # show innodb status is the only way to get visiblity into why
77
+ # the transaction deadlocked. log it.
78
+ lines = show_innodb_status
79
+ logger.warn "INNODB Status follows:"
80
+ lines.each_line do |line|
81
+ logger.warn line
82
+ end
83
+ rescue Exception => e
84
+ # Access denied, ignore
85
+ logger.warn "Cannot log innodb status: #{e.message}"
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ ActiveRecord::Base.send(:include, DeadlockRetry)
@@ -0,0 +1,108 @@
1
+ require 'rubygems'
2
+
3
+ # Change the version if you want to test a different version of ActiveRecord
4
+ gem 'activerecord', ' ~>3.0'
5
+ require 'active_record'
6
+ require 'active_record/version'
7
+ puts "Testing ActiveRecord #{ActiveRecord::VERSION::STRING}"
8
+
9
+ require 'test/unit'
10
+ require 'logger'
11
+ require "deadlock_retry"
12
+
13
+ class MockModel
14
+ @@open_transactions = 0
15
+
16
+ def self.transaction(*objects)
17
+ @@open_transactions += 1
18
+ yield
19
+ ensure
20
+ @@open_transactions -= 1
21
+ end
22
+
23
+ def self.open_transactions
24
+ @@open_transactions
25
+ end
26
+
27
+ def self.connection
28
+ self
29
+ end
30
+
31
+ def self.logger
32
+ @logger ||= Logger.new(nil)
33
+ end
34
+
35
+ def self.show_innodb_status
36
+ []
37
+ end
38
+
39
+ def self.reset_innodb_status_availability
40
+ DeadlockRetry.innodb_status_availability = nil
41
+ end
42
+
43
+ include DeadlockRetry
44
+ end
45
+
46
+ class DeadlockRetryTest < Test::Unit::TestCase
47
+ DEADLOCK_ERROR = "MySQL::Error: Deadlock found when trying to get lock"
48
+ TIMEOUT_ERROR = "MySQL::Error: Lock wait timeout exceeded"
49
+
50
+ def setup
51
+ end
52
+
53
+ def test_no_errors
54
+ assert_equal :success, MockModel.transaction { :success }
55
+ end
56
+
57
+ def test_no_errors_with_deadlock
58
+ errors = [ DEADLOCK_ERROR ] * 3
59
+ assert_equal :success, MockModel.transaction { raise ActiveRecord::StatementInvalid, errors.shift unless errors.empty?; :success }
60
+ assert errors.empty?
61
+ end
62
+
63
+ def test_no_errors_with_lock_timeout
64
+ errors = [ TIMEOUT_ERROR ] * 3
65
+ assert_equal :success, MockModel.transaction { raise ActiveRecord::StatementInvalid, errors.shift unless errors.empty?; :success }
66
+ assert errors.empty?
67
+ end
68
+
69
+ def test_error_if_limit_exceeded
70
+ assert_raise(ActiveRecord::StatementInvalid) do
71
+ MockModel.transaction { raise ActiveRecord::StatementInvalid, DEADLOCK_ERROR }
72
+ end
73
+ end
74
+
75
+ def test_error_if_unrecognized_error
76
+ assert_raise(ActiveRecord::StatementInvalid) do
77
+ MockModel.transaction { raise ActiveRecord::StatementInvalid, "Something else" }
78
+ end
79
+ end
80
+
81
+ def test_included_by_default
82
+ assert ActiveRecord::Base.respond_to?(:transaction_with_deadlock_handling)
83
+ end
84
+
85
+ def test_innodb_status_availability
86
+ DeadlockRetry.innodb_status_available = nil
87
+ MockModel.transaction {}
88
+ assert_equal true, DeadlockRetry.innodb_status_available?
89
+ end
90
+
91
+
92
+ def test_error_in_nested_transaction_should_retry_outermost_transaction
93
+ tries = 0
94
+ errors = 0
95
+
96
+ MockModel.transaction do
97
+ tries += 1
98
+ MockModel.transaction do
99
+ MockModel.transaction do
100
+ errors += 1
101
+ raise ActiveRecord::StatementInvalid, "MySQL::Error: Lock wait timeout exceeded" unless errors > 3
102
+ end
103
+ end
104
+ end
105
+
106
+ assert_equal 4, tries
107
+ end
108
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ghazel-deadlock_retry
3
+ version: !ruby/object:Gem::Version
4
+ hash: 81
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 1
10
+ - 1
11
+ version: 1.1.1.1
12
+ platform: ruby
13
+ authors:
14
+ - Jamis Buck
15
+ - Mike Perham
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-05-14 00:00:00 Z
21
+ dependencies: []
22
+
23
+ description: Provides automatic deadlock retry and logging functionality for ActiveRecord and MySQL
24
+ email: mperham@gmail.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - CHANGELOG
34
+ - LICENSE
35
+ - README
36
+ - Rakefile
37
+ - deadlock_retry.gemspec
38
+ - lib/deadlock_retry.rb
39
+ - test/deadlock_retry_test.rb
40
+ homepage: http://github.com/mperham/deadlock_retry
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ hash: 3
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.7.2
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Provides automatic deadlock retry and logging functionality for ActiveRecord and MySQL
73
+ test_files: []
74
+