Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class TursoAdapter < SQLite3Adapter
module ConnectionManagement
def self.included(base)
base.extend(ClassMethods)
base.set_callback :checkout, :before, :reconnect_for_execution_context
end

module ClassMethods
Expand Down Expand Up @@ -80,6 +81,15 @@ def configure_connection
query_timeout = @config[:query_timeout] || 30_000
@raw_connection.query_timeout = query_timeout
end

private

def reconnect_for_execution_context
return unless @raw_connection
return if @raw_connection.owned_by_current_execution_context?

reconnect!
end
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/active_record/tasks/turso_database_tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def structure_dump(filename, extra_flags)
SELECT name FROM sqlite_master
WHERE type = 'index'
AND name NOT LIKE 'sqlite_%'
AND name NOT LIKE '__turso_internal_%'
AND sql IS NOT NULL
ORDER BY name
SQL
Expand Down
9 changes: 9 additions & 0 deletions lib/turso/ar/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Connection

def initialize(config)
@config = config
@owner = owner_token
db_opts = {
busy_timeout: config[:busy_timeout] || config[:timeout] || DEFAULT_BUSY_TIMEOUT_MS,
query_timeout: config[:query_timeout] || DEFAULT_QUERY_TIMEOUT_MS
Expand All @@ -38,6 +39,10 @@ def open?
!@db.closed?
end

def owned_by_current_execution_context?
@owner == owner_token
end

def disconnect!
@db.close unless @db.closed?
end
Expand All @@ -57,6 +62,10 @@ def execute_batch(sql)

private

def owner_token
[Thread.current.object_id, Fiber.current.object_id]
end

def normalize_binds(binds)
binds.map do |value|
case value
Expand Down
25 changes: 25 additions & 0 deletions test/integration/connection_management_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ def test_pool_connections_are_independent
assert_equal 3, ConnectionRecord.count
end

def test_checked_in_connection_can_be_reused_by_another_thread
ActiveRecord::Base.establish_connection(ActiveRecordTursoTest.base_config.merge(pool: 1))
pool = ActiveRecord::Base.connection_pool

original_connection = pool.checkout
original_connection.execute("INSERT INTO connection_records (name) VALUES ('main-thread')")
original_adapter_id = original_connection.object_id
original_raw_connection_id = original_connection.raw_connection.object_id
pool.checkin(original_connection)

result = Thread.new do
pool.with_connection do |connection|
[
connection.object_id,
connection.raw_connection.object_id,
connection.query_value("SELECT name FROM connection_records")
]
end
end.value

assert_equal original_adapter_id, result[0]
refute_equal original_raw_connection_id, result[1]
assert_equal "main-thread", result[2]
end

def test_reconnect_restores_foreign_keys_pragma
conn = ActiveRecord::Base.connection
conn.reconnect!
Expand Down
31 changes: 31 additions & 0 deletions test/test_rails_database_tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ def test_structure_dump_and_load_round_trip
FileUtils.rm_f(dump)
end

def test_structure_dump_and_load_round_trip_with_fts_index
dump = File.join(ActiveRecordTursoTest::TMP, "fts_structure.sql")

ActiveRecord::Base.establish_connection(ActiveRecordTursoTest.base_config)
connection = ActiveRecord::Base.connection
connection.create_table(:messages) { |table| table.text :content, null: false }
connection.add_fts_index(:messages, :content, tokenizer: :ngram)
ActiveRecord::Base.connection_pool.disconnect!

task = build_task
task.structure_dump(dump, nil)

sql = File.read(dump)
assert_match(/CREATE INDEX .* USING fts \(content\)/, sql)
assert_match(/tokenizer = 'ngram'/, sql)
refute_match(/__turso_internal_/, sql)

task.drop
task.structure_load(dump, nil)

ActiveRecord::Base.establish_connection(ActiveRecordTursoTest.base_config)
connection = ActiveRecord::Base.connection
connection.execute("INSERT INTO messages (content) VALUES ('日本語の部分一致検索')")

assert_equal 1, connection.select_value(<<~SQL)
SELECT COUNT(*) FROM messages WHERE fts_match(content, '部分一致')
SQL
ensure
FileUtils.rm_f(dump)
end

def test_charset
task = build_task
assert_equal "UTF-8", task.charset
Expand Down
Loading