Django повторно подключается к базе данных при потере соединения
У меня есть проект django, база данных Mysql находится на отдельном хосте, подключенном по протоколу TCP. У меня есть этот скрипт:
#!/usr/bin/env python
from asyncio import sleep, run
from django import setup as django_setup
django_setup()
from django.db import connections
from django.contrib.auth.models import User
from django.db import OperationalError, close_old_connections
async def test():
while True:
try:
objs=User.objects.all()
print(await objs.acount())
except KeyboardInterrupt:
break
except OperationalError as e: # Need to reconnect
print(f'main(): OperationalError: {e}')
#raise
# (1053, 'Server shutdown in progress')
# (2013, 'Lost connection to MySQL server during query')
# (2006, 'Server has gone away')
code=e.args[0]
if code in {1053, 2006, 2013, 2026}: # Only when it's restarting, and once
#await sync_to_async(conn.connect)()
close_old_connections()
print('After reconnect')
#reconnect()
else:
#breakpoint()
pass
except Exception as e:
print(f'in _round() {type(e)}')
else:
pass
#breakpoint()
print('Sleeping')
await sleep(5)
try:
run(test())
except KeyboardInterrupt:
raise
except:
print('In run(main())')
breakpoint()
pass
Затем я использую tcpkill, чтобы прервать соединение:
tcpkill 'dst port 3306'
Как мне дать команду django на повторное подключение? Я не делаю это автоматически, close_old_connection не работает, результат выглядит следующим образом:
3
Sleeping
3
Sleeping
main(): OperationalError: (2013, 'Lost connection to MySQL server during query')
After reconnect
Sleeping
main(): OperationalError: (2013, 'Lost connection to MySQL server during query')
After reconnect
Sleeping
main(): OperationalError: (2013, 'Lost connection to MySQL server during query')
After reconnect
Sleeping
При просмотре кода я вижу, что он порождает объект курсора из объекта подключения, но не работает, а затем закрывается, в то время как, вероятно, объект подключения остается нетронутым и продолжает выдавать непригодные для использования курсоры. Я пробовал connection.connect(), но это не сработает, потому что код асинхронный.
Вы можете сделать это с помощью промежуточного программного обеспечения, я полагаю, если этот вопрос связан с пользователями, фактически использующими ваше приложение.
from django.db import connection, OperationalError
from django.utils.deprecation import MiddlewareMixin
class ReconnectMiddleware(MiddlewareMixin):
def process_request(self, request):
try:
# test the connection
connection.cursor()
except OperationalError:
# if not close and next time
# view hits it will try to reconnect
connection.close()
Теперь добавьте промежуточное программное обеспечение к вашему settings.py (Вероятно, превыше всего остального)
MIDDLEWARE = [
'[wherever].ReconnectMiddleware',
# others...
]
Это приводит к тому, что при каждом просмотре вы нажимаете на это промежуточное программное обеспечение. Оно проверяет или закрывает соединение. Так, например, если ваша база данных теряет соединение, а затем вы загружаете страницу и возникает эта ошибка, при следующей загрузке страницы (если база данных доступна) она снова подключится.
функция close_old_connections() не работает, потому что в асинхронном режиме все объекты будут находиться в разных потоках. И, вероятно, я вызывал ее по-другому, когда connect в sync_to_sync не работал. Итак, в конце концов, это работает:
await sync_to_async(reconnect)()
с помощью этой функции:
def reconnect():
conn=connections['default']
conn.connect()