transaction_reliability 0.1.0
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 +15 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +9 -0
- data/config/database.yml +37 -0
- data/config/database.yml.example +17 -0
- data/gemfiles/rails40.gemfile +8 -0
- data/gemfiles/rails40.gemfile.lock +70 -0
- data/gemfiles/rails41.gemfile +7 -0
- data/gemfiles/rails41.gemfile.lock +70 -0
- data/gemfiles/rails42.gemfile +7 -0
- data/gemfiles/rails42.gemfile.lock +70 -0
- data/lib/transaction_reliability.rb +237 -0
- data/lib/transaction_reliability/version.rb +3 -0
- data/script/rebundle.sh +4 -0
- data/script/shell +23 -0
- data/spec/lib/transaction_reliability_spec.rb +195 -0
- data/spec/spec_helper.rb +20 -0
- data/transaction_reliability.gemspec +29 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MjBmMWUyN2QyNzRhN2U0OWE3NzA5MjhmZDAyYmZjZTRjMjgyM2MwMg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTNkMjRlMWIwZjI5ODI2ZjU5YWU0ZWE0NDIzNDQ0NTdkY2ViZDQ3ZQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTYyODkxNWU5NzNjNGM3Y2Y3ODQ3NWRjOTIwZDI3YzlhNzU2OGEwNzg5NjM1
|
10
|
+
MTZmMjdkNmNlMjZjNzJiNmM5MmM2Njg4YWU0ZTU0MjdjNmNlMDUzMWE2NTYw
|
11
|
+
MGI5MjA0YTgzY2VkNzM0ZjVjMTAzYjdhODgzOWViMDQ2OWJiMTQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Yjk2NGQ3ZWVjNDdiZTlmOWY0NGZiZTQzOGYzMTBhNGVkYmJiMmE2YjlkZWVh
|
14
|
+
YzBmZWM5YmE5YjYwZTdlYmFkNzkwNzBiOWRiNTVlMGJhMzUxODBiY2M3MzVm
|
15
|
+
OGE4ODY4ZDQxNDJiNmM1MmVlNmVlOWEwNzM1YzkxMzkxNjBiOTM=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# .travis.yml
|
2
|
+
rvm:
|
3
|
+
- 1.9.3-p547
|
4
|
+
- 2.0.0-p598
|
5
|
+
- 2.1.5
|
6
|
+
- 2.2.0
|
7
|
+
gemfile:
|
8
|
+
- gemfiles/rails40.gemfile
|
9
|
+
- gemfiles/rails41.gemfile
|
10
|
+
- gemfiles/rails42.gemfile
|
11
|
+
#matrix:
|
12
|
+
# exclude:
|
13
|
+
# - rbenv: 2.0.0
|
14
|
+
# gemfile: gemfiles/rails2.gemfile
|
15
|
+
script: "bundle exec rake spec"
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
transaction_reliability (0.1.0)
|
5
|
+
activerecord (>= 4.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.0.13)
|
11
|
+
activesupport (= 4.0.13)
|
12
|
+
builder (~> 3.1.0)
|
13
|
+
activerecord (4.0.13)
|
14
|
+
activemodel (= 4.0.13)
|
15
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
16
|
+
activesupport (= 4.0.13)
|
17
|
+
arel (~> 4.0.0)
|
18
|
+
activerecord-deprecated_finders (1.0.3)
|
19
|
+
activesupport (4.0.13)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
|
+
minitest (~> 4.2)
|
22
|
+
multi_json (~> 1.3)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
tzinfo (~> 0.3.37)
|
25
|
+
arel (4.0.2)
|
26
|
+
builder (3.1.4)
|
27
|
+
coderay (1.1.0)
|
28
|
+
diff-lcs (1.2.5)
|
29
|
+
docile (1.1.5)
|
30
|
+
i18n (0.7.0)
|
31
|
+
method_source (0.8.2)
|
32
|
+
minitest (4.7.5)
|
33
|
+
multi_json (1.10.1)
|
34
|
+
pg (0.18.1)
|
35
|
+
pry (0.10.1)
|
36
|
+
coderay (~> 1.1.0)
|
37
|
+
method_source (~> 0.8.1)
|
38
|
+
slop (~> 3.4)
|
39
|
+
rake (10.4.2)
|
40
|
+
rspec (2.14.1)
|
41
|
+
rspec-core (~> 2.14.0)
|
42
|
+
rspec-expectations (~> 2.14.0)
|
43
|
+
rspec-mocks (~> 2.14.0)
|
44
|
+
rspec-core (2.14.8)
|
45
|
+
rspec-expectations (2.14.5)
|
46
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
47
|
+
rspec-mocks (2.14.6)
|
48
|
+
simplecov (0.9.1)
|
49
|
+
docile (~> 1.1.0)
|
50
|
+
multi_json (~> 1.0)
|
51
|
+
simplecov-html (~> 0.8.0)
|
52
|
+
simplecov-html (0.8.0)
|
53
|
+
slop (3.6.0)
|
54
|
+
thread_safe (0.3.4)
|
55
|
+
tzinfo (0.3.43)
|
56
|
+
wwtd (0.7.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
activerecord (~> 4.0.13)
|
63
|
+
bundler (~> 1.7)
|
64
|
+
pg (>= 0.17.0)
|
65
|
+
pry
|
66
|
+
rake (~> 10.0)
|
67
|
+
rspec (~> 2.14.0)
|
68
|
+
simplecov
|
69
|
+
transaction_reliability!
|
70
|
+
wwtd
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Robert Sanders
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# TransactionReliability
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'transaction_reliability'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install transaction_reliability
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/transaction_reliability/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/config/database.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# SQLite version 3.x
|
2
|
+
# gem install sqlite3
|
3
|
+
#
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
+
# gem 'sqlite3'
|
6
|
+
development:
|
7
|
+
adapter: postgresql
|
8
|
+
database: pg_experiments
|
9
|
+
pool: 20
|
10
|
+
encoding: UTF8
|
11
|
+
min_messages: notice
|
12
|
+
host: localhost
|
13
|
+
username: <%= `whoami`.chomp %>
|
14
|
+
timeout: 5000
|
15
|
+
|
16
|
+
# Warning: The database defined as "test" will be erased and
|
17
|
+
# re-generated from your development database when you run "rake".
|
18
|
+
# Do not set this db to the same as development or production.
|
19
|
+
test:
|
20
|
+
adapter: postgresql
|
21
|
+
database: pg_experiments_test
|
22
|
+
pool: 20
|
23
|
+
encoding: UTF8
|
24
|
+
min_messages: notice
|
25
|
+
host: localhost
|
26
|
+
username: <%= `whoami`.chomp %>
|
27
|
+
timeout: 5000
|
28
|
+
|
29
|
+
production:
|
30
|
+
adapter: postgresql
|
31
|
+
database: pg_experiments_prod
|
32
|
+
pool: 20
|
33
|
+
encoding: UTF8
|
34
|
+
min_messages: notice
|
35
|
+
host: localhost
|
36
|
+
username: <%= `whoami`.chomp %>
|
37
|
+
timeout: 5000
|
@@ -0,0 +1,17 @@
|
|
1
|
+
test:
|
2
|
+
adapter: postgresql
|
3
|
+
encoding: UTF8
|
4
|
+
database: pg_funcall_test
|
5
|
+
username: pgfuncallgem
|
6
|
+
password: pgfuncallgem
|
7
|
+
min_messages: warning
|
8
|
+
host: localhost
|
9
|
+
|
10
|
+
development:
|
11
|
+
adapter: postgresql
|
12
|
+
encoding: UTF8
|
13
|
+
database: pg_funcall_dev
|
14
|
+
username: pgfuncallgem
|
15
|
+
password: pgfuncallgem
|
16
|
+
min_messages: warning
|
17
|
+
host: localhost
|
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
transaction_reliability (0.1.0)
|
5
|
+
activerecord (>= 4.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.0.13)
|
11
|
+
activesupport (= 4.0.13)
|
12
|
+
builder (~> 3.1.0)
|
13
|
+
activerecord (4.0.13)
|
14
|
+
activemodel (= 4.0.13)
|
15
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
16
|
+
activesupport (= 4.0.13)
|
17
|
+
arel (~> 4.0.0)
|
18
|
+
activerecord-deprecated_finders (1.0.3)
|
19
|
+
activesupport (4.0.13)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
|
+
minitest (~> 4.2)
|
22
|
+
multi_json (~> 1.3)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
tzinfo (~> 0.3.37)
|
25
|
+
arel (4.0.2)
|
26
|
+
builder (3.1.4)
|
27
|
+
coderay (1.1.0)
|
28
|
+
diff-lcs (1.2.5)
|
29
|
+
docile (1.1.5)
|
30
|
+
i18n (0.7.0)
|
31
|
+
method_source (0.8.2)
|
32
|
+
minitest (4.7.5)
|
33
|
+
multi_json (1.10.1)
|
34
|
+
pg (0.17.1)
|
35
|
+
pry (0.10.1)
|
36
|
+
coderay (~> 1.1.0)
|
37
|
+
method_source (~> 0.8.1)
|
38
|
+
slop (~> 3.4)
|
39
|
+
rake (10.4.2)
|
40
|
+
rspec (2.14.1)
|
41
|
+
rspec-core (~> 2.14.0)
|
42
|
+
rspec-expectations (~> 2.14.0)
|
43
|
+
rspec-mocks (~> 2.14.0)
|
44
|
+
rspec-core (2.14.8)
|
45
|
+
rspec-expectations (2.14.5)
|
46
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
47
|
+
rspec-mocks (2.14.6)
|
48
|
+
simplecov (0.9.1)
|
49
|
+
docile (~> 1.1.0)
|
50
|
+
multi_json (~> 1.0)
|
51
|
+
simplecov-html (~> 0.8.0)
|
52
|
+
simplecov-html (0.8.0)
|
53
|
+
slop (3.6.0)
|
54
|
+
thread_safe (0.3.4)
|
55
|
+
tzinfo (0.3.42)
|
56
|
+
wwtd (0.7.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
activerecord (~> 4.0.13)
|
63
|
+
bundler (~> 1.7)
|
64
|
+
pg (~> 0.17.1)
|
65
|
+
pry
|
66
|
+
rake (~> 10.0)
|
67
|
+
rspec (~> 2.14.0)
|
68
|
+
simplecov
|
69
|
+
transaction_reliability!
|
70
|
+
wwtd
|
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
transaction_reliability (0.1.0)
|
5
|
+
activerecord (>= 4.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.1.9)
|
11
|
+
activesupport (= 4.1.9)
|
12
|
+
builder (~> 3.1)
|
13
|
+
activerecord (4.1.9)
|
14
|
+
activemodel (= 4.1.9)
|
15
|
+
activesupport (= 4.1.9)
|
16
|
+
arel (~> 5.0.0)
|
17
|
+
activesupport (4.1.9)
|
18
|
+
i18n (~> 0.6, >= 0.6.9)
|
19
|
+
json (~> 1.7, >= 1.7.7)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
thread_safe (~> 0.1)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
arel (5.0.1.20140414130214)
|
24
|
+
builder (3.2.2)
|
25
|
+
coderay (1.1.0)
|
26
|
+
diff-lcs (1.2.5)
|
27
|
+
docile (1.1.5)
|
28
|
+
i18n (0.7.0)
|
29
|
+
json (1.8.2)
|
30
|
+
method_source (0.8.2)
|
31
|
+
minitest (5.5.1)
|
32
|
+
multi_json (1.10.1)
|
33
|
+
pg (0.18.1)
|
34
|
+
pry (0.10.1)
|
35
|
+
coderay (~> 1.1.0)
|
36
|
+
method_source (~> 0.8.1)
|
37
|
+
slop (~> 3.4)
|
38
|
+
rake (10.4.2)
|
39
|
+
rspec (2.14.1)
|
40
|
+
rspec-core (~> 2.14.0)
|
41
|
+
rspec-expectations (~> 2.14.0)
|
42
|
+
rspec-mocks (~> 2.14.0)
|
43
|
+
rspec-core (2.14.8)
|
44
|
+
rspec-expectations (2.14.5)
|
45
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
46
|
+
rspec-mocks (2.14.6)
|
47
|
+
simplecov (0.9.1)
|
48
|
+
docile (~> 1.1.0)
|
49
|
+
multi_json (~> 1.0)
|
50
|
+
simplecov-html (~> 0.8.0)
|
51
|
+
simplecov-html (0.8.0)
|
52
|
+
slop (3.6.0)
|
53
|
+
thread_safe (0.3.4)
|
54
|
+
tzinfo (1.2.2)
|
55
|
+
thread_safe (~> 0.1)
|
56
|
+
wwtd (0.7.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
activerecord (~> 4.1.9)
|
63
|
+
bundler (~> 1.7)
|
64
|
+
pg (>= 0.17.0)
|
65
|
+
pry
|
66
|
+
rake (~> 10.0)
|
67
|
+
rspec (~> 2.14.0)
|
68
|
+
simplecov
|
69
|
+
transaction_reliability!
|
70
|
+
wwtd
|
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
transaction_reliability (0.1.0)
|
5
|
+
activerecord (>= 4.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.2.0)
|
11
|
+
activesupport (= 4.2.0)
|
12
|
+
builder (~> 3.1)
|
13
|
+
activerecord (4.2.0)
|
14
|
+
activemodel (= 4.2.0)
|
15
|
+
activesupport (= 4.2.0)
|
16
|
+
arel (~> 6.0)
|
17
|
+
activesupport (4.2.0)
|
18
|
+
i18n (~> 0.7)
|
19
|
+
json (~> 1.7, >= 1.7.7)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
arel (6.0.0)
|
24
|
+
builder (3.2.2)
|
25
|
+
coderay (1.1.0)
|
26
|
+
diff-lcs (1.2.5)
|
27
|
+
docile (1.1.5)
|
28
|
+
i18n (0.7.0)
|
29
|
+
json (1.8.2)
|
30
|
+
method_source (0.8.2)
|
31
|
+
minitest (5.5.1)
|
32
|
+
multi_json (1.10.1)
|
33
|
+
pg (0.18.1)
|
34
|
+
pry (0.10.1)
|
35
|
+
coderay (~> 1.1.0)
|
36
|
+
method_source (~> 0.8.1)
|
37
|
+
slop (~> 3.4)
|
38
|
+
rake (10.4.2)
|
39
|
+
rspec (2.14.1)
|
40
|
+
rspec-core (~> 2.14.0)
|
41
|
+
rspec-expectations (~> 2.14.0)
|
42
|
+
rspec-mocks (~> 2.14.0)
|
43
|
+
rspec-core (2.14.8)
|
44
|
+
rspec-expectations (2.14.5)
|
45
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
46
|
+
rspec-mocks (2.14.6)
|
47
|
+
simplecov (0.9.1)
|
48
|
+
docile (~> 1.1.0)
|
49
|
+
multi_json (~> 1.0)
|
50
|
+
simplecov-html (~> 0.8.0)
|
51
|
+
simplecov-html (0.8.0)
|
52
|
+
slop (3.6.0)
|
53
|
+
thread_safe (0.3.4)
|
54
|
+
tzinfo (1.2.2)
|
55
|
+
thread_safe (~> 0.1)
|
56
|
+
wwtd (0.7.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
activerecord (~> 4.2.0)
|
63
|
+
bundler (~> 1.7)
|
64
|
+
pg (>= 0.17.0)
|
65
|
+
pry
|
66
|
+
rake (~> 10.0)
|
67
|
+
rspec (~> 2.14.0)
|
68
|
+
simplecov
|
69
|
+
transaction_reliability!
|
70
|
+
wwtd
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#
|
2
|
+
# Provide utilities for the (more) reliable handling of database errors
|
3
|
+
# related to concurrency or connectivity.
|
4
|
+
#
|
5
|
+
# Currently only handles Postgresql and Mysql, assuming use of
|
6
|
+
# the PG and Mysql2 drivers respectively
|
7
|
+
#
|
8
|
+
module TransactionReliability
|
9
|
+
|
10
|
+
#
|
11
|
+
# We inherit from StatementInvalid because of compatibility with
|
12
|
+
# all the code which may rescue StatementInvalid
|
13
|
+
#
|
14
|
+
class TransientTransactionError < ActiveRecord::StatementInvalid
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Parent class for deadlocks, serialization failures, and other
|
19
|
+
# non-fatal errors that can be handled by retrying a transaction
|
20
|
+
#
|
21
|
+
class ConcurrencyError < TransientTransactionError
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# There has been an unresolvable write conflict between two transactions
|
26
|
+
#
|
27
|
+
class DeadlockDetected < ConcurrencyError
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# For transaction in isolation level SERIALIZABLE, some conflict
|
32
|
+
# has been detected. The transaction should be retried.
|
33
|
+
#
|
34
|
+
class SerializationFailure < ConcurrencyError
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# The connection to the database has been lost.
|
39
|
+
#
|
40
|
+
class ConnectionLost < TransientTransactionError
|
41
|
+
end
|
42
|
+
|
43
|
+
module Helpers
|
44
|
+
#
|
45
|
+
# Intended to be included in an ActiveRecord model class.
|
46
|
+
#
|
47
|
+
# Retries a block (which usually contains a transaction) under certain
|
48
|
+
# failure conditions, up to a configurable number of times with an
|
49
|
+
# exponential backoff delay between each attempt.
|
50
|
+
#
|
51
|
+
# Conditions for retrying:
|
52
|
+
#
|
53
|
+
# 1. Database connection lost
|
54
|
+
# 2. Query or txn failed due to detected deadlock
|
55
|
+
# (Mysql/InnoDB and Postgres can both signal this for just about
|
56
|
+
# any transaction)
|
57
|
+
# 3. Query or txn failed due to serialization failure
|
58
|
+
# (Postgres will signal this for transactions in isolation
|
59
|
+
# level SERIALIZABLE)
|
60
|
+
#
|
61
|
+
# options:
|
62
|
+
# retry_count - how many retries to make; default 4
|
63
|
+
#
|
64
|
+
# backoff - time period before 1st retry, in fractional seconds.
|
65
|
+
# will double at every retry. default 0.25 seconds.
|
66
|
+
#
|
67
|
+
# exit_on_disconnect
|
68
|
+
# - whether to call exit if no retry succeeds and
|
69
|
+
# the cause is a failed connection
|
70
|
+
#
|
71
|
+
# exit_on_fail - whether to call exit if no retry succeeds
|
72
|
+
#
|
73
|
+
# defaults:
|
74
|
+
#
|
75
|
+
#
|
76
|
+
def with_transaction_retry(options = {})
|
77
|
+
retry_count = options.fetch(:retry_count, 4)
|
78
|
+
backoff = options.fetch(:backoff, 0.25)
|
79
|
+
exit_on_fail = options.fetch(:exit_on_fail, false)
|
80
|
+
exit_on_disconnect = options.fetch(:exit_on_disconnect, true)
|
81
|
+
|
82
|
+
count = 0
|
83
|
+
|
84
|
+
# list of exceptions we may catch
|
85
|
+
exceptions = ['ActiveRecord::StatementInvalid', 'PG::Error', 'Mysql2::Error'].
|
86
|
+
map {|name| name.safe_constantize}.
|
87
|
+
compact
|
88
|
+
|
89
|
+
#
|
90
|
+
# There are times when, for example,
|
91
|
+
# a raw PG::Error is throw rather than a wrapped ActiveRecord::StatementInvalid
|
92
|
+
#
|
93
|
+
# Also, connector-specific classes like PG::Error may not be defined
|
94
|
+
#
|
95
|
+
begin
|
96
|
+
connection_lost = false
|
97
|
+
yield
|
98
|
+
rescue *exceptions => e
|
99
|
+
translated = TransactionReliability.rewrap_exception(e)
|
100
|
+
|
101
|
+
case translated
|
102
|
+
when ConnectionLost
|
103
|
+
(options[:connection] || ActiveRecord::Base.connection).reconnect!
|
104
|
+
connection_lost = true
|
105
|
+
when DeadlockDetected, SerializationFailure
|
106
|
+
else
|
107
|
+
raise translated
|
108
|
+
end
|
109
|
+
|
110
|
+
# Retry up to retry_count times
|
111
|
+
if count < retry_count
|
112
|
+
sleep backoff
|
113
|
+
count += 1
|
114
|
+
backoff *= 2
|
115
|
+
retry
|
116
|
+
else
|
117
|
+
if (connection_lost && exit_on_disconnect) || exit_on_fail
|
118
|
+
exit
|
119
|
+
else
|
120
|
+
raise(translated)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Execute some code in a DB transaction, with retry
|
128
|
+
#
|
129
|
+
def transaction_with_retry(txn_options = {}, retry_options = {})
|
130
|
+
base_obj = self.respond_to?(:transaction) ? self : ActiveRecord::Base
|
131
|
+
|
132
|
+
with_transaction_retry(retry_options) do
|
133
|
+
base_obj.transaction(txn_options) do
|
134
|
+
yield
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# allow the helper methods to be accessed as methods on this module
|
141
|
+
extend(Helpers)
|
142
|
+
|
143
|
+
#
|
144
|
+
# Unwrap ActiveRecord::StatementInvalid into some more specific exceptions
|
145
|
+
# that we define.
|
146
|
+
#
|
147
|
+
# Only defined for Mysql2 and PG drivers at the moment
|
148
|
+
#
|
149
|
+
def self.rewrap_exception(exception)
|
150
|
+
if exception.message.start_with?('PG::') || exception.class.name.start_with?('PG::')
|
151
|
+
rewrap_pg_exception exception
|
152
|
+
elsif exception.message =~ /^mysql2::/i
|
153
|
+
rewrap_mysql2_exception exception
|
154
|
+
else
|
155
|
+
exception
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
protected
|
160
|
+
|
161
|
+
SQLSTATE_CONNECTION_ERRORS =
|
162
|
+
[
|
163
|
+
'08000', # connection_exception
|
164
|
+
'08003', # connection_does_not_exist
|
165
|
+
'08006', # connection_failure
|
166
|
+
'08001', # sqlclient_unable_to_establish_sqlconnection
|
167
|
+
'08004', # sqlserver_rejected_establishment_of_sqlconnection
|
168
|
+
'08007', # transaction_resolution_unknown
|
169
|
+
'08P01' # protocol_violation
|
170
|
+
]
|
171
|
+
|
172
|
+
SQLSTATE_DEADLOCK_ERRORS =
|
173
|
+
[
|
174
|
+
'40P01' # deadlock detected
|
175
|
+
]
|
176
|
+
|
177
|
+
SQLSTATE_ISOLATION_ERRORS =
|
178
|
+
[
|
179
|
+
'40001' # serialization failure
|
180
|
+
]
|
181
|
+
|
182
|
+
|
183
|
+
def self.rewrap_pg_exception(exception)
|
184
|
+
message = exception.message
|
185
|
+
orig = case
|
186
|
+
when exception.is_a?(PG::Error)
|
187
|
+
exception
|
188
|
+
when exception.respond_to?(:original_exception)
|
189
|
+
exception.original_exception
|
190
|
+
else
|
191
|
+
exception
|
192
|
+
end
|
193
|
+
|
194
|
+
if orig.is_a? PG::Error
|
195
|
+
sqlstate = orig.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) rescue nil
|
196
|
+
else
|
197
|
+
sqlstate = nil
|
198
|
+
end
|
199
|
+
|
200
|
+
case
|
201
|
+
when orig.is_a?(PG::ConnectionBad) || SQLSTATE_CONNECTION_ERRORS.include?(sqlstate)
|
202
|
+
ConnectionLost.new(message, orig)
|
203
|
+
when orig.is_a?(PG::TRDeadlockDetected) || SQLSTATE_DEADLOCK_ERRORS.include?(sqlstate)
|
204
|
+
DeadlockDetected.new(message, orig)
|
205
|
+
when orig.is_a?(PG::TRSerializationFailure) || SQLSTATE_ISOLATION_ERRORS.include?(sqlstate)
|
206
|
+
SerializationFailure.new(message, orig)
|
207
|
+
else
|
208
|
+
exception
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# Ugh. This may not work if you're using non-English localization
|
214
|
+
# for your MySQL server.
|
215
|
+
#
|
216
|
+
def self.rewrap_mysql_exception(exception)
|
217
|
+
orig = exception.original_exception
|
218
|
+
message = exception.message
|
219
|
+
|
220
|
+
case
|
221
|
+
when message =~ /Serialization failure/i
|
222
|
+
SerializationFailure.new(message, orig)
|
223
|
+
when message =~ /Deadlock found when trying to get lock/i ||
|
224
|
+
message =~ /Lock wait timeout exceeded/i
|
225
|
+
DeadlockDetected.new(message, orig)
|
226
|
+
when message =~ /Lost connection to MySQL server/i ||
|
227
|
+
message =~ /Invalid connection handle/i ||
|
228
|
+
message =~ /MySQL server has gone away/i ||
|
229
|
+
message =~ /Broken pipe/i ||
|
230
|
+
message =~ /Server shutdown in progress/i
|
231
|
+
ConnectionLost.new(message, orig)
|
232
|
+
else
|
233
|
+
exception
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
data/script/rebundle.sh
ADDED
data/script/shell
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'active_record'
|
7
|
+
configs = YAML.load(File.read("config/database.yml.example"))
|
8
|
+
ActiveRecord::Base.establish_connection(configs['development'])
|
9
|
+
|
10
|
+
require 'transaction_reliability'
|
11
|
+
|
12
|
+
def conn
|
13
|
+
ActiveRecord::Base.connection
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'pry'
|
18
|
+
Pry.start
|
19
|
+
rescue LoadError
|
20
|
+
require 'irb'
|
21
|
+
IRB.start
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pg'
|
3
|
+
|
4
|
+
describe TransactionReliability do
|
5
|
+
|
6
|
+
class TrSpecTable < ActiveRecord::Base
|
7
|
+
extend TransactionReliability::Helpers
|
8
|
+
self.table_name = 'public.tr_spec_table'
|
9
|
+
end
|
10
|
+
|
11
|
+
class Foo
|
12
|
+
extend TransactionReliability::Helpers
|
13
|
+
def self.transaction(*args)
|
14
|
+
puts "txn called with #{args.inspect}"
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# before(:all) do
|
20
|
+
# ActiveRecord::Base.connection.execute <<-SQL
|
21
|
+
# DROP TABLE IF EXISTS public.tr_spec_table;
|
22
|
+
# CREATE TABLE IF NOT EXISTS public.tr_spec_table
|
23
|
+
# (name text, num integer, ts timestamp);
|
24
|
+
# SQL
|
25
|
+
#
|
26
|
+
# # ActiveRecord::Base.reset_column_information
|
27
|
+
# end
|
28
|
+
|
29
|
+
# after(:all) do
|
30
|
+
# ActiveRecord::Base.connection.execute <<-SQL
|
31
|
+
# DROP TABLE IF EXISTS public.tr_spec_table;
|
32
|
+
# SQL
|
33
|
+
# end
|
34
|
+
|
35
|
+
let! :conn1 do
|
36
|
+
ActiveRecord::Base.connection_pool.checkout
|
37
|
+
end
|
38
|
+
|
39
|
+
let! :conn2 do
|
40
|
+
ActiveRecord::Base.connection_pool.checkout
|
41
|
+
end
|
42
|
+
|
43
|
+
after(:each) do
|
44
|
+
[conn1, conn2].each {|conn| ActiveRecord::Base.connection_pool.checkin(conn) }
|
45
|
+
end
|
46
|
+
|
47
|
+
let :counter do
|
48
|
+
double('counter')
|
49
|
+
end
|
50
|
+
|
51
|
+
def fake_deadlock
|
52
|
+
raise PG::TRDeadlockDetected, "PG::TRDeadlockDetected fake"
|
53
|
+
end
|
54
|
+
|
55
|
+
def fake_serialization_failure
|
56
|
+
raise PG::TRSerializationFailure, "PG::TRSerializationFailure fake"
|
57
|
+
end
|
58
|
+
|
59
|
+
def fake_connection_lost
|
60
|
+
raise PG::ConnectionBad, "PG::ConnectionBad fake"
|
61
|
+
end
|
62
|
+
|
63
|
+
class ActiveRecord::Base
|
64
|
+
def self.transaction(*args)
|
65
|
+
puts "calling fake .transaction() in AR::Base"
|
66
|
+
yield
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.connection
|
70
|
+
double()
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'uncontended' do
|
75
|
+
context 'transaction_with_retry' do
|
76
|
+
it 'should run the block once' do
|
77
|
+
counter.should_receive(:doit).exactly(1).times
|
78
|
+
ActiveRecord::Base.should_receive(:transaction).exactly(1).times.and_call_original
|
79
|
+
|
80
|
+
TransactionReliability.transaction_with_retry do
|
81
|
+
counter.doit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with_transaction_retry' do
|
87
|
+
it 'should run the block once' do
|
88
|
+
counter.should_receive(:doit).exactly(1).times
|
89
|
+
ActiveRecord::Base.should_not_receive(:transaction)
|
90
|
+
|
91
|
+
TransactionReliability.with_transaction_retry do
|
92
|
+
counter.doit
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should not re-run on ordinary failure' do
|
97
|
+
counter.should_receive(:doit).exactly(1).times
|
98
|
+
|
99
|
+
expect do
|
100
|
+
TransactionReliability.with_transaction_retry do
|
101
|
+
counter.doit
|
102
|
+
raise ArgumentError
|
103
|
+
end
|
104
|
+
end.to raise_error(ArgumentError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'with simulated error on PG' do
|
109
|
+
it 'should run the block the maximum number of times' do
|
110
|
+
counter.should_receive(:doit).exactly(5).times
|
111
|
+
|
112
|
+
expect do
|
113
|
+
TransactionReliability.with_transaction_retry(backoff: 0) do
|
114
|
+
counter.doit
|
115
|
+
fake_deadlock
|
116
|
+
end
|
117
|
+
end.to raise_error
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should return TransactionReliability::DeadlockDetected error on unresolved deadlock' do
|
121
|
+
expect do
|
122
|
+
TransactionReliability.with_transaction_retry(retry_count: 1) do
|
123
|
+
fake_deadlock
|
124
|
+
end
|
125
|
+
end.to raise_error(TransactionReliability::DeadlockDetected)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should return TransactionReliability::SerializationFailure on unresolved serialization failure' do
|
129
|
+
expect do
|
130
|
+
TransactionReliability.with_transaction_retry(retry_count: 1) do
|
131
|
+
fake_serialization_failure
|
132
|
+
end
|
133
|
+
end.to raise_error(TransactionReliability::SerializationFailure)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'with simulated connection failure' do
|
138
|
+
let :countup do
|
139
|
+
[0]
|
140
|
+
end
|
141
|
+
|
142
|
+
let :connection do
|
143
|
+
double('Connection')
|
144
|
+
end
|
145
|
+
|
146
|
+
before do
|
147
|
+
ActiveRecord::Base.should_receive(:connection).at_least(1).times.and_return(connection)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should retry the block' do
|
151
|
+
counter.should_receive(:doit).exactly(5).times
|
152
|
+
connection.should_receive(:reconnect!).at_least(1).times
|
153
|
+
|
154
|
+
expect do
|
155
|
+
TransactionReliability.with_transaction_retry(backoff: 0) do
|
156
|
+
counter.doit
|
157
|
+
fake_connection_lost
|
158
|
+
end
|
159
|
+
end.to raise_error
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should reconnect' do
|
163
|
+
connection.should_receive(:reconnect!).exactly(1).times
|
164
|
+
counter.should_receive(:doit).exactly(2).times
|
165
|
+
|
166
|
+
TransactionReliability.with_transaction_retry(backoff: 0) do
|
167
|
+
counter.doit
|
168
|
+
countup[0] += 1
|
169
|
+
fake_connection_lost if countup[0] == 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should raise ConnectionLost if unresolved' do
|
174
|
+
connection.should_receive(:reconnect!).at_least(1).times
|
175
|
+
|
176
|
+
expect do
|
177
|
+
TransactionReliability.with_transaction_retry(retry_count: 1, exit_on_disconnect: false) do
|
178
|
+
fake_connection_lost
|
179
|
+
end
|
180
|
+
end.to raise_error(TransactionReliability::ConnectionLost)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should call exit if the connection cannot be re-established and configured to exit' do
|
184
|
+
connection.should_receive(:reconnect!).at_least(1).times
|
185
|
+
|
186
|
+
expect do
|
187
|
+
TransactionReliability.with_transaction_retry(retry_count: 1, exit_on_disconnect: true) do
|
188
|
+
fake_connection_lost
|
189
|
+
end
|
190
|
+
end.to raise_error(SystemExit)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
if ENV['COVERAGE'] == 'true'
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'active_record'
|
11
|
+
configs = YAML.load(File.read("config/database.yml.example"))
|
12
|
+
ActiveRecord::Base.establish_connection(configs['test'])
|
13
|
+
|
14
|
+
require 'transaction_reliability'
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'pry'
|
18
|
+
rescue LoadError
|
19
|
+
#
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'transaction_reliability/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "transaction_reliability"
|
8
|
+
spec.version = TransactionReliability::VERSION
|
9
|
+
spec.authors = ["Robert Sanders"]
|
10
|
+
spec.email = ["robert@curioussquid.com"]
|
11
|
+
spec.summary = %q{Functions to wrap and retry a code block when the DB declares a serialization failure or deadlock.}
|
12
|
+
# spec.description = %q{TODO: Write a longer description. Optional.}
|
13
|
+
spec.homepage = "http://github.com/rsanders/transaction_reliability"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features|gemfiles|config)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activerecord", ">= 4.0.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "pg", ">= 0.17.0"
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.14.0"
|
27
|
+
spec.add_development_dependency "simplecov"
|
28
|
+
spec.add_development_dependency "wwtd"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: transaction_reliability
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Sanders
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.17.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.17.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.14.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.14.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: wwtd
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- robert@curioussquid.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .rspec
|
120
|
+
- .travis.yml
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- config/database.yml
|
127
|
+
- config/database.yml.example
|
128
|
+
- gemfiles/rails40.gemfile
|
129
|
+
- gemfiles/rails40.gemfile.lock
|
130
|
+
- gemfiles/rails41.gemfile
|
131
|
+
- gemfiles/rails41.gemfile.lock
|
132
|
+
- gemfiles/rails42.gemfile
|
133
|
+
- gemfiles/rails42.gemfile.lock
|
134
|
+
- lib/transaction_reliability.rb
|
135
|
+
- lib/transaction_reliability/version.rb
|
136
|
+
- script/rebundle.sh
|
137
|
+
- script/shell
|
138
|
+
- spec/lib/transaction_reliability_spec.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
- transaction_reliability.gemspec
|
141
|
+
homepage: http://github.com/rsanders/transaction_reliability
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
metadata: {}
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
requirements: []
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 2.4.5
|
162
|
+
signing_key:
|
163
|
+
specification_version: 4
|
164
|
+
summary: Functions to wrap and retry a code block when the DB declares a serialization
|
165
|
+
failure or deadlock.
|
166
|
+
test_files:
|
167
|
+
- config/database.yml
|
168
|
+
- config/database.yml.example
|
169
|
+
- gemfiles/rails40.gemfile
|
170
|
+
- gemfiles/rails40.gemfile.lock
|
171
|
+
- gemfiles/rails41.gemfile
|
172
|
+
- gemfiles/rails41.gemfile.lock
|
173
|
+
- gemfiles/rails42.gemfile
|
174
|
+
- gemfiles/rails42.gemfile.lock
|
175
|
+
- spec/lib/transaction_reliability_spec.rb
|
176
|
+
- spec/spec_helper.rb
|