fibered_mysql2 0.1.0.pre.3 → 0.1.0.pre.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 767c4a6d73b876ca435d3221bdcaa298046560cd3aa1e8b946a65790c654bf1e
4
- data.tar.gz: aa0b3d54109e0e006832d7fa2fc02b99bf6b018fea394104d0ab9f1cc455d6b5
3
+ metadata.gz: 52a7c1f9db37bca0d6b9861985ad594863e1cde7847952dc83a2ed94bed72d2e
4
+ data.tar.gz: 1ac220f2391d1b7c74df9985ddfaca59855570ee01e36f8351841f8651306a98
5
5
  SHA512:
6
- metadata.gz: b8fdbb7e5fa9b592e5a17c58b42878470e8d3d25589612229bdc078c628cc6c9240c9b9626faaa72e4e9092a38414cbae5c77a616aa2fc4cae3f4eecc834fbc3
7
- data.tar.gz: c150c9d2af4a45350f67e518fb0efb279e91b2b2fe5a28479f4e344abf7587e51ae09e77423ea78602b2fee5e2a3c1bc1d971af4f4b4b4a12c3ea6777724ed54
6
+ metadata.gz: 4df2ada93ac02bf3b34b6a223c83c26690a620a88eec6c38ffc4f1a1c8a052867fc126b4f745ad3e1aa9278e86a7808c08dc7b493dbd99cb93812895b1901584
7
+ data.tar.gz: 78fafb964db2b16da1604032ea1421b2761f81ef60c52851be4df30f96e78a9f688b9a656fec1d4a0ad4b1564aca20f57bb9722a69aba64be4891835d11cfd6e
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fibered_mysql2 (0.1.0.pre.3)
4
+ fibered_mysql2 (0.1.0.pre.4)
5
5
  em-synchrony (~> 1.0)
6
6
  rails (>= 4.2, < 7)
7
7
 
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fibered_mysql2/version"
3
+ require 'fibered_mysql2/version'
4
+ require_relative '../lib/active_record/connection_adapters/fibered_mysql2_adapter'
5
+ require 'fibered_mysql2/fibered_database_connection_pool'
6
+ require 'fibered_mysql2/fibered_mutex_with_waiter_priority'
7
+ require 'fibered_mysql2/fibered_mysql2_connection_factory'
4
8
 
5
9
  module FiberedMysql2
