maria_db_cluster_pool 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Microting A/S
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,36 @@
1
+ == Database Cluster
2
+
3
+ MariaDB Cluster Pool gem is designed for usage with Maria DB Galera Cluster, so this gem will only support a master/master setup
4
+
5
+ = Configuration
6
+
7
+ == The pool configuration
8
+
9
+ The cluster connections are configured in database.yml using the maria_db_cluster_pool adapter. Any properties you configure for the connection will be inherited by all connections in the pool. In this way, you can configure ports, usernames, etc. once instead of for each connection. One exception is that you can set the pool_adapter property which each connection will inherit as the adapter property. Each connection in the pool uses all the same configuration properties as normal for the adapters.
10
+
11
+ == Example configuration
12
+
13
+ development:
14
+ adapter: maria_db_cluster_pool
15
+ database: mydb_development
16
+ username: read_user
17
+ password: abc123
18
+ pool_adapter: mysql
19
+ port: 3306
20
+ server_pool:
21
+ - host: read-db-1.example.com
22
+ pool_weight: 1
23
+ - host: read-db-2.example.com
24
+ pool_weight: 2
25
+
26
+ == License
27
+
28
+ This software is a derived work of https://github.com/bdurand/seamless_database_pool the parts which derives from that codes is copyrighted by Brian Durand
29
+
30
+ Copyright (C) 2013 Microting A/S
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
35
+
36
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'yaml'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ begin
9
+ require 'rspec'
10
+ require 'rspec/core/rake_task'
11
+ desc 'Run the unit tests'
12
+ RSpec::Core::RakeTask.new(:test)
13
+
14
+ #namespace :test do
15
+ # desc "Run all tests including for all database adapters"
16
+ # task :all do
17
+ # save_val = ENV['TEST_ADAPTERS']
18
+ # begin
19
+ # ENV['TEST_ADAPTERS'] = YAML.load_file(File.expand_path("../spec/database.yml", __FILE__)).keys.join(' ')
20
+ # Rake::Task["test"].execute
21
+ # ensure
22
+ # ENV['TEST_ADAPTERS'] = save_val
23
+ # end
24
+ # end
25
+ #
26
+ # desc "Test all database adapters defined in database.yml or just the one specified in TEST_ADAPTERS"
27
+ # task :adapters do
28
+ # save_val = ENV['TEST_ADAPTERS']
29
+ # begin
30
+ # ENV['TEST_ADAPTERS'] ||= YAML.load_file(File.expand_path("../spec/database.yml", __FILE__)).keys.join(' ')
31
+ # Rake::Task["test:adapters:specified"].execute
32
+ # ensure
33
+ # ENV['TEST_ADAPTERS'] = save_val
34
+ # end
35
+ # end
36
+ #
37
+ # namespace :adapters do
38
+ # desc "Internal task to run database adapter tests"
39
+ # RSpec::Core::RakeTask.new(:specified) do |t|
40
+ # t.pattern = FileList.new('spec/connection_adapters_spec.rb')
41
+ # end
42
+ #
43
+ # YAML.load_file(File.expand_path("../spec/database.yml", __FILE__)).keys.each do |adapter_name|
44
+ # desc "Test the #{adapter_name} database adapter"
45
+ # task adapter_name do
46
+ # save_val = ENV['TEST_ADAPTERS']
47
+ # begin
48
+ # ENV['TEST_ADAPTERS'] = adapter_name
49
+ # Rake::Task["test:adapters:specified"].execute
50
+ # ensure
51
+ # ENV['TEST_ADAPTERS'] = save_val
52
+ # end
53
+ # end
54
+ # end
55
+ # end
56
+ #end
57
+ rescue LoadError
58
+ task :test do
59
+ STDERR.puts "You must have rspec >= 2.0 to run the tests"
60
+ end
61
+ end
62
+
63
+ begin
64
+ require 'jeweler'
65
+ Jeweler::Tasks.new do |gem|
66
+ gem.name = "maria_db_cluster_pool"
67
+ gem.summary = "Add support for master/master database clusters in ActiveRecord to improve performance."
68
+ gem.email = "rm@microting.com"
69
+ gem.homepage = "https://github.com/renemadsen/maria_db_cluster_pool"
70
+ gem.authors = ["René Schultz Madsen"]
71
+ gem.files = FileList["lib/**/*", "spec/**/*", "README.rdoc", "Rakefile", "MIT-LICENSE"].to_a
72
+ gem.has_rdoc = true
73
+ gem.extra_rdoc_files = ["README.rdoc", "MIT-LICENSE"]
74
+
75
+ gem.add_dependency('activerecord', '>= 2.2.2')
76
+ gem.add_development_dependency('rspec', '>= 2.0')
77
+ gem.add_development_dependency('jeweler')
78
+ gem.add_development_dependency('mysql')
79
+ end
80
+
81
+ Jeweler::GemcutterTasks.new
82
+ rescue LoadError
83
+ end
@@ -0,0 +1,324 @@
1
+ require 'maria_db_cluster_pool/connect_timeout'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ class << self
6
+ def maria_db_cluster_pool_connection(config)
7
+ pool_weights = {}
8
+
9
+ config = config.with_indifferent_access
10
+ default_config = {:pool_weight => 1}.merge(config.merge(:adapter => config[:pool_adapter])).with_indifferent_access
11
+ default_config.delete(:server_pool)
12
+ default_config.delete(:pool_adapter)
13
+
14
+ pool_connections = []
15
+ config[:server_pool].each do |server_config|
16
+ server_config = default_config.merge(server_config).with_indifferent_access
17
+ server_config[:pool_weight] = server_config[:pool_weight].to_i
18
+ begin
19
+ establish_adapter(server_config[:adapter])
20
+ conn = send("#{server_config[:adapter]}_connection".to_sym, server_config)
21
+ conn.class.send(:include, MariaDBClusterPool::ConnectTimeout) unless conn.class.include?(MariaDBClusterPool::ConnectTimeout)
22
+ conn.connect_timeout = server_config[:connect_timeout]
23
+ pool_connections << conn
24
+ pool_weights[conn] = server_config[:pool_weight]
25
+ rescue Exception => e
26
+ if logger
27
+ logger.error("Error connecting to read connection #{server_config.inspect}")
28
+ logger.error(e)
29
+ end
30
+ end
31
+ end if config[:server_pool]
32
+
33
+ @maria_db_cluster_pool_classes ||= {}
34
+ klass = @maria_db_cluster_pool_classes[pool_connections[0].class]
35
+ unless klass
36
+ klass = ActiveRecord::ConnectionAdapters::MariaDBClusterPoolAdapter.adapter_class(pool_connections[0])
37
+ @maria_db_cluster_pool_classes[pool_connections[0].class] = klass
38
+ end
39
+
40
+ return klass.new(pool_connections[0], logger, pool_connections, pool_weights)
41
+ end
42
+
43
+ def establish_adapter(adapter)
44
+ raise AdapterNotSpecified.new("database configuration does not specify adapter") unless adapter
45
+ raise AdapterNotFound.new("database pool must specify adapters") if adapter == 'MariaDB_Cluster_Pool'
46
+
47
+ begin
48
+ require 'rubygems'
49
+ gem "activerecord-#{adapter}-adapter"
50
+ require "active_record/connection_adapters/#{adapter}_adapter"
51
+ rescue LoadError
52
+ begin
53
+ require "active_record/connection_adapters/#{adapter}_adapter"
54
+ rescue LoadError
55
+ raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
56
+ end
57
+ end
58
+
59
+ adapter_method = "#{adapter}_connection"
60
+ if !respond_to?(adapter_method)
61
+ raise AdapterNotFound, "database configuration specifies nonexistent #{adapter} adapter"
62
+ end
63
+ end
64
+ end
65
+
66
+ module MariaDBClusterPoolBehavior
67
+ def self.included(base)
68
+ base.alias_method_chain(:reload, :maria_db_cluster_pool)
69
+ end
70
+
71
+ # Force reload to use the master connection since it's probably being called for a reason.
72
+ def reload_with_maria_db_cluster_pool(*args)
73
+ reload_without_maria_db_Cluster_Pool(*args)
74
+ end
75
+ end
76
+
77
+ include(MariaDBClusterPoolBehavior) unless include?(MariaDBClusterPoolBehavior)
78
+ end
79
+
80
+ module ConnectionAdapters
81
+ class MariaDBClusterPoolAdapter < AbstractAdapter
82
+
83
+ attr_reader :connections # The total sum of connections
84
+ attr_reader :master_connection # The current connection in use
85
+ attr_reader :available_connections # The list of connections usable to the class
86
+
87
+ class << self
88
+ # Create an anonymous class that extends this one and proxies methods to the pool connections.
89
+ def adapter_class(master_connection)
90
+ # Define methods to proxy to the appropriate pool
91
+ master_methods = []
92
+ master_connection_classes = [AbstractAdapter, Quoting, DatabaseStatements, SchemaStatements]
93
+ master_connection_classes << DatabaseLimits if const_defined?(:DatabaseLimits)
94
+ master_connection_class = master_connection.class
95
+ while ![Object, AbstractAdapter].include?(master_connection_class) do
96
+ master_connection_classes << master_connection_class
97
+ master_connection_class = master_connection_class.superclass
98
+ end
99
+ master_connection_classes.each do |connection_class|
100
+ master_methods.concat(connection_class.public_instance_methods(false))
101
+ master_methods.concat(connection_class.protected_instance_methods(false))
102
+ end
103
+ master_methods.uniq!
104
+ master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
105
+ master_methods = master_methods.collect{|m| m.to_sym}
106
+
107
+ klass = Class.new(self)
108
+ master_methods.each do |method_name|
109
+ klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
110
+ def #{method_name}(*args, &block)
111
+ return proxy_connection_method(master_connection, :#{method_name}, *args, &block)
112
+ end
113
+ EOS
114
+ end
115
+
116
+ return klass
117
+ end
118
+
119
+ # Set the arel visitor on the connections.
120
+ def visitor_for(pool)
121
+ # This is ugly, but then again, so is the code in ActiveRecord for setting the arel
122
+ # visitor. There is a note in the code indicating the method signatures should be updated.
123
+ config = pool.spec.config.with_indifferent_access
124
+ adapter = config[:master][:adapter] || config[:pool_adapter]
125
+ MariaDBClusterPool.adapter_class_for(adapter).visitor_for(pool)
126
+ end
127
+ end
128
+
129
+ def initialize(connection, logger, connections, pool_weights)
130
+ super(connection, logger)
131
+
132
+ @available_connections = []
133
+ @master_connection = connection
134
+ @connections = connections.dup.freeze
135
+
136
+ pool_weights.each_pair do |conn, weight|
137
+ @available_connections[weight] = AvailableConnection.new(conn)
138
+ end
139
+ end
140
+
141
+ def adapter_name #:nodoc:
142
+ 'MariaDB_Cluster_Pool'
143
+ end
144
+
145
+ # Returns an array of the master connection and the read pool connections
146
+ def all_connections
147
+ @connections
148
+ end
149
+
150
+ def requires_reloading?
151
+ false
152
+ end
153
+
154
+ def visitor=(visitor)
155
+ all_connections.each{|conn| conn.visitor = visitor}
156
+ end
157
+
158
+ def visitor
159
+ connection.visitor
160
+ end
161
+
162
+ def active?
163
+ active = true
164
+ do_to_connections {|conn| active &= conn.active?}
165
+ return active
166
+ end
167
+
168
+ def reconnect!
169
+ do_to_connections {|conn| conn.reconnect!}
170
+ end
171
+
172
+ def disconnect!
173
+ do_to_connections {|conn| conn.disconnect!}
174
+ end
175
+
176
+ def reset!
177
+ do_to_connections {|conn| conn.reset!}
178
+ end
179
+
180
+ def verify!(*ignored)
181
+ do_to_connections {|conn| conn.verify!(*ignored)}
182
+ end
183
+
184
+ def reset_runtime
185
+ total = 0.0
186
+ do_to_connections {|conn| total += conn.reset_runtime}
187
+ total
188
+ end
189
+
190
+ class DatabaseConnectionError < StandardError
191
+ end
192
+
193
+ # This simple class puts an expire time on an array of connections. It is used so the a connection
194
+ # to a down database won't try to reconnect over and over.
195
+ class AvailableConnection
196
+ attr_reader :connection
197
+ attr_writer :failed_connection
198
+ attr_writer :expires
199
+
200
+ def initialize(connection, failed_connection = false, expires = nil)
201
+ @connection = connection
202
+ @failed_connection = failed_connection
203
+ @expires = expires
204
+ end
205
+
206
+ def expired?
207
+ @expires ? @expires <= Time.now : false
208
+ end
209
+
210
+ def failed?
211
+ @failed_connection
212
+ end
213
+
214
+ def reconnect!
215
+ @connection.reconnect!
216
+ if @connection.active?
217
+ @failed_connection = false
218
+ @expires = nil
219
+ else
220
+ raise DatabaseConnectionError.new
221
+ end
222
+ end
223
+ end
224
+
225
+ # Get the available weighted connections. When a connection is dead and cannot be reconnected, it will
226
+ # be temporarily removed from the read pool so we don't keep trying to reconnect to a database that isn't
227
+ # listening.
228
+ def available_connections
229
+ @available_connections.each do |a|
230
+ if a != nil
231
+ if a.expired?
232
+ begin
233
+ @logger.info("Adding dead database connection back to the pool : #{a.connection.inspect}") if @logger
234
+ a.reconnect!
235
+ rescue => e
236
+ a.expires = 30.seconds.from_now
237
+ @logger.warn("Failed to reconnect to database when adding connection back to the pool")
238
+ @logger.warn(e)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ @available_connections
245
+
246
+ end
247
+
248
+ def reset_available_connections
249
+ @available_connections.each do |a|
250
+ if a != nil
251
+ a.reconnect! rescue nil
252
+ end
253
+ end
254
+ end
255
+
256
+ # Temporarily remove a connection from the read pool.
257
+ def suppress_connection(conn, expire)
258
+ available = available_connections
259
+ available.each do |a|
260
+ if a != nil
261
+ if a.connection == conn
262
+ a.failed_connection = true
263
+ a.expires = expire.seconds.from_now
264
+ @logger.info("Supressing database connection from the pool : #{a.connection.inspect}") if @logger
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ def next_usable_connection
271
+ available = available_connections
272
+ available.each do |a|
273
+ if a != nil
274
+ unless a.failed?
275
+ if a.connection.active?
276
+ @logger.info("New master connection is now : #{a.connection.inspect}") if @logger
277
+ @master_connection = a.connection
278
+ break
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ private
286
+
287
+ def proxy_connection_method(connection, method, *args, &block)
288
+ available_connections
289
+ begin
290
+ connection.send(method, *args, &block)
291
+ rescue ArgumentError
292
+ begin
293
+ connection.send(method, *args)
294
+ rescue ArgumentError
295
+ connection.send(method)
296
+ end
297
+ rescue
298
+ # If the statement was a read statement and it wasn't forced against the master connection
299
+ # try to reconnect if the connection is dead and then re-run the statement.
300
+ unless connection.active?
301
+ suppress_connection(@master_connection, 30)
302
+ next_usable_connection
303
+ end
304
+ proxy_connection_method(@master_connection, method, *args, &block)
305
+ end
306
+ end
307
+
308
+ # Yield a block to each connection in the pool. If the connection is dead, ignore the error
309
+ def do_to_connections
310
+ all_connections.each do |conn|
311
+ begin
312
+ yield(conn)
313
+ rescue => e
314
+ if @logger
315
+ @logger.warn("Error in do_to_connections")
316
+ @logger.warn(e.message)
317
+ @logger.warn(e.backtrace.inspect)
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,27 @@
1
+ module Arel
2
+ module SqlCompiler
3
+ # Hook into arel to use the compiler used by the master connection.
4
+ class MariaDBClusterPoolCompiler < GenericCompiler
5
+ def self.new(relation)
6
+ @compiler_classes ||= {}
7
+ master_adapter = relation.engine.connection.master_connection.adapter_name
8
+ compiler_class = @compiler_classes[master_adapter]
9
+ unless compiler_class
10
+ begin
11
+ require "arel/engines/sql/compilers/#{master_adapter.downcase}_compiler"
12
+ rescue LoadError
13
+ begin
14
+ # try to load an externally defined compiler, in case this adapter has defined the compiler on its own.
15
+ require "#{master_adapter.downcase}/arel_compiler"
16
+ rescue LoadError
17
+ raise "#{master_adapter} is not supported by Arel."
18
+ end
19
+ end
20
+ compiler_class = Arel::SqlCompiler.const_get("#{master_adapter}Compiler")
21
+ @compiler_classes[master_adapter] = compiler_class
22
+ end
23
+ compiler_class.new(relation)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'timeout'
2
+
3
+ module MariaDBClusterPool
4
+ # This module is mixed into connection adapters to allow the reconnect! method to timeout if the
5
+ # IP address becomes unreachable. The default timeout is 1 second, but you can change it by setting
6
+ # the connect_timeout parameter in the adapter configuration.
7
+ module ConnectTimeout
8
+ attr_accessor :connect_timeout
9
+
10
+ def self.included(base)
11
+ base.alias_method_chain :reconnect!, :connect_timeout
12
+ end
13
+
14
+ def reconnect_with_connect_timeout!
15
+ begin
16
+ timeout(connect_timeout || 1) do
17
+ reconnect_without_connect_timeout!
18
+ end
19
+ rescue Timeout::Error
20
+ raise ActiveRecord::ConnectionTimeoutError.new("reconnect timed out")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,131 @@
1
+ require File.join(File.dirname(__FILE__), 'maria_db_cluster_pool', 'connect_timeout.rb')
2
+ #require File.join(File.dirname(__FILE__), 'mariadb_cluster_pool', 'connection_statistics.rb')
3
+ #require File.join(File.dirname(__FILE__), 'mariadb_cluster_pool', 'controller_filter.rb')
4
+ require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'maria_db_cluster_pool_adapter.rb')
5
+ $LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))
6
+
7
+ # This module allows setting the read pool connection type. Generally you will use one of
8
+ #
9
+ # - use_random_connection
10
+ # - use_persistent_read_connection
11
+ # - use_master_connection
12
+ #
13
+ # Each of these methods can take an optional block. If they are called with a block, they
14
+ # will set the read connection type only within the block. Otherwise they will set the default
15
+ # read connection type. If none is ever called, the read connection type will be :master.
16
+
17
+ module MariaDBClusterPool
18
+
19
+ # Adapter name to class name map. This exists because there isn't an obvious way to translate things like
20
+ # sqlite3 to SQLite3. The adapters that ship with ActiveRecord are defined here. If you use
21
+ # an adapter that doesn't translate directly to camel case, then add the mapping here in an initializer.
22
+ ADAPTER_TO_CLASS_NAME_MAP = {"sqlite" => "SQLite", "sqlite3" => "SQLite3", "postgresql" => "PostgreSQL"}
23
+
24
+ #READ_CONNECTION_METHODS = [:master, :persistent, :random]
25
+
26
+ class << self
27
+ # Call this method to use a random connection from the read pool for every select statement.
28
+ # This method is good if your replication is very fast. Otherwise there is a chance you could
29
+ # get inconsistent results from one request to the next. This can result in mysterious failures
30
+ # if your code selects a value in one statement and then uses in another statement. You can wind
31
+ # up trying to use a value from one server that hasn't been replicated to another one yet.
32
+ # This method is best if you have few processes which generate a lot of queries and you have
33
+ # fast replication.
34
+ #def use_random_connection
35
+ # if block_given?
36
+ # set_read_only_connection_type(:random){yield}
37
+ # else
38
+ # Thread.current[:read_only_connection] = :random
39
+ # end
40
+ #end
41
+
42
+ # Call this method to pick a random connection from the read pool and use it for all subsequent
43
+ # select statements. This provides consistency from one select statement to the next. This
44
+ # method should always be called with a block otherwise you can end up with an imbalanced read
45
+ # pool. This method is best if you have lots of processes which have a relatively few select
46
+ # statements or a slow replication mechanism. Generally this is the best method to use for web
47
+ # applications.
48
+ #def use_persistent_read_connection
49
+ # if block_given?
50
+ # set_read_only_connection_type(:persistent){yield}
51
+ # else
52
+ # Thread.current[:read_only_connection] = {}
53
+ # end
54
+ #end
55
+
56
+ # Call this method to use the master connection for all subsequent select statements. This
57
+ # method is most useful when you are doing lots of updates since it guarantees consistency
58
+ # if you do a select immediately after an update or insert.
59
+ #
60
+ # The master connection will also be used for selects inside any transaction blocks. It will
61
+ # also be used if you pass :readonly => false to any ActiveRecord.find method.
62
+ #def use_master_connection
63
+ # if block_given?
64
+ # set_read_only_connection_type(:master){yield}
65
+ # else
66
+ # Thread.current[:read_only_connection] = :master
67
+ # end
68
+ #end
69
+
70
+ # Set the read only connection type to either :master, :random, or :persistent.
71
+ #def set_read_only_connection_type(connection_type)
72
+ # saved_connection = Thread.current[:read_only_connection]
73
+ # retval = nil
74
+ # begin
75
+ # connection_type = {} if connection_type == :persistent
76
+ # Thread.current[:read_only_connection] = connection_type
77
+ # retval = yield if block_given?
78
+ # ensure
79
+ # Thread.current[:read_only_connection] = saved_connection
80
+ # end
81
+ # return retval
82
+ #end
83
+
84
+ # Get the read only connection type currently in use. Will be one of :master, :random, or :persistent.
85
+ #def read_only_connection_type(default = :master)
86
+ # connection_type = Thread.current[:read_only_connection] || default
87
+ # connection_type = :persistent if connection_type.kind_of?(Hash)
88
+ # return connection_type
89
+ #end
90
+
91
+ # Get a connection from a connection pool.
92
+ #def get_a_connection(pool_connection)
93
+ # #return pool_connection.master_connection if pool_connection.using_master_connection?
94
+ # #connection_type = Thread.current[:read_only_connection]
95
+ #
96
+ # if connection_type.kind_of?(Hash)
97
+ # connection = connection_type[pool_connection]
98
+ # unless connection
99
+ # connection = pool_connection.random_connection
100
+ # connection_type[pool_connection] = connection
101
+ # end
102
+ # return connection
103
+ # elsif connection_type == :random
104
+ # return pool_connection.random_connection
105
+ # else
106
+ # return pool_connection.master_connection
107
+ # end
108
+ #end
109
+
110
+ # This method is provided as a way to change the persistent connection when it fails and a new one is substituted.
111
+ #def set_persistent_read_connection(pool_connection, read_connection)
112
+ # connection_type = Thread.current[:read_only_connection]
113
+ # connection_type[pool_connection] = read_connection if connection_type.kind_of?(Hash)
114
+ #end
115
+
116
+ #def clear_read_only_connection
117
+ # Thread.current[:read_only_connection] = nil
118
+ #end
119
+
120
+ # Get the connection adapter class for an adapter name. The class will be loaded from
121
+ # ActiveRecord::ConnectionAdapters::NameAdapter where Name is the camelized version of the name.
122
+ # If the adapter class does not fit this pattern (i.e. sqlite3 => SQLite3Adapter), then add
123
+ # the mapping to the +ADAPTER_TO_CLASS_NAME_MAP+ Hash.
124
+ def adapter_class_for(name)
125
+ name = name.to_s
126
+ class_name = ADAPTER_TO_CLASS_NAME_MAP[name] || name.camelize
127
+ "ActiveRecord::ConnectionAdapters::#{class_name}Adapter".constantize
128
+ end
129
+ end
130
+
131
+ end
metadata ADDED
@@ -0,0 +1,207 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maria_db_cluster_pool
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - "Ren\xC3\xA9 Schultz Madsen"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-04-04 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 21
30
+ segments:
31
+ - 2
32
+ - 3
33
+ - 11
34
+ version: 2.3.11
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rake
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 49
46
+ segments:
47
+ - 0
48
+ - 8
49
+ - 7
50
+ version: 0.8.7
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 2
64
+ - 0
65
+ version: "2.0"
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: jeweler
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ type: :runtime
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: mysql
84
+ prerelease: false
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ type: :runtime
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: activerecord
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 2
107
+ - 2
108
+ - 2
109
+ version: 2.2.2
110
+ type: :runtime
111
+ version_requirements: *id006
112
+ - !ruby/object:Gem::Dependency
113
+ name: rspec
114
+ prerelease: false
115
+ requirement: &id007 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ hash: 3
121
+ segments:
122
+ - 2
123
+ - 0
124
+ version: "2.0"
125
+ type: :development
126
+ version_requirements: *id007
127
+ - !ruby/object:Gem::Dependency
128
+ name: jeweler
129
+ prerelease: false
130
+ requirement: &id008 !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ hash: 3
136
+ segments:
137
+ - 0
138
+ version: "0"
139
+ type: :development
140
+ version_requirements: *id008
141
+ - !ruby/object:Gem::Dependency
142
+ name: mysql
143
+ prerelease: false
144
+ requirement: &id009 !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ hash: 3
150
+ segments:
151
+ - 0
152
+ version: "0"
153
+ type: :development
154
+ version_requirements: *id009
155
+ description:
156
+ email: rm@microting.com
157
+ executables: []
158
+
159
+ extensions: []
160
+
161
+ extra_rdoc_files:
162
+ - MIT-LICENSE
163
+ - README.rdoc
164
+ files:
165
+ - MIT-LICENSE
166
+ - README.rdoc
167
+ - Rakefile
168
+ - lib/active_record/connection_adapters/maria_db_cluster_pool_adapter.rb
169
+ - lib/maria_db_cluster_pool.rb
170
+ - lib/maria_db_cluster_pool/arel_compiler.rb
171
+ - lib/maria_db_cluster_pool/connect_timeout.rb
172
+ has_rdoc: true
173
+ homepage: https://github.com/renemadsen/maria_db_cluster_pool
174
+ licenses: []
175
+
176
+ post_install_message:
177
+ rdoc_options: []
178
+
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ none: false
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ hash: 3
187
+ segments:
188
+ - 0
189
+ version: "0"
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ hash: 3
196
+ segments:
197
+ - 0
198
+ version: "0"
199
+ requirements: []
200
+
201
+ rubyforge_project:
202
+ rubygems_version: 1.5.3
203
+ signing_key:
204
+ specification_version: 3
205
+ summary: Add support for master/master database clusters in ActiveRecord to improve performance.
206
+ test_files: []
207
+