aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb51
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb189
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb2
3 files changed, 112 insertions, 130 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index cb75070e3a..a5fa9d6adc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -462,23 +462,44 @@ module ActiveRecord
#
# For example, suppose that you have 5 models, with the following hierarchy:
#
- # |
- # +-- Book
- # | |
- # | +-- ScaryBook
- # | +-- GoodBook
- # +-- Author
- # +-- BankAccount
+ # class Author < ActiveRecord::Base
+ # end
#
- # Suppose that Book is to connect to a separate database (i.e. one other
- # than the default database). Then Book, ScaryBook and GoodBook will all use
- # the same connection pool. Likewise, Author and BankAccount will use the
- # same connection pool. However, the connection pool used by Author/BankAccount
- # is not the same as the one used by Book/ScaryBook/GoodBook.
+ # class BankAccount < ActiveRecord::Base
+ # end
#
- # Normally there is only a single ConnectionHandler instance, accessible via
- # ActiveRecord::Base.connection_handler. Active Record models use this to
- # determine the connection pool that they should use.
+ # class Book < ActiveRecord::Base
+ # establish_connection "library_db"
+ # end
+ #
+ # class ScaryBook < Book
+ # end
+ #
+ # class GoodBook < Book
+ # end
+ #
+ # And a database.yml that looked like this:
+ #
+ # development:
+ # database: my_application
+ # host: localhost
+ #
+ # library_db:
+ # database: library
+ # host: some.library.org
+ #
+ # Your primary database in the development environment is "my_application"
+ # but the Book model connects to a separate database called "library_db"
+ # (this can even be a database on a different machine).
+ #
+ # Book, ScaryBook and GoodBook will all use the same connection pool to
+ # "library_db" while Author, BankAccount, and any other models you create
+ # will use the default connection pool to "my_application".
+ #
+ # The various connection pools are managed by a single instance of
+ # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
+ # All Active Record models use this handler to determine the connection pool that they
+ # should use.
class ConnectionHandler
def initialize
# These caches are keyed by klass.name, NOT klass. Keying them by klass
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 33cc22425d..46aaaae2ec 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -1,76 +1,5 @@
module ActiveRecord
module ConnectionAdapters
- class TransactionManager #:nodoc:
- def initialize(connection)
- @stack = []
- @connection = connection
- end
-
- def begin_transaction(options = {})
- transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction
- transaction = transaction_class.new(@connection, current_transaction, options)
-
- @stack.push(transaction)
- transaction
- end
-
- def commit_transaction
- @stack.pop.commit
- end
-
- def rollback_transaction
- @stack.pop.rollback
- end
-
- def within_new_transaction(options = {})
- transaction = begin_transaction options
- yield
- rescue Exception => error
- transaction.rollback if transaction
- raise
- ensure
- begin
- transaction.commit unless error
- rescue Exception
- transaction.rollback
- raise
- ensure
- @stack.pop if transaction
- end
- end
-
- def open_transactions
- @stack.size
- end
-
- def current_transaction
- @stack.last || closed_transaction
- end
-
- private
-
- def closed_transaction
- @closed_transaction ||= ClosedTransaction.new(@connection)
- end
- end
-
- class Transaction #:nodoc:
- attr_reader :connection
-
- def initialize(connection)
- @connection = connection
- @state = TransactionState.new
- end
-
- def state
- @state
- end
-
- def savepoint_name
- nil
- end
- end
-
class TransactionState
attr_reader :parent
@@ -101,65 +30,49 @@ module ActiveRecord
end
end
- class ClosedTransaction < Transaction #:nodoc:
- def number
- 0
- end
-
- def begin(options = {})
- RealTransaction.new(connection, self, options)
- end
-
- def closed?
- true
- end
+ class Transaction #:nodoc:
+ attr_reader :connection, :state
- def open?
- false
+ def initialize(connection)
+ @connection = connection
+ @state = TransactionState.new
end
- def joinable?
- false
+ def savepoint_name
+ nil
end
+ end
+ class NullTransaction < Transaction #:nodoc:
+ def initialize; end
+ def closed?; true; end
+ def open?; false; end
+ def joinable?; false; end
# This is a noop when there are no open transactions
- def add_record(record)
- end
+ def add_record(record); end
end
class OpenTransaction < Transaction #:nodoc:
- attr_reader :parent, :records
+ attr_reader :records
attr_writer :joinable
- def initialize(connection, parent, options = {})
+ def initialize(connection, options = {})
super connection
- @parent = parent
@records = []
@joinable = options.fetch(:joinable, true)
end
-
def joinable?
@joinable
end
- def number
- parent.number + 1
- end
-
- def begin(options = {})
- SavepointTransaction.new(connection, self, options)
- end
-
def rollback
perform_rollback
- parent
end
def commit
perform_commit
- parent
end
def add_record(record)
@@ -174,7 +87,7 @@ module ActiveRecord
@state.set_state(:rolledback)
records.uniq.each do |record|
begin
- record.rolledback!(parent.closed?)
+ record.rolledback!(self.is_a?(RealTransaction))
rescue => e
record.logger.error(e) if record.respond_to?(:logger) && record.logger
end
@@ -202,8 +115,8 @@ module ActiveRecord
end
class RealTransaction < OpenTransaction #:nodoc:
- def initialize(connection, parent, options = {})
- super
+ def initialize(connection, _, options = {})
+ super(connection, options)
if options[:isolation]
connection.begin_isolated_db_transaction(options[:isolation])
@@ -226,27 +139,75 @@ module ActiveRecord
class SavepointTransaction < OpenTransaction #:nodoc:
attr_reader :savepoint_name
- def initialize(connection, parent, options = {})
+ def initialize(connection, savepoint_name, options = {})
if options[:isolation]
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
end
- super
-
- # Savepoint name only counts the Savepoint transactions, so we need to subtract 1
- @savepoint_name = "active_record_#{number - 1}"
- connection.create_savepoint(@savepoint_name)
+ super(connection, options)
+ connection.create_savepoint(@savepoint_name = savepoint_name)
end
def perform_rollback
- connection.rollback_to_savepoint(@savepoint_name)
+ connection.rollback_to_savepoint(savepoint_name)
rollback_records
end
def perform_commit
@state.set_state(:committed)
- connection.release_savepoint(@savepoint_name)
+ connection.release_savepoint(savepoint_name)
+ end
+ end
+
+ class TransactionManager #:nodoc:
+ def initialize(connection)
+ @stack = []
+ @connection = connection
+ end
+
+ def begin_transaction(options = {})
+ transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction
+ transaction = transaction_class.new(@connection, "active_record_#{@stack.size}", options)
+
+ @stack.push(transaction)
+ transaction
end
+
+ def commit_transaction
+ @stack.pop.commit
+ end
+
+ def rollback_transaction
+ @stack.pop.rollback
+ end
+
+ def within_new_transaction(options = {})
+ transaction = begin_transaction options
+ yield
+ rescue Exception => error
+ transaction.rollback if transaction
+ raise
+ ensure
+ begin
+ transaction.commit unless error
+ rescue Exception
+ transaction.rollback
+ raise
+ ensure
+ @stack.pop if transaction
+ end
+ end
+
+ def open_transactions
+ @stack.size
+ end
+
+ def current_transaction
+ @stack.last || NULL_TRANSACTION
+ end
+
+ private
+ NULL_TRANSACTION = NullTransaction.new
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 99c728814a..a1b6671664 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -46,7 +46,7 @@ module ActiveRecord
autoload_at 'active_record/connection_adapters/abstract/transaction' do
autoload :TransactionManager
- autoload :ClosedTransaction
+ autoload :NullTransaction
autoload :RealTransaction
autoload :SavepointTransaction
autoload :TransactionState