[1.7.x] Fixed #21553 -- Ensured unusable database connections get closed.
Backport of 5f2f47f
from master
This commit is contained in:
parent
396d65b580
commit
f6f188ffc7
|
@ -445,9 +445,14 @@ class BaseDatabaseWrapper(object):
|
|||
def is_usable(self):
|
||||
"""
|
||||
Tests if the database connection is usable.
|
||||
|
||||
This function may assume that self.connection is not None.
|
||||
|
||||
Actual implementations should take care not to raise exceptions
|
||||
as that may prevent Django from recycling unusable connections.
|
||||
"""
|
||||
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method')
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseDatabaseWrapper may require an is_usable() method")
|
||||
|
||||
def close_if_unusable_or_obsolete(self):
|
||||
"""
|
||||
|
|
|
@ -552,7 +552,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
def is_usable(self):
|
||||
try:
|
||||
self.connection.ping()
|
||||
except DatabaseError:
|
||||
except Database.Error:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -704,7 +704,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
else:
|
||||
# Use a cx_Oracle cursor directly, bypassing Django's utilities.
|
||||
self.connection.cursor().execute("SELECT 1 FROM DUAL")
|
||||
except DatabaseError:
|
||||
except Database.Error:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -213,7 +213,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
try:
|
||||
# Use a psycopg cursor directly, bypassing Django's utilities.
|
||||
self.connection.cursor().execute("SELECT 1")
|
||||
except DatabaseError:
|
||||
except Database.Error:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -664,6 +664,37 @@ class BackendTestCase(TestCase):
|
|||
self.assertTrue(cursor.closed)
|
||||
|
||||
|
||||
class IsUsableTests(TransactionTestCase):
|
||||
# Avoid using a regular TestCase because Django really dislikes closing
|
||||
# the database connection inside a transaction at this point (#21202).
|
||||
|
||||
available_apps = []
|
||||
|
||||
# Unfortunately with sqlite3 the in-memory test database cannot be closed.
|
||||
@skipUnlessDBFeature('test_db_allows_multiple_connections')
|
||||
def test_is_usable_after_database_disconnects(self):
|
||||
"""
|
||||
Test that is_usable() doesn't crash when the database disconnects.
|
||||
|
||||
Regression for #21553.
|
||||
"""
|
||||
# Open a connection to the database.
|
||||
with connection.cursor():
|
||||
pass
|
||||
# Emulate a connection close by the database.
|
||||
connection._close()
|
||||
# Even then is_usable() should not raise an exception.
|
||||
try:
|
||||
self.assertFalse(connection.is_usable())
|
||||
finally:
|
||||
# Clean up the mess created by connection._close(). Since the
|
||||
# connection is already closed, this crashes on some backends.
|
||||
try:
|
||||
connection.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# We don't make these tests conditional because that means we would need to
|
||||
# check and differentiate between:
|
||||
# * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do).
|
||||
|
|
Loading…
Reference in New Issue