async_mysql2 0.1.0.colin.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9a315a09f613c5993e8d96c86e345c9f653a5ba7f72a7582783b3fbef08bd953
4
+ data.tar.gz: fd05519ae72f8a9882a7b8306a4ca68ed6e4cd9c49c42fe46fcb093a19892806
5
+ SHA512:
6
+ metadata.gz: a9cc434c0d9024605687ee7d37682721ef52d9d995ba8d6ba3ea60b0de724313fcf1cba77200a4b5bb50bb601d43af38364466512621f6c15ce95c04cf3e67c4
7
+ data.tar.gz: 1e3c6efaa364f81106a14c5660b99fff67c27dbfefb2cf1a09be6a12e5447cd44aa1f72879b973b6b0446aee7817269f3f502f5d4882836f80765bb6d734309b
@@ -0,0 +1 @@
1
+ * @Invoca/octothorpe
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: FiberedMySQL2 Gem Build
3
+ on: [push, pull_request]
4
+ jobs:
5
+ test:
6
+ name: Unit Tests
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [2.5, 2.6, 2.7, '3.0', 3.1]
12
+ gemfile:
13
+ - Gemfile
14
+ - gemfiles/rails_5.gemfile
15
+ - gemfiles/rails_6.gemfile
16
+ env:
17
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ bundler: 2.2.29
24
+ bundler-cache: true
25
+ - run: bundle exec rspec
@@ -0,0 +1,38 @@
1
+ ---
2
+ on:
3
+ push:
4
+ tags:
5
+ - 'v*'
6
+ - '!v*.pre*'
7
+
8
+ name: Create Release
9
+
10
+ jobs:
11
+ build:
12
+ name: Create Release
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Get version from tag
16
+ id: tag_name
17
+ shell: bash
18
+ run: |
19
+ echo ::set-output name=current_version::${GITHUB_REF#refs/tags/v}
20
+ - name: Checkout code
21
+ uses: actions/checkout@v2
22
+ - name: Get Changelog Entry
23
+ id: changelog_reader
24
+ uses: mindsers/changelog-reader-action@v1
25
+ with:
26
+ version: ${{ steps.tag_name.outputs.current_version }}
27
+ path: ./CHANGELOG.md
28
+ - name: Create Release
29
+ id: create_release
30
+ uses: actions/create-release@v1
31
+ env:
32
+ GITHUB_TOKEN: ${{ secrets.GEM_RELEASE_GIT_TOKEN }}
33
+ with:
34
+ tag_name: ${{ github.ref }}
35
+ release_name: Release ${{ github.ref }}
36
+ body: ${{ steps.changelog_reader.outputs.log_entry }}
37
+ draft: false
38
+ prerelease: false
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /gemfiles/*.lock
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.1
data/Appraisals ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'rails-5' do
4
+ gem 'rails', '~> 5.2'
5
+ end
6
+
7
+ appraise 'rails-6' do
8
+ gem 'rails', '~> 6.0.0'
9
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # CHANGELOG for `async_mysql2` (was `fibered_mysql2`)
2
+
3
+ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
+
5
+ Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - Unreleased
8
+ ### Changed
9
+ - Renamed to `async_mysql2`.
10
+ - Dropped support for `EM`, `EM::Synchrony` in favor of Ruby 3.2 and `Async`.
11
+
12
+ ## [0.2.0] - 2023-01-12
13
+ ### Added
14
+ - Added support for Rails 6+ by adding knowledge of lazy transactions to the adapter.
15
+
16
+ ## [0.1.5] - 2022-03-25
17
+ ### Changed
18
+ - Upgraded Bundler to 2.2.29 and Ruby to 2.7.5. Removed support for Rails 4.
19
+ - Modified AsyncMysql2::FiberedConditionVariable class to ensure compatibility with newer Ruby versions.
20
+
21
+ ## [0.1.4] - 2021-06-25
22
+ ### Fixed
23
+ - Disable the ConnectionPool::Reaper in Rails 5 and 6 as it was in 4. This is important since it is
24
+ threaded, not fibered.
25
+
26
+ ## [0.1.3] - 2021-06-17
27
+ ### Fixed
28
+ - When checking that @owner is a Fiber, allow nil.
29
+
30
+ ## [0.1.2] - 2021-06-16
31
+ ### Fixed
32
+ - Added checking to be certain that @owner is never overwritten with a non-Fiber by another mixin.
33
+
34
+ ## [0.1.1] - 2021-02-12
35
+ ### Fixed
36
+ - Fixed bug with Rails 5+ adapter where connections that have `steal!` called on them were not having their owner updated to the current Fiber, which would then cause an exception when trying to expire the connection (this showed up with the Rails 5 `ConnectionPool::Reaper` that reaps unused connections)
37
+
38
+ ### Changed
39
+ - Updated Rails 6 dependency to 6.0.x for now as 6.1+ requires a newer version of the mysql gem (0.5+) that we do not yet support
40
+
41
+
42
+ ## [0.1.0] - 2020-10-23
43
+ ### Added
44
+ - Added an adapter for Rails 4, 5, and 6.
45
+ - Added appraisals for Rails 4, 5, and 6.
46
+ - Added TravisCI unit test pipeline.
47
+ - Added coverage reports via Coveralls.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in async_mysql2.gemspec
6
+ gemspec
7
+
8
+ gem 'appraisal'
9
+ gem 'coveralls', require: false
10
+ gem 'mysql2', '~> 0.5'
11
+ gem 'nokogiri', '< 1.13'
12
+ gem 'pry', '~> 0.13'
13
+ gem 'pry-byebug', '~> 3.9'
14
+ gem 'rails', '< 6.1'
15
+ gem 'rake', '~> 13.0'
16
+ gem 'rspec', '~> 3.0'
data/Gemfile.lock ADDED
@@ -0,0 +1,209 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ async_mysql2 (0.1.0.colin.6)
5
+ async
6
+ rails (>= 5.2, < 7)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.0.3.4)
12
+ actionpack (= 6.0.3.4)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (6.0.3.4)
16
+ actionpack (= 6.0.3.4)
17
+ activejob (= 6.0.3.4)
18
+ activerecord (= 6.0.3.4)
19
+ activestorage (= 6.0.3.4)
20
+ activesupport (= 6.0.3.4)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.3.4)
23
+ actionpack (= 6.0.3.4)
24
+ actionview (= 6.0.3.4)
25
+ activejob (= 6.0.3.4)
26
+ mail (~> 2.5, >= 2.5.4)
27
+ rails-dom-testing (~> 2.0)
28
+ actionpack (6.0.3.4)
29
+ actionview (= 6.0.3.4)
30
+ activesupport (= 6.0.3.4)
31
+ rack (~> 2.0, >= 2.0.8)
32
+ rack-test (>= 0.6.3)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.3.4)
36
+ actionpack (= 6.0.3.4)
37
+ activerecord (= 6.0.3.4)
38
+ activestorage (= 6.0.3.4)
39
+ activesupport (= 6.0.3.4)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.3.4)
42
+ activesupport (= 6.0.3.4)
43
+ builder (~> 3.1)
44
+ erubi (~> 1.4)
45
+ rails-dom-testing (~> 2.0)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.3.4)
48
+ activesupport (= 6.0.3.4)
49
+ globalid (>= 0.3.6)
50
+ activemodel (6.0.3.4)
51
+ activesupport (= 6.0.3.4)
52
+ activerecord (6.0.3.4)
53
+ activemodel (= 6.0.3.4)
54
+ activesupport (= 6.0.3.4)
55
+ activestorage (6.0.3.4)
56
+ actionpack (= 6.0.3.4)
57
+ activejob (= 6.0.3.4)
58
+ activerecord (= 6.0.3.4)
59
+ marcel (~> 0.3.1)
60
+ activesupport (6.0.3.4)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.2, >= 2.2.2)
66
+ appraisal (2.4.0)
67
+ bundler
68
+ rake
69
+ thor (>= 0.14.0)
70
+ async (2.5.1)
71
+ console (~> 1.10)
72
+ io-event (~> 1.1)
73
+ timers (~> 4.1)
74
+ builder (3.2.4)
75
+ byebug (11.1.3)
76
+ coderay (1.1.3)
77
+ concurrent-ruby (1.1.8)
78
+ console (1.16.2)
79
+ fiber-local
80
+ coveralls (0.8.23)
81
+ json (>= 1.8, < 3)
82
+ simplecov (~> 0.16.1)
83
+ term-ansicolor (~> 1.3)
84
+ thor (>= 0.19.4, < 2.0)
85
+ tins (~> 1.6)
86
+ crass (1.0.6)
87
+ diff-lcs (1.4.4)
88
+ docile (1.3.2)
89
+ erubi (1.10.0)
90
+ fiber-local (1.0.0)
91
+ globalid (0.4.2)
92
+ activesupport (>= 4.2.0)
93
+ i18n (1.8.7)
94
+ concurrent-ruby (~> 1.0)
95
+ io-event (1.2.2)
96
+ json (2.3.1)
97
+ loofah (2.8.0)
98
+ crass (~> 1.0.2)
99
+ nokogiri (>= 1.5.9)
100
+ mail (2.7.1)
101
+ mini_mime (>= 0.1.1)
102
+ marcel (0.3.3)
103
+ mimemagic (~> 0.3.2)
104
+ method_source (1.0.0)
105
+ mimemagic (0.3.10)
106
+ nokogiri (~> 1)
107
+ rake
108
+ mini_mime (1.0.2)
109
+ mini_portile2 (2.6.1)
110
+ minitest (5.14.3)
111
+ mysql2 (0.5.5)
112
+ nio4r (2.5.4)
113
+ nokogiri (1.12.5)
114
+ mini_portile2 (~> 2.6.1)
115
+ racc (~> 1.4)
116
+ pry (0.13.1)
117
+ coderay (~> 1.1)
118
+ method_source (~> 1.0)
119
+ pry-byebug (3.9.0)
120
+ byebug (~> 11.0)
121
+ pry (~> 0.13.0)
122
+ racc (1.6.0)
123
+ rack (2.2.3)
124
+ rack-test (1.1.0)
125
+ rack (>= 1.0, < 3)
126
+ rails (6.0.3.4)
127
+ actioncable (= 6.0.3.4)
128
+ actionmailbox (= 6.0.3.4)
129
+ actionmailer (= 6.0.3.4)
130
+ actionpack (= 6.0.3.4)
131
+ actiontext (= 6.0.3.4)
132
+ actionview (= 6.0.3.4)
133
+ activejob (= 6.0.3.4)
134
+ activemodel (= 6.0.3.4)
135
+ activerecord (= 6.0.3.4)
136
+ activestorage (= 6.0.3.4)
137
+ activesupport (= 6.0.3.4)
138
+ bundler (>= 1.3.0)
139
+ railties (= 6.0.3.4)
140
+ sprockets-rails (>= 2.0.0)
141
+ rails-dom-testing (2.0.3)
142
+ activesupport (>= 4.2.0)
143
+ nokogiri (>= 1.6)
144
+ rails-html-sanitizer (1.3.0)
145
+ loofah (~> 2.3)
146
+ railties (6.0.3.4)
147
+ actionpack (= 6.0.3.4)
148
+ activesupport (= 6.0.3.4)
149
+ method_source
150
+ rake (>= 0.8.7)
151
+ thor (>= 0.20.3, < 2.0)
152
+ rake (13.0.6)
153
+ rspec (3.9.0)
154
+ rspec-core (~> 3.9.0)
155
+ rspec-expectations (~> 3.9.0)
156
+ rspec-mocks (~> 3.9.0)
157
+ rspec-core (3.9.3)
158
+ rspec-support (~> 3.9.3)
159
+ rspec-expectations (3.9.2)
160
+ diff-lcs (>= 1.2.0, < 2.0)
161
+ rspec-support (~> 3.9.0)
162
+ rspec-mocks (3.9.1)
163
+ diff-lcs (>= 1.2.0, < 2.0)
164
+ rspec-support (~> 3.9.0)
165
+ rspec-support (3.9.3)
166
+ simplecov (0.16.1)
167
+ docile (~> 1.1)
168
+ json (>= 1.8, < 3)
169
+ simplecov-html (~> 0.10.0)
170
+ simplecov-html (0.10.2)
171
+ sprockets (4.0.2)
172
+ concurrent-ruby (~> 1.0)
173
+ rack (> 1, < 3)
174
+ sprockets-rails (3.2.2)
175
+ actionpack (>= 4.0)
176
+ activesupport (>= 4.0)
177
+ sprockets (>= 3.0.0)
178
+ sync (0.5.0)
179
+ term-ansicolor (1.7.1)
180
+ tins (~> 1.0)
181
+ thor (1.0.1)
182
+ thread_safe (0.3.6)
183
+ timers (4.3.5)
184
+ tins (1.25.0)
185
+ sync
186
+ tzinfo (1.2.9)
187
+ thread_safe (~> 0.1)
188
+ websocket-driver (0.7.3)
189
+ websocket-extensions (>= 0.1.0)
190
+ websocket-extensions (0.1.5)
191
+ zeitwerk (2.4.1)
192
+
193
+ PLATFORMS
194
+ ruby
195
+
196
+ DEPENDENCIES
197
+ appraisal
198
+ async_mysql2!
199
+ coveralls
200
+ mysql2 (~> 0.5)
201
+ nokogiri (< 1.13)
202
+ pry (~> 0.13)
203
+ pry-byebug (~> 3.9)
204
+ rails (< 6.1)
205
+ rake (~> 13.0)
206
+ rspec (~> 3.0)
207
+
208
+ BUNDLED WITH
209
+ 2.2.29
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ [![Coverage Status](https://coveralls.io/repos/github/Invoca/fibered_mysql2/badge.svg?branch=master)](https://coveralls.io/github/Invoca/fibered_mysql2?branch=master)
2
+
3
+ # AsyncMysql2
4
+
5
+ AsyncMysql2 adds `Async::Task` support to `ActiveRecord::ConnectionAdapters::Mysql2Adapter`
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'async_mysql2'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install fibered_mysql2
22
+
23
+ ## Support
24
+ Tested with Rails versions 6.0.
25
+
26
+ ## Usage
27
+
28
+ Behaves the same as `ActiveRecord::ConnectionAdapters::Mysql2Adapter` but with added Async::Task safety while leasing/expiring connections.
29
+ ```ruby
30
+ connection = AsyncMysql2::AsyncMysql2Adapter.new(client, logger, options, config)
31
+ connection.lease
32
+ connection.expire
33
+ ```
34
+
35
+ ## Development
36
+
37
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
+
39
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/invoca/fibered_mysql2.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "async_mysql2/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "async_mysql2"
9
+ spec.version = AsyncMysql2::VERSION
10
+ spec.authors = ["Invoca Development"]
11
+ spec.email = ["development@invoca.com"]
12
+
13
+ spec.summary = "An adapter for mysql2 running on Async"
14
+ spec.homepage = "https://github.com/Invoca/fibered_mysql2"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ spec.metadata = {
19
+ "allowed_push_host" => "https://rubygems.org",
20
+ "homepage_uri" => spec.homepage
21
+ }
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency 'async'
33
+ spec.add_dependency 'rails', '>= 5.2', '< 7'
34
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "async_mysql2"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "coveralls", require: false
7
+ gem "mysql2", "~> 0.5"
8
+ gem "nokogiri", "< 1.13"
9
+ gem "pry", "~> 0.13"
10
+ gem "pry-byebug", "~> 3.9"
11
+ gem "rails", "~> 5.2"
12
+ gem "rake", "~> 13.0"
13
+ gem "rspec", "~> 3.0"
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "coveralls", require: false
7
+ gem "mysql2", "~> 0.5"
8
+ gem "nokogiri", "< 1.13"
9
+ gem "pry", "~> 0.13"
10
+ gem "pry-byebug", "~> 3.9"
11
+ gem "rails", "~> 6.0.0"
12
+ gem "rake", "~> 13.0"
13
+ gem "rspec", "~> 3.0"
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+ require 'active_record/errors'
5
+ require 'active_record/connection_adapters/mysql2_adapter'
6
+ require 'async_mysql2/async_task'
7
+
8
+ module AsyncMysql2
9
+ module Adapter_6
10
+ def lease
11
+ if (ot = owner_task)
12
+ msg = +"Cannot lease connection; "
13
+ if ot == (current_task = AsyncTask.current_or_none)
14
+ msg << "it is already leased by the current Async::Task."
15
+ else
16
+ msg << "it is already in use by a different Async::Task: #{ot}. " \
17
+ "Current Async::Task: #{current_task}."
18
+ end
19
+ raise ::ActiveRecord::ActiveRecordError, msg
20
+ end
21
+
22
+ @owner = AsyncTask.current_or_none
23
+ end
24
+
25
+ def expire
26
+ if (ot = owner_task)
27
+ # Because we are actively releasing connections from dead tasks, we only want
28
+ # to enforce that we're expiring the current task's connection, iff the owner
29
+ # of the connection is still alive.
30
+ if ot.alive? && ot != (current_task = AsyncTask.current_or_none)
31
+ raise ::ActiveRecord::ActiveRecordError, "Cannot expire connection; " \
32
+ "it is owned by a different Async::Task: #{ot}. " \
33
+ "Current Async::Task: #{current_task}."
34
+ end
35
+
36
+ @idle_since = ::Concurrent.monotonic_time
37
+ @owner = nil
38
+ else
39
+ raise ::ActiveRecord::ActiveRecordError, "Cannot expire connection; it is not currently leased."
40
+ end
41
+ end
42
+
43
+ def steal!
44
+ if (ot = owner_task)
45
+ if ot != (current_task = AsyncTask.current_or_none)
46
+ pool.send :remove_connection_from_thread_cache, self, ot
47
+
48
+ @owner = current_task
49
+ end
50
+ else
51
+ raise ::ActiveRecord::ActiveRecordError, "Cannot steal connection; it is not currently leased."
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def owner_task
58
+ @owner.nil? || @owner == AsyncTask::NoTaskPlaceholder || @owner.is_a?(Async::Task) or
59
+ raise "@owner must be an Async::Task or FiberedMysql2::AsyncTask::NoTaskPlaceholder! Found #{@owner.inspect}"
60
+ @owner
61
+ end
62
+ end
63
+
64
+ class AsyncMysql2Adapter < ::ActiveRecord::ConnectionAdapters::Mysql2Adapter
65
+ case ::Rails::VERSION::MAJOR
66
+ when 6
67
+ include Adapter_6
68
+ else
69
+ raise ArgumentError, "unexpected Rails version #{Rails::VERSION::MAJOR}"
70
+ end
71
+
72
+ def initialize(*args)
73
+ super
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AsyncMysql2
4
+ module AsyncTask
5
+ class NoTaskPlaceholder
6
+ class << self
7
+ def alive? = true
8
+ end
9
+ end
10
+
11
+ class << self
12
+ # Adapted from https://github.com/socketry/async/blob/main/lib/async/task.rb#L236-L238
13
+ def current_or_none
14
+ Thread.current[:async_task] || NoTaskPlaceholder
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../active_record/connection_adapters/async_mysql2_adapter'
4
+
5
+ module AsyncMysql2
6
+ module ConnectionFactory
7
+ def async_mysql2_connection(raw_config)
8
+ config = raw_config.symbolize_keys
9
+
10
+ config[:username] = 'root' if config[:username].nil?
11
+ config[:flags] = Mysql2::Client::FOUND_ROWS if Mysql2::Client.const_defined?(:FOUND_ROWS)
12
+
13
+ client =
14
+ begin
15
+ Mysql2::Client.new(config)
16
+ rescue Mysql2::Error => error
17
+ if error.message.include?("Unknown database")
18
+ raise ActiveRecord::NoDatabaseError.new(error.message)
19
+ else
20
+ raise
21
+ end
22
+ end
23
+
24
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
25
+ AsyncMysql2Adapter.new(client, logger, options, config)
26
+ end
27
+ end
28
+ end
29
+
30
+ ActiveRecord::Base.class.prepend(AsyncMysql2::ConnectionFactory)
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class behaves the same as ActiveRecord's ConnectionPool, but synchronizes with Async::Task fibers rather than threads.
4
+
5
+ # Note - trace statements have been commented out. This is useful trace but we do not want it on by default.
6
+ # When we have configurable logging we can put this back and have it off by default.
7
+
8
+ module AsyncMysql2
9
+ module FiberedDatabaseConnectionPool
10
+ case ::Rails::VERSION::MAJOR
11
+ when 6
12
+ else
13
+ raise ArgumentError, "unexpected Rails version #{Rails::VERSION::MAJOR}"
14
+ end
15
+
16
+ def cached_connections
17
+ @thread_cached_conns
18
+ end
19
+
20
+ def current_connection_id
21
+ connection_cache_key(current_thread)
22
+ end
23
+
24
+ def checkout(checkout_timeout = @checkout_timeout)
25
+ begin
26
+ reap_connections
27
+ rescue => ex
28
+ ActiveRecord::Base.logger.error("Exception occurred while executing reap_connections: #{ex.class}: #{ex.message}")
29
+ end
30
+ super
31
+ end
32
+
33
+ def release_connection(owner_task = AsyncTask.current_or_none)
34
+ if (conn = @thread_cached_conns.delete(connection_cache_key(owner_task)))
35
+ checkin(conn)
36
+ end
37
+ end
38
+
39
+ def initialize(connection_spec, *args, **keyword_args)
40
+ connection_spec.config[:reaping_frequency] and raise "reaping_frequency is not supported (the ActiveRecord Reaper is thread-based)"
41
+ connection_spec.config[:reaping_frequency] = nil # starting in Rails 5, this defaults to 60 if not explicitly set
42
+
43
+ super(connection_spec, *args, **keyword_args)
44
+
45
+ @reaper = nil # no need to keep a reference to this since it does nothing in this sub-class
46
+
47
+ # note that @reserved_connections is a ThreadSafe::Cache which is overkill in a fibered world, but harmless
48
+ end
49
+
50
+ def connection
51
+ # this is correctly done double-checked locking
52
+ # (ThreadSafe::Cache's lookups have volatile semantics)
53
+ if (result = cached_connections[current_connection_id])
54
+ result
55
+ else
56
+ synchronize do
57
+ if (result = cached_connections[current_connection_id])
58
+ result
59
+ else
60
+ cached_connections[current_connection_id] = checkout
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def reap_connections
67
+ cached_connections.values.each do |connection|
68
+ unless connection.owner.alive?
69
+ checkin(connection)
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ #--
77
+ # This hook-in method allows for easier monkey-patching fixes needed by
78
+ # JRuby users that use Fibers.
79
+ def connection_cache_key(fiber)
80
+ fiber
81
+ end
82
+
83
+ def current_thread
84
+ AsyncTask.current_or_none
85
+ end
86
+ end
87
+ end
88
+
89
+ ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(AsyncMysql2::FiberedDatabaseConnectionPool)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AsyncMysql2
4
+ module FiberedMutexWithWaiterPriority
5
+ # Note: @waiters is a bit confusing because the first waiter is actually the current fiber that has it locked;
6
+ # the _rest_ of @waiters are the actual waiters
7
+ def sleep(timeout = nil)
8
+ unlock
9
+ beg = Time.now
10
+ current = Fiber.current
11
+ @slept[current] = true
12
+ if timeout
13
+ timer = EM.add_timer(timeout) do
14
+ _wakeup(current)
15
+ end
16
+ Fiber.yield
17
+ EM.cancel_timer(timer) # if we resumed not via timer
18
+ else
19
+ Fiber.yield
20
+ end
21
+ @slept.delete(current)
22
+ yield if block_given?
23
+
24
+ # Invoca patch: inline lock that puts us at the front of the mutex @waiters queue instead of the back
25
+ # ==========================
26
+ @waiters.unshift(current)
27
+ Fiber.yield if @waiters.size > 1
28
+ # ==========================
29
+
30
+ Time.now - beg
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AsyncMysql2
4
+ VERSION = "0.1.0.colin.6"
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'async'
4
+ require 'async_mysql2/version'
5
+ require_relative '../lib/active_record/connection_adapters/async_mysql2_adapter'
6
+ require 'async_mysql2/fibered_database_connection_pool'
7
+ require 'async_mysql2/fibered_mutex_with_waiter_priority'
8
+ require 'async_mysql2/connection_factory'
9
+
10
+ module AsyncMysql2
11
+ class Error < StandardError; end
12
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async_mysql2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.colin.6
5
+ platform: ruby
6
+ authors:
7
+ - Invoca Development
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: async
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5.2'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '7'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '5.2'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '7'
47
+ description:
48
+ email:
49
+ - development@invoca.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - ".github/CODEOWNERS"
55
+ - ".github/workflows/build.yml"
56
+ - ".github/workflows/release.yml"
57
+ - ".gitignore"
58
+ - ".rspec"
59
+ - ".ruby-version"
60
+ - Appraisals
61
+ - CHANGELOG.md
62
+ - Gemfile
63
+ - Gemfile.lock
64
+ - README.md
65
+ - Rakefile
66
+ - async_mysql2.gemspec
67
+ - bin/console
68
+ - bin/setup
69
+ - gemfiles/rails_5.gemfile
70
+ - gemfiles/rails_6.gemfile
71
+ - lib/active_record/connection_adapters/async_mysql2_adapter.rb
72
+ - lib/async_mysql2.rb
73
+ - lib/async_mysql2/async_task.rb
74
+ - lib/async_mysql2/connection_factory.rb
75
+ - lib/async_mysql2/fibered_database_connection_pool.rb
76
+ - lib/async_mysql2/fibered_mutex_with_waiter_priority.rb
77
+ - lib/async_mysql2/version.rb
78
+ homepage: https://github.com/Invoca/fibered_mysql2
79
+ licenses: []
80
+ metadata:
81
+ allowed_push_host: https://rubygems.org
82
+ homepage_uri: https://github.com/Invoca/fibered_mysql2
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.1
97
+ requirements: []
98
+ rubygems_version: 3.4.6
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: An adapter for mysql2 running on Async
102
+ test_files: []