--- /dev/null
+.. change::
+ :tags: bug, engine
+ :tickets: 4225
+ :versions: 1.3.0b1
+
+ Fixed bug in connection pool where a connection could be present in the
+ pool without all of its "connect" event handlers called, if a previous
+ "connect" handler threw an exception; note that the dialects themselves
+ have connect handlers that emit SQL, such as those which set transaction
+ isolation, which can fail if the database is in a non-available state, but
+ still allows a connection. The connection is now invalidated first if any
+ of the connect handlers fail.
rec = pool._do_get()
try:
dbapi_connection = rec.get_connection()
- except:
+ except Exception as err:
with util.safe_reraise():
- rec.checkin()
+ rec._checkin_failed(err)
echo = pool._should_log_debug()
fairy = _ConnectionFairy(dbapi_connection, rec, echo)
rec.fairy_ref = weakref.ref(
dbapi_connection)
return fairy
+ def _checkin_failed(self, err):
+ self.invalidate(e=err)
+ self.checkin()
+
def checkin(self):
self.fairy_ref = None
connection = self.connection
try:
fairy.connection = \
fairy._connection_record.get_connection()
- except:
+ except Exception as err:
with util.safe_reraise():
- fairy._connection_record.checkin()
+ fairy._connection_record._checkin_failed(err)
attempts -= 1
p2.connect()
eq_(canary, ["listen_one", "listen_two", "listen_one", "listen_three"])
+ def test_connect_event_fails_invalidates(self):
+ fail = False
+
+ def listen_one(conn, rec):
+ if fail:
+ raise Exception("it failed")
+
+ def listen_two(conn, rec):
+ rec.info['important_flag'] = True
+
+ p1 = pool.QueuePool(
+ creator=MockDBAPI().connect, pool_size=1, max_overflow=0)
+ event.listen(p1, 'connect', listen_one)
+ event.listen(p1, 'connect', listen_two)
+
+ conn = p1.connect()
+ eq_(conn.info['important_flag'], True)
+ conn.invalidate()
+ conn.close()
+
+ fail = True
+ assert_raises(Exception, p1.connect)
+
+ fail = False
+
+ conn = p1.connect()
+ eq_(conn.info['important_flag'], True)
+ conn.close()
+
def teardown(self):
# TODO: need to get remove() functionality
# going