activerecord-turso 0.3.0 → 0.3.2

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: cfdd23462f8313c9a19d64bab33314e3d5a4b7532249695f01811ebcf3fcccbd
4
- data.tar.gz: 74e33f479629655b50621e300dc7a1d38b2d94d95edd2da9b57b3d18c5799dbe
3
+ metadata.gz: 8dc77c288fa28d1c9872459245f9afe593a510dac77d5e0630bdb58d7bab4b90
4
+ data.tar.gz: 192ddeb9179b451e2844ce533f1bbaf7a96bae2c493a1b0a322398401dae838a
5
5
  SHA512:
6
- metadata.gz: 9dee234b1fa1abd37883de7c10b876cad339858a41643000eee969348ae0ad1da8458a8b546145aa1c7e38648e5e7da3734d35fa484b08c0d87260dc170f3fe5
7
- data.tar.gz: 314bda7c65ccc415af50204690e098859517419f8f7ba035852a6230c0654c40f94a82ba3bd22ba54a829920cfb7f9159fbf1acc0de8e04816641669b6789938
6
+ metadata.gz: 502dce7acddd392a64255e2e59d3fa7290196218968456dc4a4032bdfe83fde510f31625ae260bb9f4d979fc5af1626ac3dfc4484608b32dd223cb37d75d9dfc
7
+ data.tar.gz: 6f8570e0a2e17733238babaaf41a17dba6d5cf2e8ca1b983d883dd4a3b0a0fc7373975c9fb21d4bac0e273de3feaa1377f263e68f5123e703761c73067b840ce
data/README.md CHANGED
@@ -14,7 +14,7 @@ Production ready for **embedded/local** ActiveRecord 8.1 applications using the
14
14
 
15
15
  ## Runtime dependencies
16
16
 
17
- This adapter uses the `turso` gem and does **not** require the `sqlite3` Ruby gem at runtime.
17
+ This adapter uses the `turso` gem and does **not** require the `sqlite3` Ruby gem at runtime. The `sqlite3` gem is used only in the Rails conformance test harness.
18
18
 
19
19
  ## Installation
20
20
 
@@ -247,6 +247,12 @@ TURSO_TEST_EXPERIMENTAL_FEATURES=generated_columns bundle exec rake test
247
247
 
248
248
  The CI workflow in `.github/workflows/ci.yml` runs all three configurations automatically.
249
249
 
250
+ Run the Rails sqlite3 adapter conformance suite:
251
+
252
+ ```bash
253
+ bundle exec rake test:conformance
254
+ ```
255
+
250
256
  ## License
251
257
 
252
258
  MIT
@@ -6,6 +6,7 @@ module ActiveRecord
6
6
  module ConnectionManagement
7
7
  def self.included(base)
8
8
  base.extend(ClassMethods)
9
+ base.set_callback :checkout, :before, :reconnect_for_execution_context
9
10
  end
10
11
 
11
12
  module ClassMethods
@@ -17,12 +18,28 @@ module ActiveRecord
17
18
  end
18
19
 
19
20
  def active?
20
- return false unless @raw_connection
21
- !@raw_connection.closed?
21
+ connected?
22
+ end
23
+
24
+ def connected?
25
+ !(@raw_connection.nil? || @raw_connection.closed?)
22
26
  rescue ::Turso::Error
23
27
  false
24
28
  end
25
29
 
30
+ def disconnect!
31
+ super
32
+
33
+ if @raw_connection && !@raw_connection.closed?
34
+ begin
35
+ @raw_connection.execute("PRAGMA wal_checkpoint(TRUNCATE)")
36
+ rescue ::Turso::Error
37
+ end
38
+ @raw_connection.close
39
+ end
40
+ @raw_connection = nil
41
+ end
42
+
26
43
  def reconnect!(**)
27
44
  @lock.synchronize do
28
45
  disconnect!
@@ -31,13 +48,6 @@ module ActiveRecord
31
48
  end
32
49
  end
33
50
 
34
- def disconnect!
35
- @lock.synchronize do
36
- @raw_connection&.close
37
- @raw_connection = nil
38
- end
39
- end
40
-
41
51
  def raw_connection
42
52
  @lock.synchronize { @raw_connection }
43
53
  end
@@ -62,12 +72,24 @@ module ActiveRecord
62
72
 
63
73
  execute("PRAGMA foreign_keys = ON")
64
74
 
75
+ autocheckpoint = @config.fetch(:wal_autocheckpoint, 1000)
76
+ execute("PRAGMA wal_autocheckpoint = #{autocheckpoint.to_i}")
77
+
65
78
  timeout = @config[:busy_timeout] || @config[:timeout] || 5000
66
79
  @raw_connection.busy_timeout = timeout
67
80
 
68
81
  query_timeout = @config[:query_timeout] || 30_000
69
82
  @raw_connection.query_timeout = query_timeout
70
83
  end
84
+
85
+ private
86
+
87
+ def reconnect_for_execution_context
88
+ return unless @raw_connection
89
+ return if @raw_connection.owned_by_current_execution_context?
90
+
91
+ reconnect!
92
+ end
71
93
  end
