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 +4 -4
- data/README.md +7 -1
- data/lib/active_record/connection_adapters/turso_adapter/connection_management.rb +31 -9
- data/lib/active_record/connection_adapters/turso_adapter/database_statements.rb +4 -0
- data/lib/active_record/connection_adapters/turso_adapter/error_translation.rb +11 -2
- data/lib/active_record/connection_adapters/turso_adapter/transaction_management.rb +3 -1
- data/lib/active_record/tasks/turso_database_tasks.rb +1 -0
- data/lib/activerecord-turso/version.rb +1 -1
- data/lib/activerecord-turso.rb +4 -0
- data/lib/turso/ar/connection.rb +11 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8dc77c288fa28d1c9872459245f9afe593a510dac77d5e0630bdb58d7bab4b90
|
|
4
|
+
data.tar.gz: 192ddeb9179b451e2844ce533f1bbaf7a96bae2c493a1b0a322398401dae838a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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")
|
|
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)
|
data/lib/activerecord-turso.rb
CHANGED
|
@@ -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
|
data/lib/turso/ar/connection.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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: []
|