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

Sign up to get free protection for your applications and to get access to all the features.
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: []