diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-07-22 17:59:34 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-07-22 18:03:36 -0400 |
commit | 4096ad0f0980f6940be57aaee85791df8f975cd7 (patch) | |
tree | 3cbba9826c3d8be0dfa4da2fa0241e5847d83d42 /test/engine/test_pool.py | |
parent | a6e83b204ffc13817fd0ef5bd7483ec811704992 (diff) | |
download | sqlalchemy-4096ad0f0980f6940be57aaee85791df8f975cd7.tar.gz |
- Fixed critical issue whereby the pool "checkout" event handler
may be called against a stale connection without the "connect"
event handler having been called, in the case where the pool
attempted to reconnect after being invalidated and failed; the stale
connection would remain present and would be used on a subsequent
attempt. This issue has a greater impact in the 1.0 series subsequent
to 1.0.2, as it also delivers a blanked-out ``.info`` dictionary to
the event handler; prior to 1.0.2 the ``.info`` dictionary is still
the previous one.
fixes #3497
Diffstat (limited to 'test/engine/test_pool.py')
-rw-r--r-- | test/engine/test_pool.py | 95 |
1 files changed, 94 insertions, 1 deletions
diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index 451cb8b0e..8551e1fcb 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -8,8 +8,9 @@ from sqlalchemy.testing import eq_, assert_raises, is_not_, is_ from sqlalchemy.testing.engines import testing_engine from sqlalchemy.testing import fixtures import random -from sqlalchemy.testing.mock import Mock, call, patch +from sqlalchemy.testing.mock import Mock, call, patch, ANY import weakref +import collections join_timeout = 10 @@ -1480,6 +1481,98 @@ class QueuePoolTest(PoolTestBase): time.sleep(1.5) self._assert_cleanup_on_pooled_reconnect(dbapi, p) + def test_connect_handler_not_called_for_recycled(self): + """test [ticket:3497]""" + + dbapi, p = self._queuepool_dbapi_fixture( + pool_size=2, max_overflow=2) + + canary = Mock() + + c1 = p.connect() + c2 = p.connect() + + c1.close() + c2.close() + + dbapi.shutdown(True) + + bad = p.connect() + p._invalidate(bad) + bad.close() + assert p._invalidate_time + + event.listen(p, "connect", canary.connect) + event.listen(p, "checkout", canary.checkout) + + assert_raises( + Exception, + p.connect + ) + + p._pool.queue = collections.deque( + [ + c for c in p._pool.queue + if c.connection is not None + ] + ) + + dbapi.shutdown(False) + c = p.connect() + c.close() + + eq_( + canary.mock_calls, + [ + call.connect(ANY, ANY), + call.checkout(ANY, ANY, ANY) + ] + ) + + def test_connect_checkout_handler_always_gets_info(self): + """test [ticket:3497]""" + + dbapi, p = self._queuepool_dbapi_fixture( + pool_size=2, max_overflow=2) + + c1 = p.connect() + c2 = p.connect() + + c1.close() + c2.close() + + dbapi.shutdown(True) + + bad = p.connect() + p._invalidate(bad) + bad.close() + assert p._invalidate_time + + @event.listens_for(p, "connect") + def connect(conn, conn_rec): + conn_rec.info['x'] = True + + @event.listens_for(p, "checkout") + def checkout(conn, conn_rec, conn_f): + assert 'x' in conn_rec.info + + assert_raises( + Exception, + p.connect + ) + + p._pool.queue = collections.deque( + [ + c for c in p._pool.queue + if c.connection is not None + ] + ) + + dbapi.shutdown(False) + c = p.connect() + c.close() + + def test_error_on_pooled_reconnect_cleanup_wcheckout_event(self): dbapi, p = self._queuepool_dbapi_fixture(pool_size=1, max_overflow=2) |