72
94
  end
73
95
  end
@@ -69,6 +69,10 @@ module ActiveRecord
69
69
  type_map = build_type_map(stmt)
70
70
  ActiveRecord::Result.new(columns, rows, type_map, affected_rows: affected_rows)
71
71
  end
72
+ rescue
73
+ @statements.delete(sql) if prepare
74
+ stmt.close
75
+ raise
72
76
  ensure
73
77
  stmt.close unless prepare
74
78
  end
@@ -5,8 +5,7 @@ module ActiveRecord
5
5
  class TursoAdapter < SQLite3Adapter
6
6
  module ErrorTranslation
7
7
  def translate_exception(exception, message:, sql:, binds:)
8
- cause = exception.cause || exception
9
- case cause
8
+ case exception
10
9
  when ::Turso::ConstraintException
11
10
  translate_constraint_error(message, sql, binds)
12
11
  when ::Turso::NotADatabaseException
@@ -25,11 +24,21 @@ module ActiveRecord
25
24
  ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
26
25
  when ::Turso::MisuseException
27
26
  ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
27
+ when ::Turso::Exception
28
+ ActiveRecord::ConnectionFailed.new(message, connection_pool: pool)
28
29
  else
29
30
  super
30
31
  end
31
32
  end
32
33
 
34
+ def retryable_connection_error?(exception)
35
+ super || exception.is_a?(::Turso::Exception)
36
+ end
37
+
38
+ def retryable_query_error?(exception)
39
+ super || exception.is_a?(::Turso::Exception)
40
+ end
41
+
33
42
  private
34
43
 
35
44
  def translate_constraint_error(message, sql, binds)
@@ -97,7 +97,9 @@ module ActiveRecord
97
97
 
98
98
  def rollback_if_active
99
99
  return unless @raw_connection && !@raw_connection.closed?
100
- @raw_connection.execute("ROLLBACK") rescue nil
100
+ @raw_connection.execute("ROLLBACK")
101
+ rescue ::Turso::Error
102
+ disconnect! rescue nil
101
103
  end
102
104
 
103
105
  def backoff(base_delay_ms, retries)
@@ -65,6 +65,7 @@ module ActiveRecord
65
65
  SELECT name FROM sqlite_master
66
66
  WHERE type = 'index'
67
67
  AND name NOT LIKE 'sqlite_%'
68
+ AND name NOT LIKE '__turso_internal_%'
68
69
  AND sql IS NOT NULL
69
70
  ORDER BY name
70
71
  SQL
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordTurso
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
@@ -17,3 +17,7 @@ require "active_record/tasks/database_tasks"
17
17
  require_relative "active_record/tasks/turso_database_tasks"
18
18
 
19
19
  ActiveRecord::Tasks::DatabaseTasks.register_task(/turso/, "ActiveRecord::Tasks::TursoDatabaseTasks")
20
+
21
+ at_exit do
22
+ ActiveRecord::Base.connection_pool&.disconnect! rescue nil
23
+ end
@@ -16,6 +16,7 @@ module Turso
16
16
 
17
17
  def initialize(config)
18
18
  @config = config
19
+ @owner = owner_token
19
20
  db_opts = {
20
21
  busy_timeout: config[:busy_timeout] || config[:timeout] || DEFAULT_BUSY_TIMEOUT_MS,
21
22
  query_timeout: config[:query_timeout] || DEFAULT_QUERY_TIMEOUT_MS
@@ -24,8 +25,8 @@ module Turso
24
25
  features = Array(config[:experimental_features]).map(&:to_s).reject(&:empty?)
25
26
  db_opts[:experimental_features] = features.join(",") unless features.empty?
26
27
  end
27
- database = ::Turso::Database.new(config[:database].to_s, **db_opts)
28
- @db = database.connection
28
+ @database = ::Turso::Database.new(config[:database].to_s, **db_opts)
29
+ @db = @database.connection
29
30
  @db.busy_timeout = db_opts[:busy_timeout]
30
31
  @db.query_timeout = db_opts[:query_timeout]
31
32
  end
@@ -38,6 +39,10 @@ module Turso
38
39
  !@db.closed?
39
40
  end
40
41
 
42
+ def owned_by_current_execution_context?
43
+ @owner == owner_token
44
+ end
45
+
41
46
  def disconnect!
42
47
  @db.close unless @db.closed?
43
48
  end
@@ -57,6 +62,10 @@ module Turso
57
62
 
58
63
  private
59
64
 
65
+ def owner_token
66
+ [Thread.current.object_id, Fiber.current.object_id]
67
+ end
68
+
60
69
  def normalize_binds(binds)
61
70
  binds.map do |value|
62
71
  case value
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-turso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben D'Angelo
@@ -85,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
85
  - !ruby/object:Gem::Version
86
86
  version: '0'
87
87
  requirements: []
88
- rubygems_version: 4.0.15
88
+ rubygems_version: 4.0.18
89
89
  specification_version: 4
90
90
  summary: Embedded ActiveRecord adapter for Turso (SQLite-compatible)
91
91
  test_files: []