activerecord-enhancedsqlite3-adapter 0.5.0 → 0.6.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: 6bd667df1779690780802a3f91133b9a6b8155f9f3c040393b5a722a299ce370
4
- data.tar.gz: ed4008074efd597d2bc79ab2a4b3aea8e7ccccb37fd8158892843d20918b0015
3
+ metadata.gz: e0d679b0ee45b0838fc294f50e43b806301fba662e54f280b3d0e747de3e7670
4
+ data.tar.gz: 5b345ff772a3cba527a4fe638724bb372f488bc8ec3ceb4d3e8b76ee40b2f434
5
5
  SHA512:
6
- metadata.gz: 3252cf16cc3619a857488b450542ef6b9402b666bdb1a5458d3615d880c073226cbf8d7472032b74bcfa205196f4febf0aaa8bc1fa2e6e00f00dc0d66e975d97
7
- data.tar.gz: ddbafb131266c8edcf5e66303083ab964e8ac9248eaa12eeea4c377501f00e820b54edf406280eb832f0996120e666434e256e308a108d6df945bb21168f8416
6
+ metadata.gz: 47288376cd918173cce7edf28fc2a8cbb8a86c749aa44aaf88e4b2f81fc922b3b5369e579abb8e9bab7585f7c8b2ae732c3f6a8601117e935a61effcf7d86276
7
+ data.tar.gz: 22ae8604263b8a75379a0f9c132d60cf4616d6695665742b4a6a358f24f0fa4daf1b5b1708ecf5599617293bc4a8fc76db89fbd204deaa70b430233db2f6891a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
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
+
3
10
  ## [0.5.0] - 2023-12-24
4
11
 
5
12
  - Load extensions installed via project-scoped `sqlpkg`
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
@@ -44,10 +44,27 @@ module EnhancedSQLite3
44
44
  configure_busy_handler_timeout
45
45
  check_version
46
46
  configure_pragmas
47
- load_extensions
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
@@ -110,13 +127,8 @@ module EnhancedSQLite3
110
127
  end
111
128
  end
112
129
 
113
- def load_extensions
130
+ def configure_extensions
114
131
  @raw_connection.enable_load_extension(true)
115
- # first, load any extensions installed via `sqlpkg`
116
- Dir.glob(".sqlpkg/**/*.{dll,so,dylib}") do |extension_path|
117
- @raw_connection.load_extension(extension_path)
118
- end
119
- # then, load any extensions specified in the `database.yml`
120
132
  @config.fetch(:extensions, []).each do |extension_name|
121
133
  require extension_name
122
134
  extension_classname = extension_name.camelize
@@ -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,3 +1,3 @@
1
1
  module EnhancedSQLite3
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
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.5.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-23 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