transaction_isolation_continued 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.github/workflows/main.yml +35 -20
  4. data/.gitignore +3 -0
  5. data/README.md +77 -56
  6. data/docker/ruby-2.5/Dockerfile +6 -0
  7. data/docker/ruby-2.7/Dockerfile +6 -0
  8. data/docker/ruby-3.0/Dockerfile +6 -0
  9. data/docker/ruby-3.1/Dockerfile +6 -0
  10. data/docker/test-ruby-2.5.sh +26 -0
  11. data/docker/test-ruby-2.7.sh +29 -0
  12. data/docker/test-ruby-3.0.sh +26 -0
  13. data/docker/test-ruby-3.1.sh +26 -0
  14. data/docker/test-ruby.sh +8 -0
  15. data/docker-compose.yml +60 -0
  16. data/gemfiles/Gemfile.base +4 -0
  17. data/gemfiles/activerecord-5.2/Gemfile.sqlite3 +1 -1
  18. data/gemfiles/activerecord-6.0/Gemfile.sqlite3 +1 -1
  19. data/gemfiles/activerecord-6.1/Gemfile.sqlite3 +1 -1
  20. data/gemfiles/activerecord-7.0/Gemfile.sqlite3 +1 -1
  21. data/lib/transaction_isolation/active_record/base.rb +5 -3
  22. data/lib/transaction_isolation/active_record/connection_adapters/abstract_adapter.rb +10 -9
  23. data/lib/transaction_isolation/active_record/connection_adapters/mysql2_adapter.rb +29 -27
  24. data/lib/transaction_isolation/active_record/connection_adapters/postgresql_adapter.rb +26 -24
  25. data/lib/transaction_isolation/active_record/connection_adapters/sqlite3_adapter.rb +24 -21
  26. data/lib/transaction_isolation/active_record/errors.rb +2 -0
  27. data/lib/transaction_isolation/configuration.rb +6 -4
  28. data/lib/transaction_isolation/version.rb +3 -1
  29. data/lib/transaction_isolation.rb +9 -9
  30. data/test/db/all.rb +2 -0
  31. data/test/db/db.rb +15 -15
  32. data/test/db/migrations.rb +6 -7
  33. data/test/db/queued_job.rb +2 -0
  34. data/test/integration/active_record/base/isolation_level_test.rb +3 -9
  35. data/test/integration/active_record/connection_adapters/any_adapter/current_isolation_level_test.rb +8 -16
  36. data/test/integration/active_record/connection_adapters/any_adapter/current_vendor_isolation_level_test.rb +8 -16
  37. data/test/integration/active_record/connection_adapters/any_adapter/isolation_level_test.rb +18 -29
  38. data/test/integration/active_record/connection_adapters/any_adapter/supports_isolation_levels_test.rb +2 -10
  39. data/test/integration/active_record/connection_adapters/any_adapter/translate_exception_test.rb +20 -26
  40. data/test/library_setup.rb +11 -9
  41. data/test/test_helper.rb +9 -6
  42. data/test/test_runner.rb +3 -2
  43. data/transaction_isolation_continued.gemspec +14 -14
  44. metadata +25 -19
  45. data/Gemfile.local +0 -18
  46. data/d +0 -1
  47. 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: b85d83da9e7e46096a510a63ebd92c58941515d208e394d5f0633a327d5d2efe
4
+ data.tar.gz: 4507ff405e9706bcd57e9aa94d203ca19c2e154ded14beb398bd6d9b6dc3f4e6
5
5
  SHA512:
6
- metadata.gz: 56a421307a4fd2a8c66bfa35e9189b99c160cd8db82fc5103a5d6c7ead76a0a8699cc3f2f089d79c521fa0d8b475129530d7aeaab7c9e4ac77f714f6dc550cdd
7
- data.tar.gz: 66f6c3dd3ee7a0fd8ecad4292cfdf13ae563dcd5afdacada47030bbdd93c5c11917f3fac07caa62ec67b466a30677d95b7595d96c5fd384021f9fccc7290458e
6
+ metadata.gz: e58e5fd934a0a3ab519e43f44cc825f53d46195d9363e189dd62e53f8be9cd671e0d0d308f6bb16bb207bdc69f0fa772d49b7bb2dc1c9d7e7cf63243940c74be
7
+ data.tar.gz: 26a59f350392c3f46372220caff5f9fc799a84c73b91cf0b01e41cb561dc22710436a476ca9be81aad046f84078f9a11a7c981e1c10fe4ca79cdfac8d6517bf5
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
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
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, 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_5 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