activerecord-enhancedsqlite3-adapter 0.4.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd134c96488a861c3fdc36c32396547a31ea596c11243aff923995acff8ae4b5
4
- data.tar.gz: d2029b2162c66df5689e65cb4e281f73d21049adafaecf9bbf78490570a10321
3
+ metadata.gz: e0d679b0ee45b0838fc294f50e43b806301fba662e54f280b3d0e747de3e7670
4
+ data.tar.gz: 5b345ff772a3cba527a4fe638724bb372f488bc8ec3ceb4d3e8b76ee40b2f434
5
5
  SHA512:
6
- metadata.gz: 6803168a5f067839e6ea7416f2f067bbc044202d14fd21a9536d068bebe35d7d89a481cbfcf5e230d4063705a7b5241aeb76d8f92846e2ecf5cdc2afa8b805f0
7
- data.tar.gz: 2845ea0983129dd0e6e66b66e8f10fca3538192dcdaec62ec55f497ebb8f82f52bdc2352eb1fdf85b859bbd7436e86b57d15da4ce1135c00e23f8996db5cfc59
6
+ metadata.gz: 47288376cd918173cce7edf28fc2a8cbb8a86c749aa44aaf88e4b2f81fc922b3b5369e579abb8e9bab7585f7c8b2ae732c3f6a8601117e935a61effcf7d86276
7
+ data.tar.gz: 22ae8604263b8a75379a0f9c132d60cf4616d6695665742b4a6a358f24f0fa4daf1b5b1708ecf5599617293bc4a8fc76db89fbd204deaa70b430233db2f6891a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2024-04-10
4
+
5
+ - Use the same busy_handler function as will be in the sqlite3-ruby gem ([@fractaledmind](https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter/pull/11))
6
+ - Allow for isolated reading and writing connection pools ([@fractaledmind](https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter/pull/12))
7
+ - Ensure that even Rails 7.0 apps can use insert returning ([@fractaledmind](https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter/pull/8))
8
+ - Load virtual columns extension only if the app is running 7.1 or less ([@npezza93](https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter/pull/7))
9
+
10
+ ## [0.5.0] - 2023-12-24
11
+
12
+ - Load extensions installed via project-scoped `sqlpkg`
13
+
3
14
  ## [0.4.0] - 2023-12-10
4
15
 
5
16
  - Ensure transactions are IMMEDIATE and not DEFERRED
data/README.md CHANGED
@@ -21,6 +21,14 @@ This gem hooks into your Rails application to enhance the `SQLite3Adapter` autom
21
21
 
22
22
  Once installed, you can take advantage of the added features.
23
23
 
