active_record_proxy_adapters 0.7.2 → 0.9.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -1
  3. data/lib/active_record/connection_adapters/mysql2_proxy_adapter.rb +0 -6
  4. data/lib/active_record/connection_adapters/postgresql_proxy_adapter.rb +0 -6
  5. data/lib/active_record/connection_adapters/trilogy_proxy_adapter.rb +1 -7
  6. data/lib/active_record_proxy_adapters/active_record_context.rb +1 -25
  7. data/lib/active_record_proxy_adapters/configuration.rb +65 -4
  8. data/lib/active_record_proxy_adapters/connection_handling/sqlite3_proxy.rb +28 -0
  9. data/lib/active_record_proxy_adapters/connection_handling.rb +4 -4
  10. data/lib/active_record_proxy_adapters/core.rb +13 -0
  11. data/lib/active_record_proxy_adapters/errors.rb +7 -0
  12. data/lib/active_record_proxy_adapters/log_subscriber.rb +1 -11
  13. data/lib/active_record_proxy_adapters/mixin/configuration.rb +14 -1
  14. data/lib/active_record_proxy_adapters/primary_replica_proxy.rb +26 -7
  15. data/lib/active_record_proxy_adapters/railtie.rb +5 -12
  16. data/lib/active_record_proxy_adapters/railties/mysql2.rb +13 -0
  17. data/lib/active_record_proxy_adapters/railties/postgresql.rb +21 -0
  18. data/lib/active_record_proxy_adapters/railties/sqlite3.rb +21 -0
  19. data/lib/active_record_proxy_adapters/railties/trilogy.rb +13 -0
  20. data/lib/active_record_proxy_adapters/trilogy_proxy.rb +1 -0
  21. data/lib/active_record_proxy_adapters/version.rb +1 -1
  22. data/lib/active_record_proxy_adapters.rb +0 -19
  23. metadata +44 -11
  24. data/lib/active_record_proxy_adapters/connection_handling/sqlite3.rb +0 -68
  25. data/lib/active_record_proxy_adapters/transactionable_proxy_a_r_70.rb +0 -31
  26. /data/lib/active_record_proxy_adapters/connection_handling/{mysql2.rb → mysql2_proxy.rb} +0 -0
  27. /data/lib/active_record_proxy_adapters/connection_handling/{postgresql.rb → postgresql_proxy.rb} +0 -0
  28. /data/lib/active_record_proxy_adapters/connection_handling/{trilogy.rb → trilogy_proxy.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5942f8ab80752d3abe9ac1015a991cc8bb8cbe7faa3961824e617ec478fd1cf0
4
- data.tar.gz: daaf2fb00ad05b38c0477bb14d5a8f02d405863fd592252c63a887964385cc2f
3
+ metadata.gz: f404b61f94eec82c838124e111eba202e7fdbea61c07596739219749e7c76ac2
4
+ data.tar.gz: 930327b485fb9b7637a1b810c0cb52def9d0164777103f918862a9dd4a0a89e7
5
5
  SHA512:
6
- metadata.gz: 2f61e7d36e3d968c9cf4f873f3cc7d70c5a1054337aa0b773ad59d76ad52b33088dc2c4588cf996725a7fa2efda42de77888654db6b3e53a93026a06dced44e4
7
- data.tar.gz: 022acae0581a7910927a18a7452750e9628b238cc590b05e3b3bf15c0b3d4f8b163fbcc7fcc30071f3673dc7d1c6cd33fa01c2bd1c88d0c1e5611de6f77c060f
6
+ metadata.gz: 4d0c6210521feb5db9ce40fc476607fb862d818e16feb23d795e95f20f20b8db0b1311049a5895f6e9795ba2740cdef140ecb6ec67f5f1c7c7aec759e0a4c2ca
7
+ data.tar.gz: 1134342f6be3093cb5a3ddaba514ef702adb611e5646a3abd3c68073fcb7060459a39a5845983733b4c5f0bc24d5928f57e8333b7741bbb8b3fa812d9472f066
data/README.md CHANGED
@@ -25,6 +25,18 @@ Install the gem and add to the application's Gemfile by executing:
25
25
 
26
26
  $ bundle add 'active_record_proxy_adapters'
27
27
 
28
+ That will install and load the proxies for all Rails-supported adapters (i.e. `postgresql_proxy`, `mysql2_proxy`, `sqlite3_proxy` and `trilogy_proxy`).
29
+
30
+ If you wish to load only one specific adapter for a faster application boot, use:
31
+
32
+ $ bundle add 'active_record_proxy_adapters' --require 'active_record_proxy_adapters/railties/<postgresql|mysql2|sqlite3|trilogy>'
33
+
34
+ Or, in your Gemfile, use:
35
+
36
+ ```ruby
37
+ gem "active_record_proxy_adapters", require: 'active_record_proxy_adapters/railties/<postgresql|mysql2|sqlite3|trilogy>'
38
+ ```
39
+
28
40
  If bundler is not being used to manage dependencies, install the gem by executing:
29
41
 
30
42
  $ gem install active_record_proxy_adapters
@@ -558,7 +570,7 @@ To create a proxy adapter for an existing database `FoobarAdapter`, follow these
558
570
  return
559
571
  end
560
572
 
561
- # This is only required for Rails 7.0 or earlier.
573
+ # This is only required for Rails 7.1 or earlier.
562
574
  module ActiveRecordProxyAdapters
563
575
  module Foobar
564
576
  module ConnectionHandling
@@ -13,12 +13,6 @@ module ActiveRecord
13
13
  class Mysql2ProxyAdapter < Mysql2Adapter
14
14
  include ActiveRecordProxyAdapters::Hijackable
15
15
 
16
- if ActiveRecordProxyAdapters::ActiveRecordContext.active_record_v7_0?
17
- require "active_record_proxy_adapters/transactionable_proxy_a_r_70"
18
-
19
- include ActiveRecordProxyAdapters::TransactionableProxyAR70
20
- end
21
-
22
16
  ADAPTER_NAME = "Mysql2Proxy"
23
17
 
24
18
  delegate_to_proxy(*ActiveRecordProxyAdapters::ActiveRecordContext.hijackable_methods)
@@ -13,12 +13,6 @@ module ActiveRecord
13
13
  class PostgreSQLProxyAdapter < PostgreSQLAdapter
14
14
  include ActiveRecordProxyAdapters::Hijackable
15
15
 
16
- if ActiveRecordProxyAdapters::ActiveRecordContext.active_record_v7_0?
17
- require "active_record_proxy_adapters/transactionable_proxy_a_r_70"
18
-
19
- include ActiveRecordProxyAdapters::TransactionableProxyAR70
20
- end
21
-
22
16
  ADAPTER_NAME = "PostgreSQLProxy"
23
17
 
24
18
  delegate_to_proxy(*ActiveRecordProxyAdapters::ActiveRecordContext.hijackable_methods)
@@ -13,15 +13,9 @@ module ActiveRecord
13
13
  class TrilogyProxyAdapter < TrilogyAdapter
14
14
  include ActiveRecordProxyAdapters::Hijackable
15
15
 
16
- if ActiveRecordProxyAdapters::ActiveRecordContext.active_record_v7_0?
17
- require "active_record_proxy_adapters/transactionable_proxy_a_r_70"
18
-
19
- include ActiveRecordProxyAdapters::TransactionableProxyAR70
20
- end
21
-
22
16
  ADAPTER_NAME = "TrilogyProxy"
23
17
 
24
- delegate_to_proxy(*ActiveRecordProxyAdapters::ActiveRecordContext.hijackable_methods)
18
+ delegate_to_proxy(*ActiveRecordProxyAdapters::ActiveRecordContext.hijackable_methods, :exec_insert)
25
19
 
26
20
  def initialize(...)
27
21
  @proxy = ActiveRecordProxyAdapters::TrilogyProxy.new(self)
@@ -5,38 +5,18 @@ module ActiveRecordProxyAdapters
5
5
  # versions of rails.
6
6
  class ActiveRecordContext
7
7
  delegate :reading_role, :reading_role=, :writing_role, :writing_role=, to: :ActiveRecord
8
- delegate :legacy_connection_handling, :legacy_connection_handling=, to: :connection_handling_context
9
8
  delegate :version, to: :ActiveRecord, prefix: :active_record
10
9
 
11
10
  class << self
12
11
  delegate_missing_to :new
13
12
  end
14
13
 
15
- # rubocop:disable Naming/PredicateMethod
16
- NullConnectionHandlingContext = Class.new do
17
- def legacy_connection_handling
18
- false
19
- end
20
-
21
- def legacy_connection_handling=(_value)
22
- nil
23
- end
24
- end
25
- # rubocop:enable Naming/PredicateMethod
26
-
27
14
  def connection_class_for(connection)
28
15
  return connection.connection_descriptor.name.constantize if active_record_v8_0_2_or_greater?
29
16
 
30
17
  connection.connection_class || ActiveRecord::Base
31
18
  end
32
19
 
33
- def connection_handling_context
34
- # This config option has been removed in Rails 7.1+
35
- return NullConnectionHandlingContext.new if active_record_v7_1_or_greater?
36
-
37
- ActiveRecord
38
- end
39
-
40
20
  def hijackable_methods
41
21
  hijackable = %i[execute exec_query]
42
22
 
@@ -46,11 +26,7 @@ module ActiveRecordProxyAdapters
46
26
  end
47
27
 
48
28
  def active_record_v7?
49
- active_record_version >= Gem::Version.new("7.0") && active_record_version < Gem::Version.new("8.0")
50
- end
51
-
52
- def active_record_v7_0?
53
- active_record_version >= Gem::Version.new("7.0") && active_record_version < Gem::Version.new("7.1")
29
+ active_record_version >= Gem::Version.new("7.1") && active_record_version < Gem::Version.new("8.0")
54
30
  end
55
31
 
56
32
  def active_record_v7_1_or_greater?
@@ -3,26 +3,64 @@
3
3
  require "active_record_proxy_adapters/cache_configuration"
4
4
  require "active_record_proxy_adapters/context"
5
5
  require "active_record_proxy_adapters/database_configuration"
6
+ require "active_record_proxy_adapters/errors"
6
7
  require "active_record_proxy_adapters/synchronizable_configuration"
7
8
  require "active_support/core_ext/integer/time"
9
+ require "logger"
10
+
11
+ module ActiveRecordProxyAdapters # rubocop:disable Style/Documentation
12
+ module_function
13
+
14
+ def configure
15
+ yield(config)
16
+ end
17
+
18
+ def bust_query_cache
19
+ config.cache.bust
20
+ end
21
+
22
+ def config
23
+ @config ||= Configuration.new
24
+ end
8
25
 
9
- module ActiveRecordProxyAdapters
10
26
  # Provides a global configuration object to configure how the proxy should behave.
11
27
  class Configuration
12
28
  include SynchronizableConfiguration
13
29
 
14
30
  DEFAULT_DATABASE_NAME = :primary
15
31
  DEFAULT_REPLICA_DATABASE_NAME = :primary_replica
32
+ TIMEOUT_MESSAGE_BUILDER = proc { |sql_string, regex = nil|
33
+ [regex, "timed out. Input too big (#{sql_string.size})."].compact_blank.join(" ")
34
+ }.freeze
35
+
36
+ private_constant :TIMEOUT_MESSAGE_BUILDER
37
+
38
+ REGEXP_TIMEOUT_STRATEGY_REGISTRY = {
39
+ log: proc { |sql_string, regex = nil|
40
+ ActiveRecordProxyAdapters.config.logger.error(TIMEOUT_MESSAGE_BUILDER.call(sql_string, regex))
41
+ },
42
+ raise: proc { |sql_string, regex = nil|
43
+ raise ActiveRecordProxyAdapters::RegexpTimeoutError, TIMEOUT_MESSAGE_BUILDER.call(sql_string, regex)
44
+ }
45
+ }.freeze
16
46
 
17
47
  # @return [Class] The context that is used to store the current request's state.
18
48
  attr_reader :context_store
19
49
 
50
+ # @return [Proc] The timeout strategy to use for regex matching.
51
+ attr_reader :regexp_timeout_strategy
52
+
53
+ # @return [Logger] The logger to use for logging messages.
54
+ attr_reader :logger
55
+
20
56
  def initialize
21
57
  @lock = Monitor.new
22
58
 
23
- self.cache_configuration = CacheConfiguration.new(@lock)
24
- self.context_store = ActiveRecordProxyAdapters::Context
25
- @database_configurations = {}
59
+ self.cache_configuration = CacheConfiguration.new(@lock)
60
+ self.context_store = ActiveRecordProxyAdapters::Context
61
+ self.regexp_timeout_strategy = :log
62
+ self.logger = ActiveRecord::Base.logger || Logger.new($stdout)
63
+ @database_configurations = {}
26
64
  end
27
65
 
28
66
  def log_subscriber_primary_prefix=(prefix)
@@ -57,6 +95,25 @@ module ActiveRecordProxyAdapters
57
95
  default_database_config.checkout_timeout = checkout_timeout
58
96
  end
59
97
 
98
+ def logger=(logger)
99
+ synchronize_update(:logger, from: @logger, to: logger) do
100
+ @logger = logger
101
+ end
102
+ end
103
+
104
+ def regexp_timeout_strategy=(strategy)
105
+ synchronize_update(:regexp_timeout_strategy, from: @regexp_timeout_strategy, to: strategy) do
106
+ @regexp_timeout_strategy = if strategy.respond_to?(:call)
107
+ strategy
108
+ else
109
+ REGEXP_TIMEOUT_STRATEGY_REGISTRY.fetch(strategy)
110
+ end
111
+ rescue KeyError
112
+ raise ActiveRecordProxyAdapters::ConfigurationError,
113
+ "Invalid regex timeout strategy: #{strategy.inspect}. Must be one of: #{valid_regexp_timeout_strategies}"
114
+ end
115
+ end
116
+
60
117
  def database(database_name)
61
118
  key = database_name.to_s
62
119
  lock.synchronize { @database_configurations[key] ||= DatabaseConfiguration.new }
@@ -72,6 +129,10 @@ module ActiveRecordProxyAdapters
72
129
 
73
130
  attr_reader :cache_configuration, :database_configurations, :lock
74
131
 
132
+ def valid_regexp_timeout_strategies
133
+ REGEXP_TIMEOUT_STRATEGY_REGISTRY.keys
134
+ end
135
+
75
136
  def default_database_config
76
137
  database(DEFAULT_DATABASE_NAME)
77
138
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "active_record/connection_adapters/sqlite3_proxy_adapter"
5
+ rescue LoadError
6
+ # sqlite3 not available
7
+ return
8
+ end
9
+
10
+ module ActiveRecordProxyAdapters
11
+ module SQLite3
12
+ # Module to extend ActiveRecord::Base with the connection handling methods.
13
+ # Required to make adapter work in ActiveRecord versions <= 7.2.x
14
+ module ConnectionHandling
15
+ def sqlite3_proxy_adapter_class
16
+ ActiveRecord::ConnectionAdapters::SQLite3ProxyAdapter
17
+ end
18
+
19
+ def sqlite3_proxy_connection(config)
20
+ sqlite3_proxy_adapter_class.new(config)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ ActiveSupport.on_load(:active_record) do
27
+ ActiveRecord::Base.extend(ActiveRecordProxyAdapters::SQLite3::ConnectionHandling)
28
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record_proxy_adapters/connection_handling/postgresql"
4
- require "active_record_proxy_adapters/connection_handling/mysql2"
5
- require "active_record_proxy_adapters/connection_handling/trilogy"
6
- require "active_record_proxy_adapters/connection_handling/sqlite3"
3
+ require "active_record_proxy_adapters/connection_handling/postgresql_proxy"
4
+ require "active_record_proxy_adapters/connection_handling/mysql2_proxy"
5
+ require "active_record_proxy_adapters/connection_handling/trilogy_proxy"
6
+ require "active_record_proxy_adapters/connection_handling/sqlite3_proxy"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "active_record_proxy_adapters/version"
5
+ require "active_record_proxy_adapters/active_record_context"
6
+ require "active_record_proxy_adapters/configuration"
7
+ require "active_record_proxy_adapters/context"
8
+ require "active_record_proxy_adapters/contextualizer"
9
+ require "active_record_proxy_adapters/database_tasks"
10
+ require "active_record_proxy_adapters/errors"
11
+ require "active_record_proxy_adapters/hijackable"
12
+ require "active_record_proxy_adapters/mixin/configuration"
13
+ require "active_record_proxy_adapters/primary_replica_proxy"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordProxyAdapters
4
+ Error = Class.new(StandardError)
5
+ RegexpTimeoutError = Class.new(Error)
6
+ ConfigurationError = Class.new(Error)
7
+ end
@@ -22,22 +22,12 @@ module ActiveRecordProxyAdapters
22
22
 
23
23
  def database_instance_prefix_for(event)
24
24
  connection = event.payload[:connection]
25
- db_config = connection.pool.try(:db_config) || NullConfig.new # AR 7.0.x does not support "NullConfig"
25
+ db_config = connection.pool.try(:db_config)
26
26
  connection_name = db_config.name
27
27
 
28
28
  prefix = log_subscriber_prefix(connection_name)
29
29
 
30
30
  "[#{prefix.call(event)}]"
31
31
  end
32
-
33
- class NullConfig # rubocop:disable Style/Documentation
34
- def method_missing(...)
35
- nil
36
- end
37
-
38
- def respond_to_missing?(*)
39
- true
40
- end
41
- end
42
32
  end
43
33
  end
@@ -3,7 +3,6 @@
3
3
  require "active_support/core_ext/integer/time"
4
4
  require "active_record_proxy_adapters/synchronizable_configuration"
5
5
  require "active_record_proxy_adapters/cache_configuration"
6
- require "active_record_proxy_adapters/context"
7
6
 
8
7
  module ActiveRecordProxyAdapters
9
8
  module Mixin
@@ -40,6 +39,20 @@ module ActiveRecordProxyAdapters
40
39
  proxy_config.context_store
41
40
  end
42
41
 
42
+ # Helper to retrieve the logger from the configuration stored in
43
+ # {ActiveRecordProxyAdapters::Configuration#logger}.
44
+ # @return [Logger]
45
+ def logger
46
+ proxy_config.logger
47
+ end
48
+
49
+ # Helper to retrieve the timeout strategy from the configuration stored in
50
+ # {ActiveRecordProxyAdapters::Configuration#regexp_timeout_strategy}.
51
+ # @return [Proc]
52
+ def regexp_timeout_strategy
53
+ proxy_config.regexp_timeout_strategy
54
+ end
55
+
43
56
  # Helper to retrieve the cache store from the configuration stored in
44
57
  # {ActiveRecordProxyAdapters::CacheConfiguration#store}.
45
58
  # @return [ActiveSupport::Cache::Store]
@@ -7,6 +7,7 @@ require "active_record_proxy_adapters/hijackable"
7
7
  require "active_record_proxy_adapters/mixin/configuration"
8
8
  require "active_support/core_ext/module/delegation"
9
9
  require "active_support/core_ext/object/blank"
10
+ require "timeout"
10
11
 
11
12
  module ActiveRecordProxyAdapters
12
13
  # This is the base class for all proxies. It defines the methods that should be proxied
@@ -113,8 +114,9 @@ module ActiveRecordProxyAdapters
113
114
  end.last
114
115
  end
115
116
 
116
- def roles_for(sql_string) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
117
- return [top_of_connection_stack_role] if top_of_connection_stack_role.present?
117
+ def roles_for(sql_string) # rubocop:disable Metrics/MethodLength
118
+ top_of_stack_role = top_of_connection_stack_role
119
+ return [top_of_stack_role] if top_of_stack_role.present?
118
120
  return [writing_role] if recent_write_to_primary? || in_transaction?
119
121
 
120
122
  cache_key = cache_key_for(sql_string)
@@ -136,17 +138,23 @@ module ActiveRecordProxyAdapters
136
138
  return if connected_to_stack.empty?
137
139
 
138
140
  top = connected_to_stack.last
139
- role = top[:role]
141
+ role, klasses = top.values_at(:role, :klasses)
140
142
  return unless role.present?
141
143
 
142
- [reading_role, writing_role].include?(role) ? role : nil
144
+ # ActiveRecord::Base is the parent record for all models so,
145
+ # if the top of the stack includes it, we should respect it.
146
+ role_for_current_class = klasses.include?(connection_class) || klasses.include?(ActiveRecord::Base)
147
+
148
+ [reading_role, writing_role].include?(role) && role_for_current_class ? role : nil
143
149
  end
144
150
 
145
151
  def connected_to_stack
146
152
  return connection_class.connected_to_stack if connection_class.respond_to?(:connected_to_stack)
147
153
 
148
154
  # handle Rails 7.2+ pending migrations Connection
149
- return [{ role: writing_role }] if pending_migration_connection?
155
+ if pending_migration_connection?
156
+ return [{ role: writing_role, shard: nil, prevent_writes: false, klasses: [connection_class] }]
157
+ end
150
158
 
151
159
  []
152
160
  end
@@ -181,7 +189,7 @@ module ActiveRecordProxyAdapters
181
189
  end
182
190
 
183
191
  def checkout_replica_connection
184
- replica_pool.checkout(checkout_timeout(primary_connection_name))
192
+ replica_pool.checkout(proxy_checkout_timeout)
185
193
  # rescue NoDatabaseError to avoid crashing when running db:create rake task
186
194
  # rescue ConnectionNotEstablished to handle connectivity issues in the replica
187
195
  # (for example, replication delay)
@@ -215,7 +223,14 @@ module ActiveRecordProxyAdapters
215
223
  end
216
224
 
217
225
  def match_sql?(sql_string)
218
- proc { |matcher| matcher.match?(sql_string) }
226
+ proc do |matcher|
227
+ # TODO: switch to regexp timeout once Ruby 3.1 support is dropped.
228
+ Timeout.timeout(proxy_checkout_timeout.to_f) { matcher.match?(sql_string) }
229
+ rescue Timeout::Error
230
+ regexp_timeout_strategy.call(sql_string, matcher)
231
+
232
+ false
233
+ end
219
234
  end
220
235
 
221
236
  # @return Boolean
@@ -235,6 +250,10 @@ module ActiveRecordProxyAdapters
235
250
  @primary_connection_name ||= primary_connection.pool.try(:db_config).try(:name).try(:to_s)
236
251
  end
237
252
 
253
+ def proxy_checkout_timeout
254
+ checkout_timeout(primary_connection_name)
255
+ end
256
+
238
257
  def proxy_context
239
258
  self.current_context ||= context_store.new({})
240
259
  end
@@ -1,24 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "active_record_proxy_adapters/core"
5
+ require "active_record_proxy_adapters/railties/postgresql"
6
+ require "active_record_proxy_adapters/railties/mysql2"
7
+ require "active_record_proxy_adapters/railties/trilogy"
8
+ require "active_record_proxy_adapters/railties/sqlite3"
4
9
 
5
10
  module ActiveRecordProxyAdapters
6
11
  # Hooks into rails boot process to extend ActiveRecord with the proxy adapter.
7
12
  class Railtie < Rails::Railtie
8
- require "active_record_proxy_adapters/connection_handling"
9
13
  require "active_record_proxy_adapters/middleware"
10
14
 
11
- config.to_prepare do
12
- Rails.autoloaders.each do |autoloader|
13
- autoloader.inflector.inflect(
14
- "postgresql_proxy_adapter" => "PostgreSQLProxyAdapter"
15
- )
16
- autoloader.inflector.inflect(
17
- "sqlite3_proxy_adapter" => "SQLite3ProxyAdapter"
18
- )
19
- end
20
- end
21
-
22
15
  initializer "active_record_proxy_adapters.configure_rails_initialization" do |app|
23
16
  app.middleware.use ActiveRecordProxyAdapters::Middleware
24
17
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_record_proxy_adapters/core"
5
+
6
+ module ActiveRecordProxyAdapters
7
+ module Railties
8
+ # Hooks into rails boot process to load the Mysql2 Proxy adapter.
9
+ class Mysql2Proxy < Rails::Railtie
10
+ require "active_record_proxy_adapters/connection_handling/mysql2_proxy"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_record_proxy_adapters/core"
5
+
6
+ module ActiveRecordProxyAdapters
7
+ module Railties
8
+ # Hooks into rails boot process to load the PostgreSQL Proxy adapter.
9
+ class PostgreSQLProxy < Rails::Railtie
10
+ require "active_record_proxy_adapters/connection_handling/postgresql_proxy"
11
+
12
+ config.to_prepare do
13
+ Rails.autoloaders.each do |autoloader|
14
+ autoloader.inflector.inflect(
15
+ "postgresql_proxy_adapter" => "PostgreSQLProxyAdapter"
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_record_proxy_adapters/core"
5
+
6
+ module ActiveRecordProxyAdapters
7
+ module Railties
8
+ # Hooks into rails boot process to load the SQLite3 Proxy adapter.
9
+ class SQLite3Proxy < Rails::Railtie
10
+ require "active_record_proxy_adapters/connection_handling/sqlite3_proxy"
11
+
12
+ config.to_prepare do
13
+ Rails.autoloaders.each do |autoloader|
14
+ autoloader.inflector.inflect(
15
+ "sqlite3_proxy_adapter" => "SQLite3ProxyAdapter"
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_record_proxy_adapters/core"
5
+
6
+ module ActiveRecordProxyAdapters
7
+ module Railties
8
+ # Hooks into rails boot process to load the Trilogy Proxy adapter.
9
+ class TrilogyProxy < Rails::Railtie
10
+ require "active_record_proxy_adapters/connection_handling/trilogy_proxy"
11
+ end
12
+ end
13
+ end
@@ -5,5 +5,6 @@ require "active_record_proxy_adapters/mysql2_proxy"
5
5
  module ActiveRecordProxyAdapters
6
6
  # Proxy to the Mysql2Proxy, allowing the use of the ActiveRecordProxyAdapters::PrimaryReplicaProxy.
7
7
  class TrilogyProxy < Mysql2Proxy
8
+ hijack_method :exec_insert
8
9
  end
9
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordProxyAdapters
4
- VERSION = "0.7.2"
4
+ VERSION = "0.9.0"
5
5
  end
@@ -1,26 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record"
4
- require "active_record_proxy_adapters/version"
5
- require "active_record_proxy_adapters/configuration"
6
-
7
3
  # The gem namespace.
8
4
  module ActiveRecordProxyAdapters
9
- class Error < StandardError; end
10
-
11
- module_function
12
-
13
- def configure
14
- yield(config)
15
- end
16
-
17
- def bust_query_cache
18
- config.cache.bust
19
- end
20
-
21
- def config
22
- @config ||= Configuration.new
23
- end
24
5
  end
25
6
 
26
7
  require_relative "active_record_proxy_adapters/railtie" if defined?(Rails::Railtie)
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.7.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Cruz
@@ -15,7 +15,7 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 7.0.0
18
+ version: 7.1.0
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '8.1'
@@ -25,7 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 7.0.0
28
+ version: 7.1.0
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '8.1'
@@ -35,7 +35,7 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 7.0.0
38
+ version: 7.1.0
39
39
  - - "<"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '8.1'
@@ -45,7 +45,7 @@ dependencies:
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 7.0.0
48
+ version: 7.1.0
49
49
  - - "<"
50
50
  - !ruby/object:Gem::Version
51
51
  version: '8.1'
@@ -77,6 +77,34 @@ dependencies:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
+ - !ruby/object:Gem::Dependency
81
+ name: logger
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: timeout
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ type: :runtime
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
80
108
  description: |-
81
109
  This gem allows automatic connection switching between a primary and one read replica database in ActiveRecord.
82
110
  It pattern matches the SQL statement being sent to decide whether it should go to the replica (SELECT) or the
@@ -104,14 +132,16 @@ files:
104
132
  - lib/active_record_proxy_adapters/cache_configuration.rb
105
133
  - lib/active_record_proxy_adapters/configuration.rb
106
134
  - lib/active_record_proxy_adapters/connection_handling.rb
107
- - lib/active_record_proxy_adapters/connection_handling/mysql2.rb
108
- - lib/active_record_proxy_adapters/connection_handling/postgresql.rb
109
- - lib/active_record_proxy_adapters/connection_handling/sqlite3.rb
110
- - lib/active_record_proxy_adapters/connection_handling/trilogy.rb
135
+ - lib/active_record_proxy_adapters/connection_handling/mysql2_proxy.rb
136
+ - lib/active_record_proxy_adapters/connection_handling/postgresql_proxy.rb
137
+ - lib/active_record_proxy_adapters/connection_handling/sqlite3_proxy.rb
138
+ - lib/active_record_proxy_adapters/connection_handling/trilogy_proxy.rb
111
139
  - lib/active_record_proxy_adapters/context.rb
112
140
  - lib/active_record_proxy_adapters/contextualizer.rb
141
+ - lib/active_record_proxy_adapters/core.rb
113
142
  - lib/active_record_proxy_adapters/database_configuration.rb
114
143
  - lib/active_record_proxy_adapters/database_tasks.rb
144
+ - lib/active_record_proxy_adapters/errors.rb
115
145
  - lib/active_record_proxy_adapters/hijackable.rb
116
146
  - lib/active_record_proxy_adapters/log_subscriber.rb
117
147
  - lib/active_record_proxy_adapters/middleware.rb
@@ -120,9 +150,12 @@ files:
120
150
  - lib/active_record_proxy_adapters/postgresql_proxy.rb
121
151
  - lib/active_record_proxy_adapters/primary_replica_proxy.rb
122
152
  - lib/active_record_proxy_adapters/railtie.rb
153
+ - lib/active_record_proxy_adapters/railties/mysql2.rb
154
+ - lib/active_record_proxy_adapters/railties/postgresql.rb
155
+ - lib/active_record_proxy_adapters/railties/sqlite3.rb
156
+ - lib/active_record_proxy_adapters/railties/trilogy.rb
123
157
  - lib/active_record_proxy_adapters/sqlite3_proxy.rb
124
158
  - lib/active_record_proxy_adapters/synchronizable_configuration.rb
125
- - lib/active_record_proxy_adapters/transactionable_proxy_a_r_70.rb
126
159
  - lib/active_record_proxy_adapters/trilogy_proxy.rb
127
160
  - lib/active_record_proxy_adapters/version.rb
128
161
  homepage: https://github.com/Nasdaq/active_record_proxy_adapters
@@ -141,7 +174,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
174
  requirements:
142
175
  - - ">="
143
176
  - !ruby/object:Gem::Version
144
- version: 3.1.0
177
+ version: 3.2.0
145
178
  required_rubygems_version: !ruby/object:Gem::Requirement
146
179
  requirements:
147
180
  - - ">="
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- begin
4
- require "active_record/connection_adapters/sqlite3_proxy_adapter"
5
- rescue LoadError
6
- # sqlite3 not available
7
- return
8
- end
9
-
10
- module ActiveRecordProxyAdapters
11
- module SQLite3
12
- # Module to extend ActiveRecord::Base with the connection handling methods.
13
- # Required to make adapter work in ActiveRecord versions <= 7.2.x
14
- module ConnectionHandling
15
- def sqlite3_proxy_adapter_class
16
- ActiveRecord::ConnectionAdapters::SQLite3ProxyAdapter
17
- end
18
-
19
- def sqlite3_proxy_connection(config)
20
- connection_factory_mapping
21
- .fetch("#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}")
22
- .call(config)
23
- end
24
-
25
- def connection_factory_mapping
26
- {
27
- "7.0" => ->(config) { sqlite3_proxy_connection_ar_v70(config) },
28
- "7.1" => ->(config) { sqlite3_proxy_connection_ar_v71(config) }
29
- }
30
- end
31
-
32
- def sqlite3_proxy_connection_ar_v70(config) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
33
- config = config.symbolize_keys
34
-
35
- # Require database.
36
- raise ArgumentError, "No database file specified. Missing argument: database" unless config[:database]
37
-
38
- # Allow database path relative to Rails.root, but only if the database
39
- # path is not the special path that tells sqlite to build a database only
40
- # in memory.
41
- if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:") # rubocop:disable Style/YodaCondition
42
- config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
43
- dirname = File.dirname(config[:database])
44
- Dir.mkdir(dirname) unless File.directory?(dirname)
45
- end
46
-
47
- db = ::SQLite3::Database.new(
48
- config[:database].to_s,
49
- config.merge(results_as_hash: true)
50
- )
51
-
52
- sqlite3_proxy_adapter_class.new(db, logger, nil, config)
53
- rescue Errno::ENOENT => e
54
- raise ActiveRecord::NoDatabaseError if e.message.include?("No such file or directory")
55
-
56
- raise
57
- end
58
-
59
- def sqlite3_proxy_connection_ar_v71(config)
60
- sqlite3_proxy_adapter_class.new(config)
61
- end
62
- end
63
- end
64
- end
65
-
66
- ActiveSupport.on_load(:active_record) do
67
- ActiveRecord::Base.extend(ActiveRecordProxyAdapters::SQLite3::ConnectionHandling)
68
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveRecordProxyAdapters
4
- module TransactionableProxyAR70 # rubocop:disable Style/Documentation
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- def begin_db_transaction # :nodoc:
9
- bypass_proxy_or_else("BEGIN", "TRANSACTION") { super }
10
- end
11
-
12
- def commit_db_transaction # :nodoc:
13
- bypass_proxy_or_else("COMMIT", "TRANSACTION") { super }
14
- end
15
-
16
- def exec_rollback_db_transaction # :nodoc:
17
- bypass_proxy_or_else("ROLLBACK", "TRANSACTION") { super }
18
- end
19
-
20
- private
21
-
22
- def bypass_proxy_or_else(*args)
23
- method_name = proxy_method_name_for(:execute)
24
-
25
- return public_send(method_name, *args) if respond_to?(method_name)
26
-
27
- yield
28
- end
29
- end
30
- end
31
- end