transaction_isolation_continued 1.0.5 → 1.1.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.github/workflows/main.yml +35 -20
  4. data/.github/workflows/rubygem.yml +28 -0
  5. data/.gitignore +3 -0
  6. data/{Gemfile.local → Gemfile.old.local} +9 -0
  7. data/README.md +77 -56
  8. data/docker/ruby-2.5/Dockerfile +6 -0
  9. data/docker/ruby-2.7/Dockerfile +6 -0
  10. data/docker/ruby-3.0/Dockerfile +6 -0
  11. data/docker/ruby-3.1/Dockerfile +6 -0
  12. data/docker/test-ruby-2.5.sh +26 -0
  13. data/docker/test-ruby-2.7.sh +29 -0
  14. data/docker/test-ruby-3.0.sh +26 -0
  15. data/docker/test-ruby-3.1.sh +26 -0
  16. data/docker/test-ruby.sh +8 -0
  17. data/docker-compose.yml +60 -0
  18. data/gemfiles/Gemfile.base +4 -0
  19. data/gemfiles/activerecord-5.2/Gemfile.sqlite3 +1 -1
  20. data/gemfiles/activerecord-6.0/Gemfile.sqlite3 +1 -1
  21. data/gemfiles/activerecord-6.1/Gemfile.sqlite3 +1 -1
  22. data/gemfiles/activerecord-7.0/Gemfile.sqlite3 +1 -1
  23. data/lib/transaction_isolation/active_record/base.rb +5 -3
  24. data/lib/transaction_isolation/active_record/connection_adapters/abstract_adapter.rb +10 -9
  25. data/lib/transaction_isolation/active_record/connection_adapters/mysql2_adapter.rb +29 -27
  26. data/lib/transaction_isolation/active_record/connection_adapters/postgresql_adapter.rb +26 -24
  27. data/lib/transaction_isolation/active_record/connection_adapters/sqlite3_adapter.rb +24 -21
  28. data/lib/transaction_isolation/active_record/errors.rb +2 -0
  29. data/lib/transaction_isolation/configuration.rb +6 -4
  30. data/lib/transaction_isolation/version.rb +3 -1
  31. data/lib/transaction_isolation.rb +16 -11
  32. data/lib/transaction_isolation_continued.rb +1 -0
  33. data/spec/gem_template_spec.rb +11 -0
  34. data/spec/spec_helper.rb +15 -0
  35. data/test/db/all.rb +2 -0
  36. data/test/db/db.rb +15 -15
  37. data/test/db/migrations.rb +6 -7
  38. data/test/db/queued_job.rb +2 -0
  39. data/test/integration/active_record/base/isolation_level_test.rb +3 -9
  40. data/test/integration/active_record/connection_adapters/any_adapter/current_isolation_level_test.rb +8 -16
  41. data/test/integration/active_record/connection_adapters/any_adapter/current_vendor_isolation_level_test.rb +8 -16
  42. data/test/integration/active_record/connection_adapters/any_adapter/isolation_level_test.rb +18 -29
  43. data/test/integration/active_record/connection_adapters/any_adapter/supports_isolation_levels_test.rb +2 -10
  44. data/test/integration/active_record/connection_adapters/any_adapter/translate_exception_test.rb +20 -26
  45. data/test/library_setup.rb +11 -9
  46. data/test/test_helper.rb +9 -6
  47. data/test/test_runner.rb +3 -2
  48. data/transaction_isolation_continued.gemspec +15 -15
  49. metadata +33 -20
  50. data/d +0 -1
  51. data/test/test_console.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32f804fa89e4072268ea34713263099072269395caf1bca34528d1632ac37a33
4
- data.tar.gz: 9fa045df1dd64b0e45fb74eb8e507f7ff0aa2376e61843a203bec64dfaf8a5e2
3
+ metadata.gz: f5abbf92825c1d63049fbae813f27d3df651bc4895a01626d6d01c4223ebc6ed
4
+ data.tar.gz: c589d78b41a18f1984f36e94f3eacd5f61740617a02889e6d754964c456671dd
5
5
  SHA512:
