]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Use MySQL protocol-level ping.
authorMaxim Bublis <satori@dropbox.com>
Mon, 2 Jul 2018 16:34:32 +0000 (12:34 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Jul 2018 21:59:20 +0000 (17:59 -0400)
Utilizes MySQL protocol-level pings for disconnection detection.
This is just a 5-byte packet followed by a 7-byte response.

Affects MySQLdb, MySQL Connector and PyMySQL dialects.

Change-Id: I672f75e3746878d88987a31750444dde0cf8eb9b
Pull-request: https://github.com/zzzeek/sqlalchemy/pull/460

doc/build/changelog/migration_13.rst
doc/build/changelog/unreleased_13/mysql_ping.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/mysql/mysqlconnector.py
lib/sqlalchemy/dialects/mysql/mysqldb.py
lib/sqlalchemy/dialects/mysql/pymysql.py

index 71ef4d0113348bf61a66ac5305e882363a97ecf2..a4ac864c6906c7cf0b5694b81c2234a3ed7d5c86 100644 (file)
@@ -99,6 +99,18 @@ Dialect Improvements and Changes - PostgreSQL
 Dialect Improvements and Changes - MySQL
 =============================================
 
+.. _change_mysql_ping:
+
+Protocol-level ping now used for pre-ping
+------------------------------------------
+
+The MySQL dialects including mysqlclient, python-mysql, PyMySQL and
+mysql-connector-python now use the ``connection.ping()`` method for the
+pool pre-ping feature, described at :ref:`pool_disconnects_pessimistic`.
+This is a much more lightweight ping than the previous method of emitting
+"SELECT 1" on the connection.
+
+
 Dialect Improvements and Changes - SQLite
 =============================================
 
diff --git a/doc/build/changelog/unreleased_13/mysql_ping.rst b/doc/build/changelog/unreleased_13/mysql_ping.rst
new file mode 100644 (file)
index 0000000..daf1125
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: feature, mysql
+
+    The "pre-ping" feature of the connection pool now uses
+    the ``ping()`` method of the DBAPI connection in the case of
+    mysqlclient, PyMySQL and mysql-connector-python.  Pull request
+    courtesy Maxim Bublis.
+
+    .. seealso::
+
+        :ref:`change_mysql_ping`
index 1ead8aaf50a819dbdb4955906efb63e9173fa141..e16b68badab747a1fea1ab64b062fdbd394be012 100644 (file)
@@ -160,6 +160,17 @@ class MySQLDialect_mysqlconnector(MySQLDialect):
         from mysql import connector
         return connector
 
+    def do_ping(self, dbapi_connection):
+        try:
+            dbapi_connection.ping(False)
+        except self.dbapi.Error as err:
+            if self.is_disconnect(err, dbapi_connection, None):
+                return False
+            else:
+                raise
+        else:
+            return True
+
     def create_connect_args(self, url):
         opts = url.translate_connect_args(username='user')
 
@@ -225,7 +236,8 @@ class MySQLDialect_mysqlconnector(MySQLDialect):
         exceptions = (self.dbapi.OperationalError, self.dbapi.InterfaceError)
         if isinstance(e, exceptions):
             return e.errno in errnos or \
-                "MySQL Connection not available." in str(e)
+                "MySQL Connection not available." in str(e) or \
+                "Connection to MySQL is not available" in str(e)
         else:
             return False
 
index 535c8ec52d56ebd9e4192f0f01d1658e11fc4e4f..7554d244cc46e9ca2bea7ef9124502200f12b996 100644 (file)
@@ -101,6 +101,17 @@ class MySQLDialect_mysqldb(MySQLDialect):
     def dbapi(cls):
         return __import__('MySQLdb')
 
+    def do_ping(self, dbapi_connection):
+        try:
+            dbapi_connection.ping(False)
+        except self.dbapi.Error as err:
+            if self.is_disconnect(err, dbapi_connection, None):
+                return False
+            else:
+                raise
+        else:
+            return True
+
     def do_executemany(self, cursor, statement, parameters, context=None):
         rowcount = cursor.executemany(statement, parameters)
         if context is not None:
index 4f1c792f958c7096e8baac03b9a1b11681217262..5f176cef2c01c93ef343beee5004566072c84853 100644 (file)
@@ -61,6 +61,14 @@ class MySQLDialect_pymysql(MySQLDialect_mysqldb):
     def dbapi(cls):
         return __import__('pymysql')
 
+    def is_disconnect(self, e, connection, cursor):
+        if super(MySQLDialect_pymysql, self).is_disconnect(e, connection, cursor):
+            return True
+        elif isinstance(e, self.dbapi.Error):
+            return "Already closed" in str(e)
+        else:
+            return False
+
     if py3k:
         def _extract_error_code(self, exception):
             if isinstance(exception.args[0], Exception):