diff options
Diffstat (limited to 'doc/build/orm')
-rw-r--r-- | doc/build/orm/session_transaction.rst | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/doc/build/orm/session_transaction.rst b/doc/build/orm/session_transaction.rst index ce5757dd0..5ad02d217 100644 --- a/doc/build/orm/session_transaction.rst +++ b/doc/build/orm/session_transaction.rst @@ -271,6 +271,133 @@ transactions set the flag ``twophase=True`` on the session:: # before committing both transactions session.commit() + +.. _session_transaction_isolation: + +Setting Transaction Isolation Levels +------------------------------------ + +:term:`isolation` refers to the behavior of the transaction at the database +level in relation to other transactions occurring concurrently. There +are four well-known modes of isolation, and typically the Python DBAPI +allows these to be set on a per-connection basis, either through explicit +APIs or via database-specific calls. + +SQLAlchemy's dialects support settable isolation modes on a per-:class:`.Engine` +or per-:class:`.Connection` basis, using flags at both the +:func:`.create_engine` level as well as at the :meth:`.Connection.execution_options` +level. + +When using the ORM :class:`.Session`, it acts as a *facade* for engines and +connections, but does not expose transaction isolation directly. So in +order to affect transaction isolation level, we need to act upon the +:class:`.Engine` or :class:`.Connection` as appropriate. + +.. seealso:: + + :ref:`Postgresql Isolation Level <postgresql_isolation_level>_` + + :ref:`MySQL Isolation Level <mysql_isolation_level>_` + +Setting Isolation Engine-Wide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To set up a :class:`.Session` or :class:`.sessionmaker` with a specific +isolation level globally, use the :paramref:`.create_engine.isolation_level` +parameter:: + + from sqlalchemy import create_engine + from sqlalchemy.orm import sessionmaker + + eng = create_engine( + "postgresql://scott:tiger@localhost/test", + isolation_level='REPEATABLE_READ') + + maker = sessionmaker(bind=eng) + + session = maker() + + +Setting Isolation for Individual Sessions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When we make a new :class:`.Session`, either using the constructor directly +or when we call upon the callable produced by a :class:`.sessionmaker`, +we can pass the ``bind`` argument directly, overriding the pre-existing bind. +We can combine this with the :meth:`.Engine.execution_options` method +in order to produce a copy of the original :class:`.Engine` that will +add this option:: + + session = maker( + bind=engine.execution_options(isolation_level='SERIALIZABLE')) + +For the case where the :class:`.Session` or :class:`.sessionmaker` is +configured with multiple "binds", we can either re-specify the ``binds`` +argument fully, or if we want to only replace specific binds, we +can use the :meth:`.Session.bind_mapper` or :meth:`.Session.bind_table` +methods:: + + session = maker() + session.bind_mapper( + User, user_engine.execution_options(isolation_level='SERIALIZABLE')) + +We can also use the individual transaction method that follows. + +Setting Isolation for Individual Transactions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A key caveat regarding isolation level is that the setting cannot be +safely modified on a :class:`.Connection` where a transaction has already +started. Databases cannot change the isolation level of a transaction +in progress, and to accommodate this, drivers and some SQLAlchemy dialects +have inconsitent approaches to this; the psycopg2 DBAPI will implicitly +roll back any existing transaction, while the MySQLdb dialect in SQLAlchemy +emits an implicit 'COMMIT'. Therefore SQLAlchemy emits a warning if this +option is set when a transaction is already in play, and the :class:`.Session` +does not provide for us a :class:`.Connection` for use in a transaction +where the transaction is not already begun. So here, we need to pass +execution options to the :class:`.Session` at the start of a transaction +by passing :paramref:`.Session.connection.execution_options` +using the :meth:`.Session.connection` method:: + + from sqlalchemy.orm import Session + + sess = Session(bind=engine) + sess.connection(execution_options={'isolation_level': 'SERIALIZABLE'}) + + # work with session + + # commit transaction. the connection is released + # and reverted to its previous isolation level. + sess.commit() + +Above, we first produce a :class:`.Session` using either the constructor +or a :class:`.sessionmaker`. Then we explicitly set up the start of +a transaction by calling upon :meth:`.Session.connection`, which provides +for execution options that will be passed to the connection before the +transaction is begun. If we are working with a :class:`.Session` that +has multiple binds or some other custom scheme for :meth:`.Session.get_bind`, +we can pass additional arguments to :meth:`.Session.connection` in order to +affect how the bind is procured:: + + sess = my_sesssionmaker() + + # set up a transaction for the bind associated with + # the User mapper + sess.connection( + mapper=User, + execution_options={'isolation_level': 'SERIALIZABLE'}) + + # work with session + + # commit transaction. the connection is released + # and reverted to its previous isolation level. + sess.commit() + +.. _versionadded:: 0.9.9 Added the + :paramref:`.Session.connection.execution_options` + parameter to :meth:`.Session.connection`. + .. _session_external_transaction: Joining a Session into an External Transaction (such as for test suites) |