python - 带有 QueuePool 和两个进程的 SQLAlchemy:结帐时关闭共享连接可以吗?
问题描述
我正在使用烧瓶、flask_SQLAlchemy 和 postgres。两个单线程进程正在为我的开发应用程序提供服务。起初我有以下代码片段:
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {'pool_size': 1, 'pool_pre_ping': True, 'pool_recycle': 280}
db = SQLAlchemy(app)
db.create_all()
这个在测试我的 API 时产生了 OperationalError。我找到了三个参考文献 [1] [2] [3] 来解释这个问题。他们指出,在分叉之后,两个进程共享源自 create_all() 的相同连接,这恰好是问题的原因。因此,我利用已经提供的解决方案之一在结帐时从池中删除共享连接:
def add_engine_pidguard(engine):
"""Add multiprocessing guards.
Forces a connection to be reconnected if it is detected
as having been shared to a sub-process.
"""
@event.listens_for(engine, "connect")
def connect(dbapi_connection, connection_record):
connection_record.info['pid'] = os.getpid()
@event.listens_for(engine, "checkout")
def checkout(dbapi_connection, connection_record, connection_proxy):
pid = os.getpid()
if connection_record.info['pid'] != pid:
warnings.warn(
"Parent process %(orig)s forked (%(newproc)s) with an open "
"database connection, "
"which is being discarded and recreated." %
{"newproc": pid, "orig": connection_record.info['pid']})
connection_record.connection = connection_proxy.connection = None
raise exc.DisconnectionError(
"Connection record belongs to pid %s, "
"attempting to check out in pid %s" %
(connection_record.info['pid'], pid)
)
并且add_engine_pidguard(db.engine)
紧随其后db = SQLAlchemy(app)
。这种方法终于摆脱了错误,但是
SELECT *
FROM pg_stat_activity
WHERE query NOT LIKE '%%FROM pg_stat_activity%%';
表明,虽然第一个连接被从池中丢弃,但仍保留在数据库中。所以我最终得到了三个连接。我觉得根据先前 SQL 语句的“查询”字段,第一个连接似乎被用于 pre_pring 有点可怕。为了防止多个进程再次共享同一个连接,我添加了:
if not dbapi_connection.closed:
dbapi_connection.close()
在我提出 DisconnectionError 之前def checkout(dbapi_connection, connection_record, connection_proxy):
之后,我的数据库中只出现了两个连接,到目前为止没有发生任何错误。
现在请问,这样做安全吗?我担心这两个进程可能会同时尝试关闭连接。
编辑:我知道使用 NullPool 而不是 QueuePool 可以解决这样的问题。但是,NullPool 会导致成本高昂的操作,例如为每个请求创建和关闭数据库连接,我不太确定在有很多请求的情况下这会有多糟糕。
[1] https://davidcaron.dev/sqlalchemy-multiple-threads-and-processes/
[2] https://docs.sqlalchemy.org/en/13/core/pooling.html#using-connection-pools-with-multiprocessing
[3] https://docs-sqlalchemy.readthedocs.io/ko/latest/faq/connections.html
解决方案
推荐阅读
- python - Python 循环无法遍历 html 表的所有值
- qt - 如何在不手动操作 .ui 文件的情况下在 QtDesigner 中编辑 QtElement 的网格位置?
- python - DJANGO gte, lte 没有显示任何结果
- wpf - 从 WPF-Windows (XAML) 调用 WPF-Control 中的 RelayCommand
- serialization - 如何在 Hazelcast 上安排在 IMap 上查询的任务?
- r - 如何在 R 中创建一个用户生成的函数,将列中的所有值转换为日期格式?
- firebase - Flutter/Firebase 错误 - 未处理的异常:类型 '_CompactLinkedHashSet
' 不是类型 'FutureOr 的子类型 >>' - java - AltUnityDriver create-无法执行命令'mobiledevice tunnel -u (device udid)13000 13000'
- blazor - Blazor - 在 RenderFragment 部分刷新子组件
- shell - 如何将变量从一个目录存储或捕获到另一个目录并在 Linux 中使用 cut 命令执行