24
+ ### Configuration
25
+
26
+ One optional advanced feature is to have this gem isolate reading and writing connection pools. This is useful if you have a large amount of write operations and want to avoid blocking reads.
27
+
28
+ You can configure this gem via the Rails configuration object, under the `enhanced_sqlite3` key. Currently, only 1 configuration option is available:
29
+
30
+ * `isolate_connection_pools` - Whether or not to isolate reading from writing connection pools. See [below](#isolated-connection-pools) for more information.
31
+
24
32
  ### Generated columns
25
33
 
26
34
  You can now create `virtual` columns, both stored and dynamic. The [SQLite docs](https://www.sqlite.org/gencol.html) explain the difference:
@@ -81,6 +89,25 @@ default: &default
81
89
  - sqlite_ulid
82
90
  ```
83
91
 
92
+ ### Isolated connection pools
93
+
94
+ By default, Rails uses a single connection pool for both reading and writing. This can lead to contention if you have a large number of write operations. This gem allows you to isolate the connection pools for reading and writing.
95
+
96
+ To enable this feature, set the `isolate_connection_pools` configuration option to `true` in your `config/environments/*.rb` file or `config/application.rb` file:
97
+
98
+ ```ruby
99
+ config.enhanced_sqlite3.isolate_connection_pools = true
100
+ ```
101
+
102
+ If enabled, the gem will patch your application in 3 ways:
103
+
104
+ 1. define separate `reader` and `writer` database configurations
105
+ 2. activate Rails' automatic role database switching middleware, defaulting all requests to the `reader` connection pool
106
+ 3. patch the ActiveRecord `#transaction` method to switch to the `writer` connection pool for write operations
107
+ 4. patch the ActiveRecord `#log` method to log the database name for each database operation
108
+
109
+ This feature is experimental and may not work with all Rails configurations. Please report any issues you encounter.
110
+
84
111
  ## Development
85
112
 
86
113
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,3 +1,15 @@
1
1
  require "active_record"
2
2
  require "enhanced_sqlite3/version"
3
3
  require "enhanced_sqlite3/railtie"
4
+
5
+ module EnhancedSQLite3
6
+ Error = Class.new(StandardError)
7
+
8
+ mattr_writer :isolate_connection_pools
9
+
10
+ class << self
11
+ def isolate_connection_pools?
12
+ @isolate_connection_pools ||= @@isolate_connection_pools || false
13
+ end
14
+ end
15
+ end
@@ -46,8 +46,25 @@ module EnhancedSQLite3
46
46
  configure_pragmas
47
47
  configure_extensions
48
48
 
49
- EnhancedSQLite3::SupportsVirtualColumns.apply!
50
- EnhancedSQLite3::SupportsDeferrableConstraints.apply!
49
+ EnhancedSQLite3::SupportsVirtualColumns.apply! unless try(:supports_virtual_columns?)
50
+ EnhancedSQLite3::SupportsDeferrableConstraints.apply! unless try(:supports_deferrable_constraints?)
51
+ end
52
+
53
+ # Patch the #transaction method to ensure that all transactions are sent to the writing role database connection pool.
54
+ def transaction(...)
55
+ ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: false) do
56
+ super(...)
57
+ end
58
+ end
59
+
60
+ # Patch the #log method to ensure that all log messages are tagged with the database connection name.
61
+ def log(...)
62
+ db_connection_name = ActiveRecord::Base.connection_db_config.name
63
+ if Rails.logger.formatter.current_tags.include? db_connection_name
64
+ super
65
+ else
66
+ Rails.logger.tagged(db_connection_name) { super }
67
+ end
51
68
  end
52
69
 
53
70
  private
@@ -2,16 +2,63 @@
2
2
 
3
3
  require "rails/railtie"
4
4
  require "enhanced_sqlite3/adapter"
5
+ require "enhanced_sqlite3/resolver"
5
6
 
6
7
  module EnhancedSQLite3
7
8
  class Railtie < ::Rails::Railtie
9
+ config.enhanced_sqlite3 = ActiveSupport::OrderedOptions.new
10
+
11
+ initializer "enhanced_sqlite3.config" do
12
+ config.enhanced_sqlite3.each do |name, value|
13
+ EnhancedSQLite3.public_send(:"#{name}=", value)
14
+ end
15
+ end
16
+
8
17
  # Enhance the SQLite3 ActiveRecord adapter with optimized defaults
9
18
  initializer "enhanced_sqlite3.enhance_active_record_sqlite3adapter" do |app|
10
19
  ActiveSupport.on_load(:active_record_sqlite3adapter) do
11
- # self refers to `SQLite3Adapter` here,
12
- # so we can call .prepend
20
+ # self refers to `SQLite3Adapter` here
13
21
  prepend EnhancedSQLite3::Adapter
14
22
  end
15
23
  end
24
+
25
+ # Enhance the application with isolated reading and writing connection pools
26
+ initializer "enhanced_sqlite3.setup_isolated_connection_pools" do |app|
27
+ next unless EnhancedSQLite3.isolate_connection_pools?
28
+
29
+ ActiveSupport.on_load(:active_record) do
30
+ # self refers to `ActiveRecord::Base` here
31
+ env_configs = configurations.configs_for env_name: Rails.env
32
+ remaining_configs = configurations.configurations.reject { |configuration| env_configs.include? configuration }
33
+ if env_configs.one?
34
+ config = env_configs.first
35
+ reader = ActiveRecord::DatabaseConfigurations::HashConfig.new(
36
+ Rails.env, "reader", config.configuration_hash.merge(readonly: true)
37
+ )
38
+ writer = ActiveRecord::DatabaseConfigurations::HashConfig.new(
39
+ Rails.env, "writer", config.configuration_hash.merge(pool: 1)
40
+ )
41
+
42
+ # Replace the single production configuration with two separate reader and writer configurations
43
+ self.configurations = remaining_configs + [reader, writer]
44
+ else
45
+ reader = env_configs.find { |config| config.name == "reader" }
46
+ writer = env_configs.find { |config| config.name == "writer" }
47
+
48
+ # Ensure that that there is a reader and writer configuration for the current Rails environment
49
+ raise Error.new("#{Rails.env} has #{env_configs.size} configurations") unless reader && writer
50
+ end
51
+
52
+ connects_to database: {writing: :writer, reading: :reader}
53
+ end
54
+
55
+ # Since we aren't actually using separate databases, only separate connections,
56
+ # we don't need to ensure that requests "read your own writes" with a `delay`
57
+ config.active_record.database_selector = {delay: 0}
58
+ # Use our custom resolver to ensure that benchmarking requests are sent to the reading database connection
59
+ config.active_record.database_resolver = EnhancedSQLite3::Resolver
60
+ # Keep Rails' default resolver context
61
+ config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
62
+ end
16
63
  end
17
64
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EnhancedSQLite3
4
+ class Resolver < ActiveRecord::Middleware::DatabaseSelector::Resolver
5
+ def reading_request?(request)
6
+ true
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module EnhancedSQLite3
4
- VERSION = "0.4.0"
2
+ VERSION = "0.6.0"
5
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-enhancedsqlite3-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-10 00:00:00.000000000 Z
11
+ date: 2024-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -111,6 +111,7 @@ files:
111
111
  - lib/activerecord-enhancedsqlite3-adapter.rb
112
112
  - lib/enhanced_sqlite3/adapter.rb
113
113
  - lib/enhanced_sqlite3/railtie.rb
114
+ - lib/enhanced_sqlite3/resolver.rb
114
115
  - lib/enhanced_sqlite3/supports_deferrable_constraints.rb
115
116
  - lib/enhanced_sqlite3/supports_virtual_columns.rb
116
117
  - lib/enhanced_sqlite3/version.rb
@@ -137,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
138
  - !ruby/object:Gem::Version
138
139
  version: '0'
139
140
  requirements: []
140
- rubygems_version: 3.4.19
141
+ rubygems_version: 3.5.1
141
142
  signing_key:
142
143
  specification_version: 4
143
144
  summary: ActiveRecord adapter for SQLite that enhances the default.