Поиск источника ошибки PyMySQL - err.InterfaceError((0, ''))

Я разработчик бота для Discord и недавно выполнил заказ. У клиента при настройке приложения на своем сервере изначально не было проблем, но, по их словам, после работы в течение «около трех часов» программа начинает выдавать определенную ошибку трассировки стека и больше не принимает команды.

Бот создан с использованием Discord.py и использует Peewee в качестве ORM, используя PyMySQL в качестве драйвера базы данных. Сервер, на котором работает клиент, размещается в DigitalOcean, и если требуется какая-либо информация об оборудовании и т. д., клиент может предоставить мне эту информацию по запросу. Мы уже пытались удалить и переустановить все зависимости, а также попробовать разные их дистрибутивы, но ошибки сохраняются.

Это точная трассировка, которую получает клиент:

  File "/usr/local/lib/python3.6/dist-packages/peewee.py", line 2666, in __exit__
    reraise(new_type, new_type(*exc_args), traceback)
  File "/usr/local/lib/python3.6/dist-packages/peewee.py", line 179, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/dist-packages/peewee.py", line 2875, in execute_sql
    cursor.execute(sql, params or ())
  File "/usr/local/lib/python3.6/dist-packages/pymysql/cursors.py", line 170, in execute
    result = self._query(query)
  File "/usr/local/lib/python3.6/dist-packages/pymysql/cursors.py", line 328, in _query
    conn.query(q)
  File "/usr/local/lib/python3.6/dist-packages/pymysql/connections.py", line 516, in query
    self._execute_command(COMMAND.COM_QUERY, sql)
  File "/usr/local/lib/python3.6/dist-packages/pymysql/connections.py", line 750, in _execute_command
    raise err.InterfaceError("(0, '')")
peewee.InterfaceError: (0, '')

Соответствующие части из моего файла database.py, где открыто соединение с базой данных:

import discord
from peewee import *

from config import db_host, db_port, mysql_db_name, \
    mysql_db_username, mysql_db_password

db_connection = MySQLDatabase(
    mysql_db_name,
    user = mysql_db_username,
    password = mysql_db_password,
    host = db_host,
    port = db_port
)

def create_db_tables():
    # create_tables() does safe creation by default, and will simply not create
    # table if it already exists
    db_connection.create_tables([User])

Соответствующие части моего файла bot.py, в частности функция запуска бота, которая запускается при первом открытии бота, и строки, которые создают и запускают клиент бота:

client = discord.Client()

async def bot_startup():
    # Check for config changes
    if client.user.name != config.bot_username:
        print("Username Updated To: {}".format(config.bot_username))
        await client.edit_profile(username=config.bot_username)

    # Start 'playing' message
    await client.change_presence(
        game=discord.Game( name=config.playing_message )
    )

    # Prepare database
    database.create_db_tables()
    print("Database Connected")

    print("Connected Successfully")

# ...

@client.event
async def on_ready():
    await bot_startup()

# ...

client.run(config.token)

По словам клиента, перезапуск бота временно решает проблему, и он работает нормально в течение нескольких часов, прежде чем ошибки снова начнутся. Бот больше не отвечает на любые входящие команды после начала ошибок, а если выдается достаточное количество ошибок, полностью падает.

Что обычно является причиной этой ошибки и какие шаги следует предпринять, чтобы исправить ее причину?


person Sparrow    schedule 27.05.2019    source источник
comment
Отвечает ли это на ваш вопрос? InterfaceError (0, '')   -  person Wasi Master    schedule 12.11.2020


Ответы (1)


Время появления ошибки заставляет меня задуматься о проблеме, с которой я также столкнулся при общении с базой данных.

Проблема может заключаться в том, что поскольку соединение открывается при запуске бота (или даже когда программа начинает свое выполнение) и используется спорадически, база данных может закрыть соединение. , поэтому любое дальнейшее выполнение приведет к ошибке. Чтобы бороться с этим, я создаю этот декоратор для всех методов моего класса базы данных.

def ensures_connected(f):
    def wrapper(*args):
        args[0].connection.ping(reconnect=True, attempts=3, delay=2)
        return f(*args)

    return wrapper

быть размещенным над любым методом или функцией, которая должна взаимодействовать с базой данных.

Строка args[0].connection.ping(reconnect=True, attempts=3, delay=2) означает, что мы будем вызывать self.connection (так как это первый аргумент, передаваемый методу при вызове) метод ping, что позволяет переподключиться, если соединение было разорвано.

self.connection в моем коде — это объект, возвращаемый вызовом метода MySQLConnection.connect и должен быть эквивалентен вашему объекту db_connection

Этот декоратор должен быть размещен над определением метода, как в следующем примере:


def ensures_connected(f):
    def wrapper(*args):
        args[0].connection.ping(reconnect=True, attempts=3, delay=2)
        return f(*args)

    return wrapper

class Database:
    def __init__(self):
        self.connection = mysql.connector.connect(
            user=self.username, password=password, host=self.host, database=self.database)

    # ...

    @ensures_connected
    def is_member_registered(self, guild: Guild, member: Member):
        # ...
        return
person Nathan Marotte    schedule 17.02.2021