replidog 0.1.0

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/Appraisals +17 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +188 -0
  7. data/Rakefile +2 -0
  8. data/gemfiles/rails32.gemfile +7 -0
  9. data/gemfiles/rails4.gemfile +7 -0
  10. data/gemfiles/rails41.gemfile +7 -0
  11. data/gemfiles/rails42.gemfile +7 -0
  12. data/lib/replidog/active_record.rb +1 -0
  13. data/lib/replidog/model.rb +89 -0
  14. data/lib/replidog/proxy.rb +189 -0
  15. data/lib/replidog/proxy_handler.rb +68 -0
  16. data/lib/replidog/scope_proxy.rb +24 -0
  17. data/lib/replidog/version.rb +3 -0
  18. data/lib/replidog.rb +7 -0
  19. data/replidog.gemspec +43 -0
  20. data/spec/dummy/Rakefile +7 -0
  21. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  22. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/models/admin.rb +3 -0
  26. data/spec/dummy/app/models/ingredient.rb +3 -0
  27. data/spec/dummy/app/models/profile.rb +3 -0
  28. data/spec/dummy/app/models/recipe.rb +6 -0
  29. data/spec/dummy/app/models/user.rb +3 -0
  30. data/spec/dummy/app/models/user_table.rb +5 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/config/application.rb +51 -0
  33. data/spec/dummy/config/boot.rb +10 -0
  34. data/spec/dummy/config/database.yml +65 -0
  35. data/spec/dummy/config/environment.rb +5 -0
  36. data/spec/dummy/config/environments/development.rb +23 -0
  37. data/spec/dummy/config/environments/production.rb +71 -0
  38. data/spec/dummy/config/environments/test.rb +33 -0
  39. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/dummy/config/initializers/inflections.rb +15 -0
  41. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  42. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  43. data/spec/dummy/config/initializers/session_store.rb +8 -0
  44. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/spec/dummy/config/locales/en.yml +5 -0
  46. data/spec/dummy/config/routes.rb +2 -0
  47. data/spec/dummy/config.ru +4 -0
  48. data/spec/dummy/db/schema.rb +46 -0
  49. data/spec/dummy/public/404.html +26 -0
  50. data/spec/dummy/public/422.html +26 -0
  51. data/spec/dummy/public/500.html +25 -0
  52. data/spec/dummy/public/favicon.ico +0 -0
  53. data/spec/dummy/script/rails +6 -0
  54. data/spec/replidog/model_spec.rb +273 -0
  55. data/spec/replidog/proxy_spec.rb +58 -0
  56. data/spec/spec_helper.rb +52 -0
  57. metadata +388 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2419525361d0fe6b11f5150aa4641aaa33358978
