How to handle connection drops - mysql
I'm using a connection pool and I'm clueless about what to do when the mysql server drops my client's connection due to inactivity/mysql server goes down. I'm calling the below function everytime I've to make a query:
def getDbCnx():
try:
dbConn = mysql.connector.connect(pool_name = "connectionPool", pool_size = 3, pool_reset_session=True, **dbConfig)
except mysql.connector.Error as err:
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Something is wrong with your user name or password")
dbConn.close()
return None
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exist")
dbConn.close()
return None
else:
print(err)
dbConn.close()
return None
else:
return dbConn
As per my current understanding, the connection pool will be initialised on the first call of this function. And after that, it will just return a free connection from the already initialised pool. Now, suppose connection pool gets initialised successfully on the first call. And after sometime, say the mysql server goes down or it drops the connection due to inactivity. What will happen, when I query after such a situation. Because I suppose the older context would have gone stale.
Basically how do I ensure that the connection pool refreshes its internal contexts everytime it loses connectivity with the mysql server.
When you invoke dbConn.close() The connection will be reset (and we can observe the source here: https://github.com/mysql/mysql-connector-python/.../mysql/connector/pooling.py#L118 we can expect session variables deallocated, lost uncommitted transactions, etc.). The connection is not fully closed and it can be check by printing the connection id (it should not change if it is the same connection).
Once you attempt to retrieve another connection from the pool with mysql.connector.connect(pool_name = "connectionPool") it will check the connection and if the connection can not be reconnected, a new connection will be open (with a new session id), but in the case the new connection fails an error will be raised. So, if there is a server online and the user account you are using exist in the server is almost certain you will get the connection if the pool is not exhausted and server is online, even if the server was restarted or if you have updated your server after the creation of the connection pool, and also if the server has closed the inactive session, so make sure you close the connection so it can back to the pool and can be reused.
In the below example I shutdown the server with SHUTDOWN command from the MySQL console and then restart it with mysqladmin, you can see the connection id of each connection in the pool (some connections where reused), and that the variables are deallocated due to the connection being reset when goes back to the pool.
from time import sleep
import mysql.connector
from mysql.connector import errorcode
from builtins import range
from mysql.connector import errors
dbConfig = {
'host': '127.0.0.1',
'user': 'some_user', 'password': 'some_pass',
'port': 4824,
}
def getDbCnx():
try:
dbConn = mysql.connector.connect(
pool_name = "connectionPool",
pool_size = 3,
pool_reset_session=True,
**dbConfig
)
return dbConn
except (AttributeError, errors.InterfaceError) as err:
# Errors from bad configuration. not supported options or not valid
print(f"Something is wrong with the connection pool: {err}", flush=True)
except (errors.PoolError) as err:
# Errors from bad connection pool configuration or pool exhausted
print(f"Something is wrong with the connection pool: {err}", flush=True)
except errors.OperationalError as err:
# Errors from MySQL like Lost connection 2013 2055
print(f"Something is wrong with the MySQL server: {err}", flush=True)
except errors.ProgrammingError as err:
# Errors from bad connection data
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Something is wrong with your user name or password", flush=True)
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exist", flush=True)
except mysql.connector.Error as err:
print(f"{err}", flush=True)
print(f"err type: {type(err)}")
return None
def can_connect():
print("Getting connections...")
greetings = ["hello", "hi", "howdy", "hola"]
for n in range(4):
print(f"getting connection {n}")
cnx = getDbCnx()
if not cnx:
print("No database connection!!!")
return False
cur = cnx.cursor()
cur.execute("select connection_id()")
res = cur.fetchall()
print(f"connection id: {res}")
cur.execute('show variables like "%greeting%"')
res = cur.fetchall()
print(f"greeting?: {res}")
cur.execute(f"select #greeting")
greet = cur.fetchall()
print(f"greet: {greet}")
cur.execute(f"SET #greeting='{greetings[n]}'")
cur.execute(f"select #greeting")
greet = cur.fetchall()
print(f"greet: {greet}\n")
cur.close()
cnx.close()
print("")
return True
def pause(sleep_secs=30, count_down=29):
sleep(sleep_secs)
for s in range(count_down, 0, -1):
print(f"{s}, ", end='')
sleep(1)
print()
def test():
print("Initial test")
assert can_connect()
print("\nStop the server now...")
pause(10, 20)
print("\ntest with server stoped")
print("\ngetting connections with server shutdown should fail")
assert not can_connect()
print("\nStart the server now...")
pause()
print("\ntest if we can get connections again")
print("second test")
assert can_connect()
if __name__ == "__main__":
test()
Here is the output of the example above, even if the server was shutdown you can still retrieve connections once the server comes back online:
Initial test
Getting connections...
getting connection 0
connection id: [(9,)]
greeting?: []
greet: [(None,)]
greet: [('hello',)]
getting connection 1
connection id: [(10,)]
greeting?: []
greet: [(None,)]
greet: [('hi',)]
getting connection 2
connection id: [(11,)]
greeting?: []
greet: [(None,)]
greet: [('howdy',)]
getting connection 3
connection id: [(9,)]
greeting?: []
greet: [(None,)]
greet: [('hola',)]
Stop the server now...
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
test with server stoped
getting connections with server shutdown should fail
Getting connections...
getting connection 0
Something is wrong with the connection pool: Can not reconnect to MySQL after 1 attempt(s): 2003: Can't connect to MySQL server on '127.0.0.1:4824' (10061 No connection could be made because the target machine actively refused it)
No database connection!!!
Start the server now...
29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
test if we can get connections again
second test
Getting connections...
getting connection 0
connection id: [(23,)]
greeting?: []
greet: [(None,)]
greet: [('hello',)]
getting connection 1
connection id: [(24,)]
greeting?: []
greet: [(None,)]
greet: [('hi',)]
getting connection 2
connection id: [(25,)]
greeting?: []
greet: [(None,)]
greet: [('howdy',)]
getting connection 3
connection id: [(23,)]
greeting?: []
greet: [(None,)]
greet: [('hola',)]
We can see that the first time we retrieve connections from the pool we have the connections ids [9, 10, 11] and the connection 9 was reused. Later when the shutdown the connection the "No database connection!!!" text is printed and after I started the server the connections ids where [23, 24, 25] and the connection with id 23 was reused. In addition the greeting variable was deallocated in the server.
Related
Over time losing connection to mysql server during queries
I am running a discord bot using a MySQL server which are BOTH running on my UBUNTU 18.04 server. It works fine, but after a few hours I start to get an error any time I access the database. Traceback (most recent call last): File "/home/narnar/.local/lib/python3.7/site-packages/discord_slash/client.py", line 1352, in invoke_command await func.invoke(ctx, **args) File "/home/narnar/.local/lib/python3.7/site-packages/discord_slash/model.py", line 209, in invoke return await self.func(self.cog, *args, **kwargs) File "/home/narnar/cool-art/cogs/artlevels.py", line 120, in leaderboardCommand cur.execute("SELECT * FROM artLevels") File "/home/narnar/.local/lib/python3.7/site-packages/mysql/connector/cursor_cext.py", line 271, in execute raw_as_string=self._raw_as_string) File "/home/narnar/.local/lib/python3.7/site-packages/mysql/connector/connection_cext.py", line 522, in cmd_query sqlstate=exc.sqlstate) mysql.connector.errors.OperationalError: 2013 (HY000): Lost connection to MySQL server during query
I solved this issue by not having connections open for too long. My program connected to the MySQL server at the beginning of the program and used that same connection all the time. So instead of having one connection, I just opened a connection when I needed to access the database then closed it afterwards. Just make sure you don't have your connection open for too long Don't do this: con = mysql.connector.connect( host=host, user="ImNotThat", passwd="Stupid", database="toShowMyPasswords" ) async def someListenerEvent(): doThing() con.commit() Do this: async def someListenerEvent(): con = mysql.connector.connect( host=host, user="ImNotThat", passwd="Stupid", database="toShowMyPasswords" ) doThing() con.commit() cur.close() con.close()
MySQL connection pooling in Python2.7 doesn't work as expected. Facing issue with close() function of connection pool
I have taken the python sample code for MySQL Connection pool and tried to execute it with django framework. I'm able to execute the queries and retrieve the data if I don't close the cursor and connection object. When I call close() function connection object it's failing with following error, Please help me to solve this. ProgrammingError at /settings 1045 (28000): Access denied for user 'root'#'localhost' (using password: NO) Method: GET Request URL: http://sample.app.com/settings Django Version: 1.11.5 Exception Type: ProgrammingError Exception Value: 1045 (28000): Access denied for user 'root'#'localhost' (using password: NO) Exception Location: venv/local/lib/python2.7/site-packages/mysql/connector/connection.py in _auth_switch_request, line 256 Python Executable: venv/bin/python2 Python Version: 2.7.17 The code which I'm using, Coding in bold are creating the issue. If don't close connection, its working fine. when i close it doesn't work and throws an error. db_pool.py import mysql.connector.pooling dbconfig = { "host":"127.0.0.1", "port":"3306", "user":"root", "password":"root", "database":"test", } class MySQLPool(object): """ create a pool when connect mysql. """ def __init__(self, host="127.0.0.1", port="3306", user="root", password="root", database="test", pool_name="mypool", pool_size=3): res = {} self._host = host self._port = port self._user = user self._password = password self._database = database res["host"] = self._host res["port"] = self._port res["user"] = self._user res["password"] = self._password res["database"] = self._database self.dbconfig = res self.pool = self.create_pool(pool_name=pool_name, pool_size=pool_size) def create_pool(self, pool_name="mypool", pool_size=3): """ Create a connection pool """ pool = mysql.connector.pooling.MySQLConnectionPool( pool_name=pool_name, pool_size=pool_size, pool_reset_session=True, **self.dbconfig) return pool > > def close(self, conn, cursor): > > """ Here I'm facing issue, close is causing the issue. this close must release the connection object and add it to the connection pool. """ > > cursor.close() > > conn.close() def execute(self, sql, args=None, commit=False): """ Execute a sql """ # get connection form connection pool. conn = self.pool.get_connection() cursor = conn.cursor() if args: cursor.execute(sql, args) else: cursor.execute(sql) if commit is True: conn.commit() self.close(conn, cursor) return None else: res = cursor.fetchall() self.close(conn, cursor) return res Using above code in another file db_operation.py from db_pool import MySQLPool def testing(request, bid, *args, **kwargs): mysql_pool = MySQLPool() query = "select * from test.table1;" result = mysql_pool.execute(query) print 'RESULT : ', result Please help me to solve this or share if any best examples to use connection pool properly. Thanks in advance.
I have solved this issue. By default pool_reset_session is set to True and modified this to False. Now, it's working fine. When pool_reset_session is true, it's resetting the session with default config's for host, user and no password. As per my current DB connection, it will not allow without password. So, pool_reset_session is caused an issue and solved it. Thanks.
Database connection error while celery worker remains idle for 24 hours
I have a django based web application where I am using Kafka to process some orders. Now I use Celery Workers to assign a Kafka Consumer to each topics. Each Kafka Consumer is assigned to a Kafka topic in the form of a Kafka tasks. However after a day or so, when I am submitting a task I am getting the following error : _mysql.connection.query(self, query) _mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away') The above exception was the direct cause of the following exception: Below is how my tasks.py file looks like : #shared_task def init_kafka_consumer(topic): try: if topic is None: raise Exception("Topic is none, unable to initialize kafka consumer") logger.info("Spawning new task to subscribe to topic") params = [] params.append(topic) background_thread = Thread(target=sunscribe_consumer, args=params) background_thread.start() except Exception : logger.exception("An exception occurred while reading message from kafka") def sunscribe_consumer(topic) : try: if topic is None: raise Exception("Topic is none, unable to initialize kafka consumer") conf = {'bootstrap.servers': "localhost:9092", 'group.id': 'test', 'session.timeout.ms': 6000, 'auto.offset.reset': 'earliest'} c = Consumer(conf) logger.info("Subscribing consumer to topic "+str(topic[0])) c.subscribe(topic) # Read messages from Kafka try: while True: msg = c.poll(timeout=1.0) if msg is None: continue if msg.error(): raise KafkaException(msg.error()) else: try: objs = serializers.deserialize("json", msg.value()) for obj in objs: order = obj.object order = BuyOrder.objects.get(id=order.id) #Getting an error while accessing DB if order.is_pushed_to_kafka : return order.is_pushed_to_kafka = True order.save() from web3 import HTTPProvider, Web3, exceptions w3 = Web3(HTTPProvider(INFURA_MAIN_NET_ETH_URL)) processBuyerPayout(order,w3) except Exception : logger.exception("An exception occurred while de-serializing message") except Exception : logger.exception("An exception occurred while reading message from kafka") finally: c.close() except Exception : logger.exception("An exception occurred while reading message from kafka") Is there anyway that I could check if database connection exists as soon as a task is received and if not, I can re-establish the connection?
According to https://github.com/celery/django-celery-results/issues/58#issuecomment-418413369 and comments above putting this code: from django.db import close_old_connections close_old_connections() which is closing old connection and opening new one inside your task should helps.
mysql.connector.connect doesn't seem to have persistent connection through functions
Im having a little problem when using mysql in python with Flask framework. I have set up a function(get_db()) to connect to the database once a request and serve the same connection to functions requesting it later on(within the same request). import mysql.connector #mysql-connector-python==8.0.12 def get_db(): if 'db' not in g: try: click.echo("Trying to connect") cnx = mysql.connector.connect(user='stack', password='overflow', host='127.0.0.1', database=DB_NAME) g.db = cnx return g.db except mysql.connector.Error as err: if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: click.echo("Connection failed! Check username and password") elif err.errno == errorcode.ER_BAD_DB_ERROR: click.echo("Database does not exist") else: click.echo("Unknown error: {} ".format(err)) else: cnx.close() # Connection to db cannot # be established. # click.echo("Will exit from database") exit(1) click.echo("Using existing connection") return g.db In other functions I use it like this: (...) cnx = get_db() cursor = cnx.cursor() (...) The first function that uses the database works fine. Then when another one tries to connect the cursor failes because cnx doesn't have a connection: raise errors.OperationalError("MySQL Connection not available.") Does anyone have a way to handle this? One solution is to create the connection again for each function but for sake of performance I rather reuse a connection if possible.
The solution I have currently ended up with involves a reconnect at each time a method needs the db connection. I don't know if this will get the overhead a connect() get's but it is suitable for the current use case. The code will look like this: def get_db(): if 'db' not in g: try: cnx = mysql.connector.connect(user='tom', password='jerry', host='127.0.0.1', database=DB_NAME) g.db = cnx click.echo("Returns new connection") return g.db except mysql.connector.Error as err: if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: click.echo("Connection failed! Check username and password") elif err.errno == errorcode.ER_BAD_DB_ERROR: click.echo("Database does not exist") else: click.echo("Unknown error: {} ".format(err)) cnx = g.db cnx.reconnect() <--- Doing a reconnect each time return cnx
you can use pooling : https://dev.mysql.com/doc/connector-python/en/connector-python-connection-pooling.html import mysql.connector conf = {"user":"username", "password":"*****", host="hostname"} pool_cnc = mysql.connector.connect(pool_name="my_pool", **conf) # Take one connection using the name of the pool: cnc1 = mysql.connector.connect(pool_name = pool_cnc.pool_name) cnc1.is_connected() # Should output True At the given link there is also an example about the explicitly pooling connection
Close MySQL connection using node-mysql in gcloud functions?
I have hosted an application in gcloud which uses mysql. But when the function crashes (error/exceptions) I need to end the connection. When the function gets crashed the logs emits : error: Function worker killed by signal: SIGTERM So how can I close my mysql connection (connection.end()) before the function gets terminated ?
I don't know how you can end the connection when your app crashes. But maybe you can use mysql.createPool(...); rather then mysql.createConnection(...); var mysql = require('mysql'); var pool = mysql.createPool({ connectionLimit : 10, host : 'example.org', user : 'bob', password : 'secret', database : 'my_db' }); This way you can set pooling options for the connection and let the MySQL sort of manages the termination.