slave_pools 0.1.2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca6c92616b9b71f41b9a8f0acf4487266dddc08e
4
+ data.tar.gz: 353fe4fd321793f797ca765a6a1fbac2cd942485
5
+ SHA512:
6
+ metadata.gz: e8a36182804f50de04b2c049dd671244a1b6a90dc6ab53a0ba57df3898caf171da964af02c2871e19b2a1f673ba14560cb6e797e0b950ef463f228858524d606
7
+ data.tar.gz: e22b49cd625efcbfc5baff7b7868f79494f0f9499d992fafc8cdccfba6580855a55c59023e0b18492db0e784dc24f32974b807b8f0d2dbdaecd3d33e37569ea8
data/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # SlavePools
2
+
3
+ Easy Single Master/ Multiple Slave Setup for use in Ruby/Rails projects
4
+
5
+ [![Build
6
+ Status](https://travis-ci.org/kickstarter/slave_pools.png?branch=owningit)](https://travis-ci.org/kickstarter/slave_pools)
7
+
8
+ ## Overview
9
+
10
+ SlavePools replaces ActiveRecord's connection with a proxy that routes database interactions to the proper connection. Safe (whitelisted) methods may go to the current replica, and all other methods go to the master connection.
11
+
12
+ SlavePools also provides helpers so you can customize your replica strategy. You can organize replicas into pools and cycle through them (e.g. in a before_filter). You can make the connection default to the master, or the default replica pool, and then use block helpers to temporarily change the behavior (e.g. in an around_filter).
13
+
14
+ * Uses a naming convention in database.yml to designate replica pools.
15
+ * Defaults to a given replica pool, but may also be configured to default to master.
16
+ * Routes database interactions (queries) to the right connection
17
+ * Whitelisted queries go to the current connection (might be a replica).
18
+ * All queries inside a transaction run on master.
19
+ * All other queries are also sent to the master connection.
20
+ * Supports ActiveRecord's in-memory query caching.
21
+ * Helper methods can be used to easily load balance replicas, route traffic to different replica pools, or run directly against master. (examples below)
22
+
23
+ ## Not Supported
24
+
25
+ * Sharding.
26
+ * Automatic load balancing strategies.
27
+ * Replica weights. You can accomplish this in your own load balancing strategy.
28
+ * Whitelisting models that always use master.
29
+ * Blacklisting poorly performing replicas. This could cause load spikes on your master. Whatever provisions your database.yml should make this choice.
30
+
31
+ ## Installation and Setup
32
+
33
+ Add to your Gemfile:
34
+
35
+ gem 'slave_pools'
36
+
37
+ ### Adding Replicas
38
+
39
+ Add entries to your database.yml in the form of `<environment>_pool_<pool_name>_name_<db_name>`
40
+
41
+ For example:
42
+
43
+ # Master connection for production environment
44
+ production:
45
+ adapter: mysql
46
+ database: myapp_production
47
+ username: root
48
+ password:
49
+ host: localhost
50
+
51
+ # Default pool for production environment
52
+ production_pool_default_name_replica1:
53
+ adapter: mysql
54
+ database: replica_db1
55
+ username: root
56
+ password:
57
+ host: 10.0.0.2
58
+ production_pool_default_name_replica2:
59
+ ...
60
+
61
+ # Special pool for production environment
62
+ production_pool_admin_name_replica1:
63
+ ...
64
+ production_pool_admin_name_another_replica:
65
+ ...
66
+
67
+ ### Simulating Replicas
68
+
69
+ If you don't have any replicas (e.g. in your development environment), SlavePools will create a default pool containing only master. But if you want to mimic your production environment more closely you can create a read-only mysql user and use it like a replica.
70
+
71
+ # Development connection
72
+ development: &dev
73
+ adapter: mysql
74
+ database: myapp_development
75
+ username: root
76
+ password:
77
+ host: localhost
78
+
79
+ development_pool_default_name_replica1:
80
+ username: readonly
81
+ <<: &dev
82
+
83
+ Don't do this in your test environment if you use transactional tests though! The replica connections won't be able to see any fixtures or factory data.
84
+
85
+ ### Configuring
86
+
87
+ Add a `config/initializers/slave_pools.rb` if you want to change config settings:
88
+
89
+ SlavePools.config.defaults_to_master = true
90
+
91
+ ## Usage
92
+
93
+ Toggle to next replica:
94
+
95
+ SlavePools.next_slave!
96
+
97
+ Specify a pool besides the default:
98
+
99
+ SlavePools.with_pool('other_pool') { #do stuff }
100
+
101
+ Specifically use the master for a call:
102
+
103
+ SlavePools.with_master { #do stuff }
104
+
105
+ ### Load Balancing
106
+
107
+ If you have multiple replicas in a pool and you'd like to load balance requests between them, you can easily accomplish this with a `before_filter`:
108
+
109
+ class ApplicationController < ActionController::Base
110
+ after_filter :switch_to_next_slave
111
+
112
+ protected
113
+
114
+ def switch_to_next_slave
115
+ SlavePools.next_slave!
116
+ end
117
+ end
118
+
119
+ ### Specialty Pools
120
+
121
+ If you have specialized replica pools and would like to use them for different controllers or actions, you can use an `around_filter`:
122
+
123
+ class ApplicationController < ActionController::Base
124
+ around_filter :use_special_replicas
125
+
126
+ protected
127
+
128
+ def use_special_replicas
129
+ SlavePools.with_pool('special'){ yield }
130
+ end
131
+ end
132
+
133
+ ### Replica Lag
134
+
135
+ By default, writes are sent to the master and reads are sent to replicas. But replicas might lag behind the master by seconds or even minutes. So if you write to master during a request you probably want to read from master in that request as well. You may even want to read from the master on the _next_ request, to cover redirects.
136
+
137
+ Here's one way to accomplish that:
138
+
139
+ class ApplicationController < ActionController::Base
140
+
141
+ around_filter :stick_to_master_for_updates
142
+ around_filter :use_master_for_redirect #goes with above
143
+
144
+ def stick_to_master_for_updates
145
+ if request.get?
146
+ yield
147
+ else
148
+ SlavePools.with_master { yield }
149
+ session[:stick_to_master] = 1
150
+ end
151
+ end
152
+
153
+ def use_master_for_redirect
154
+ if session[:stick_to_master]
155
+ session[:stick_to_master] = nil
156
+ SlavePools.with_master { yield }
157
+ else
158
+ yield
159
+ end
160
+ end
161
+ end
162
+
163
+ ## Running specs
164
+
165
+ If you haven't already, install the rspec gem, then set up your database
166
+ with a test database and a read_only user.
167
+
168
+ To match spec/config/database.yml, you can run:
169
+
170
+ rake bootstrap
171
+
172
+ From the plugin directory, run:
173
+
174
+ rspec spec
175
+
176
+ ## Authors
177
+
178
+ Author: Dan Drabik, Lance Ivy
179
+
180
+ Copyright (c) 2012-2013, Kickstarter
181
+
182
+ Released under the MIT license
183
+
184
+ ## See also
185
+
186
+ ### MultiDb
187
+
188
+ The project is based on:
189
+
190
+ * https://github.com/schoefmax/multi_db
191
+
192
+ ### Masochism
193
+
194
+ The original master/slave plugin:
195
+
196
+ * http://github.com/technoweenie/masochism
@@ -1,23 +1,18 @@
1
- module SlavePoolsModule
1
+ module SlavePools
2
2
  module ActiveRecordExtensions
3
3
  def self.included(base)
4
- base.send :include, InstanceMethods
5
4
  base.send :extend, ClassMethods
6
5
  base.cattr_accessor :connection_proxy
7
- # handle subclasses which were defined by the framework or plugins
8
- base.send(:descendants).each do |child|
9
- child.hijack_connection
10
- end
11
6
  end
12
7
 
13
- module InstanceMethods
14
- def reload(options = nil)
15
- self.connection_proxy.with_master { super }
16
- end
8
+ def reload(options = nil)
9
+ self.connection_proxy.with_master { super }
17
10
  end
18
11
 
19
12
  module ClassMethods
20
- # Make sure transactions always switch to the master
13
+ # Make sure transactions run on master
14
+ # Even if they're initiated from ActiveRecord::Base
15
+ # (which doesn't have our hijack).
21
16
  def transaction(options = {}, &block)
22
17
  if self.connection.kind_of?(ConnectionProxy)
23
18
  super
@@ -25,30 +20,6 @@ module SlavePoolsModule
25
20
  self.connection_proxy.with_master { super }
26
21
  end
27
22
  end
28
-
29
- # Make sure caching always uses master connection
30
- def cache(&block)
31
- if ActiveRecord::Base.configurations.blank?
32
- yield
33
- else
34
- ActiveRecord::Base.connection.cache(&block)
35
- end
36
- end
37
-
38
- def inherited(child)
39
- super
40
- child.hijack_connection
41
- end
42
-
43
- def hijack_connection
44
- return if ConnectionProxy.master_models.include?(self.to_s)
45
- # logger.info "[SlavePools] hijacking connection for #{self.to_s}" # commenting out noisy logging
46
- class << self
47
- def connection
48
- self.connection_proxy
49
- end
50
- end
51
- end
52
23
  end
53
24
  end
54
- end
25
+ end
@@ -0,0 +1,22 @@
1
+ module SlavePools
2
+ class Config
3
+ # The current environment. Normally set to Rails.env, but
4
+ # will default to 'development' outside of Rails apps.
5
+ attr_accessor :environment
6
+
7
+ # When true, all queries will go to master unless wrapped in with_pool{}.
8
+ # When false, all safe queries will go to the current replica unless wrapped in with_master{}.
9
+ # Defaults to false.
10
+ attr_accessor :defaults_to_master
11
+
12
+ # The list of methods considered safe to send to a readonly connection.
13
+ # Defaults are based on Rails version.
14
+ attr_accessor :safe_methods
15
+
16
+ def initialize
17
+ @environment = 'development'
18
+ @defaults_to_master = false
19
+ @safe_methods = []
20
+ end
21
+ end
22
+ end
@@ -1,282 +1,143 @@
1
1
  require 'active_record/connection_adapters/abstract/query_cache'
2
2
  require 'set'
3
3
 
4
- module SlavePoolsModule
4
+ module SlavePools
5
5
  class ConnectionProxy
6
- include ActiveRecord::ConnectionAdapters::QueryCache
7
- include QueryCacheCompat
8
-
9
- # Safe methods are those that should either go to the slave ONLY or go
10
- # to the current active connection.
11
- SAFE_METHODS = Set.new([ :select_all, :select_one, :select_value, :select_values,
12
- :select_rows, :select, :verify!, :raw_connection, :active?, :reconnect!,
13
- :disconnect!, :reset_runtime, :log, :log_info ])
14
-
15
- if ActiveRecord.const_defined?(:SessionStore) # >= Rails 2.3
16
- DEFAULT_MASTER_MODELS = ['ActiveRecord::SessionStore::Session']
17
- else # =< Rails 2.3
18
- DEFAULT_MASTER_MODELS = ['CGI::Session::ActiveRecordStore::Session']
19
- end
6
+ include SlavePools::QueryCache
20
7
 
21
8
  attr_accessor :master
22
- attr_accessor :master_depth, :current, :current_pool
9
+ attr_accessor :master_depth, :current, :current_pool, :slave_pools
23
10
 
24
11
  class << self
25
-
26
- # defaults to Rails.env if multi_db is used with Rails
27
- # defaults to 'development' when used outside Rails
28
- attr_accessor :environment
29
-
30
- # a list of models that should always go directly to the master
31
- #
32
- # Example:
33
- #
34
- # SlavePool::ConnectionProxy.master_models = ['MySessionStore', 'PaymentTransaction']
35
- attr_accessor :master_models
36
-
37
- #true or false - whether you want to include the ActionController helpers or not
38
- #this allow
39
-
40
- # if master should be the default db
41
- attr_accessor :defaults_to_master
42
-
43
- # #setting a config instance variable so that thinking sphinx,and other gems that use the connection.instance_variable_get(:@config), work correctly
44
- attr_accessor :config
45
-
46
- # Replaces the connection of ActiveRecord::Base with a proxy and
47
- # establishes the connections to the slaves.
48
- def setup!
49
- self.master_models ||= DEFAULT_MASTER_MODELS
50
- self.environment ||= (defined?(Rails.env) ? Rails.env : 'development')
51
-
52
- slave_pools = init_slave_pools
53
- # if there are no slave pools, we just want to silently exit and not edit the ActiveRecord::Base.connection
54
- if !slave_pools.empty?
55
- master = ActiveRecord::Base
56
- master.send :include, SlavePoolsModule::ActiveRecordExtensions
57
- ActiveRecord::Observer.send :include, SlavePoolsModule::ObserverExtensions
58
-
59
- master.connection_proxy = new(master, slave_pools)
60
- master.logger.info("** slave_pools with master and #{slave_pools.length} slave_pool#{"s" if slave_pools.length > 1} (#{slave_pools.keys}) loaded.")
61
- else
62
- ActiveRecord::Base.logger.info(" No Slave Pools specified for this environment") #this is currently not logging
12
+ def generate_safe_delegations
13
+ SlavePools.config.safe_methods.each do |method|
14
+ generate_safe_delegation(method) unless instance_methods.include?(method)
63
15
  end
64
16
  end
65
17
 
66
18
  protected
67
19
 
68
- def init_slave_pools
69
- slave_pools = {}
70
- ActiveRecord::Base.configurations.each do |name, db_config|
71
- # look for dbs matching the slave_pool format and verify a test connection before adding it to the pools
72
- if name.to_s =~ /#{self.environment}_pool_(.*)_name_(.*)/ && connection_valid?(db_config)
73
- slave_pools = add_to_pool(slave_pools, $1, $2, name, db_config)
20
+ def generate_safe_delegation(method)
21
+ class_eval <<-END, __FILE__, __LINE__ + 1
22
+ def #{method}(*args, &block)
23
+ route_to(current, :#{method}, *args, &block)
74
24
  end
75
- end
76
- return slave_pools
25
+ END
77
26
  end
27
+ end
78
28
 
79
- private :new
80
-
81
- end # end class << self
82
-
83
- def initialize(master, slave_pools)
84
- @slave_pools = {}
85
- slave_pools.each do |pool_name, slaves|
86
- @slave_pools[pool_name.to_sym] = SlavePool.new(pool_name, slaves)
87
- end
88
- @master = master
89
- @reconnect = false
29
+ def initialize(master, pools)
30
+ @master = master
31
+ @slave_pools = pools
32
+ @master_depth = 0
90
33
  @current_pool = default_pool
91
- if self.class.defaults_to_master
92
- @current = @master
93
- @master_depth = 1
94
- @config = master.connection.instance_variable_get(:@config)
34
+
35
+ if SlavePools.config.defaults_to_master
36
+ @current = master
95
37
  else
96
- @current = slave
97
- @master_depth = 0
98
- @config = @current.config_hash #setting this
38
+ @current = current_slave
99
39
  end
100
40
 
101
- end
102
-
103
- def default_pool
104
- @slave_pools[:default] || @slave_pools.values.first #if there is no default specified, use the first pool found
105
- end
106
-
107
- def slave_pools
108
- @slave_pools
109
- end
110
-
111
- def slave
112
- @current_pool.current
41
+ # this ivar is for ConnectionAdapter compatibility
42
+ # some gems (e.g. newrelic_rpm) will actually use
43
+ # instance_variable_get(:@config) to find it.
44
+ @config = current.connection_config
113
45
  end
114
46
 
115
47
  def with_pool(pool_name = 'default')
116
- @current_pool = @slave_pools[pool_name.to_sym] || default_pool
117
- @current = slave unless within_master_block?
48
+ last_conn, last_pool = self.current, self.current_pool
49
+ self.current_pool = slave_pools[pool_name.to_sym] || default_pool
50
+ self.current = current_slave unless within_master_block?
118
51
  yield
119
52
  ensure
120
- @current_pool = default_pool
121
- @current = slave unless within_master_block?
53
+ self.current_pool = last_pool
54
+ self.current = last_conn
122
55
  end
123
56
 
124
57
  def with_master
125
- @current = @master
126
- @master_depth += 1
58
+ last_conn = self.current
59
+ self.current = master
60
+ self.master_depth += 1
127
61
  yield
128
62
  ensure
129
- @master_depth -= 1
130
- @master_depth = 0 if @master_depth < 0 # ensure that master depth never gets below 0
131
- @current = slave if !within_master_block?
132
- end
133
-
134
- def within_master_block?
135
- @master_depth > 0
63
+ self.master_depth = [master_depth - 1, 0].max
64
+ self.current = last_conn
136
65
  end
137
66
 
138
- def transaction(start_db_transaction = true, &block)
139
- with_master { @master.retrieve_connection.transaction(start_db_transaction, &block) }
67
+ def transaction(*args, &block)
68
+ with_master { master.transaction(*args, &block) }
140
69
  end
141
70
 
142
- # Calls the method on master/slave and dynamically creates a new
143
- # method on success to speed up subsequent calls
144
- def method_missing(method, *args, &block)
145
- send(target_method(method), method, *args, &block).tap do
146
- create_delegation_method!(method)
147
- end
148
- end
149
-
150
- # Switches to the next slave database for read operations.
151
- # Fails over to the master database if all slaves are unavailable.
152
71
  def next_slave!
153
- return if within_master_block? # don't if in with_master block
154
- @current = @current_pool.next
155
- rescue
156
- @current = @master
157
- end
158
-
159
- protected
160
-
161
- def create_delegation_method!(method)
162
- self.instance_eval %Q{
163
- def #{method}(*args, &block)
164
- #{target_method(method)}(:#{method}, *args, &block)
165
- end
166
- }, __FILE__, __LINE__
167
- end
168
-
169
- def target_method(method)
170
- unsafe?(method) ? :send_to_master : :send_to_current
171
- end
172
-
173
- def send_to_master(method, *args, &block)
174
- reconnect_master! if @reconnect
175
- @master.retrieve_connection.send(method, *args, &block)
176
- rescue => e
177
- log_errors(e, 'send_to_master', method)
178
- raise_master_error(e)
179
- end
180
-
181
- def send_to_current(method, *args, &block)
182
- reconnect_master! if @reconnect && master?
183
- # logger.debug "[SlavePools] Using #{@current.name}"
184
- @current = @master if unsafe?(method) #failsafe to avoid sending dangerous method to master
185
- @current.retrieve_connection.send(method, *args, &block)
186
- rescue Mysql2::Error, ActiveRecord::StatementInvalid => e
187
- log_errors(e, 'send_to_current', method)
188
- raise_master_error(e) if master?
189
- logger.warn "[SlavePools] Error reading from slave database"
190
- logger.error %(#{e.message}\n#{e.backtrace.join("\n")})
191
- if e.message.match(/Timeout waiting for a response from the last query/)
192
- # Verify that the connection is active & re-raise
193
- logger.error "[SlavePools] Slave Query Timeout - do not send to master"
194
- @current.retrieve_connection.verify!
195
- raise e
196
- else
197
- logger.error "[SlavePools] Slave Query Error - sending to master"
198
- send_to_master(method, *args, &block) # if cant connect, send the query to master
199
- end
72
+ return if within_master_block?
73
+ self.current = current_pool.next
200
74
  end
201
75
 
202
- def reconnect_master!
203
- @master.retrieve_connection.reconnect!
204
- @reconnect = false
76
+ def current_slave
77
+ current_pool.current
205
78
  end
206
79
 
207
- def raise_master_error(error)
208
- logger.fatal "[SlavePools] Error accessing master database. Scheduling reconnect"
209
- @reconnect = true
210
- raise error
211
- end
80
+ protected
212
81
 
213
- def unsafe?(method)
214
- !SAFE_METHODS.include?(method)
82
+ def default_pool
83
+ slave_pools[:default] || slave_pools.values.first
215
84
  end
216
85
 
217
- def master?
218
- @current == @master
86
+ # Proxies any unknown methods to master.
87
+ # Safe methods have been generated during `setup!`.
88
+ # Creates a method to speed up subsequent calls.
89
+ def method_missing(method, *args, &block)
90
+ generate_unsafe_delegation(method)
91
+ send(method, *args, &block)
219
92
  end
220
93
 
221
- def logger
222
- ActiveRecord::Base.logger
94
+ def within_master_block?
95
+ master_depth > 0
223
96
  end
224
97
 
225
- private
226
-
227
- def self.add_to_pool(slave_pools, pool_name, slave_name, full_db_name, db_config)
228
- slave_pools[pool_name] ||= []
229
- db_config_with_symbols = db_config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
230
- SlavePoolsModule.module_eval %Q{
231
- class #{pool_name.camelize}#{slave_name.camelize} < ActiveRecord::Base
232
- self.abstract_class = true
233
- establish_connection :#{full_db_name}
234
- def self.config_hash
235
- #{db_config_with_symbols.inspect}
236
- end
98
+ def generate_unsafe_delegation(method)
99
+ self.class_eval <<-END, __FILE__, __LINE__ + 1
100
+ def #{method}(*args, &block)
101
+ route_to(master, :#{method}, *args, &block)
237
102
  end
238
- }, __FILE__, __LINE__
239
- slave_pools[pool_name] << "SlavePoolsModule::#{pool_name.camelize}#{slave_name.camelize}".constantize
240
- return slave_pools
103
+ END
241
104
  end
242
105
 
243
- # method to verify whether DB connection is active?
244
- def self.connection_valid?(db_config = nil)
245
- is_connected = false
246
- if db_config
247
- begin
248
- ActiveRecord::Base.establish_connection(db_config)
249
- ActiveRecord::Base.connection
250
- is_connected = ActiveRecord::Base.connected?
251
- rescue => e
252
- log_errors(e, 'self.connection_valid?')
253
- ensure
254
- ActiveRecord::Base.establish_connection(environment) #rollback to the current environment to avoid issues
255
- end
106
+ def route_to(conn, method, *args, &block)
107
+ conn.retrieve_connection.send(method, *args, &block)
108
+ rescue => e
109
+ SlavePools.log :error, "Error during ##{method}: #{e}"
110
+ log_proxy_state
111
+ raise if conn == master
112
+
113
+ if safe_to_replay(e)
114
+ SlavePools.log :error, %(#{e.message}\n#{e.backtrace.join("\n")})
115
+ SlavePools.log :error, "Replaying on master."
116
+ route_to(master, method, *args, &block)
117
+ else
118
+ current.retrieve_connection.verify! # may reconnect
119
+ raise e
256
120
  end
257
- return is_connected
258
121
  end
259
122
 
260
- # logging instance errors
261
- def log_errors(error, sp_method, db_method)
262
- logger.error "[SlavePools] - Error: #{error}"
263
- logger.error "[SlavePools] - SlavePool Method: #{sp_method}"
264
- logger.error "[SlavePools] - Master Value: #{@master}"
265
- logger.error "[SlavePools] - Master Depth: #{@master_depth}"
266
- logger.error "[SlavePools] - Current Value: #{@current}"
267
- logger.error "[SlavePools] - Current Pool: #{@current_pool}"
268
- logger.error "[SlavePools] - Current Pool Slaves: #{@current_pool.slaves}" if @current_pool
269
- logger.error "[SlavePools] - Current Pool Name: #{@current_pool.name}" if @current_pool
270
- logger.error "[SlavePools] - Reconnect Value: #{@reconnect}"
271
- logger.error "[SlavePools] - Default Pool: #{default_pool}"
272
- logger.error "[SlavePools] - DB Method: #{db_method}"
123
+ # decides whether to replay query against master based on the
124
+ # exception raised. this could become more sophisticated.
125
+ def safe_to_replay(e)
126
+ # don't replay queries that time out. we don't have the time, and they
127
+ # could be dangerous.
128
+ ! e.message.match(/Timeout waiting for a response from the last query/)
273
129
  end
274
130
 
275
- # logging class errors
276
- def self.log_errors(error, sp_method)
277
- logger = ActiveRecord::Base.logger
278
- logger.error "[SlavePools] - Error: #{error}"
279
- logger.error "[SlavePools] - SlavePool Method: #{sp_method}"
131
+ private
132
+
133
+ def log_proxy_state
134
+ SlavePools.log :error, "Master Value: #{master}"
135
+ SlavePools.log :error, "Master Depth: #{master_depth}"
136
+ SlavePools.log :error, "Current Value: #{current}"
137
+ SlavePools.log :error, "Current Pool: #{current_pool}"
138
+ SlavePools.log :error, "Current Pool Slaves: #{current_pool.slaves}" if current_pool
139
+ SlavePools.log :error, "Current Pool Name: #{current_pool.name}" if current_pool
140
+ SlavePools.log :error, "Default Pool: #{default_pool}"
280
141
  end
281
142
  end
282
143
  end
@@ -0,0 +1,24 @@
1
+ require 'rails/engine'
2
+
3
+ module SlavePools
4
+ class Engine < Rails::Engine
5
+ initializer 'slave_pools.defaults' do
6
+ SlavePools.config.environment = Rails.env
7
+
8
+ SlavePools.config.safe_methods =
9
+ if ActiveRecord::VERSION::MAJOR == 3
10
+ [
11
+ :select_all, :select_one, :select_value, :select_values,
12
+ :select_rows, :select, :verify!, :raw_connection, :active?, :reconnect!,
13
+ :disconnect!, :reset_runtime, :log, :log_info
14
+ ]
15
+ else
16
+ warn "Unsupported ActiveRecord version #{ActiveRecord.version}. Please whitelist the safe methods."
17
+ end
18
+ end
19
+
20
+ config.after_initialize do
21
+ SlavePools.setup!
22
+ end
23
+ end
24
+ end