6
- metadata.gz: 56a421307a4fd2a8c66bfa35e9189b99c160cd8db82fc5103a5d6c7ead76a0a8699cc3f2f089d79c521fa0d8b475129530d7aeaab7c9e4ac77f714f6dc550cdd
7
- data.tar.gz: 66f6c3dd3ee7a0fd8ecad4292cfdf13ae563dcd5afdacada47030bbdd93c5c11917f3fac07caa62ec67b466a30677d95b7595d96c5fd384021f9fccc7290458e
6
+ metadata.gz: 25c0ae13dc44a64f176d182a9874473c43bf3b98e1d29df6a1ede734a3730d8fb9604a7059417961989c2ab95e9f21f8c799bd491b03213c40b5930e90e5afd2
7
+ data.tar.gz: 9f74455b9ae024d26958f12bda07bb7c714db27ad20ae7f7f94d9bdde1a6978f2a6237aa39acfbdfc50ea5c9854d2edb057e447dd5a6bf46bb87c198a16e1bba
data/.dockerignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.*.lock
@@ -102,7 +102,7 @@ jobs:
102
102
  POSTGRESQL_DB_PASS: database
103
103
  POSTGRESQL_DB_NAME: transaction_isolation_continued_test
104
104
  steps:
105
- - uses: actions/checkout@v2
105
+ - uses: actions/checkout@v4
106
106
  - name: Set up Ruby
107
107
  uses: ruby/setup-ruby@v1
108
108
  with:
@@ -110,6 +110,11 @@ jobs:
110
110
  bundler-cache: true
111
111
  - name: Run bundle update
112
112
  run: bundle update
113
+ - name: Setup Code Climate test-reporter
114
+ run: |
115
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
116
+ chmod +x ./cc-test-reporter
117
+ ./cc-test-reporter before-build
113
118
  - name: Start Mysql
114
119
  if: matrix.db == 'mysql2'
115
120
  run: |
@@ -145,27 +150,37 @@ jobs:
145
150
  echo $(( COUNT++ )) > /dev/null
146
151
  sleep 2
147
152
  done
148
- - name: test
149
- run: echo $BUNDLE_GEMFILE
150
153
  - name: Run tests
151
154
  run: db=${{ matrix.db }} bundle exec rake test
152
155
  - name: Shutdown database
153
156
  if: always() && (matrix.db == 'postgresql' || matrix.db == 'mysql2')
154
157
  run: docker stop database
155
- # - name: Coveralls Parallel
156
- # if: "${{ !env.ACT }}"
157
- # uses: coverallsapp/github-action@master
158
- # with:
159
- # github-token: "${{ secrets.GITHUB_TOKEN }}"
160
- # flag-name: run-${{ matrix.ruby }}-${{ matrix.activerecord }}-${{ matrix.db }}-${{ matrix.dbversion }}
161
- # parallel: true
162
- # finish:
163
- # needs: test
164
- # runs-on: ubuntu-latest
165
- # steps:
166
- # - name: Coveralls Finished
167
- # if: "${{ !env.ACT }}"
168
- # uses: coverallsapp/github-action@master
169
- # with:
170
- # github-token: "${{ secrets.GITHUB_TOKEN }}"
171
- # parallel-finished: true
158
+ - name: Format coverage
159
+ run: |
160
+ ./cc-test-reporter format-coverage -t simplecov -o coverage/cc_resultset.json
161
+ - name: Upload reports' artifacts
162
+ if: success() || failure()
163
+ uses: actions/upload-artifact@v4
164
+ with:
165
+ name: coverage_artifact_ruby_${{ matrix.ruby }}_ar_${{ matrix.activerecord }}_db_${{ matrix.db }}
166
+ if-no-files-found: ignore
167
+ path: coverage
168
+ retention-days: 1
169
+ finish:
170
+ needs: test
171
+ runs-on: ubuntu-latest
172
+ steps:
173
+ - uses: actions/checkout@v4
174
+ - name: Download Code Climate test-reporter
175
+ run: |
176
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
177
+ chmod +x ./cc-test-reporter
178
+ - name: Download reports' artifacts
179
+ uses: actions/download-artifact@v4
180
+ with:
181
+ path: coverage_reports
182
+ pattern: coverage_artifact_*
183
+ - name: sum-coverage
184
+ run: |
185
+ ./cc-test-reporter sum-coverage coverage_reports/coverage_artifact_ruby*/cc_resultset.json -o coverage/codeclimate.json
186
+ ./cc-test-reporter upload-coverage -r ${{secrets.CC_TEST_REPORTER_ID}} -i coverage/codeclimate.json
@@ -0,0 +1,28 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ build:
10
+ name: Build + Publish
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ - name: Set up Ruby
15
+ uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: 2.7
18
+ bundler-cache: true
19
+ - name: Publish to RubyGems
20
+ run: |
21
+ mkdir -p $HOME/.gem
22
+ touch $HOME/.gem/credentials
23
+ chmod 0600 $HOME/.gem/credentials
24
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
25
+ gem build *.gemspec
26
+ gem push *.gem
27
+ env:
28
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
data/.gitignore CHANGED
@@ -1,6 +1,9 @@
1
1
  *.gem
