首页 > 解决方案 > Sqlalchemy - 使用 InternalError 关闭游标 Traceback 时出错:找到未读结果

问题描述

我正在使用 sqlalchemy 将数据保存到 rds 中的 mysql 数据库(aurora-serverless)。这是通过 lambda 函数完成的。

一切正常,除了每当初始化一个新的 lamdba 容器时,我都会收到以下错误。之后,除非创建新容器,否则一切都会再次顺利运行。据我所知,旧容器中游标的未读结果会以某种方式保留,当它在初始化步骤尝试创建一个新容器时,它会尝试以某种方式关闭旧游标,然后抛出此异常。

我在我的 lambda 的全局级别上创建引擎和 sessionmaker,并创建一个会话并在每次调用我的处理程序时销毁它。

[ERROR] 2019-08-28T13:37:22.454Z    Error closing cursor
Traceback (most recent call last):
File "/var/task/sqlalchemy/engine/base.py", line 1338, in _safe_close_cursor
cursor.close()
File "/var/task/mysql/connector/cursor_cext.py", line 402, in close
self._cnx.handle_unread_result()
File "/var/task/mysql/connector/connection_cext.py", line 695, in handle_unread_result
raise errors.InternalError("Unread result found")
mysql.connector.errors.InternalError: Unread result found

在这方面的任何帮助都会非常有帮助。

以下是帮助理解引发此错误的 lambda 的代码片段

def connect_db():
    def connect():
        return mysql.connector.connect(host=hostname, user=username, password=password, database=database, port=port)

    db = create_engine(f'mysql+mysqlconnector://', creator=connect, isolation_level='SERIALIZABLE', echo=False)
    return db


# establish connection once per container
engine = connect_db()
Data.__table__.create(bind=engine, checkfirst=True)
Session = sessionmaker(bind=engine)

def save_to_database(data):
    session = Session()
    try:
        ids = [result[0] for result in session.query(Data.id).all()]
        ...
        #saving logic
        ...
        session.bulk_insert_mappings(Data, insert_data)
        session.flush()

        session.bulk_update_mappings(Data, update_data)
        session.flush()

        session.commit()

    except:
        session.rollback()
        raise
    finally:
        session.close()

def handler(event, context):
    output = save_to_database(event)
    return {'records': output}

标签: pythonsqlalchemyaws-lambdaamazon-aurora

解决方案


该错误似乎与 Cursor 的剩余行在关闭时未用尽有关。此问题的典型情况是当您执行 Select 查询时,使用 fetchone() 并且不使用其余行(请参阅https://stackoverflow.com/a/29774476/6203472),但这意味着除了问题中显示的批量插入之外,您还有另一段代码。由于某种原因,驱动程序不喜欢这样,您必须使用整个游标(在关闭游标或使用缓冲游标之前调用 fetchall())


推荐阅读