11# frozen_string_literal: true
22
3+ require 'active_record'
4+ require 'active_record/connection_adapters/mysql2_adapter'
5+
6+ # Monkeypatch the MySQL2 adapter to return hashes with symbol keys by default
7+ module MysqlFramework
8+ module Mysql2AdapterPatch
9+ def configure_connection
10+ super
11+ @raw_connection . query_options [ :as ] = :hash
12+ @raw_connection . query_options [ :symbolize_keys ] = true
13+ @raw_connection . query_options [ :cast_booleans ] = true
14+ end
15+ end
16+ end
17+
18+ ActiveRecord ::ConnectionAdapters ::Mysql2Adapter . prepend ( MysqlFramework ::Mysql2AdapterPatch )
19+
320module MysqlFramework
421 class Connector
522 def initialize ( options = { } )
623 @options = default_options . merge ( options )
7- @mutex = Mutex . new
8-
9- Mysql2 ::Client . default_query_options . merge! ( symbolize_keys : true , cast_booleans : true )
24+ @connection_map = nil
25+ @map_mutex = Mutex . new
26+ @setup_mutex = Mutex . new
27+ @setup_complete = false
1028 end
1129
12- # This method is called to setup a pool of MySQL connections .
30+ # This method is called to setup the ActiveRecord connection pool .
1331 def setup
14- return unless connection_pool_enabled?
32+ return if @setup_complete
1533
16- @connection_pool = ::Queue . new
34+ @setup_mutex . synchronize do
35+ return if @setup_complete
1736
18- start_pool_size . times { @connection_pool . push ( new_client ) }
19-
20- @created_connections = start_pool_size
37+ ActiveRecord ::Base . establish_connection ( active_record_config )
38+ @connection_map = { }
39+ @setup_complete = true
40+ end
2141 end
2242
2343 # This method is called to close all MySQL connections in the pool and dispose of the pool itself.
2444 def dispose
25- return if @connection_pool . nil?
45+ return unless @setup_complete
2646
27- until @connection_pool . empty?
28- conn = @connection_pool . pop ( true )
29- conn &.close
47+ ActiveRecord ::Base . connection_pool . disconnect!
48+
49+ @map_mutex . synchronize do
50+ @connection_map . clear
3051 end
3152
32- @connection_pool = nil
53+ @setup_complete = false
3354 end
3455
35- # This method is called to get the idle connection queue for this connector.
56+ # This method is called to get the connection pool for this connector.
3657 def connections
37- @connection_pool
58+ return nil unless @setup_complete
59+
60+ ActiveRecord ::Base . connection_pool
3861 end
3962
4063 # This method is called to fetch a client from the connection pool.
4164 def check_out
42- @mutex . synchronize do
43- begin
44- return new_client unless connection_pool_enabled?
45-
46- client = @connection_pool . pop ( true )
47-
48- client . ping if @options [ :reconnect ]
49-
50- client
51- rescue ThreadError
52- if @created_connections < max_pool_size
53- client = new_client
54- @created_connections += 1
55- return client
56- end
65+ setup unless @setup_complete
5766
58- MysqlFramework . logger . error { "[#{ self . class } ] - Database connection pool depleted." }
67+ adapter = ActiveRecord ::Base . connection_pool . checkout
68+ raw_conn = adapter . raw_connection
5969
60- raise 'Database connection pool depleted.'
61- end
70+ @map_mutex . synchronize do
71+ @connection_map [ raw_conn . object_id ] = adapter
6272 end
73+
74+ raw_conn
6375 end
6476
6577 # This method is called to check a client back in to the connection when no longer needed.
6678 def check_in ( client )
67- @mutex . synchronize do
68- return client &.close unless connection_pool_enabled?
79+ return if client . nil? || !@setup_complete
80+
81+ adapter = @map_mutex . synchronize do
82+ @connection_map . delete ( client . object_id )
83+ end
6984
70- client = new_client if client &.closed?
71- @connection_pool . push ( client )
85+ if adapter
86+ ActiveRecord ::Base . connection_pool . checkin ( adapter )
87+ else
88+ MysqlFramework . logger . warn { "[#{ self . class } ] - Unable to find adapter for raw connection during check_in" }
7289 end
7390 end
7491
7592 # This method is called to use a client from the connection pool.
7693 def with_client ( provided = nil )
77- client = provided || check_out
78- yield client
79- ensure
80- check_in ( client ) if client && !provided
94+ if provided
95+ yield provided
96+ else
97+ setup unless @setup_complete
98+ ActiveRecord ::Base . connection_pool . with_connection do |connection |
99+ yield connection . raw_connection
100+ end
101+ end
81102 end
82103
83104 # This method is called to execute a prepared statement
@@ -87,14 +108,12 @@ def with_client(provided = nil)
87108 # running different queries at the same time.
88109 def execute ( query , provided_client = nil )
89110 with_client ( provided_client ) do |client |
90- begin
91- statement = client . prepare ( query . sql )
92- result = statement . execute ( *query . params )
93- result &.to_a
94- ensure
95- result &.free
96- statement &.close
97- end
111+ statement = client . prepare ( query . sql )
112+ result = statement . execute ( *query . params )
113+ result &.to_a
114+ ensure
115+ result &.free
116+ statement &.close
98117 end
99118 end
100119
@@ -146,20 +165,28 @@ def default_options
146165 }
147166 end
148167
149- def new_client
150- Mysql2 ::Client . new ( @options )
151- end
152-
153- def connection_pool_enabled?
154- @connection_pool_enabled ||= ENV . fetch ( 'MYSQL_CONNECTION_POOL_ENABLED' , 'true' ) . casecmp? ( 'true' )
155- end
156-
157- def start_pool_size
158- @start_pool_size ||= Integer ( ENV . fetch ( 'MYSQL_START_POOL_SIZE' , 1 ) )
168+ def active_record_config
169+ {
170+ adapter : 'mysql2' ,
171+ host : @options [ :host ] ,
172+ port : @options [ :port ] ,
173+ database : @options [ :database ] ,
174+ username : @options [ :username ] ,
175+ password : @options [ :password ] ,
176+ reconnect : @options [ :reconnect ] ,
177+ read_timeout : @options [ :read_timeout ] ,
178+ write_timeout : @options [ :write_timeout ] ,
179+ pool : max_pool_size ,
180+ checkout_timeout : pool_timeout
181+ }
159182 end
160183
161184 def max_pool_size
162185 @max_pool_size ||= Integer ( ENV . fetch ( 'MYSQL_MAX_POOL_SIZE' , 5 ) )
163186 end
187+
188+ def pool_timeout
189+ @pool_timeout ||= Integer ( ENV . fetch ( 'MYSQL_POOL_TIMEOUT' , 5 ) )
190+ end
164191 end
165192end
0 commit comments