replica_pools 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -7
- data/lib/replica_pools.rb +7 -0
- data/lib/replica_pools/active_record_extensions.rb +5 -1
- data/lib/replica_pools/config.rb +5 -0
- data/lib/replica_pools/connection_proxy.rb +8 -3
- data/lib/replica_pools/engine.rb +5 -1
- data/lib/replica_pools/version.rb +1 -1
- data/spec/connection_proxy_spec.rb +42 -1
- data/spec/slave_pools_spec.rb +10 -0
- data/spec/spec_helper.rb +9 -2
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ccf84c0499a86c7e36948d178c8fb150fc71003b
|
4
|
+
data.tar.gz: 93bc95a40cd6a411ea255bc3fc94e2e8860add71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 947621a358c1034cc8a45b70e1f56d1a58126552c700ec18f3e636cfbd80f44eeb9c3288ad3cc78f142a52a3a9df7668cc746f14ba71cd242596998d96bd3182
|
7
|
+
data.tar.gz: 4cc0aab455a96d11d2c56fb17e3af40ec8ae33502bf1a70064c71c9e26a043f41ea7ab47937923735408827bb358cb766d5e8dadedeefe57b1206df8ba44e302
|
data/README.md
CHANGED
@@ -160,24 +160,37 @@ Here's one way to accomplish that:
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
+
## Disabling Leader
|
164
|
+
|
165
|
+
To disable queries to the leader database -- for instance, in a production
|
166
|
+
console -- set the disable_leader configuration to false. This will raise
|
167
|
+
a ReplicaPools::LeaderDisabled error:
|
168
|
+
|
169
|
+
ReplicaPools.config.disable_leader = false
|
170
|
+
|
163
171
|
## Running specs
|
164
172
|
|
165
|
-
|
166
|
-
with a test database and a read_only user.
|
173
|
+
Tests are run against MySQL 5.6 using docker-compose. 🐋
|
167
174
|
|
168
|
-
To
|
175
|
+
To get set up, first run:
|
169
176
|
|
170
|
-
|
177
|
+
```bash
|
178
|
+
$ docker-compose up
|
179
|
+
$ bundle install
|
180
|
+
$ bundle exec rake bootstrap
|
181
|
+
```
|
171
182
|
|
172
|
-
|
183
|
+
Then you can run tests with:
|
173
184
|
|
174
|
-
|
185
|
+
```bash
|
186
|
+
$ bundle exec rake spec
|
187
|
+
```
|
175
188
|
|
176
189
|
## Authors
|
177
190
|
|
178
191
|
Author: Dan Drabik, Lance Ivy
|
179
192
|
|
180
|
-
Copyright (c) 2012-
|
193
|
+
Copyright (c) 2012-2018, Kickstarter
|
181
194
|
|
182
195
|
Released under the MIT license
|
183
196
|
|
data/lib/replica_pools.rb
CHANGED
@@ -11,6 +11,12 @@ require 'replica_pools/engine' if defined? Rails
|
|
11
11
|
ActiveRecord::Base.send :include, ReplicaPools::ActiveRecordExtensions
|
12
12
|
|
13
13
|
module ReplicaPools
|
14
|
+
class LeaderDisabled < StandardError
|
15
|
+
def to_s
|
16
|
+
"Leader database has been disabled. Re-enable with ReplicaPools.config.disable_leader = false."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
14
20
|
class << self
|
15
21
|
|
16
22
|
def config
|
@@ -45,6 +51,7 @@ module ReplicaPools
|
|
45
51
|
end
|
46
52
|
|
47
53
|
def with_leader
|
54
|
+
raise LeaderDisabled.new if ReplicaPools.config.disable_leader
|
48
55
|
proxy.with_leader{ yield }
|
49
56
|
end
|
50
57
|
|
data/lib/replica_pools/config.rb
CHANGED
@@ -9,6 +9,10 @@ module ReplicaPools
|
|
9
9
|
# Defaults to false.
|
10
10
|
attr_accessor :defaults_to_leader
|
11
11
|
|
12
|
+
# When true, the leader database will not be selectable.
|
13
|
+
# Defaults to false.
|
14
|
+
attr_accessor :disable_leader
|
15
|
+
|
12
16
|
# The list of methods considered safe to send to a readonly connection.
|
13
17
|
# Defaults are based on Rails version.
|
14
18
|
attr_accessor :safe_methods
|
@@ -16,6 +20,7 @@ module ReplicaPools
|
|
16
20
|
def initialize
|
17
21
|
@environment = 'development'
|
18
22
|
@defaults_to_leader = false
|
23
|
+
@disable_leader = false
|
19
24
|
@safe_methods = []
|
20
25
|
end
|
21
26
|
end
|
@@ -55,13 +55,17 @@ module ReplicaPools
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def with_leader
|
58
|
+
raise LeaderDisabled.new if ReplicaPools.config.disable_leader
|
59
|
+
|
58
60
|
last_conn = self.current
|
59
61
|
self.current = leader
|
60
62
|
self.leader_depth += 1
|
61
63
|
yield
|
62
64
|
ensure
|
63
|
-
|
64
|
-
|
65
|
+
if last_conn
|
66
|
+
self.leader_depth = [leader_depth - 1, 0].max
|
67
|
+
self.current = last_conn
|
68
|
+
end
|
65
69
|
end
|
66
70
|
|
67
71
|
def transaction(*args, &block)
|
@@ -104,6 +108,7 @@ module ReplicaPools
|
|
104
108
|
end
|
105
109
|
|
106
110
|
def route_to(conn, method, *args, &block)
|
111
|
+
raise ReplicaPools::LeaderDisabled.new if ReplicaPools.config.disable_leader && conn == leader
|
107
112
|
conn.retrieve_connection.send(method, *args, &block)
|
108
113
|
rescue => e
|
109
114
|
ReplicaPools.log :error, "Error during ##{method}: #{e}"
|
@@ -117,7 +122,7 @@ module ReplicaPools
|
|
117
122
|
ReplicaPools.log :error, "Current Connection: #{current}"
|
118
123
|
ReplicaPools.log :error, "Current Pool Name: #{current_pool.name}"
|
119
124
|
ReplicaPools.log :error, "Current Pool Members: #{current_pool.replicas}"
|
120
|
-
ReplicaPools.log :error, "
|
125
|
+
ReplicaPools.log :error, "Leader Depth: #{leader_depth}"
|
121
126
|
end
|
122
127
|
end
|
123
128
|
end
|
data/lib/replica_pools/engine.rb
CHANGED
@@ -25,7 +25,11 @@ module ReplicaPools
|
|
25
25
|
[
|
26
26
|
:select_all, :select_one, :select_value, :select_values,
|
27
27
|
:select_rows, :select, :select_prepared, :verify!, :raw_connection,
|
28
|
-
:active?, :reconnect!, :disconnect!, :reset_runtime, :log
|
28
|
+
:active?, :reconnect!, :disconnect!, :reset_runtime, :log,
|
29
|
+
:lookup_cast_type_from_column, :sanitize_limit,
|
30
|
+
:combine_bind_parameters, :quote_table_name, :quote, :quote_column_names, :quote_table_names,
|
31
|
+
:case_sensitive_comparison, :case_insensitive_comparison,
|
32
|
+
:schema_cache, :cacheable_query, :prepared_statements, :clear_cache!
|
29
33
|
]
|
30
34
|
else
|
31
35
|
warn "Unsupported ActiveRecord version #{ActiveRecord.version}. Please whitelist the safe methods."
|
@@ -54,6 +54,19 @@ describe ReplicaPools do
|
|
54
54
|
end
|
55
55
|
@proxy.send(:within_leader_block?).should_not be
|
56
56
|
end
|
57
|
+
|
58
|
+
context "with leader_disabled=true" do
|
59
|
+
before { ReplicaPools.config.disable_leader = true }
|
60
|
+
after { ReplicaPools.config.disable_leader = false }
|
61
|
+
|
62
|
+
it 'should not execute query, and maintain replica connection' do
|
63
|
+
@executed = false
|
64
|
+
@proxy.current = @proxy.current_replica
|
65
|
+
expect { @proxy.with_leader { @executed = true } }.to raise_error(ReplicaPools::LeaderDisabled)
|
66
|
+
@executed.should eq(false)
|
67
|
+
@proxy.current.name.should eq('ReplicaPools::DefaultDb1')
|
68
|
+
end
|
69
|
+
end
|
57
70
|
end
|
58
71
|
|
59
72
|
context "transaction" do
|
@@ -124,6 +137,18 @@ describe ReplicaPools do
|
|
124
137
|
end
|
125
138
|
end
|
126
139
|
|
140
|
+
context "with leader_disabled=true" do
|
141
|
+
before { ReplicaPools.config.disable_leader = true }
|
142
|
+
after { ReplicaPools.config.disable_leader = false }
|
143
|
+
it 'should raise an error instead of sending dangerous methods to the leader' do
|
144
|
+
meths = [:insert, :update, :delete, :execute]
|
145
|
+
meths.each do |meth|
|
146
|
+
@default_replica1.stub(meth).and_raise(RuntimeError)
|
147
|
+
expect { @proxy.send(meth, @sql) }.to raise_error(ReplicaPools::LeaderDisabled)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
127
152
|
it "should not allow leader depth to get below 0" do
|
128
153
|
@proxy.instance_variable_set("@leader_depth", -500)
|
129
154
|
@proxy.instance_variable_get("@leader_depth").should == -500
|
@@ -132,7 +157,9 @@ describe ReplicaPools do
|
|
132
157
|
end
|
133
158
|
|
134
159
|
it 'should pre-generate safe methods' do
|
135
|
-
|
160
|
+
ReplicaPools.config.safe_methods.each do |m|
|
161
|
+
@proxy.should respond_to(m)
|
162
|
+
end
|
136
163
|
end
|
137
164
|
|
138
165
|
it 'should dynamically generate unsafe methods' do
|
@@ -158,6 +185,20 @@ describe ReplicaPools do
|
|
158
185
|
foo.reload
|
159
186
|
end
|
160
187
|
|
188
|
+
context "with leader_disabled=true" do
|
189
|
+
after { ReplicaPools.config.disable_leader = false }
|
190
|
+
|
191
|
+
it 'should reload models from a replica' do
|
192
|
+
foo = TestModel.create!
|
193
|
+
ReplicaPools.config.disable_leader = true
|
194
|
+
foo = TestModel.last
|
195
|
+
@leader.should_not_receive(:select_all)
|
196
|
+
@default_replica1.should_receive(:select_all).and_return(ActiveRecord::Result.new(["id"], ["1"]))
|
197
|
+
@default_replica2.should_not_receive(:select_all)
|
198
|
+
foo.reload
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
161
202
|
context "with_pool" do
|
162
203
|
|
163
204
|
it "should switch to the named pool" do
|
data/spec/slave_pools_spec.rb
CHANGED
@@ -22,6 +22,16 @@ describe ReplicaPools do
|
|
22
22
|
ReplicaPools.with_leader
|
23
23
|
end
|
24
24
|
|
25
|
+
describe 'with leader disabled' do
|
26
|
+
before { ReplicaPools.config.disable_leader = true }
|
27
|
+
after { ReplicaPools.config.disable_leader = false }
|
28
|
+
|
29
|
+
it 'should delegate with_leader call to connection proxy' do
|
30
|
+
@proxy.should_receive(:with_leader).exactly(0)
|
31
|
+
expect { ReplicaPools.with_leader }.to raise_error(ReplicaPools::LeaderDisabled)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
25
35
|
it 'should delegate current call to connection proxy' do
|
26
36
|
@proxy.should_receive(:current).exactly(1)
|
27
37
|
ReplicaPools.current
|
data/spec/spec_helper.rb
CHANGED
@@ -11,8 +11,7 @@ end
|
|
11
11
|
require 'active_record'
|
12
12
|
spec_dir = File.dirname(__FILE__)
|
13
13
|
ActiveRecord::Base.logger = Logger.new(spec_dir + "/debug.log")
|
14
|
-
ActiveRecord::Base.configurations = YAML::load(File.
|
15
|
-
|
14
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(File.read(spec_dir + '/config/database.yml')).result)
|
16
15
|
ActiveRecord::Base.establish_connection :test
|
17
16
|
ActiveRecord::Migration.verbose = false
|
18
17
|
ActiveRecord::Migration.create_table(:test_models, :force => true) {}
|
@@ -41,4 +40,12 @@ end
|
|
41
40
|
|
42
41
|
RSpec.configure do |c|
|
43
42
|
c.include ReplicaPools::Testing
|
43
|
+
|
44
|
+
# This gem mostly uses the old deprecated shoulda syntax. Support both versions for now.
|
45
|
+
c.expect_with :rspec do |c|
|
46
|
+
c.syntax = [:expect, :should]
|
47
|
+
end
|
48
|
+
c.mock_with :rspec do |c|
|
49
|
+
c.syntax = [:expect, :should]
|
50
|
+
end
|
44
51
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: replica_pools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Drabik
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-12-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.
|
34
|
+
version: 0.4.4
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 0.
|
41
|
+
version: 0.4.4
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rack
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,14 +139,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
139
|
version: '1.2'
|
140
140
|
requirements: []
|
141
141
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.4.5.
|
142
|
+
rubygems_version: 2.4.5.1
|
143
143
|
signing_key:
|
144
144
|
specification_version: 4
|
145
145
|
summary: Connection proxy for ActiveRecord for leader / replica setups.
|
146
146
|
test_files:
|
147
|
-
- spec/
|
148
|
-
- spec/connection_proxy_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
149
148
|
- spec/pool_spec.rb
|
150
|
-
- spec/query_cache_spec.rb
|
151
149
|
- spec/slave_pools_spec.rb
|
152
|
-
- spec/
|
150
|
+
- spec/config/test_model.rb
|
151
|
+
- spec/query_cache_spec.rb
|
152
|
+
- spec/connection_proxy_spec.rb
|