]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added new method :meth:`.Session.invalidate`, functions similarly
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 13 Dec 2014 00:59:11 +0000 (19:59 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 13 Dec 2014 01:00:14 +0000 (20:00 -0500)
to :meth:`.Session.close`, except also calls
:meth:`.Connection.invalidate`
on all connections, guaranteeing that they will not be returned to
the connection pool.  This is useful in situations e.g. dealing
with gevent timeouts when it is not safe to use the connection further,
even for rollbacks.
references #3258

(cherry picked from commit cf7981f60d485f17465f44c6ff651ae283ade377)

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/orm/session.py
test/orm/test_session.py
test/orm/test_transaction.py

index f10d48273056f90b8f647126c9ed3456552eab67..4198279594ccf9cd874118326a263ca5d6931079 100644 (file)
 .. changelog::
     :version: 0.9.9
 
+    .. change::
+        :tags: enhancement, orm
+        :versions: 1.0.0
+
+        Added new method :meth:`.Session.invalidate`, functions similarly
+        to :meth:`.Session.close`, except also calls
+        :meth:`.Connection.invalidate`
+        on all connections, guaranteeing that they will not be returned to
+        the connection pool.  This is useful in situations e.g. dealing
+        with gevent timeouts when it is not safe to use the connection further,
+        even for rollbacks.
+
     .. change::
         :tags: bug, examples
         :versions: 1.0.0
index f5c52a6adad649b6b7918a92636c69e41663ca1b..37f4ad5abefa74e1fd9c9c662bd71843fcdc4130 100644 (file)
@@ -435,11 +435,13 @@ class SessionTransaction(object):
 
         self.session.dispatch.after_rollback(self.session)
 
-    def close(self):
+    def close(self, invalidate=False):
         self.session.transaction = self._parent
         if self._parent is None:
             for connection, transaction, autoclose in \
                     set(self._connections.values()):
+                if invalidate:
+                    connection.invalidate()
                 if autoclose:
                     connection.close()
                 else:
@@ -1006,10 +1008,46 @@ class Session(_SessionClassMethods):
         not use any connection resources until they are first needed.
 
         """
+        self._close_impl(invalidate=False)
+
+    def invalidate(self):
+        """Close this Session, using connection invalidation.
+
+        This is a variant of :meth:`.Session.close` that will additionally
+        ensure that the :meth:`.Connection.invalidate` method will be called
+        on all :class:`.Connection` objects.  This can be called when
+        the database is known to be in a state where the connections are
+        no longer safe to be used.
+
+        E.g.::
+
+            try:
+                sess = Session()
+                sess.add(User())
+                sess.commit()
+            except gevent.Timeout:
+                sess.invalidate()
+                raise
+            except:
+                sess.rollback()
+                raise
+
+        This clears all items and ends any transaction in progress.
+
+        If this session were created with ``autocommit=False``, a new
+        transaction is immediately begun.  Note that this new transaction does
+        not use any connection resources until they are first needed.
+
+        .. versionadded:: 0.9.9
+
+        """
+        self._close_impl(invalidate=True)
+
+    def _close_impl(self, invalidate):
         self.expunge_all()
         if self.transaction is not None:
             for transaction in self.transaction._iterate_parents():
-                transaction.close()
+                transaction.close(invalidate)
 
     def expunge_all(self):
         """Remove all object instances from this ``Session``.
index 186b7a781ae1e4aedd05276388ad6fa61be4b02b..4057051ae83806320f4c1c68a99eebda9756a9f9 100644 (file)
@@ -1503,6 +1503,9 @@ class DisposedStates(fixtures.MappedTest):
     def test_close(self):
         self._test_session().close()
 
+    def test_invalidate(self):
+        self._test_session().invalidate()
+
     def test_expunge_all(self):
         self._test_session().expunge_all()
 
index ba31e4c7d2d985ec7a1bdc59cf54d8e13e43aa11..1d7e8e6936e57b5848ca70fea430e9dcb3d5a664 100644 (file)
@@ -184,6 +184,23 @@ class SessionTransactionTest(FixtureTest):
         assert users.count().scalar() == 1
         assert addresses.count().scalar() == 1
 
+    @testing.requires.independent_connections
+    def test_invalidate(self):
+        User, users = self.classes.User, self.tables.users
+        mapper(User, users)
+        sess = Session()
+        u = User(name='u1')
+        sess.add(u)
+        sess.flush()
+        c1 = sess.connection(User)
+
+        sess.invalidate()
+        assert c1.invalidated
+
+        eq_(sess.query(User).all(), [])
+        c2 = sess.connection(User)
+        assert not c2.invalidated
+
     def test_subtransaction_on_noautocommit(self):
         User, users = self.classes.User, self.tables.users