6
10
  class Error < StandardError; end
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class behaves the same as ActiveRecord's ConnectionPool, but synchronizes with 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
+ require 'em-synchrony'
9
+ require 'em-synchrony/thread'
10
+ require 'fibered_mysql2/fibered_mutex_with_waiter_priority'
11
+
12
+ EventMachine::Synchrony::Thread::Mutex.prepend(FiberedMysql2::FiberedMutexWithWaiterPriority)
13
+
14
+ module FiberedMysql2
15
+ class FiberedConditionVariable < MonitorMixin::ConditionVariable
16
+ def initialize(monitor)
17
+ @monitor = monitor
18
+ @cond = EM::Synchrony::Thread::ConditionVariable.new
19
+ end
20
+ end
21
+
22
+ # From Ruby's MonitorMixin, with all occurrences of Thread changed to Fiber
23
+ module FiberedMonitorMixin
24
+ def self.extend_object(obj)
25
+ super
26
+ obj.__send__(:mon_initialize)
27
+ end
28
+
29
+ #
30
+ # Attempts to enter exclusive section. Returns +false+ if lock fails.
31
+ #
32
+ def mon_try_enter
33
+ if @mon_owner != Fiber.current
34
+ @mon_mutex.try_lock or return false
35
+ @mon_owner = Fiber.current
36
+ @mon_count = 0
37
+ end
38
+ @mon_count += 1
39
+ true
40
+ end
41
+
42
+ #
43
+ # Enters exclusive section.
44
+ #
45
+ def mon_enter
46
+ if @mon_owner != Fiber.current
47
+ @mon_mutex.lock
48
+ @mon_owner = Fiber.current
49
+ @mon_count = 0
50
+ end
51
+ @mon_count += 1
52
+ end
53
+
54
+ #
55
+ # Leaves exclusive section.
56
+ #
57
+ def mon_exit
58
+ mon_check_owner
59
+ @mon_count -= 1
60
+ if @mon_count == 0
61
+ @mon_owner = nil
62
+ @mon_mutex.unlock
63
+ end
64
+ end
65
+
66
+ #
67
+ # Enters exclusive section and executes the block. Leaves the exclusive
68
+ # section automatically when the block exits. See example under
69
+ # +MonitorMixin+.
70
+ #
71
+ def mon_synchronize
72
+ mon_enter
73
+ begin
74
+ yield
75
+ ensure
76
+ begin
77
+ mon_exit
78
+ rescue => ex
79
+ ActiveRecord::Base.logger.error("Exception occurred while executing mon_exit: #{ex}")
80
+ end
81
+ end
82
+ end
83
+ alias synchronize mon_synchronize
84
+
85
+ #
86
+ # Creates a new FiberedConditionVariable associated with the
87
+ # receiver.
88
+ #
89
+ def new_cond
90
+ FiberedConditionVariable.new(self)
91
+ end
92
+
93
+ private
94
+
95
+ # Initializes the FiberedMonitorMixin after being included in a class
96
+ def mon_initialize
97
+ @mon_owner = nil
98
+ @mon_count = 0
99
+ @mon_mutex = EM::Synchrony::Thread::Mutex.new
100
+ end
101
+
102
+ def mon_check_owner
103
+ @mon_owner == Fiber.current or raise FiberError, "current fiber not owner"
104
+ end
105
+
106
+ def mon_enter_for_cond(count)
107
+ @mon_owner = Fiber.current
108
+ @mon_count = count
109
+ end
110
+
111
+ # returns the old mon_count
112
+ def mon_exit_for_cond
113
+ count = @mon_count
114
+ @mon_owner = nil
115
+ @mon_count = 0
116
+ count
117
+ end
118
+ end
119
+
120
+ module FiberedDatabaseConnectionPool
121
+ include FiberedMonitorMixin
122
+
123
+ module Adapter_4_2
124
+ def cached_connections
125
+ @reserved_connections
126
+ end
127
+ end
128
+
129
+ module Adapter_5_2
130
+ def cached_connections
131
+ @thread_cached_conns
132
+ end
133
+ end
134
+
135
+ case Rails::VERSION::MAJOR
136
+ when 4
137
+ include Adapter_4_2
138
+ when 5, 6
139
+ include Adapter_5_2
140
+ end
141
+
142
+ def initialize(connection_spec)
143
+ connection_spec.config[:reaping_frequency] and raise "reaping_frequency is not supported (the ActiveRecord Reaper is thread-based)"
144
+
145
+ super(connection_spec)
146
+
147
+ @reaper = nil # no need to keep a reference to this since it does nothing in this sub-class
148
+
149
+ # note that @reserved_connections is a ThreadSafe::Cache which is overkill in a fibered world, but harmless
150
+ end
151
+
152
+ def connection
153
+ # this is correctly done double-checked locking
154
+ # (ThreadSafe::Cache's lookups have volatile semantics)
155
+ if (result = cached_connections[current_connection_id])
156
+ result
157
+ else
158
+ synchronize do
159
+ if (result = cached_connections[current_connection_id])
160
+ result
161
+ else
162
+ cached_connections[current_connection_id] = checkout
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ if Rails::VERSION::MAJOR > 4
169
+ def release_connection(owner_thread = Fiber.current)
170
+ if (conn = @thread_cached_conns.delete(connection_cache_key(owner_thread)))
171
+ checkin(conn)
172
+ end
173
+ end
174
+ end
175
+
176
+ def current_connection_id
177
+ case Rails::VERSION::MAJOR
178
+ when 4
179
+ ActiveRecord::Base.connection_id ||= Fiber.current.object_id
180
+ else
181
+ connection_cache_key(current_thread)
182
+ end
183
+ end
184
+
185
+ def checkout(checkout_timeout = @checkout_timeout)
186
+ begin
187
+ reap_connections
188
+ rescue => ex
189
+ ActiveRecord::Base.logger.error("Exception occurred while executing reap_connections: #{ex}")
190
+ end
191
+ if Rails::VERSION::MAJOR > 4
192
+ super
193
+ else
194
+ super()
195
+ end
196
+ end
197
+
198
+ def reap_connections
199
+ cached_connections.values.each do |connection|
200
+ unless connection.owner.alive?
201
+ checkin(connection)
202
+ end
203
+ end
204
+ end
205
+
206
+ private
207
+
208
+ #--
209
+ # This hook-in method allows for easier monkey-patching fixes needed by
210
+ # JRuby users that use Fibers.
211
+ def connection_cache_key(fiber)
212
+ fiber
213
+ end
214
+
215
+ def current_thread
216
+ Fiber.current
217
+ end
218
+ end
219
+ end
220
+
221
+ ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(FiberedMysql2::FiberedDatabaseConnectionPool)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FiberedMysql2
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,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../active_record/connection_adapters/fibered_mysql2_adapter'
4
+
5
+ module EM::Synchrony
6
+ module ActiveRecord
7
+ _ = Adapter_4_2
8
+ module Adapter_4_2
9
+ def configure_connection
10
+ super # undo EM::Synchrony's override here
11
+ end
12
+
13
+ def transaction(*args)
14
+ super # and here
15
+ end
16
+
17
+ _ = TransactionManager
18
+ class TransactionManager < _
19
+ if Rails::VERSION::MAJOR > 5
20
+ # Overriding the em-synchrony override to bring it up to rails 6 requirements.
21
+ # Changes from the original Rails 6 source are:
22
+ # 1. the usage of _current_stack created by em-synchrony instead of the Rails provided @stack instance variable
23
+ # 2. the usage of Fiber.current.object_id as a part of the savepoint transaction name
24
+ #
25
+ # Original EM Synchrony Source:
26
+ # https://github.com/igrigorik/em-synchrony/blob/master/lib/em-synchrony/activerecord_4_2.rb#L35-L44
27
+ #
28
+ # Original Rails Source:
29
+ # https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L205-L224
30
+ def begin_transaction(options = {})
31
+ @connection.lock.synchronize do
32
+ run_commit_callbacks = !current_transaction.joinable?
33
+ transaction =
34
+ if _current_stack.empty?
35
+ ::ActiveRecord::ConnectionAdapters::RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
36
+ else
37
+ ::ActiveRecord::ConnectionAdapters::SavepointTransaction.new(@connection, "active_record_#{Fiber.current.object_id}_#{open_transactions}", _current_stack.last, options,
38
+ run_commit_callbacks: run_commit_callbacks)
39
+ end
40
+
41
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
42
+ @has_unmaterialized_transactions = true
43
+ else
44
+ transaction.materialize!
45
+ end
46
+ _current_stack.push(transaction)
47
+ transaction
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ module FiberedMysql2
57
+ module FiberedMysql2ConnectionFactory
58
+ def fibered_mysql2_connection(raw_config)
59
+ config = raw_config.symbolize_keys
60
+
61
+ config[:username] = 'root' if config[:username].nil?
62
+ config[:flags] = Mysql2::Client::FOUND_ROWS if Mysql2::Client.const_defined?(:FOUND_ROWS)
63
+
64
+ client =
65
+ begin
66
+ Mysql2::EM::Client.new(config)
67
+ rescue Mysql2::Error => error
68
+ if error.message.include?("Unknown database")
69
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
70
+ else
71
+ raise
72
+ end
73
+ end
74
+
75
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
76
+ FiberedMysql2Adapter.new(client, logger, options, config)
77
+ end
78
+ end
79
+ end
80
+
81
+ ActiveRecord::Base.class.prepend(FiberedMysql2::FiberedMysql2ConnectionFactory)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FiberedMysql2
4
- VERSION = "0.1.0.pre.3"
4
+ VERSION = "0.1.0.pre.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fibered_mysql2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.3
4
+ version: 0.1.0.pre.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Octothorp
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-07 00:00:00.000000000 Z
11
+ date: 2020-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-synchrony
@@ -68,6 +68,9 @@ files:
68
68
  - gemfiles/rails_6.gemfile
69
69
  - lib/active_record/connection_adapters/fibered_mysql2_adapter.rb
70
70
  - lib/fibered_mysql2.rb
71
+ - lib/fibered_mysql2/fibered_database_connection_pool.rb
72
+ - lib/fibered_mysql2/fibered_mutex_with_waiter_priority.rb
73
+ - lib/fibered_mysql2/fibered_mysql2_connection_factory.rb
71
74
  - lib/fibered_mysql2/version.rb
72
75
  homepage: https://github.com/Invoca/fibered_mysql2
73
76
  licenses: []