4
+ data.tar.gz: 954cdd81a2a5056cf153083d3315e6d5ef39e020
5
+ SHA512:
6
+ metadata.gz: 48aab863f60e64c3efc4aa8f0ca6fe64a3830e0e5b0a3efcfb8a5dd9469aae8b0a8f3c6ff9df1f4767d7afcc43db879488038625d3f51ef25f258f74d97dd1b2
7
+ data.tar.gz: ff4b99cd46fe00763a8f82caf7fcbf14ba9c33417bb3993799ddcd669f8fcb827ff61349db335243f68d355c3d5026227eba698f022d0dfb4a867963551ae7ac
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/dummy/log/*.log
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ gemfiles/*.gemfile.lock
16
+ mkmf.log
data/Appraisals ADDED
@@ -0,0 +1,17 @@
1
+ appraise "rails32" do
2
+ gem "activerecord", "~> 3.2.0"
3
+ end
4
+
5
+ appraise "rails4" do
6
+ gem "activerecord", "~> 4.0.0"
7
+ end
8
+
9
+ appraise "rails41" do
10
+ gem "activerecord", "~> 4.1.0"
11
+ end
12
+
13
+ appraise "rails42" do
14
+ gem "activerecord", "~> 4.2.0"
15
+ end
16
+ # vim: ft=ruby
17
+ #
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in replidog.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Manabu Ejima
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # Replidog
2
+
3
+ Multiple mlaster/slave helper for ActiveRecord
4
+
5
+ Based on https://github.com/r7kamura/replicat
6
+
7
+ ## Features
8
+
9
+ * Multiple master/slave
10
+ * Auto switching between master/slave
11
+ * Supports connection management and query cache
12
+ * Supports Rails 3.2, 4.0, 4,1, 4.2
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'replidog'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ ```sh
25
+ $ bundle
26
+ ```
27
+
28
+ Or install it yourself as:
29
+
30
+ ```sh
31
+ $ gem install replidog
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### configuration
37
+
38
+ ```ruby
39
+ # config/database.yml
40
+ production:
41
+ adapter: mysql2
42
+ encoding: utf8
43
+ host: 192.168.24.1
44
+ port: 3306
45
+ replications:
46
+ slave1:
47
+ adapter: mysql2
48
+ encoding: utf8
49
+ host: 192.168.24.2
50
+ port: 3306
51
+ slave2:
52
+ adapter: mysql2
53
+ encoding: utf8
54
+ host: 192.168.24.3
55
+ port: 3306
56
+ slave3:
57
+ adapter: mysql2
58
+ encoding: utf8
59
+ host: 192.168.24.4
60
+ port: 3306
61
+ ```
62
+
63
+ ### replication
64
+
65
+ Now SELECT queries of User model will be sent to slave connections.
66
+
67
+ ```ruby
68
+ # INSERT query is sent to master.
69
+ User.create(name: "replicat")
70
+
71
+ # SELECT query is sent to slave.
72
+ User.first
73
+ ```
74
+
75
+ ### using
76
+
77
+ `using` can help you specify particular connection.
78
+ When you want to send queries to master,
79
+ you can use `using(:master)` to do that (:master is reserved name for `using` method).
80
+ When you want to send queries to a particular slave,
81
+ you can use the slave's name on database.yml like `using(:slave1)`.
82
+
83
+ ```ruby
84
+ # SELECT query is sent to master.
85
+ User.using(:master).first
86
+
87
+ # INSERT query is sent to slave1.
88
+ User.using(:slave1).create(name: "replicat")
89
+
90
+ # :slave1 is used for User connection in the passed block.
91
+ User.using(:slave1) { blog.user }
92
+ ```
93
+
94
+ ### round-robin
95
+
96
+ slave connections are balanced by round-robin way.
97
+
98
+ ```ruby
99
+ User.first # sent to slave1
100
+ User.first # sent to slave2
101
+ User.first # sent to slave3
102
+ User.first # sent to slave1
103
+ User.first # sent to slave2
104
+ User.first # sent to slave3
105
+ User.first # sent to slave1
106
+ User.first # sent to slave2
107
+ User.first # sent to slave3
108
+ ```
109
+
110
+ ### multi master-slave set
111
+
112
+ Pass the master's connection name to `replicate` method.
113
+
114
+ ```ruby
115
+ # app/models/recipe.rb
116
+ class Recipe < RecipeTable
117
+ establish_connection :production_recipe
118
+ end
119
+
120
+ # config/database.yml
121
+ production_base:
122
+ adapter: mysql2
123
+ encoding: utf8
124
+ port: 3306
125
+ production:
126
+ <<: *production_base
127
+ host: 192.168.24.1
128
+ replications:
129
+ slave1:
130
+ <<: *slave
131
+ host: 192.168.24.2
132
+ slave2:
133
+ <<: *slave
134
+ host: 192.168.24.3
135
+ slave3:
136
+ <<: *slave
137
+ host: 192.168.24.4
138
+
139
+ production_recipe:
140
+ <<: *production_base
141
+ host: 192.168.24.5
142
+ replications:
143
+ slave1:
144
+ <<: *slave
145
+ host: 192.168.24.6
146
+ slave2:
147
+ <<: *slave
148
+ host: 192.168.24.7
149
+ slave3:
150
+ <<: *slave
151
+ host: 192.168.24.8
152
+ ```
153
+
154
+ If you want to connecto to `production_recipe` in multiple models, Create Abstract class and extend it.
155
+
156
+ ```ruby
157
+ # app/models/recipe_table.rb
158
+ class RecipeTable < ActiveRecord::Base
159
+ self.abstract_class = true
160
+ establish_connection :production_recipe
161
+ end
162
+
163
+ # app/models/recipe.rb
164
+ class Recipe < RecipeTable
165
+ end
166
+ ```
167
+
168
+ ### connection management / query cache
169
+
170
+ To handle all master/slave connections togegher, the methods related with connection management and query cache are overridden.
171
+ So you don't need to update middlewares and configurations for app servers.
172
+
173
+ List of overridden methods
174
+ * ActiveRecord::Base.clear_active_connections
175
+ * ActiveRecord::Base.clear_reloadable_connections
176
+ * ActiveRecord::Base.clear_all_connections
177
+ * ActiveRecord::Base.connection.enable_query_cache!
178
+ * ActiveRecord::Base.connection.disable_query_cache!
179
+ * ActiveRecord::Base.connection.clear_query_cache!
180
+
181
+
182
+ ## Contributing
183
+
184
+ 1. Fork it ( https://github.com/[my-github-username]/replidog/fork )
185
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
186
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
187
+ 4. Push to the branch (`git push origin my-new-feature`)
188
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 3.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1 @@
1
+ ActiveRecord::Base.extend Replidog::Model
@@ -0,0 +1,89 @@
1
+ require "active_support/core_ext/module/aliasing"
2
+ require "active_support/core_ext/class/attribute"
3
+
4
+ module Replidog
5
+ module Model
6
+ def self.extended(base)
7
+ base.class_attribute :proxy_handler, instance_writer: false
8
+ base.proxy_handler = Replidog::ProxyHandler.new
9
+
10
+ class << base
11
+ alias_method_chain(:establish_connection, :replidog)
12
+ alias_method_chain(:connection, :replidog)
13
+ alias_method_chain(:connected?, :replidog)
14
+ alias_method_chain(:clear_reloadable_connections!, :replidog)
15
+ alias_method_chain(:clear_active_connections!, :replidog)
16
+ alias_method_chain(:clear_all_connections!, :replidog)
17
+ end
18
+ end
19
+
20
+ def establish_connection_with_replidog(spec = ENV["DATABASE_URL"])
21
+ establish_connection_without_replidog(spec)
22
+ proxy_handler.remove_connection(self)
23
+ proxy_handler.establish_connection(connection_pool.spec, self)
24
+ end
25
+
26
+ def connection_with_replidog
27
+ if replicated?
28
+ proxy_handler.retrieve_proxy(self).tap do |proxy|
29
+ proxy.current_model = self
30
+ end
31
+ else
32
+ connection_without_replidog
33
+ end
34
+ end
35
+
36
+ def replicated?
37
+ connection_config[:replications].present?
38
+ end
39
+
40
+ def using(connection_name, &block)
41
+ if replicated?
42
+ _using(connection_name, &block)
43
+ else
44
+ if block_given?
45
+ yield
46
+ else
47
+ self
48
+ end
49
+ end
50
+ end
51
+
52
+ def connected_with_replidog?
53
+ if replicated?
54
+ connection.connected?
55
+ else
56
+ connected_without_replidog?
57
+ end
58
+ end
59
+
60
+ def clear_active_connections_with_replidog!
61
+ clear_active_connections_without_replidog!
62
+ proxy_handler.clear_active_slave_connections! if replicated?
63
+ end
64
+
65
+ def clear_reloadable_connections_with_replidog!
66
+ clear_reloadable_connections_without_replidog!
67
+ proxy_handler.clear_reloadable_slave_connections! if replicated?
68
+ end
69
+
70
+ def clear_all_connections_with_replidog!
71
+ clear_all_connections_without_replidog!
72
+ proxy_handler.clear_all_slave_connections! if replicated?
73
+ end
74
+
75
+ private
76
+
77
+ def _using(connection_name)
78
+ if block_given?
79
+ connection.current_connection_name = connection_name
80
+ yield
81
+ else
82
+ ScopeProxy.new(klass: self, connection_name: connection_name)
83
+ end
84
+ ensure
85
+ connection.current_connection_name = nil if block_given?
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,189 @@
1
+ require "active_record"
2
+ require "active_support/core_ext/object/try"
3
+ require "active_support/core_ext/class/attribute"
4
+
5
+ module Replidog
6
+ class Proxy
7
+ REPLICABLE_METHOD_NAMES = [/^select(?:_\w+)$/]
8
+ REPLICABLE_METHOD_NAMES_REGEXP = /\A#{Regexp.union(REPLICABLE_METHOD_NAMES)}\z/
9
+
10
+ attr_writer :index
11
+
12
+ attr_reader :configuration
13
+
14
+ def initialize(handler, configuration)
15
+ @handler = handler
16
+ @configuration = configuration
17
+ end
18
+
19
+ def current_model
20
+ Thread.current['replidog.current_model']
21
+ end
22
+
23
+ def current_model=(model)
24
+ Thread.current['replidog.current_model'] = model.is_a?(ActiveRecord::Base) ? model.class : model
25
+ end
26
+
27
+ def current_connection_name
28
+ Thread.current['replidog.current_connection_name']
29
+ end
30
+
31
+ def current_connection_name=(connection_name)
32
+ Thread.current['replidog.current_connection_name'] = connection_name
33
+ end
34
+
35
+ def transaction(options = {}, &block)
36
+ old = current_connection_name
37
+ self.current_connection_name ||= :master
38
+ current_connection.transaction(options, &block)
39
+ ensure
40
+ self.current_connection_name = old
41
+ end
42
+
43
+ def connected?
44
+ current_model.connected_without_replidog? && slave_connection_pool_table.values.any?(&:connected?)
45
+ end
46
+
47
+ def clear_active_slave_connections!
48
+ slave_connection_pool_table.each_value do |pool|
49
+ pool.release_connection
50
+ end
51
+ end
52
+
53
+ def clear_reloadable_slave_connections!
54
+ slave_connection_pool_table.each_value do |pool|
55
+ pool.clear_reloadable_connections!
56
+ end
57
+ end
58
+
59
+ def clear_all_slave_connections!
60
+ slave_connection_pool_table.each_value do |pool|
61
+ pool.disconnect!
62
+ end
63
+ end
64
+
65
+ def enable_query_cache!
66
+ @handler.enable_query_cache!
67
+ end
68
+
69
+ def enable_query_cache_for_slaves!
70
+ slave_connection_pool_table.each_value do |pool|
71
+ pool.connection.enable_query_cache!
72
+ end
73
+ end
74
+
75
+ def disable_query_cache!
76
+ @handler.disable_query_cache!
77
+ end
78
+
79
+ def disable_query_cache_for_slaves!
80
+ slave_connection_pool_table.values.each do |pool|
81
+ pool.connection.disable_query_cache!
82
+ end
83
+ end
84
+
85
+ def clear_query_cache
86
+ @handler.clear_query_cache
87
+ end
88
+
89
+ def clear_query_cache_for_slaves
90
+ slave_connection_pool_table.values.each do |pool|
91
+ pool.connection.clear_query_cache
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def method_missing(method_name, *args, &block)
98
+ if current_connection_name
99
+ current_connection.send(method_name, *args, &block)
100
+ else
101
+ connection_by_method_name(method_name).send(method_name, *args, &block)
102
+ end
103
+ end
104
+
105
+ def respond_to_missing?(method, *args)
106
+ master_connection.respond_to?(method, *args) || super
107
+ end
108
+
109
+ def connection_by_method_name(method_name)
110
+ REPLICABLE_METHOD_NAMES_REGEXP === method_name ? slave_connection : master_connection
111
+ end
112
+
113
+ def current_connection
114
+ if current_connection_name.to_s == "master"
115
+ master_connection
116
+ else
117
+ slave_connection_pool_table[current_connection_name.to_s].try(:connection) or raise_connection_not_found
118
+ end
119
+ end
120
+
121
+ def master_connection
122
+ current_model.connection_without_replidog
123
+ end
124
+
125
+ def slave_connection
126
+ slave_connection_pool.connection
127
+ end
128
+
129
+ def slave_connection_pool
130
+ slave_connection_pool_table.values[slave_connection_index]
131
+ end
132
+
133
+ def replicated?
134
+ replications
135
+ end
136
+
137
+ def replications
138
+ @configuration.config[:replications] || []
139
+ end
140
+
141
+ def slave_connection_pool_table
142
+ @slave_connection_pools ||= replications.inject({}) do |table, (name, configuration)|
143
+ table.merge(name => ConnectionPoolCreater.create(configuration))
144
+ end
145
+ end
146
+
147
+ def raise_connection_not_found
148
+ raise StandardError, "connection #{current_connection_name} is not found"
149
+ end
150
+
151
+ def slave_connection_index
152
+ index.tap { increment_slave_connection_index }
153
+ end
154
+
155
+ def increment_slave_connection_index
156
+ self.index = (index + 1) % slave_connection_pool_table.size
157
+ end
158
+
159
+ def index
160
+ @index ||= rand(slave_connection_pool_table.size)
161
+ end
162
+
163
+ # Creates database connection pool from configuration Hash table.
164
+ class ConnectionPoolCreater
165
+ def self.create(*args)
166
+ new(*args).create
167
+ end
168
+
169
+ def initialize(configuration)
170
+ @configuration = configuration.dup
171
+ end
172
+
173
+ def create
174
+ spec =
175
+ if ActiveRecord::VERSION::MAJOR >= 4
176
+ if ActiveRecord::VERSION::MINOR < 1
177
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(@configuration, {}).spec
178
+ else
179
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new({}).spec(@configuration)
180
+ end
181
+ else
182
+ ActiveRecord::Base::ConnectionSpecification::Resolver.new(@configuration, {}).spec
183
+ end
184
+
185
+ ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,68 @@
1
+ module Replidog
2
+ class ProxyHandler
3
+ def initialize
4
+ @proxies = {}
5
+ @class_to_proxy = {}
6
+ end
7
+
8
+ def establish_connection(configuration, klass)
9
+ @proxies[configuration] ||= Proxy.new(self, configuration)
10
+ @class_to_proxy[klass.name] ||= @proxies[configuration]
11
+ end
12
+
13
+ def retrieve_proxy(klass)
14
+ proxy = @class_to_proxy[klass.name]
15
+ return proxy if proxy
16
+ return nil if ActiveRecord::Base == klass
17
+ retrieve_proxy(klass.superclass)
18
+ end
19
+
20
+ def remove_connection(klass)
21
+ proxy = @class_to_proxy.delete(klass.name)
22
+ return nil unless proxy
23
+
24
+ @proxies.delete(proxy.configuration)
25
+ proxy.clear_all_slave_connections!
26
+ proxy.configuration.config
27
+ end
28
+
29
+ def clear_active_slave_connections!
30
+ @proxies.each_value do |proxy|
31
+ proxy.clear_active_slave_connections!
32
+ end
33
+ end
34
+
35
+ def clear_reloadable_slave_connections!
36
+ @proxies.each_value do |proxy|
37
+ proxy.clear_reloadable_slave_connections!
38
+ end
39
+ end
40
+
41
+ def clear_all_slave_connections!
42
+ @proxies.each_value do |proxy|
43
+ proxy.clear_all_slave_connections!
44
+ end
45
+ end
46
+
47
+ def enable_query_cache!
48
+ ActiveRecord::Base.connection_handler.connection_pools.each_value do |connection_pool|
49
+ connection_pool.connection.enable_query_cache!
50
+ end
51
+ @proxies.each_value(&:enable_query_cache_for_slaves!)
52
+ end
53
+
54
+ def disable_query_cache!
55
+ ActiveRecord::Base.connection_handler.connection_pools.each_value do |connection_pool|
56
+ connection_pool.connection.disable_query_cache!
57
+ end
58
+ @proxies.each_value(&:disable_query_cache_for_slaves!)
59
+ end
60
+
61
+ def clear_query_cache
62
+ ActiveRecord::Base.connection_handler.connection_pools.each_value do |connection_pool|
63
+ connection_pool.connection.clear_query_cache
64
+ end
65
+ @proxies.each_value(&:clear_query_cache_for_slaves)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ module Replidog
2
+ class ScopeProxy
3
+ def initialize(attributes)
4
+ @klass = attributes[:klass]
5
+ @connection_name = attributes[:connection_name]
6
+ end
7
+
8
+ private
9
+
10
+ def method_missing(method_name, *args, &block)
11
+ result = using { @klass.send(method_name, *args, &block) }
12
+ if result.respond_to?(:scoped)
13
+ @klass = result
14
+ self
15
+ else
16
+ result
17
+ end
18
+ end
19
+
20
+ def using
21
+ @klass.using(@connection_name) { yield }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Replidog
2
+ VERSION = "0.1.0"
3
+ end
data/lib/replidog.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "active_record"
2
+ require "replidog/model"
3
+ require "replidog/proxy"
4
+ require "replidog/proxy_handler"
5
+ require "replidog/scope_proxy"
6
+ require "replidog/version"
7
+ require "replidog/active_record"