SQLAlchemy使用连接池时出现KeyError错误。

3

我为Person创建了一个仓库类,并添加了以下方法:

def find_by_order_id(self, order_id: str) -> [Person]:
    results = []

    client_table = Table(TBL_CLIENT, self._metadata, autoload=True,
                             autoload_with=self._connection)
    order_table = Table(TBL_ORDER, self._metadata, autoload=True,
                             autoload_with=self._connection)

    query = select([client_table.c.first_name, client_table.c.last_name, client_table.c.date_of_birth])
    .select_from(
        order_table.join(client_table, client_table.c.order_id = order_table.c.order_id)
    ).where(order_table.c.order_id == order_id).distinct()

    for row in self._connection.execute(query).fetchall():
        results.append(dict(row))

    return results

由于某些我无法解释的原因,当我调试代码时,有时候SQLAlchemy会报告KeyError,但实际上该键确实存在:

File "/home/tghasemi/miniconda3/envs/myproj/lib/python3.6/site-packages/sqlalchemy/util/_collections.py", line 210, in __getattr__
    return self._data[key]
KeyError: 'order_id'

我注意到这种情况只会在多线程时从连接池中获取连接断开时发生(每个线程仅使用一个连接 - 我知道连接不是线程安全的):
if use_pooling:
        self._engine = create_engine(connection_string, pool_size=db_pool_size,
                                     pool_pre_ping=db_pool_pre_ping, echo=db_echo)
else:
        self._engine = create_engine(connection_string, echo=db_echo)

鉴于当异常发生时,我在异常抛出的位置打断点并检查了该键是否存在,所以我怀疑在构建查询时表的加载还未完成。

有人知道为什么会发生这样的情况吗?我放弃了!


1
这篇博客文章可能会有相关的内容,特别是它指出,多个线程同时尝试访问同一个连接可能会引发异常,因为其中一个线程关闭了连接,尽管我不会期望出现KeyError - Jamie Phan
1个回答

2

我想我成功解决了这个问题。

在我的问题中,我没有提到的一件事(实际上我没有想到它是可能的),就是引擎和元数据对象都来自我为数据库创建的单例类:

Original Answer翻译成"最初的回答"

class Database(metaclass=SingletonMetaClass):

    def __init__(self, config: Config, use_pooling: bool = True, logger: Logger = None):
        # Initializing the stuff here ...


    @property
    def metadata(self): # used to return self._metadata here 
        return MetaData(self._engine, reflect=False, schema=self._db_schema)

    @property
    def engine(self):
        return self._engine
<最初的回答> 尽管SQLAlchemy的官方文档表示MetaData对象对于读操作是线程安全的,但在我的情况下(这是一个读操作),它却引起了问题。不与我的线程共享此对象,问题就消失了(不能百分之百确定它是否真的消失了,但它不再发生了)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接