pip install mysql_server_has_gone_away
settings.py:
DATABASES = {
'default': {
'ENGINE': 'mysql_server_has_gone_away'
}
}
TL;DR
In my case I was using django ORM with long living request (webocket). CONN_MAX_AGE
doesn't work in my case.
At the beginning I created a wrapper which would try catch the error, but because of django lazy-loading it's just not clear which things you should wrap it for. So I end up duplicating this code in across the project which was a kind of pain in the ass. Instead of writing e.g. User.objects.get(id=3)
I would do do_db(User.objects.get, id=3)
and the db was try: return callback(*args, **kwargs); catch e: conn.close(); callback(*args, **kwargs)
.
When digging into django backend we can migrate this solution on connection level. Thus every query that goes to db will be wrapped with it:
settings.py:
DATABASES = {
'default': {
'ENGINE': 'lol'
}
}
lol/base.py:
"""
https://stackoverflow.com/a/60894948/3872976
"""
from django.db.backends.mysql import base
def check_mysql_gone_away(db_wrapper):
def decorate(f):
def wrapper(self, query, args=None):
try:
return f(self, query, args)
except (base.Database.OperationalError, base.Database.InterfaceError) as e:
if 'MySQL server has gone away' in str(e):
db_wrapper.connection.close()
db_wrapper.connect()
self.cursor = db_wrapper.connection.cursor()
return f(self, query, args)
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
if e.args[0] in self.codes_for_integrityerror:
raise base.utils.IntegrityError(*tuple(e.args))
raise
return wrapper
return decorate
class DatabaseWrapper(base.DatabaseWrapper):
def create_cursor(self, name=None):
class CursorWrapper(base.CursorWrapper):
@check_mysql_gone_away(self)
def execute(self, query, args=None):
return self.cursor.execute(query, args)
@check_mysql_gone_away(self)
def executemany(self, query, args):
return self.cursor.executemany(query, args)
cursor = self.connection.cursor()
return CursorWrapper(cursor)
You should pay attention, that you're gonna have issues with transactions if you mysql disconnects during the atomic operation. But unfortunately there's no other way around it.