Source code for relstorage.adapters.mysql.connmanager
################################################################################ Copyright (c) 2008 Zope Foundation and Contributors.# All Rights Reserved.## This software is subject to the provisions of the Zope Public License,# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS# FOR A PARTICULAR PURPOSE.###############################################################################"""MySQL adapter for RelStorage."""from__future__importabsolute_importfrom__future__importprint_functionimportloggingfrom..connmanagerimportAbstractConnectionManagerlog=logging.getLogger(__name__)
[docs]classMySQLdbConnectionManager(AbstractConnectionManager):# https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html# Each statement gets its own snapshot. Persistent locking is minimized to# the exact index records found (assuming they were found with unique queries.# So only query on primary key or unique indexes).isolation_read_committed="ISOLATION LEVEL READ COMMITTED"# (DEFAULT) A snapshot is taken when the first statement is# executed. All reads are from that snapshot. You can use 'START# TRANSACTION WITH CONSISTENT SNAPSHOT' to take a snapshot immediately.# If read-only, supposed to barely use locks.isolation_repeatable_read="ISOLATION LEVEL REPEATABLE READ"# READ ONLY was new in 5.6isolation_repeatable_read_ro=isolation_repeatable_read+' , READ ONLY'# Serializable takes a snapshot, but also implicitly adds 'FOR# SHARE' to the end of every SELECT statement (even in READ ONLY# mode) --- but only for specific queries like 'SELECT * FROM# object_state WHERE zoid = X'; range queries, OTOH, that return# the same rows, are unaffected. Therefore, it fails to achieve# concurrency with writes.isolation_serializable='ISOLATION LEVEL SERIALIZABLE'def__init__(self,driver,params,options):self._params=params.copy()self._db_connect=driver.connectself._db_driver=driversuper().__init__(options,driver)self.isolation_load=self.isolation_repeatable_read_roself.isolation_store=self.isolation_read_committeddef_alter_params(self,replica):"""Alter the connection parameters to use the specified replica. The replica parameter is a string specifying either host or host:port. """params=self._params.copy()if':'inreplica:host,port=replica.split(':')params['host']=hostparams['port']=int(port)else:params['host']=replicareturnparams
[docs]defopen(self,isolation=None,read_only=False,deferrable=False,replica_selector=None,application_name=None,**kwargs):"""Open a database connection and return (conn, cursor)."""# pylint:disable=arguments-differ,unused-argumentifreplica_selectorisNone:replica_selector=self.replica_selectorifreplica_selectorisnotNone:replica=replica_selector.current()params=self._alter_params(replica)else:replica=Noneparams=self._paramsifisolationisNone:isolation=self.isolation_loadifread_onlyand'READ ONLY'notinisolation:isolation+=' , READ ONLY'whileTrue:__traceback_info__={k:vifk!='passwd'else'<*****>'fork,vinparams.items()}try:conn=self._db_connect(**params)cursor=self.cursor_for_connection(conn)self._db_driver.set_autocommit(conn,True)# Transaction isolation cannot be changed inside a# transaction. 'SET SESSION' changes it for all# upcoming transactions.stmt="SET SESSION TRANSACTION %s"%isolation__traceback_info__=stmtcursor.execute(stmt)self._db_driver.set_autocommit(conn,False)conn.replica=replicaconn.readonly=read_onlyreturnconn,cursorexceptself.driver.use_replica_exceptionsase:ifreplicaisnotNone:next_replica=replica_selector.next()ifnext_replicaisnotNone:log.warning("Unable to connect to replica %s: %s, ""now trying %s",replica,e,next_replica)replica=next_replicaparams=self._alter_params(replica)continuelog.warning("Unable to connect: %s",e)raise
def_do_open_for_load(self):returnself.open(isolation=self.isolation_load,read_only=True,replica_selector=self.ro_replica_selector)defrollback_store_quietly(self,conn,cur):# MySQL leaks state in temporary tables across# transactions, so if we don't drop the connection,# we need to clean up temp tables (if they exist!)clean=self.rollback_quietly(conn,cur)ifclean:try:cur.execute('CALL clean_temp_state(true)')cur.fetchall()exceptself.driver.driver_module.Error:log.debug("Failed to clean temp state; maybe not exists yet?")# But we still consider it a clean rollbackreturnclean