Revert "Merge pull request #37798 from Edouard-chin/ec-sqlite3-connection-transaction"

This reverts commit b4ab1f19d8, reversing
changes made to 9926fd8662.

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:
eileencodes 2019-12-10 14:07:03 -05:00
parent 774e77c2cc
commit 25daafbc00
5 changed files with 3 additions and 149 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.