replica_pools 2.2.0.beta1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aba6d5a57bda0c33f6531172ff1f8a9e735168a94688318c09388032063ca637
4
- data.tar.gz: 10677ab3c419b42fa1a8ebe8367204a42ff87bbdc01a1a986575a70f12cb3925
3
+ metadata.gz: 9d9219fb04b0b4118f99202ad78486d565bb88fe57079f743336b381b50d5864
4
+ data.tar.gz: e3fefd1456ce775cf127e0561d5827b39d4a45fab0ef4359178bf58d841887bc
5
5
  SHA512:
6
- metadata.gz: 3c0bd1e057eb02a547d33792d8d3bbc7b378c7cc49574a8635a885c0ce9668fe90c1dc880435a0a3eabd8d3322d5f89970c1809b0d8c98ddbe538289232c7b61
7
- data.tar.gz: 42b3bc14c121185c8df5c7de20c556d3604576878ea6f3deaddf9e8ad1a0b726e99c12f23bbb1ca9d104e757dc18d04f3848c3bf86304a49efa54b3ae259dc63
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
- [![Build
6
- Status](https://travis-ci.org/kickstarter/replica_pools.png?branch=owningit)](https://travis-ci.org/kickstarter/replica_pools)
5
+ [![Spec](https://github.com/kickstarter/replica_pools/actions/workflows/spec.yml/badge.svg)](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
- * 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 leader.
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 leader.
19
- * All other queries are also sent to the leader 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 leader. (examples below)
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
- * 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 leader.
29
- * Blacklisting poorly performing replicas. This could cause load spikes on your leader. Whatever provisions your database.yml should make this choice.
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
- ReplicaPools.config.disable_leader = false
168
+ ReplicaPools.config.disable_leader = false
170
169
 
171
170
  ## Running specs
172
171
 
@@ -186,11 +185,15 @@ Then you can run tests with:
186
185
  $ bundle exec rake spec
187
186
  ```
188
187
 
188
+ ## Releasing a New Version
189
+
190
+ First bump the version as appropriate in `lib/replica_pools/version.rb` and then run `bundle exec rake release`. This will push git tags to github and package and push the gem to rubygems.org.
191
+
189
192
  ## Authors
190
193
 
191
194
  Author: Dan Drabik, Lance Ivy
192
195
 
193
- Copyright (c) 2012-2019, Kickstarter
196
+ Copyright (c) 2012-2021, Kickstarter
194
197
 
195
198
  Released under the MIT license
196
199
 
@@ -200,10 +203,10 @@ Released under the MIT license
200
203
 
201
204
  The project is based on:
202
205
 
203
- * https://github.com/schoefmax/multi_db
206
+ - https://github.com/schoefmax/multi_db
204
207
 
205
208
  ### Masochism
206
209
 
207
210
  The original leader/replica plugin:
208
211
 
209
- * http://github.com/technoweenie/masochism
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
- generate_safe_delegation(method) unless instance_methods.include?(method)
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 = leader
31
- @replica_pools = pools
32
- @leader_depth = 0
33
- @current_pool = default_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
- generate_unsafe_delegation(method)
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)
@@ -10,26 +10,70 @@ module ReplicaPools
10
10
 
11
11
  ReplicaPools.config.safe_methods =
12
12
  if ActiveRecord::VERSION::MAJOR == 3
13
- [
14
- :select_all, :select_one, :select_value, :select_values,
15
- :select_rows, :select, :verify!, :raw_connection, :active?, :reconnect!,
16
- :disconnect!, :reset_runtime, :log, :log_info
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
- :select_all, :select_one, :select_value, :select_values,
21
- :select_rows, :select, :verify!, :raw_connection, :active?, :reconnect!,
22
- :disconnect!, :reset_runtime, :log
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
- :select_all, :select_one, :select_value, :select_values,
27
- :select_rows, :select, :select_prepared, :verify!, :raw_connection,
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, :table_alias_for,
31
- :case_sensitive_comparison, :case_insensitive_comparison,
32
- :schema_cache, :cacheable_query, :prepared_statements, :clear_cache!, :column_name_for_operation
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."
@@ -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{ |conn_name, _, replica_name|
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.module_eval %Q{
51
- class #{class_name} < ActiveRecord::Base
52
- self.abstract_class = true
53
- establish_connection :#{connection_name}
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
- }, __FILE__, __LINE__
59
- ReplicaPools.const_get(class_name)
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
- module_eval <<-END, __FILE__, __LINE__ + 1
14
- def #{method_name}(*a, &b)
15
- ActiveRecord::Base.connection.#{method_name}(*a, &b)
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
@@ -24,6 +22,8 @@ module ReplicaPools
24
22
  # there may be more args for Rails 5.0+, but we only care about arel, name, and binds for caching.
25
23
  relation, name, raw_binds = args
26
24
 
25
+ # Rails 6.2 breaks this method as locked? is no longer available
26
+ # https://github.com/kickstarter/replica_pools/issues/26
27
27
  if !query_cache_enabled || locked?(relation)
28
28
  return route_to(current, :select_all, *args)
29
29
  end
@@ -32,7 +32,7 @@ module ReplicaPools
32
32
  if raw_binds.blank? && relation.is_a?(ActiveRecord::Relation)
33
33
  arel, binds = relation.arel, relation.bind_values
34
34
  else
35
- arel, binds = relation, raw_binds
35
+ arel, binds = relation, Array(raw_binds)
36
36
  end
37
37
 
38
38
  sql = to_sql(arel, binds)
@@ -1,3 +1,3 @@
1
1
  module ReplicaPools
2
- VERSION = "2.2.0.beta1"
2
+ VERSION = "2.3.0"
3
3
  end
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
@@ -1,5 +1,6 @@
1
1
  require 'rack'
2
2
  require_relative 'spec_helper'
3
+ require_relative 'config/test_model'
3
4
 
4
5
  describe ReplicaPools::QueryCache do
5
6
  before(:each) do
@@ -103,4 +104,16 @@ describe ReplicaPools::QueryCache do
103
104
  end
104
105
  end
105
106
  end
107
+
108
+ describe '.pluck regression test' do
109
+ it 'should work with query caching' do
110
+ TestModel.connection.enable_query_cache!
111
+ expect(TestModel.pluck(:id).count).to eql TestModel.all.count
112
+ end
113
+
114
+ it 'should work if query cache is not enabled' do
115
+ TestModel.connection.disable_query_cache!
116
+ expect(TestModel.pluck(:id).count).to eql TestModel.all.count
117
+ end
118
+ end
106
119
  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.2.0.beta1
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: 2019-08-05 00:00:00.000000000 Z
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: 3.2.12
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: 3.2.12
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.4.4
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.4.4
41
+ version: '0.5'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rack
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +95,20 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
98
112
  description: Connection proxy for ActiveRecord for leader / replica setups.
99
113
  email: dan@kickstarter.com
100
114
  executables: []
@@ -123,7 +137,7 @@ homepage: https://github.com/kickstarter/replica_pools
123
137
  licenses:
124
138
  - MIT
125
139
  metadata: {}
126
- post_install_message:
140
+ post_install_message:
127
141
  rdoc_options: []
128
142
  require_paths:
129
143
  - lib
@@ -138,8 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
152
  - !ruby/object:Gem::Version
139
153
  version: '1.2'
140
154
  requirements: []
141
- rubygems_version: 3.0.3
142
- signing_key:
155
+ rubygems_version: 3.1.6
156
+ signing_key:
143
157
  specification_version: 4
144
158
  summary: Connection proxy for ActiveRecord for leader / replica setups.
145
159
  test_files: