replica_pools 2.1.0 → 2.1.1
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.
- 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
|