提交 e4c197c7 编写于 作者: M Matthew Draper

Add comprehensive locking around DB transactions

Transactional-fixture using tests with racing threads and inter-thread
synchronisation inside transaction blocks will now deadlock... but
without this, they would just crash.

In 5.0, the threads didn't share a connection at all, so it would've
worked... but with the main thread inside the fixture transaction, they
wouldn't've been able to see each other.

So: as far as I can tell, the set of operations this "breaks" never had
a compelling use case. Meanwhile, it provides an increased level of
coherency to the operational feel of transactional fixtures.

If this does cause anyone problems, they're probably best off disabling
transactional fixtures on the affected tests, and managing transactions
themselves.
上级 24ac36be
......@@ -149,6 +149,7 @@ def initialize(connection)
end
def begin_transaction(options = {})
@connection.lock.synchronize do
run_commit_callbacks = !current_transaction.joinable?
transaction =
if @stack.empty?
......@@ -161,8 +162,10 @@ def begin_transaction(options = {})
@stack.push(transaction)
transaction
end
end
def commit_transaction
@connection.lock.synchronize do
transaction = @stack.last
begin
......@@ -174,14 +177,19 @@ def commit_transaction
transaction.commit
transaction.commit_records
end
end
def rollback_transaction(transaction = nil)
@connection.lock.synchronize do
transaction ||= @stack.pop
transaction.rollback
transaction.rollback_records
end
end
def within_new_transaction(options = {})
@connection.lock.synchronize do
begin
transaction = begin_transaction options
yield
rescue Exception => error
......@@ -204,6 +212,8 @@ def within_new_transaction(options = {})
end
end
end
end
end
def open_transactions
@stack.size
......
......@@ -74,7 +74,7 @@ class AbstractAdapter
SIMPLE_INT = /\A\d+\z/
attr_accessor :visitor, :pool
attr_reader :schema_cache, :owner, :logger, :prepared_statements
attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
alias :in_use? :owner
def self.type_cast_config_to_integer(config)
......
......@@ -239,7 +239,9 @@ def truncate(table_name, name = nil)
# Is this connection alive and ready for queries?
def active?
@lock.synchronize do
@connection.query "SELECT 1"
end
true
rescue PG::Error
false
......@@ -247,12 +249,15 @@ def active?
# Close then reopen the connection.
def reconnect!
@lock.synchronize do
super
@connection.reset
configure_connection
end
end
def reset!
@lock.synchronize do
clear_cache!
reset_transaction
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
......@@ -261,13 +266,16 @@ def reset!
@connection.query "DISCARD ALL"
configure_connection
end
end
# Disconnects from the database if already connected. Otherwise, this
# method does nothing.
def disconnect!
@lock.synchronize do
super
@connection.close rescue nil
end
end
def native_database_types #:nodoc:
NATIVE_DATABASE_TYPES
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册