replica_pools 2.2.1 → 2.3.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.
- checksums.yaml +4 -4
- data/README.md +17 -18
- data/lib/replica_pools/connection_proxy.rb +14 -24
- data/lib/replica_pools/engine.rb +60 -16
- data/lib/replica_pools/pools.rb +12 -12
- data/lib/replica_pools/query_cache.rb +3 -5
- data/lib/replica_pools/version.rb +1 -1
- data/lib/replica_pools.rb +3 -1
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d9219fb04b0b4118f99202ad78486d565bb88fe57079f743336b381b50d5864
|
4
|
+
data.tar.gz: e3fefd1456ce775cf127e0561d5827b39d4a45fab0ef4359178bf58d841887bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1777856dd70f4fd661ef3a7ca6d149f57772e72e9636cd0ef674476c858fa8ce1e086a846a79e624924c1838765e39a88273fb0d3d6b01812c01be7668fe69c
|
7
|
+
data.tar.gz: 0f6f739901cfcf978c9d6b0b1a4e9efbd46c66c33954ab577186343c74eb5a9e499c15ed2d8fa6d8c97afc1f85854e81ef72cbeced472cc5cd0d7f0db59e3e15
|
data/README.md
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
Easy Single Leader / Multiple Replica Setup for use in Ruby/Rails projects
|
4
4
|
|
5
|
-
[](https://travis-ci.org/kickstarter/replica_pools)
|
5
|
+
[](https://github.com/kickstarter/replica_pools/actions/workflows/spec.yml)
|
7
6
|
|
8
7
|
## Overview
|
9
8
|
|
@@ -11,22 +10,22 @@ ReplicaPools replaces ActiveRecord's connection with a proxy that routes databas
|
|
11
10
|
|
12
11
|
ReplicaPools 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 leader, or the default replica pool, and then use block helpers to temporarily change the behavior (e.g. in an around_filter).
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
- Uses a naming convention in database.yml to designate replica pools.
|
14
|
+
- Defaults to a given replica pool, but may also be configured to default to leader.
|
15
|
+
- Routes database interactions (queries) to the right connection
|
16
|
+
- Whitelisted queries go to the current connection (might be a replica).
|
17
|
+
- All queries inside a transaction run on leader.
|
18
|
+
- All other queries are also sent to the leader connection.
|
19
|
+
- Supports ActiveRecord's in-memory query caching.
|
20
|
+
- Helper methods can be used to easily load balance replicas, route traffic to different replica pools, or run directly against leader. (examples below)
|
22
21
|
|
23
22
|
## Not Supported
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
- Sharding.
|
25
|
+
- Automatic load balancing strategies.
|
26
|
+
- Replica weights. You can accomplish this in your own load balancing strategy.
|
27
|
+
- Whitelisting models that always use leader.
|
28
|
+
- Blacklisting poorly performing replicas. This could cause load spikes on your leader. Whatever provisions your database.yml should make this choice.
|
30
29
|
|
31
30
|
## Installation and Setup
|
32
31
|
|
@@ -166,7 +165,7 @@ To disable queries to the leader database -- for instance, in a production
|
|
166
165
|
console -- set the disable_leader configuration to false. This will raise
|
167
166
|
a ReplicaPools::LeaderDisabled error:
|
168
167
|
|
169
|
-
|
168
|
+
ReplicaPools.config.disable_leader = false
|
170
169
|
|
171
170
|
## Running specs
|
172
171
|
|
@@ -204,10 +203,10 @@ Released under the MIT license
|
|
204
203
|
|
205
204
|
The project is based on:
|
206
205
|
|
207
|
-
|
206
|
+
- https://github.com/schoefmax/multi_db
|
208
207
|
|
209
208
|
### Masochism
|
210
209
|
|
211
210
|
The original leader/replica plugin:
|
212
211
|
|
213
|
-
|
212
|
+
- http://github.com/technoweenie/masochism
|
@@ -11,26 +11,18 @@ module ReplicaPools
|
|
11
11
|
class << self
|
12
12
|
def generate_safe_delegations
|
13
13
|
ReplicaPools.config.safe_methods.each do |method|
|
14
|
-
|
14
|
+
self.define_method(method) do |*args, &block|
|
15
|
+
route_to(current, method, *args, &block)
|
16
|
+
end unless instance_methods.include?(method)
|
15
17
|
end
|
16
18
|
end
|
17
|
-
|
18
|
-
protected
|
19
|
-
|
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)
|
24
|
-
end
|
25
|
-
END
|
26
|
-
end
|
27
19
|
end
|
28
20
|
|
29
21
|
def initialize(leader, pools)
|
30
|
-
@leader
|
31
|
-
@replica_pools
|
32
|
-
@leader_depth
|
33
|
-
@current_pool
|
22
|
+
@leader = leader
|
23
|
+
@replica_pools = pools
|
24
|
+
@leader_depth = 0
|
25
|
+
@current_pool = default_pool
|
34
26
|
|
35
27
|
if ReplicaPools.config.defaults_to_leader
|
36
28
|
@current = leader
|
@@ -91,7 +83,13 @@ module ReplicaPools
|
|
91
83
|
# Safe methods have been generated during `setup!`.
|
92
84
|
# Creates a method to speed up subsequent calls.
|
93
85
|
def method_missing(method, *args, &block)
|
94
|
-
|
86
|
+
self.class.define_method(method) do |*args, &block|
|
87
|
+
route_to(leader, method, *args, &block).tap do
|
88
|
+
if %i[insert delete update].include?(method)
|
89
|
+
leader.retrieve_connection.clear_query_cache
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
95
93
|
send(method, *args, &block)
|
96
94
|
end
|
97
95
|
|
@@ -99,14 +97,6 @@ module ReplicaPools
|
|
99
97
|
leader_depth > 0
|
100
98
|
end
|
101
99
|
|
102
|
-
def generate_unsafe_delegation(method)
|
103
|
-
self.class_eval <<-END, __FILE__, __LINE__ + 1
|
104
|
-
def #{method}(*args, &block)
|
105
|
-
route_to(leader, :#{method}, *args, &block)
|
106
|
-
end
|
107
|
-
END
|
108
|
-
end
|
109
|
-
|
110
100
|
def route_to(conn, method, *args, &block)
|
111
101
|
raise ReplicaPools::LeaderDisabled.new if ReplicaPools.config.disable_leader && conn == leader
|
112
102
|
conn.retrieve_connection.send(method, *args, &block)
|
data/lib/replica_pools/engine.rb
CHANGED
@@ -10,26 +10,70 @@ module ReplicaPools
|
|
10
10
|
|
11
11
|
ReplicaPools.config.safe_methods =
|
12
12
|
if ActiveRecord::VERSION::MAJOR == 3
|
13
|
-
[
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
%i[
|
14
|
+
active?
|
15
|
+
disconnect!
|
16
|
+
log
|
17
|
+
log_info
|
18
|
+
raw_connection
|
19
|
+
reconnect!
|
20
|
+
reset_runtime
|
21
|
+
select
|
22
|
+
select_all
|
23
|
+
select_one
|
24
|
+
select_rows
|
25
|
+
select_value
|
26
|
+
select_values
|
27
|
+
verify!
|
17
28
|
]
|
18
29
|
elsif ActiveRecord::VERSION::MAJOR == 4
|
19
|
-
[
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
%i[
|
31
|
+
active?
|
32
|
+
disconnect!
|
33
|
+
log
|
34
|
+
raw_connection
|
35
|
+
reconnect!
|
36
|
+
reset_runtime
|
37
|
+
select
|
38
|
+
select_all
|
39
|
+
select_one
|
40
|
+
select_rows
|
41
|
+
select_value
|
42
|
+
select_values
|
43
|
+
verify!
|
23
44
|
]
|
24
45
|
elsif [5, 6].include?(ActiveRecord::VERSION::MAJOR)
|
25
|
-
[
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
46
|
+
%i[
|
47
|
+
active?
|
48
|
+
cacheable_query
|
49
|
+
case_insensitive_comparison
|
50
|
+
case_sensitive_comparison
|
51
|
+
clear_cache!
|
52
|
+
column_name_for_operation
|
53
|
+
column_name_with_order_matcher
|
54
|
+
combine_bind_parameters
|
55
|
+
disconnect!
|
56
|
+
log
|
57
|
+
lookup_cast_type_from_column
|
58
|
+
prepared_statements
|
59
|
+
quote
|
60
|
+
quote_column_names
|
61
|
+
quote_table_name
|
62
|
+
quote_table_names
|
63
|
+
raw_connection
|
64
|
+
reconnect!
|
65
|
+
reset_runtime
|
66
|
+
sanitize_limit
|
67
|
+
schema_cache
|
68
|
+
select
|
69
|
+
select_all
|
70
|
+
select_one
|
71
|
+
select_prepared
|
72
|
+
select_rows
|
73
|
+
select_value
|
74
|
+
select_values
|
75
|
+
table_alias_for
|
76
|
+
verify!
|
33
77
|
]
|
34
78
|
else
|
35
79
|
warn "Unsupported ActiveRecord version #{ActiveRecord.version}. Please whitelist the safe methods."
|
data/lib/replica_pools/pools.rb
CHANGED
@@ -6,12 +6,12 @@ module ReplicaPools
|
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
pools = {}
|
9
|
-
pool_configurations.group_by{|_, name, _| name }.each do |name, set|
|
9
|
+
pool_configurations.group_by{ |_, name, _| name }.each do |name, set|
|
10
10
|
pools[name.to_sym] = ReplicaPools::Pool.new(
|
11
11
|
name,
|
12
|
-
set.map
|
12
|
+
set.map do |conn_name, _, replica_name|
|
13
13
|
connection_class(name, replica_name, conn_name)
|
14
|
-
|
14
|
+
end
|
15
15
|
)
|
16
16
|
end
|
17
17
|
|
@@ -47,16 +47,16 @@ module ReplicaPools
|
|
47
47
|
def connection_class(pool_name, replica_name, connection_name)
|
48
48
|
class_name = "#{pool_name.camelize}#{replica_name.camelize}"
|
49
49
|
|
50
|
-
ReplicaPools.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def self.connection_config
|
55
|
-
configurations[#{connection_name.to_s.inspect}]
|
56
|
-
end
|
50
|
+
ReplicaPools.const_set(class_name, Class.new(ActiveRecord::Base) do |c|
|
51
|
+
c.abstract_class = true
|
52
|
+
c.define_singleton_method(:connection_config) do
|
53
|
+
configurations[connection_name.to_s]
|
57
54
|
end
|
58
|
-
|
59
|
-
|
55
|
+
end)
|
56
|
+
|
57
|
+
ReplicaPools.const_get(class_name).tap do |c|
|
58
|
+
c.establish_connection(connection_name.to_sym)
|
59
|
+
end
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -10,11 +10,9 @@ module ReplicaPools
|
|
10
10
|
|
11
11
|
# these methods can all use the leader connection
|
12
12
|
(query_cache_methods - [:select_all]).each do |method_name|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
END
|
13
|
+
define_method(method_name) do |*args, &block|
|
14
|
+
ActiveRecord::Base.connection.send(method_name, *args, &block)
|
15
|
+
end
|
18
16
|
end
|
19
17
|
|
20
18
|
# select_all is trickier. it needs to use the leader
|
data/lib/replica_pools.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
require 'replica_pools/config'
|
3
5
|
require 'replica_pools/pool'
|
@@ -64,7 +66,7 @@ module ReplicaPools
|
|
64
66
|
end
|
65
67
|
|
66
68
|
def logger
|
67
|
-
ActiveRecord::Base.logger
|
69
|
+
ActiveRecord::Base.logger || Logger.new(STDOUT)
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
metadata
CHANGED
@@ -1,44 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: replica_pools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Drabik
|
8
8
|
- Lance Ivy
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-04-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: '6.0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version:
|
27
|
+
version: '6.0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: mysql2
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.
|
34
|
+
version: '0.5'
|
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.5'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rack
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,7 +137,7 @@ homepage: https://github.com/kickstarter/replica_pools
|
|
137
137
|
licenses:
|
138
138
|
- MIT
|
139
139
|
metadata: {}
|
140
|
-
post_install_message:
|
140
|
+
post_install_message:
|
141
141
|
rdoc_options: []
|
142
142
|
require_paths:
|
143
143
|
- lib
|
@@ -152,8 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: '1.2'
|
154
154
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
156
|
-
signing_key:
|
155
|
+
rubygems_version: 3.1.6
|
156
|
+
signing_key:
|
157
157
|
specification_version: 4
|
158
158
|
summary: Connection proxy for ActiveRecord for leader / replica setups.
|
159
159
|
test_files:
|