2
2
  .bundle
3
3
  Gemfile.lock
4
+ Gemfile.local
4
5
  pkg/*
5
6
  .idea
6
7
  test/log/*.log
8
+ coverage
9
+ gemfiles/*/Gemfile.*lock
@@ -11,6 +11,15 @@ platform :ruby do
11
11
  gem "pg"
12
12
  end
13
13
 
14
+ gem 'ruby2_keywords' if RUBY_VERSION < '2.7'
15
+
16
+ group :test do
17
+ gem 'simplecov'
18
+ gem 'simplecov_json_formatter'
19
+ end
20
+
21
+ gem 'rubocop', require: false
22
+
14
23
  platform :jruby do
15
24
  gem 'activerecord-jdbcpostgresql-adapter'
16
25
  gem 'activerecord-jdbcmysql-adapter'
data/README.md CHANGED
@@ -1,104 +1,125 @@
1
- NO LONGER MAINTAINED - PLEASE FORK OR SEEK OTHER SOLUTIONS
1
+ [![Gem Version](https://badge.fury.io/rb/transaction_isolation_continued.svg)](https://badge.fury.io/rb/transaction_isolation_continued)
2
+ [![Maintainability](https://api.codeclimate.com/v1/badges/01a5d17010b32f041ac3/maintainability)](https://codeclimate.com/github/iagopiimenta/transaction_isolation_continued/maintainability)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/01a5d17010b32f041ac3/test_coverage)](https://codeclimate.com/github/iagopiimenta/transaction_isolation_continued/test_coverage)
4
+ [![CI PR Builds](https://github.com/iagopiimenta/transaction_isolation_continued/actions/workflows/main.yml/badge.svg)](https://github.com/iagopiimenta/transaction_isolation_continued/actions/workflows/main.yml)
2
5
 
3
-
4
- # transaction_isolation
6
+ # transaction_isolation_continued
5
7
 
6
8
  Set transaction isolation level in the ActiveRecord in a database agnostic way.
7
9
  Works with MySQL, PostgreSQL and SQLite as long as you are using new adapters mysql2, pg or sqlite3.
8
- Supports all ANSI SQL isolation levels: :serializable, :repeatable_read, :read_committed, :read_uncommitted.
10
+ Supports all ANSI SQL isolation levels: `:serializable`, `:repeatable_read`, `:read_committed`, `:read_uncommitted`.
9
11
 
10
12
  See also [transaction_retry](https://github.com/qertoip/transaction_retry) gem for auto-retrying transactions
11
13
  on deadlocks and serialization errors.
12
14
 
13
15
  ## Example
14
16
 
15
- ActiveRecord::Base.isolation_level( :serializable ) do
16
- # your code
17
- end
17
+ ```ruby
18
+ ActiveRecord::Base.isolation_level(:serializable) do
19
+ # your code
20
+ end
21
+ ```
22
+
23
+ ## Requirements
24
+
25
+ - Rails: ActiveRecord 5.2+.
26
+ - Database: MySQL(compatible with mysql 5 and 8), PostgreSQL, SQLite.
27
+ - Ruby: MRI 2.5+.
18
28
 
19
29
  ## Installation
20
30
 
21
31
  Add this to your Gemfile:
22
32
 
23
- gem 'transaction_isolation'
33
+ ```ruby
34
+ gem 'transaction_isolation'
35
+ ```
24
36
 
25
37
  Then run:
26
38
 
27
- bundle
39
+ ```bash
40
+ bundle
41
+ ```
28
42
 
29
- __It works out of the box with Ruby on Rails__.
43
+ **It works out of the box with Ruby on Rails**.
30
44
 
31
45
  If you have a standalone ActiveRecord-based project you'll need to call:
32
46
 
33
- TransactionIsolation.apply_activerecord_patch # after connecting to the database
47
+ ```ruby
48
+ TransactionIsolation.apply_activerecord_patch # after connecting to the database
49
+ ```
34
50
 
35
- __after__ connecting to the database. This is because ActiveRecord loads adapters lazilly and only then they can be patched.
51
+ **after** connecting to the database. This is because ActiveRecord loads adapters lazilly and only then they can be patched.
36
52
 
37
53
  ## Features
38
54
 
39
- * Setting transaction isolation level: :serializable, :repeatable_read, :read_committed, :read_uncommitted
40
- * Auto-reverting to the original isolation level after the block
41
- * Database agnostic
42
- * MySQL, PostgreSQL and SQLite supported
43
- * Exception translation. All deadlocks and serialization errors are wrapped in a ActiveRecord::TransactionIsolationConflict exception
44
- * Use it in your Rails application or a standalone ActiveRecord-based project
45
-
46
- ## Testimonials
47
-
48
- This gem was initially developed for and successfully works in production at [Kontomierz.pl](http://kontomierz.pl) - the finest Polish personal finance app.
55
+ - Setting transaction isolation level: `:serializable`, `:repeatable_read`, `:read_committed`, `:read_uncommitted`
56
+ - Auto-reverting to the original isolation level after the block
57
+ - Database agnostic
58
+ - MySQL, PostgreSQL and SQLite supported
59
+ - Exception translation. All deadlocks and serialization errors are wrapped in a `ActiveRecord::TransactionIsolationConflict` exception
60
+ - Use it in your Rails application or a standalone ActiveRecord-based project
49
61
 
50
62
  ## Real world example
51
63
 
52
64
  When implementing a table-based job queue you should ensure that only one worker process can pop a particular job from the queue.
65
+
53
66
  Wrapping your code in a transaction is not enough because by default databases do not isolate transactions to the full extent,
54
- which leads to occasional phantom reads. It is therefore necessary to manually raise the transaction isolation level.
67
+ which leads to occasional phantom reads.
68
+
69
+ It is therefore necessary to manually raise the transaction isolation level.
70
+
55
71
  The highest level of transaction isolation is called "serializable" and that's what we need here:
56
72
 
57
- class QueuedJob < ActiveRecord::Base
58
-
59
- # Job status
60
- TODO = 1
61
- PROCESSING = 2
62
- DONE = 3
63
-
64
- # Returns first job from the queue or nil if the queue is empty
65
- def pop
66
- QueuedJob.isolation_level( :serializable ) do
67
- QueuedJob.transaction do
68
- queued_job = find_by_status( TODO )
69
- if queud_job
70
- queued_job.update_attribute( :status, PROCESSING )
71
- return queued_job
72
- else
73
- return nil
74
- end
75
- end
73
+ ```ruby
74
+ class QueuedJob < ActiveRecord::Base
75
+ # Job status
76
+ TODO = 1
77
+ PROCESSING = 2
78
+ DONE = 3
79
+
80
+ # Returns first job from the queue or nil if the queue is empty
81
+ def pop
82
+ QueuedJob.isolation_level(:serializable) do
83
+ QueuedJob.transaction do
84
+ queued_job = find_by_status(TODO)
85
+ if queud_job
86
+ queued_job.update_attribute(:status, PROCESSING)
87
+ return queued_job
88
+ else
89
+ return nil
76
90
  end
77
- rescue ActiveRecord::TransactionIsolationConflict => e
78
- logger.warn( e.message )
79
- retry
80
91
  end
81
-
82
92
  end
93
+ rescue ActiveRecord::TransactionIsolationConflict => e
94
+ logger.warn(e.message)
95
+ retry
96
+ end
97
+ end
98
+ ```
83
99
 
84
100
  [Read more about isolation levels in Wikipedia](http://tinyurl.com/nrqjbb)
85
101
 
86
- ## Requirements
87
-
88
- * Ruby 1.9.2
89
- * ActiveRecord 3.0.11+
90
-
91
102
  ## Running tests
92
103
 
93
104
  Run tests on the selected database (mysql2 by default):
94
105
 
95
- db=mysql2 bundle exec rake test
96
- db=postgresql bundle exec rake test
97
- db=sqlite3 bundle exec rake test
106
+ ```bash
107
+ # passing desired database, active record version and ruby version
108
+ docker compose run -e db=sqlite3 -e BUNDLE_GEMFILE=gemfiles/activerecord-7.0/Gemfile.sqlite3 ruby_2_7 bash -c ./docker/test-ruby.sh
109
+
110
+ # db options: mysql2, postgresql, sqlite3
111
+ # active record version options: 5.2, 6.0, 6.1, 7.0
112
+ # ruby version options: 2.5, 2.7, 3.0, 3.1
113
+ ```
98
114
 
99
- Run tests on all supported databases:
115
+ Run tests on all supported databases by ruby version:
100
116
 
101
- ./tests
117
+ ```bash
118
+ docker compose up ruby_2_5
119
+ docker compose up ruby_2_7
120
+ docker compose up ruby_3_0
121
+ docker compose up ruby_3_1
122
+ ```
102
123
 
103
124
  Database configuration is hardcoded in test/db/db.rb; feel free to improve this and submit a pull request.
104
125
 
@@ -0,0 +1,6 @@
1
+ FROM ruby:2.5-slim
2
+
3
+ RUN apt-get update &&\
4
+ apt-get install -y build-essential git sqlite3 libsqlite3-dev mariadb-client libmariadb-dev postgresql-client libpq-dev &&\
5
+ apt-get clean &&\
6
+ rm -rf /var/lib/apt/lists/*
@@ -0,0 +1,6 @@
1
+ FROM ruby:2.7-slim
2
+
3
+ RUN apt-get update &&\
4
+ apt-get install -y build-essential git sqlite3 libsqlite3-dev mariadb-client libmariadb-dev postgresql-client libpq-dev &&\
5
+ apt-get clean &&\
6
+ rm -rf /var/lib/apt/lists/*
@@ -0,0 +1,6 @@
1
+ FROM ruby:3.0-slim
2
+
3
+ RUN apt-get update &&\
4
+ apt-get install -y build-essential git sqlite3 libsqlite3-dev mariadb-client libmariadb-dev postgresql-client libpq-dev &&\
5
+ apt-get clean &&\
6
+ rm -rf /var/lib/apt/lists/*
@@ -0,0 +1,6 @@
1
+ FROM ruby:3.0-slim
2
+
3
+ RUN apt-get update &&\
4
+ apt-get install -y build-essential git sqlite3 libsqlite3-dev mariadb-client libmariadb-dev postgresql-client libpq-dev &&\
5
+ apt-get clean &&\
6
+ rm -rf /var/lib/apt/lists/*
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # fix conflicts between different ruby versions
4
+ rm -rf gemfiles/**/Gemfile.*.lock
5
+
6
+ gemfiles=(
7
+ "gemfiles/activerecord-5.2/Gemfile.sqlite3"
8
+ "gemfiles/activerecord-6.0/Gemfile.sqlite3"
9
+ "gemfiles/activerecord-6.1/Gemfile.sqlite3"
10
+
11
+ "gemfiles/activerecord-5.2/Gemfile.mysql2"
12
+ "gemfiles/activerecord-6.0/Gemfile.mysql2"
13
+ "gemfiles/activerecord-6.1/Gemfile.mysql2"
14
+
15
+ "gemfiles/activerecord-5.2/Gemfile.postgresql"
16
+ "gemfiles/activerecord-6.0/Gemfile.postgresql"
17
+ "gemfiles/activerecord-6.1/Gemfile.postgresql"
18
+ )
19
+
20
+ for gemfile in "${gemfiles[@]}"; do
21
+ database=$(echo $gemfile | cut -d'.' -f3)
22
+
23
+ echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
24
+ BUNDLE_GEMFILE=$gemfile bundle install
25
+ BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
26
+ done
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # fix conflicts between different ruby versions
4
+ rm -rf gemfiles/**/Gemfile.*.lock
5
+
6
+ gemfiles=(
7
+ "gemfiles/activerecord-5.2/Gemfile.sqlite3"
8
+ "gemfiles/activerecord-6.0/Gemfile.sqlite3"
9
+ "gemfiles/activerecord-6.1/Gemfile.sqlite3"
10
+ "gemfiles/activerecord-7.0/Gemfile.sqlite3"
11
+
12
+ "gemfiles/activerecord-5.2/Gemfile.mysql2"
13
+ "gemfiles/activerecord-6.0/Gemfile.mysql2"
14
+ "gemfiles/activerecord-6.1/Gemfile.mysql2"
15
+ "gemfiles/activerecord-7.0/Gemfile.mysql2"
16
+
17
+ "gemfiles/activerecord-5.2/Gemfile.postgresql"
18
+ "gemfiles/activerecord-6.0/Gemfile.postgresql"
19
+ "gemfiles/activerecord-6.1/Gemfile.postgresql"
20
+ "gemfiles/activerecord-7.0/Gemfile.postgresql"
21
+ )
22
+
23
+ for gemfile in "${gemfiles[@]}"; do
24
+ database=$(echo $gemfile | cut -d'.' -f3)
25
+
26
+ echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
27
+ BUNDLE_GEMFILE=$gemfile bundle install
28
+ BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
29
+ done
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # fix conflicts between different ruby versions
4
+ rm -rf gemfiles/**/Gemfile.*.lock
5
+
6
+ gemfiles=(
7
+ "gemfiles/activerecord-6.0/Gemfile.sqlite3"
8
+ "gemfiles/activerecord-6.1/Gemfile.sqlite3"
9
+ "gemfiles/activerecord-7.0/Gemfile.sqlite3"
10
+
11
+ "gemfiles/activerecord-6.0/Gemfile.mysql2"
12
+ "gemfiles/activerecord-6.1/Gemfile.mysql2"
13
+ "gemfiles/activerecord-7.0/Gemfile.mysql2"
14
+
15
+ "gemfiles/activerecord-6.0/Gemfile.postgresql"
16
+ "gemfiles/activerecord-6.1/Gemfile.postgresql"
17
+ "gemfiles/activerecord-7.0/Gemfile.postgresql"
18
+ )
19
+
20
+ for gemfile in "${gemfiles[@]}"; do
21
+ database=$(echo $gemfile | cut -d'.' -f3)
22
+
23
+ echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
24
+ BUNDLE_GEMFILE=$gemfile bundle install
25
+ BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
26
+ done
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # fix conflicts between different ruby versions
4
+ rm -rf gemfiles/**/Gemfile.*.lock
5
+
6
+ gemfiles=(
7
+ "gemfiles/activerecord-6.0/Gemfile.sqlite3"
8
+ "gemfiles/activerecord-6.1/Gemfile.sqlite3"
9
+ "gemfiles/activerecord-7.0/Gemfile.sqlite3"
10
+
11
+ "gemfiles/activerecord-6.0/Gemfile.mysql2"
12
+ "gemfiles/activerecord-6.1/Gemfile.mysql2"
13
+ "gemfiles/activerecord-7.0/Gemfile.mysql2"
14
+
15
+ "gemfiles/activerecord-6.0/Gemfile.postgresql"
16
+ "gemfiles/activerecord-6.1/Gemfile.postgresql"
17
+ "gemfiles/activerecord-7.0/Gemfile.postgresql"
18
+ )
19
+
20
+ for gemfile in "${gemfiles[@]}"; do
21
+ database=$(echo $gemfile | cut -d'.' -f3)
22
+
23
+ echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
24
+ BUNDLE_GEMFILE=$gemfile bundle install
25
+ BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
26
+ done
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # fix conflicts between different ruby versions
4
+ rm -rf gemfiles/**/Gemfile.*.lock
5
+
6
+ echo BUNDLE_GEMFILE=$BUNDLE_GEMFILE db=$db bundle exec rake test
7
+ BUNDLE_GEMFILE=$BUNDLE_GEMFILE bundle install
8
+ BUNDLE_GEMFILE=$BUNDLE_GEMFILE db=$db bundle exec rake test
@@ -0,0 +1,60 @@
1
+ services:
2
+ ruby_2_5: &ruby_2_5
3
+ working_dir: /app
4
+ build:
5
+ context: .
6
+ dockerfile: docker/ruby-2.5/Dockerfile
7
+ volumes:
8
+ - .:/app
9
+ command: bash -c "bundle install && bundle exec rake test"
10
+ environment:
11
+ POSTGRESQL_DB_HOST: postgres
12
+ POSTGRESQL_DB_USER: transaction_retry_continued
13
+ POSTGRESQL_DB_PASS: database
14
+ POSTGRESQL_DB_NAME: transaction_isolation_continued_test
15
+ MYSQL_DB_HOST: mysql
16
+ MYSQL_DB_USER: root
17
+ MYSQL_DB_PASS: database
18
+ MYSQL_DB_NAME: transaction_isolation_continued_test
19
+ depends_on:
20
+ - mysql
21
+ - postgres
22
+ ruby_2_7:
23
+ <<: *ruby_2_5
24
+ build:
25
+ context: .
26
+ dockerfile: docker/ruby-2.7/Dockerfile
27
+ ruby_3_0:
28
+ <<: *ruby_2_5
29
+ command: ./docker/test-ruby-3.0.sh
30
+ build:
31
+ context: .
32
+ dockerfile: docker/ruby-3.0/Dockerfile
33
+ ruby_3_1:
34
+ <<: *ruby_2_5
35
+ command: ./docker/test-ruby-3.1.sh
36
+ build:
37
+ context: .
38
+ dockerfile: docker/ruby-3.1/Dockerfile
39
+ postgres:
40
+ image: postgres:9.6
41
+ environment:
42
+ POSTGRES_USER: transaction_retry_continued
43
+ POSTGRES_PASSWORD: database
44
+ POSTGRES_DB: transaction_isolation_continued_test
45
+ ports:
46
+ - "5432:5432"
47
+ healthcheck:
48
+ test: [ "CMD-SHELL", "pg_isready -q" ]
49
+ interval: 5s
50
+ timeout: 5s
51
+ retries: 5
52
+ mysql:
53
+ image: biarms/mysql:5.7
54
+ environment:
55
+ MYSQL_ROOT_PASSWORD: database
56
+ MYSQL_DATABASE: transaction_isolation_continued_test
57
+ ports:
58
+ - "3306:3306"
59
+ healthcheck:
60
+ test: "mysqladmin ping --host=0.0.0.0 --password=database --silent"
@@ -3,4 +3,8 @@ gemspec path: File.expand_path('..', __FILE__)
3
3
 
4
4
  gem 'ruby2_keywords' if RUBY_VERSION < '2.7'
5
5
 
6
+ group :test do
7
+ gem 'simplecov'
8
+ end
9
+
6
10
  File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local
@@ -2,7 +2,7 @@ base_gemfile = File.expand_path('../Gemfile.base', __FILE__)
2
2
  eval File.read(base_gemfile), binding, base_gemfile
3
3
 
4
4
  platform :ruby do
5
- gem "sqlite3", "~> 1.4"
5
+ gem "sqlite3", "~> 1.4.0"
6
6
  end
7
7
 
8
8
  platform :jruby do
@@ -2,7 +2,7 @@ base_gemfile = File.expand_path('../Gemfile.base', __FILE__)
2
2
  eval File.read(base_gemfile), binding, base_gemfile
3
3
 
4
4
  platform :ruby do
5
- gem "sqlite3", "~> 1.4"
5
+ gem "sqlite3", "~> 1.4.0"
6
6
  end
7
7
 
8
8
  platform :jruby do
@@ -2,7 +2,7 @@ base_gemfile = File.expand_path('../Gemfile.base', __FILE__)
2
2
  eval File.read(base_gemfile), binding, base_gemfile
3
3
 
4
4
  platform :ruby do
5
- gem "sqlite3", "~> 1.4"
5
+ gem "sqlite3", "~> 1.4.0"
6
6
  end
7
7
 
8
8
  platform :jruby do
@@ -3,7 +3,7 @@ eval File.read(base_gemfile), binding, base_gemfile
3
3
 
4
4
  platform :ruby do
5
5
  # https://github.com/rails/rails/pull/51592
6
- gem "sqlite3", "~> 1.4"
6
+ gem "sqlite3", "~> 1.4.0"
7
7
  end
8
8
 
9
9
  platform :jruby do
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/base'
2
4
 
3
5
  module TransactionIsolation
4
6
  module ActiveRecord
5
7
  module Base
6
- def isolation_level( isolation_level, &block )
7
- connection.isolation_level( isolation_level, &block )
8
+ def isolation_level(isolation_level, &block)
9
+ connection.isolation_level(isolation_level, &block)
8
10
  end
9
11
  end
10
12
  end
11
13
  end
12
14
 
13
- ActiveRecord::Base.extend( TransactionIsolation::ActiveRecord::Base )
15
+ ActiveRecord::Base.extend(TransactionIsolation::ActiveRecord::Base)
@@ -1,32 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/connection_adapters/abstract_adapter'
2
4
 
3
5
  module TransactionIsolation
4
6
  module ActiveRecord
5
7
  module ConnectionAdapters # :nodoc:
6
8
  module AbstractAdapter
7
-
8
- VALID_ISOLATION_LEVELS = [:read_uncommitted, :read_committed, :repeatable_read, :serializable]
9
+ VALID_ISOLATION_LEVELS = %i[read_uncommitted read_committed repeatable_read serializable].freeze
9
10
 
10
11
  # If true, #isolation_level(level) method is available
11
12
  def supports_isolation_levels?
12
13
  false
13
14
  end
14
15
 
15
- def isolation_level( level )
16
+ def isolation_level(level)
16
17
  raise NotImplementedError
17
18
  end
18
19
 
19
20
  private
20
21
 
21
- def validate_isolation_level( isolation_level )
22
- unless VALID_ISOLATION_LEVELS.include?( isolation_level )
23
- raise ArgumentError, "Invalid isolation level '#{isolation_level}'. Supported levels include #{VALID_ISOLATION_LEVELS.join( ', ' )}."
24
- end
25
- end
22
+ def validate_isolation_level(isolation_level)
23
+ return if VALID_ISOLATION_LEVELS.include?(isolation_level)
26
24
 
25
+ raise ArgumentError,
26
+ "Invalid isolation level '#{isolation_level}'. Supported levels include #{VALID_ISOLATION_LEVELS.join(', ')}."
27
+ end
27
28
  end
28
29
  end
29
30
  end
30
31
  end
31
32
 
32
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::AbstractAdapter )
33
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.include TransactionIsolation::ActiveRecord::ConnectionAdapters::AbstractAdapter