mirror of https://github.com/rails/rails
Revert "Merge pull request #37798 from Edouard-chin/ec-sqlite3-connection-transaction"
This reverts commitb4ab1f19d8
, reversing changes made to9926fd8662
. This is causing CI to fail about 50% of the time, and can be reproduced locally 100% of the time on my machine. Ex failing build https://buildkite.com/rails/rails/builds/65467#efaa1dd5-aaf4-43a1-a204-d1c42abf614d Error: ``` SQLite3::BusyException: database is locked (ActiveRecord::StatementInvalid) ```
This commit is contained in:
parent
774e77c2cc
commit
25daafbc00
|
@ -74,37 +74,19 @@ module ActiveRecord
|
|||
end
|
||||
alias :exec_update :exec_delete
|
||||
|
||||
def begin_isolated_db_transaction(isolation) #:nodoc
|
||||
raise ArgumentError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
||||
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
||||
|
||||
Thread.current.thread_variable_set("read_uncommitted", @connection.get_first_value("PRAGMA read_uncommitted"))
|
||||
@connection.read_uncommitted = true
|
||||
begin_db_transaction
|
||||
end
|
||||
|
||||
def begin_db_transaction #:nodoc:
|
||||
log("begin transaction", "TRANSACTION") { @connection.transaction }
|
||||
end
|
||||
|
||||
def commit_db_transaction #:nodoc:
|
||||
log("commit transaction", "TRANSACTION") { @connection.commit }
|
||||
reset_read_uncommitted
|
||||
end
|
||||
|
||||
def exec_rollback_db_transaction #:nodoc:
|
||||
log("rollback transaction", "TRANSACTION") { @connection.rollback }
|
||||
reset_read_uncommitted
|
||||
end
|
||||
|
||||
private
|
||||
def reset_read_uncommitted
|
||||
read_uncommitted = Thread.current.thread_variable_get("read_uncommitted")
|
||||
return unless read_uncommitted
|
||||
|
||||
@connection.read_uncommitted = read_uncommitted
|
||||
end
|
||||
|
||||
def execute_batch(statements, name = nil)
|
||||
sql = combine_multi_statements(statements)
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module ActiveRecord
|
|||
# Allow database path relative to Rails.root, but only if the database
|
||||
# path is not the special path that tells sqlite to build a database only
|
||||
# in memory.
|
||||
if ":memory:" != config[:database] && !config[:database].to_s.starts_with?("file:")
|
||||
if ":memory:" != config[:database]
|
||||
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
||||
dirname = File.dirname(config[:database])
|
||||
Dir.mkdir(dirname) unless File.directory?(dirname)
|
||||
|
@ -116,10 +116,6 @@ module ActiveRecord
|
|||
true
|
||||
end
|
||||
|
||||
def supports_transaction_isolation?
|
||||
true
|
||||
end
|
||||
|
||||
def supports_partial_index?
|
||||
true
|
||||
end
|
||||
|
@ -329,10 +325,6 @@ module ActiveRecord
|
|||
sql
|
||||
end
|
||||
|
||||
def shared_cache? # :nodoc:
|
||||
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
||||
end
|
||||
|
||||
def get_database_version # :nodoc:
|
||||
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
||||
end
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "cases/helper"
|
||||
|
||||
class SQLite3TransactionTest < ActiveRecord::SQLite3TestCase
|
||||
test "shared_cached? is true when cache-mode is enabled" do
|
||||
with_connection(flags: shared_cache_flags) do |conn|
|
||||
assert_predicate(conn, :shared_cache?)
|
||||
end
|
||||
end
|
||||
|
||||
test "shared_cached? is false when cache-mode is disabled" do
|
||||
flags =::SQLite3::Constants::Open::READWRITE | SQLite3::Constants::Open::CREATE
|
||||
|
||||
with_connection(flags: flags) do |conn|
|
||||
assert_not_predicate(conn, :shared_cache?)
|
||||
end
|
||||
end
|
||||
|
||||
test "raises when trying to open a transaction in a isolation level other than `read_uncommitted`" do
|
||||
with_connection do |conn|
|
||||
assert_raises(ArgumentError) do
|
||||
conn.transaction(requires_new: true, isolation: :something) do
|
||||
conn.transaction_manager.materialize_transactions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "raises when trying to open a read_uncommitted transaction but shared-cache mode is turned off" do
|
||||
with_connection do |conn|
|
||||
error = assert_raises(StandardError) do
|
||||
conn.transaction(requires_new: true, isolation: :read_uncommitted) do
|
||||
conn.transaction_manager.materialize_transactions
|
||||
end
|
||||
end
|
||||
|
||||
assert_match("You need to enable the shared-cache mode", error.message)
|
||||
end
|
||||
end
|
||||
|
||||
test "opens a `read_uncommitted` transaction" do
|
||||
with_connection(flags: shared_cache_flags) do |conn1|
|
||||
conn1.create_table(:zines) { |t| t.column(:title, :string) } if in_memory_db?
|
||||
conn1.transaction do
|
||||
conn1.transaction_manager.materialize_transactions
|
||||
conn1.execute("INSERT INTO zines (title) VALUES ('foo')")
|
||||
|
||||
with_connection(flags: shared_cache_flags) do |conn2|
|
||||
conn2.transaction(joinable: false, isolation: :read_uncommitted) do
|
||||
assert_not_empty(conn2.execute("SELECT * FROM zines WHERE title = 'foo'"))
|
||||
end
|
||||
end
|
||||
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "reset the read_uncommitted PRAGMA when transactions is rolled back" do
|
||||
with_connection(flags: shared_cache_flags) do |conn|
|
||||
conn.transaction(joinable: false, isolation: :read_uncommitted) do
|
||||
assert_not(read_uncommitted?(conn))
|
||||
conn.transaction_manager.materialize_transactions
|
||||
assert(read_uncommitted?(conn))
|
||||
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
assert_not(read_uncommitted?(conn))
|
||||
end
|
||||
end
|
||||
|
||||
test "reset the read_uncommitted PRAGMA when transactions is commited" do
|
||||
with_connection(flags: shared_cache_flags) do |conn|
|
||||
conn.transaction(joinable: false, isolation: :read_uncommitted) do
|
||||
assert_not(read_uncommitted?(conn))
|
||||
conn.transaction_manager.materialize_transactions
|
||||
assert(read_uncommitted?(conn))
|
||||
end
|
||||
|
||||
assert_not(read_uncommitted?(conn))
|
||||
end
|
||||
end
|
||||
|
||||
test "set the read_uncommited PRAGMA to its previous value" do
|
||||
with_connection(flags: shared_cache_flags) do |conn|
|
||||
conn.transaction(joinable: false, isolation: :read_uncommitted) do
|
||||
conn.instance_variable_get(:@connection).read_uncommitted = true
|
||||
assert(read_uncommitted?(conn))
|
||||
conn.transaction_manager.materialize_transactions
|
||||
assert(read_uncommitted?(conn))
|
||||
end
|
||||
|
||||
assert(read_uncommitted?(conn))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def read_uncommitted?(conn)
|
||||
conn.instance_variable_get(:@connection).get_first_value("PRAGMA read_uncommitted") != 0
|
||||
end
|
||||
|
||||
def shared_cache_flags
|
||||
::SQLite3::Constants::Open::READWRITE | SQLite3::Constants::Open::CREATE | ::SQLite3::Constants::Open::SHAREDCACHE | ::SQLite3::Constants::Open::URI
|
||||
end
|
||||
|
||||
def with_connection(options = {})
|
||||
conn_options = options.reverse_merge(
|
||||
database: in_memory_db? ? "file::memory:" : ActiveRecord::Base.configurations["arunit"][:database]
|
||||
)
|
||||
conn = ActiveRecord::Base.sqlite3_connection(conn_options)
|
||||
|
||||
yield(conn)
|
||||
ensure
|
||||
conn.disconnect! if conn
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require "cases/helper"
|
||||
|
||||
unless ActiveRecord::Base.connection.supports_transaction_isolation? && !current_adapter?(:SQLite3Adapter)
|
||||
unless ActiveRecord::Base.connection.supports_transaction_isolation?
|
||||
class TransactionIsolationUnsupportedTest < ActiveRecord::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
|
@ -10,8 +10,6 @@ unless ActiveRecord::Base.connection.supports_transaction_isolation? && !current
|
|||
end
|
||||
|
||||
test "setting the isolation level raises an error" do
|
||||
skip if current_adapter?(:SQLite3Adapter)
|
||||
|
||||
assert_raises(ActiveRecord::TransactionIsolationError) do
|
||||
Tag.transaction(isolation: :serializable) { Tag.connection.materialize_transactions }
|
||||
end
|
||||
|
|
|
@ -1127,7 +1127,7 @@ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
|
|||
end
|
||||
end if Topic.connection.supports_savepoints?
|
||||
|
||||
if ActiveRecord::Base.connection.supports_transaction_isolation? && !in_memory_db?
|
||||
if ActiveRecord::Base.connection.supports_transaction_isolation?
|
||||
class ConcurrentTransactionTest < TransactionTest
|
||||
# This will cause transactions to overlap and fail unless they are performed on
|
||||
# separate database connections.
|
||||
|
|
Loading…
Reference in New Issue