active_record_proxy_adapters 0.6.3 → 0.7.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 +21 -2
- data/lib/active_record_proxy_adapters/cache_configuration.rb +5 -5
- data/lib/active_record_proxy_adapters/configuration.rb +41 -45
- data/lib/active_record_proxy_adapters/context.rb +8 -4
- data/lib/active_record_proxy_adapters/database_configuration.rb +74 -0
- data/lib/active_record_proxy_adapters/log_subscriber.rb +16 -10
- data/lib/active_record_proxy_adapters/middleware.rb +80 -0
- data/lib/active_record_proxy_adapters/mixin/configuration.rb +29 -6
- data/lib/active_record_proxy_adapters/primary_replica_proxy.rb +9 -10
- data/lib/active_record_proxy_adapters/railtie.rb +5 -0
- data/lib/active_record_proxy_adapters/synchronizable_configuration.rb +0 -2
- data/lib/active_record_proxy_adapters/version.rb +1 -1
- metadata +17 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eaa34c675630f13046dbc7170773fca75998eecef0b6e4990ce53110bdfdecef
|
4
|
+
data.tar.gz: 92c024345625535b4f3bfcb241b898e3a26c09836a5f3c6dac61749c913aef85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76493c2922eb8f725410bc69223b9bc9dd121cece620e188a73b941ea7b8ef549108ffea8d3c1c38ddee2ed5686b6160284f87665ea431e41f4a32bccc80472a
|
7
|
+
data.tar.gz: 24c3cf701b469e364a5206ace80d3e8a609fddcd1839337069f0b0f7bbbf27f6c35940d7642a5f717b6030bf776afa988912fb8306b0e8b5d4d2e7fe5bde0ff4
|
data/README.md
CHANGED
@@ -150,6 +150,25 @@ ActiveRecordProxyAdapters.configure do |config|
|
|
150
150
|
end
|
151
151
|
```
|
152
152
|
|
153
|
+
### Configuring multiple connections
|
154
|
+
General settings are automatically applied to the `primary` database by default.
|
155
|
+
|
156
|
+
If your application has multiple databases that need separate proxy settings, you can use the `database` block for individual database settings.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
ActiveRecordProxyAdapters.configure do |config|
|
160
|
+
config.database :primary do |primary_config|
|
161
|
+
primary_config.proxy_delay = 2.seconds
|
162
|
+
end
|
163
|
+
|
164
|
+
config.database :secondary do |secondary_config|
|
165
|
+
secondary_config.proxy_delay = 5.seconds
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
With those settings, any model that `connects_to database: { writing: :primary }` will have a 2-second delay, and any model that `connects_to database: { writing: :secondary }` will have a 5-second delay.
|
171
|
+
|
153
172
|
## Logging
|
154
173
|
|
155
174
|
```ruby
|
@@ -157,8 +176,8 @@ end
|
|
157
176
|
require "active_record_proxy_adapters/log_subscriber"
|
158
177
|
|
159
178
|
ActiveRecordProxyAdapters.configure do |config|
|
160
|
-
config.log_subscriber_primary_prefix = "My primary tag" # defaults to
|
161
|
-
config.log_subscriber_replica_prefix = "My replica tag" # defaults to
|
179
|
+
config.log_subscriber_primary_prefix = "My primary tag" # defaults to ActiveRecord configuration name", i.e "primary"
|
180
|
+
config.log_subscriber_replica_prefix = "My replica tag" # defaults to ActiveRecord configuration name", i.e "primary_replica"
|
162
181
|
end
|
163
182
|
|
164
183
|
# You may want to remove duplicate logs
|
@@ -7,12 +7,8 @@ module ActiveRecordProxyAdapters
|
|
7
7
|
include SynchronizableConfiguration
|
8
8
|
|
9
9
|
# Sets the cache store to use for caching SQL statements.
|
10
|
-
#
|
11
|
-
# @param store [ActiveSupport::Cache::Store] The cache store to use for caching SQL statements.
|
12
|
-
# Defaults to ActiveSupport::Cache::NullStore, which does not cache anything.
|
13
|
-
# Thread safe.
|
10
|
+
# Defaults to {ActiveSupport::Cache::NullStore}, which does not cache anything.
|
14
11
|
# @return [ActiveSupport::Cache::Store] The cache store to use for caching SQL statements.
|
15
|
-
# Defaults to ActiveSupport::Cache::NullStore, which does not cache anything.
|
16
12
|
# Thread safe.
|
17
13
|
attr_reader :store
|
18
14
|
|
@@ -51,5 +47,9 @@ module ActiveRecordProxyAdapters
|
|
51
47
|
def bust
|
52
48
|
store.delete_matched("#{key_prefix}*")
|
53
49
|
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :lock
|
54
54
|
end
|
55
55
|
end
|
@@ -1,32 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/integer/time"
|
4
|
-
require "active_record_proxy_adapters/synchronizable_configuration"
|
5
3
|
require "active_record_proxy_adapters/cache_configuration"
|
6
4
|
require "active_record_proxy_adapters/context"
|
5
|
+
require "active_record_proxy_adapters/database_configuration"
|
6
|
+
require "active_record_proxy_adapters/synchronizable_configuration"
|
7
|
+
require "active_support/core_ext/integer/time"
|
7
8
|
|
8
9
|
module ActiveRecordProxyAdapters
|
9
10
|
# Provides a global configuration object to configure how the proxy should behave.
|
10
11
|
class Configuration
|
11
12
|
include SynchronizableConfiguration
|
12
13
|
|
13
|
-
|
14
|
-
CHECKOUT_TIMEOUT = 2.seconds.freeze
|
15
|
-
LOG_SUBSCRIBER_PRIMARY_PREFIX = proc { |event| "#{event.payload[:connection].class::ADAPTER_NAME} Primary" }.freeze
|
16
|
-
LOG_SUBSCRIBER_REPLICA_PREFIX = proc { |event| "#{event.payload[:connection].class::ADAPTER_NAME} Replica" }.freeze
|
17
|
-
|
18
|
-
# @return [ActiveSupport::Duration] How long the proxy should reroute all read requests to the primary database
|
19
|
-
# since the latest write. Defaults to PROXY_DELAY. Thread safe.
|
20
|
-
attr_reader :proxy_delay
|
21
|
-
# @return [ActiveSupport::Duration] How long the proxy should wait for a connection from the replica pool.
|
22
|
-
# Defaults to CHECKOUT_TIMEOUT. Thread safe.
|
23
|
-
attr_reader :checkout_timeout
|
24
|
-
|
25
|
-
# @return [Proc] Prefix for the log subscriber when the primary database is used. Thread safe.
|
26
|
-
attr_reader :log_subscriber_primary_prefix
|
27
|
-
|
28
|
-
# @return [Proc] Prefix for the log subscriber when the replica database is used. Thread safe.
|
29
|
-
attr_reader :log_subscriber_replica_prefix
|
14
|
+
DEFAULT_DATABASE_NAME = :primary
|
30
15
|
|
31
16
|
# @return [Class] The context that is used to store the current request's state.
|
32
17
|
attr_reader :context_store
|
@@ -34,46 +19,48 @@ module ActiveRecordProxyAdapters
|
|
34
19
|
def initialize
|
35
20
|
@lock = Monitor.new
|
36
21
|
|
37
|
-
self.
|
38
|
-
self.
|
39
|
-
|
40
|
-
self.log_subscriber_replica_prefix = LOG_SUBSCRIBER_REPLICA_PREFIX
|
41
|
-
self.cache_configuration = CacheConfiguration.new(@lock)
|
42
|
-
self.context_store = ActiveRecordProxyAdapters::Context
|
22
|
+
self.cache_configuration = CacheConfiguration.new(@lock)
|
23
|
+
self.context_store = ActiveRecordProxyAdapters::Context
|
24
|
+
@database_configurations = {}
|
43
25
|
end
|
44
26
|
|
45
27
|
def log_subscriber_primary_prefix=(prefix)
|
46
|
-
|
28
|
+
default_database_config.log_subscriber_primary_prefix = prefix
|
29
|
+
end
|
47
30
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
31
|
+
def log_subscriber_primary_prefix
|
32
|
+
default_database_config.log_subscriber_primary_prefix
|
51
33
|
end
|
52
34
|
|
53
35
|
def log_subscriber_replica_prefix=(prefix)
|
54
|
-
|
36
|
+
default_database_config.log_subscriber_replica_prefix = prefix
|
37
|
+
end
|
55
38
|
|
56
|
-
|
57
|
-
|
58
|
-
|
39
|
+
def log_subscriber_replica_prefix
|
40
|
+
default_database_config.log_subscriber_replica_prefix
|
41
|
+
end
|
42
|
+
|
43
|
+
def proxy_delay
|
44
|
+
default_database_config.proxy_delay
|
59
45
|
end
|
60
46
|
|
61
47
|
def proxy_delay=(proxy_delay)
|
62
|
-
|
63
|
-
|
64
|
-
|
48
|
+
default_database_config.proxy_delay = proxy_delay
|
49
|
+
end
|
50
|
+
|
51
|
+
def checkout_timeout
|
52
|
+
default_database_config.checkout_timeout
|
65
53
|
end
|
66
54
|
|
67
55
|
def checkout_timeout=(checkout_timeout)
|
68
|
-
|
69
|
-
@checkout_timeout = checkout_timeout
|
70
|
-
end
|
56
|
+
default_database_config.checkout_timeout = checkout_timeout
|
71
57
|
end
|
72
58
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
59
|
+
def database(database_name)
|
60
|
+
key = database_name.to_s
|
61
|
+
lock.synchronize { @database_configurations[key] ||= DatabaseConfiguration.new }
|
62
|
+
|
63
|
+
block_given? ? yield(database_configurations[key]) : database_configurations[key]
|
77
64
|
end
|
78
65
|
|
79
66
|
def cache
|
@@ -82,13 +69,22 @@ module ActiveRecordProxyAdapters
|
|
82
69
|
|
83
70
|
private
|
84
71
|
|
85
|
-
|
86
|
-
|
72
|
+
attr_reader :cache_configuration, :database_configurations, :lock
|
73
|
+
|
74
|
+
def default_database_config
|
75
|
+
database(DEFAULT_DATABASE_NAME)
|
76
|
+
end
|
87
77
|
|
88
78
|
def cache_configuration=(cache_configuration)
|
89
79
|
synchronize_update(:cache_configuration, from: @cache_configuration, to: cache_configuration) do
|
90
80
|
@cache_configuration = cache_configuration
|
91
81
|
end
|
92
82
|
end
|
83
|
+
|
84
|
+
def context_store=(context_store)
|
85
|
+
synchronize_update(:context_store, from: @context_store, to: context_store) do
|
86
|
+
@context_store = context_store
|
87
|
+
end
|
88
|
+
end
|
93
89
|
end
|
94
90
|
end
|
@@ -16,19 +16,23 @@ module ActiveRecordProxyAdapters
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def recent_write_to_primary?(connection_name)
|
19
|
-
now - self[connection_name] < proxy_delay
|
19
|
+
now - self[connection_name.to_s] < proxy_delay(connection_name)
|
20
20
|
end
|
21
21
|
|
22
22
|
def update_for(connection_name)
|
23
|
-
self[connection_name] = now
|
23
|
+
self[connection_name.to_s] = now
|
24
24
|
end
|
25
25
|
|
26
26
|
def [](connection_name)
|
27
|
-
timestamp_registry[connection_name] || 0
|
27
|
+
timestamp_registry[connection_name.to_s] || 0
|
28
28
|
end
|
29
29
|
|
30
30
|
def []=(connection_name, timestamp)
|
31
|
-
timestamp_registry[connection_name] = timestamp
|
31
|
+
timestamp_registry[connection_name.to_s] = timestamp
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_h
|
35
|
+
timestamp_registry.dup
|
32
36
|
end
|
33
37
|
|
34
38
|
private
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/integer/time"
|
4
|
+
require "active_record_proxy_adapters/synchronizable_configuration"
|
5
|
+
require "active_record_proxy_adapters/cache_configuration"
|
6
|
+
require "active_record_proxy_adapters/context"
|
7
|
+
|
8
|
+
module ActiveRecordProxyAdapters
|
9
|
+
# Provides a global configuration object to configure how the proxy should behave.
|
10
|
+
class DatabaseConfiguration
|
11
|
+
include SynchronizableConfiguration
|
12
|
+
|
13
|
+
PROXY_DELAY = 2.seconds.freeze
|
14
|
+
CHECKOUT_TIMEOUT = 2.seconds.freeze
|
15
|
+
DEFAULT_PREFIX = proc do |event|
|
16
|
+
connection = event.payload[:connection]
|
17
|
+
|
18
|
+
connection.pool.try(:db_config).try(:name) || connection.class::ADAPTER_NAME
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [ActiveSupport::Duration] How long the proxy should reroute all read requests to the primary database
|
22
|
+
# since the latest write. Defaults to PROXY_DELAY. Thread safe.
|
23
|
+
attr_reader :proxy_delay
|
24
|
+
# @return [ActiveSupport::Duration] How long the proxy should wait for a connection from the replica pool.
|
25
|
+
# Defaults to CHECKOUT_TIMEOUT. Thread safe.
|
26
|
+
attr_reader :checkout_timeout
|
27
|
+
|
28
|
+
# @return [Proc] Prefix for the log subscriber when the primary database is used. Thread safe.
|
29
|
+
attr_reader :log_subscriber_primary_prefix
|
30
|
+
|
31
|
+
# @return [Proc] Prefix for the log subscriber when the replica database is used. Thread safe.
|
32
|
+
attr_reader :log_subscriber_replica_prefix
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@lock = Monitor.new
|
36
|
+
self.proxy_delay = PROXY_DELAY
|
37
|
+
self.checkout_timeout = CHECKOUT_TIMEOUT
|
38
|
+
self.log_subscriber_primary_prefix = DEFAULT_PREFIX
|
39
|
+
self.log_subscriber_replica_prefix = DEFAULT_PREFIX
|
40
|
+
end
|
41
|
+
|
42
|
+
def proxy_delay=(proxy_delay)
|
43
|
+
synchronize_update(:proxy_delay, from: @proxy_delay, to: proxy_delay) do
|
44
|
+
@proxy_delay = proxy_delay
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def checkout_timeout=(checkout_timeout)
|
49
|
+
synchronize_update(:checkout_timeout, from: @checkout_timeout, to: checkout_timeout) do
|
50
|
+
@checkout_timeout = checkout_timeout
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def log_subscriber_primary_prefix=(prefix)
|
55
|
+
prefix_proc = prefix.is_a?(Proc) ? prefix : proc { prefix.to_s }
|
56
|
+
|
57
|
+
synchronize_update(:log_subscriber_primary_prefix, from: @log_subscriber_primary_prefix, to: prefix_proc) do
|
58
|
+
@log_subscriber_primary_prefix = prefix_proc
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def log_subscriber_replica_prefix=(prefix)
|
63
|
+
prefix_proc = prefix.is_a?(Proc) ? prefix : proc { prefix.to_s }
|
64
|
+
|
65
|
+
synchronize_update(:log_subscriber_replica_prefix, from: @log_subscriber_replica_prefix, to: prefix_proc) do
|
66
|
+
@log_subscriber_replica_prefix = prefix_proc
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :lock
|
73
|
+
end
|
74
|
+
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecordProxyAdapters
|
4
4
|
class LogSubscriber < ActiveRecord::LogSubscriber # rubocop:disable Style/Documentation
|
5
|
+
include Mixin::Configuration
|
6
|
+
|
5
7
|
attach_to :active_record
|
6
8
|
|
7
9
|
IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN].freeze
|
@@ -19,23 +21,27 @@ module ActiveRecordProxyAdapters
|
|
19
21
|
protected
|
20
22
|
|
21
23
|
def database_instance_prefix_for(event)
|
22
|
-
connection
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
connection = event.payload[:connection]
|
25
|
+
db_config = connection.pool.try(:db_config) || NullConfig.new # AR 7.0.x does not support "NullConfig"
|
26
|
+
connection_name = db_config.name
|
27
|
+
|
28
|
+
prefix = if db_config.replica?
|
29
|
+
log_subscriber_replica_prefix(connection_name)
|
26
30
|
else
|
27
|
-
log_subscriber_primary_prefix
|
31
|
+
log_subscriber_primary_prefix(connection_name)
|
28
32
|
end
|
29
33
|
|
30
34
|
"[#{prefix.call(event)}]"
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
37
|
+
class NullConfig # rubocop:disable Style/Documentation
|
38
|
+
def method_missing(...)
|
39
|
+
nil
|
40
|
+
end
|
36
41
|
|
37
|
-
|
38
|
-
|
42
|
+
def respond_to_missing?(*)
|
43
|
+
true
|
44
|
+
end
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
require "json"
|
5
|
+
require "active_record_proxy_adapters/context"
|
6
|
+
require "active_record_proxy_adapters/contextualizer"
|
7
|
+
require "active_record_proxy_adapters/mixin/configuration"
|
8
|
+
|
9
|
+
module ActiveRecordProxyAdapters
|
10
|
+
class Middleware # rubocop:disable Style/Documentation
|
11
|
+
include Contextualizer
|
12
|
+
include Mixin::Configuration
|
13
|
+
|
14
|
+
COOKIE_NAME = "arpa_context"
|
15
|
+
COOKIE_BUFFER = 5.seconds.freeze
|
16
|
+
DEFAULT_COOKIE_OPTIONS = {
|
17
|
+
path: "/",
|
18
|
+
http_only: true
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
class << self
|
22
|
+
include Mixin::Configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
COOKIE_READER = lambda do |rack_env|
|
26
|
+
rack_request = Rack::Request.new(rack_env)
|
27
|
+
arpa_cookie = rack_request.cookies[COOKIE_NAME]
|
28
|
+
JSON.parse(arpa_cookie || "{}")
|
29
|
+
rescue JSON::ParserError
|
30
|
+
{}
|
31
|
+
end.freeze
|
32
|
+
|
33
|
+
COOKIE_WRITER = lambda do |headers, cookie_hash, options|
|
34
|
+
cookie = DEFAULT_COOKIE_OPTIONS.merge(options)
|
35
|
+
max_value = cookie_hash.values.max || 0
|
36
|
+
then_time = Time.at(max_value).utc
|
37
|
+
expires = then_time + proxy_delay(cookie_hash.key(max_value)) + COOKIE_BUFFER
|
38
|
+
max_age = expires - then_time
|
39
|
+
cookie[:expires] = expires
|
40
|
+
cookie[:max_age] = max_age
|
41
|
+
cookie[:value] = cookie_hash.to_json
|
42
|
+
|
43
|
+
Rack::Utils.set_cookie_header!(headers, COOKIE_NAME, cookie)
|
44
|
+
end.freeze
|
45
|
+
|
46
|
+
def initialize(app, cookie_options = {})
|
47
|
+
@app = app
|
48
|
+
@cookie_options = cookie_options
|
49
|
+
end
|
50
|
+
|
51
|
+
def call(env)
|
52
|
+
return @app.call(env) if ignore_request?(env)
|
53
|
+
|
54
|
+
self.current_context = context_store.new(COOKIE_READER.call(env))
|
55
|
+
|
56
|
+
status, headers, body = @app.call(env)
|
57
|
+
|
58
|
+
update_cookie_from_context(headers)
|
59
|
+
|
60
|
+
[status, headers, body]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def update_cookie_from_context(headers)
|
66
|
+
COOKIE_WRITER.call(headers, current_context.to_h, @cookie_options)
|
67
|
+
end
|
68
|
+
|
69
|
+
def ignore_request?(env)
|
70
|
+
return false unless defined?(Rails)
|
71
|
+
return false unless asset_prefix
|
72
|
+
|
73
|
+
/^#{asset_prefix}/.match?(env["PATH_INFO"].to_s)
|
74
|
+
end
|
75
|
+
|
76
|
+
def asset_prefix
|
77
|
+
Rails.try(:application).try(:config).try(:assets).try(:prefix)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -10,17 +10,35 @@ module ActiveRecordProxyAdapters
|
|
10
10
|
# Provides helpers to access to reduce boilerplate while retrieving configuration properties.
|
11
11
|
module Configuration
|
12
12
|
# Helper to retrieve the proxy delay from the configuration stored in
|
13
|
-
# {ActiveRecordProxyAdapters::
|
13
|
+
# {ActiveRecordProxyAdapters::DatabaseConfiguration#log_subscriber_primary_prefix}.
|
14
|
+
# @param database_name [Symbol, String] The name of the database to retrieve the prefix.
|
15
|
+
# @return [Proc]
|
16
|
+
def log_subscriber_primary_prefix(database_name)
|
17
|
+
database_config(database_name).log_subscriber_primary_prefix
|
18
|
+
end
|
19
|
+
|
20
|
+
# Helper to retrieve the proxy delay from the configuration stored in
|
21
|
+
# {ActiveRecordProxyAdapters::DatabaseConfiguration#log_subscriber_replica_prefix}.
|
22
|
+
# @param database_name [Symbol, String] The name of the database to retrieve the prefix.
|
23
|
+
# @return [Proc]
|
24
|
+
def log_subscriber_replica_prefix(database_name)
|
25
|
+
database_config(database_name).log_subscriber_replica_prefix
|
26
|
+
end
|
27
|
+
|
28
|
+
# Helper to retrieve the proxy delay from the configuration stored in
|
29
|
+
# {ActiveRecordProxyAdapters::DatabaseConfiguration#proxy_delay}.
|
30
|
+
# @param database_name [Symbol, String] The name of the database to retrieve the proxy delay for.
|
14
31
|
# @return [ActiveSupport::Duration]
|
15
|
-
def proxy_delay
|
16
|
-
|
32
|
+
def proxy_delay(database_name)
|
33
|
+
database_config(database_name).proxy_delay
|
17
34
|
end
|
18
35
|
|
19
36
|
# Helper to retrieve the checkout timeout from the configuration stored in
|
20
|
-
# {ActiveRecordProxyAdapters::
|
37
|
+
# {ActiveRecordProxyAdapters::DatabaseConfiguration#checkout_timeout}.
|
38
|
+
# @param database_name [Symbol, String] The name of the database to retrieve the checkout timeout for.
|
21
39
|
# @return [ActiveSupport::Duration]
|
22
|
-
def checkout_timeout
|
23
|
-
|
40
|
+
def checkout_timeout(database_name)
|
41
|
+
database_config(database_name).checkout_timeout
|
24
42
|
end
|
25
43
|
|
26
44
|
# Helper to retrieve the context store class from the configuration stored in
|
@@ -50,6 +68,11 @@ module ActiveRecordProxyAdapters
|
|
50
68
|
proxy_config.cache
|
51
69
|
end
|
52
70
|
|
71
|
+
# @!visibility private
|
72
|
+
def database_config(database_name)
|
73
|
+
proxy_config.database(database_name)
|
74
|
+
end
|
75
|
+
|
53
76
|
# @!visibility private
|
54
77
|
def proxy_config
|
55
78
|
ActiveRecordProxyAdapters.config
|
@@ -35,13 +35,13 @@ module ActiveRecordProxyAdapters
|
|
35
35
|
# These patterns define which database statments are considered write statments, so we can shortly re-route all
|
36
36
|
# requests to the primary database so the replica has time to replicate
|
37
37
|
WRITE_STATEMENT_MATCHERS = [
|
38
|
-
/
|
39
|
-
/
|
40
|
-
/
|
41
|
-
/
|
42
|
-
/
|
43
|
-
/
|
44
|
-
/
|
38
|
+
/\ABEGIN/i,
|
39
|
+
/\ACOMMIT/i,
|
40
|
+
/\AROLLBACK/i,
|
41
|
+
/INSERT\s[\s\S]*INTO\s[\s\S]*/i,
|
42
|
+
/UPDATE\s[\s\S]*/i,
|
43
|
+
/DELETE\s[\s\S]*FROM\s[\s\S]*/i,
|
44
|
+
/DROP\s/i
|
45
45
|
].map(&:freeze).freeze
|
46
46
|
|
47
47
|
# Abstract adapter methods that should be proxied.
|
@@ -181,7 +181,7 @@ module ActiveRecordProxyAdapters
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def checkout_replica_connection
|
184
|
-
replica_pool.checkout(checkout_timeout)
|
184
|
+
replica_pool.checkout(checkout_timeout(primary_connection_name))
|
185
185
|
# rescue NoDatabaseError to avoid crashing when running db:create rake task
|
186
186
|
# rescue ConnectionNotEstablished to handle connectivity issues in the replica
|
187
187
|
# (for example, replication delay)
|
@@ -189,7 +189,6 @@ module ActiveRecordProxyAdapters
|
|
189
189
|
primary_connection
|
190
190
|
end
|
191
191
|
|
192
|
-
# @return [TrueClass] if there has been a write within the last {#proxy_delay} seconds
|
193
192
|
# @return [TrueClass] if sql_string matches a write statement (i.e. INSERT, UPDATE, DELETE, SELECT FOR UPDATE)
|
194
193
|
# @return [FalseClass] if sql_string matches a read statement (i.e. SELECT)
|
195
194
|
def need_primary?(sql_string)
|
@@ -233,7 +232,7 @@ module ActiveRecordProxyAdapters
|
|
233
232
|
end
|
234
233
|
|
235
234
|
def primary_connection_name
|
236
|
-
@primary_connection_name ||= primary_connection.pool.try(:db_config).try(:name).try(:
|
235
|
+
@primary_connection_name ||= primary_connection.pool.try(:db_config).try(:name).try(:to_s)
|
237
236
|
end
|
238
237
|
|
239
238
|
def proxy_context
|
@@ -6,6 +6,7 @@ module ActiveRecordProxyAdapters
|
|
6
6
|
# Hooks into rails boot process to extend ActiveRecord with the proxy adapter.
|
7
7
|
class Railtie < Rails::Railtie
|
8
8
|
require "active_record_proxy_adapters/connection_handling"
|
9
|
+
require "active_record_proxy_adapters/middleware"
|
9
10
|
|
10
11
|
config.to_prepare do
|
11
12
|
Rails.autoloaders.each do |autoloader|
|
@@ -17,5 +18,9 @@ module ActiveRecordProxyAdapters
|
|
17
18
|
)
|
18
19
|
end
|
19
20
|
end
|
21
|
+
|
22
|
+
initializer "active_record_proxy_adapters.configure_rails_initialization" do |app|
|
23
|
+
app.middleware.use ActiveRecordProxyAdapters::Middleware
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_proxy_adapters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Cruz
|
@@ -63,6 +63,20 @@ dependencies:
|
|
63
63
|
- - ">="
|
64
64
|
- !ruby/object:Gem::Version
|
65
65
|
version: 3.1.0
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: json
|
68
|
+
requirement: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
type: :runtime
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
66
80
|
description: |-
|
67
81
|
This gem allows automatic connection switching between a primary and one read replica database in ActiveRecord.
|
68
82
|
It pattern matches the SQL statement being sent to decide whether it should go to the replica (SELECT) or the
|
@@ -96,9 +110,11 @@ files:
|
|
96
110
|
- lib/active_record_proxy_adapters/connection_handling/trilogy.rb
|
97
111
|
- lib/active_record_proxy_adapters/context.rb
|
98
112
|
- lib/active_record_proxy_adapters/contextualizer.rb
|
113
|
+
- lib/active_record_proxy_adapters/database_configuration.rb
|
99
114
|
- lib/active_record_proxy_adapters/database_tasks.rb
|
100
115
|
- lib/active_record_proxy_adapters/hijackable.rb
|
101
116
|
- lib/active_record_proxy_adapters/log_subscriber.rb
|
117
|
+
- lib/active_record_proxy_adapters/middleware.rb
|
102
118
|
- lib/active_record_proxy_adapters/mixin/configuration.rb
|
103
119
|
- lib/active_record_proxy_adapters/mysql2_proxy.rb
|
104
120
|
- lib/active_record_proxy_adapters/postgresql_proxy.rb
|