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 +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/build.yml +25 -0
- data/.github/workflows/release.yml +38 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Appraisals +9 -0
- data/CHANGELOG.md +47 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +209 -0
- data/README.md +43 -0
- data/Rakefile +8 -0
- data/async_mysql2.gemspec +34 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gemfiles/rails_5.gemfile +15 -0
- data/gemfiles/rails_6.gemfile +15 -0
- data/lib/active_record/connection_adapters/async_mysql2_adapter.rb +76 -0
- data/lib/async_mysql2/async_task.rb +18 -0
- data/lib/async_mysql2/connection_factory.rb +30 -0
- data/lib/async_mysql2/fibered_database_connection_pool.rb +89 -0
- data/lib/async_mysql2/fibered_mutex_with_waiter_priority.rb +33 -0
- data/lib/async_mysql2/version.rb +5 -0
- data/lib/async_mysql2.rb +12 -0
- metadata +102 -0
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
|
data/.github/CODEOWNERS
ADDED
@@ -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
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.1
|
data/Appraisals
ADDED
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,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,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
|
data/lib/async_mysql2.rb
ADDED
@@ -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: []
|