我正在使用 MongoDB Atlas。我有这个 URL 用于连接:mongodb+srv://<LOGIN>:<PASSWORD>@<URL>/<DB>?retryWrites=true&w=majority&authSource=admin
。
我正在使用这个堆栈:flask、gunicorn、pymongo、mongoengine(我不认为它是相关的,因为 mongoengine 实际上使用 pymongo)。
独角兽配置:
- 同步工作者,
- 线程 = 1,
- 工人数 = 8
连接时,我正在使用,connect=False
因为 pymongo 本身不是分叉安全的。connect=False
它将在第一次操作之前连接到数据库。它需要设置为False
,因为 gunicorn 使用 pre-fork 模型。
但我面临以下情况:
- 在第一次请求(端点进行一些数据库操作)时,我收到此错误:
pymongo.errors.OperationFailure: Authentication failed., full error: {'ok': 0, 'errmsg': 'Authentication failed.', 'code': 8000, 'codeName': 'AtlasError'}
- 在第二次请求时,一切都很好(即,数据库操作已成功完成)。
- 如果你按F5多次,那么有时你会出现“验证失败”的错误,有时一切都会好起来的。
如果我准备好connect=True
了,一切都会好起来的。我从未见过此“身份验证失败”错误。
但后来我收到了这个警告信息:UserWarning: MongoClient opened before fork. Create MongoClient only after forking. See PyMongo's documentation for details: https://pymongo.readthedocs.io/en/stable/faq.html#is-pymongo-fork-safe
。
所以,显然,connect
需要设置为False
.
我接受了两名工人的测试。看起来会出现这种情况:
- 假设我有两个具有以下 pid 的 gunicorn 同步工作者:22429 和 22430。
- 我收到请求。工人 22429 处理它。一切都很好,因为它是第一个工作人员,看起来它成功地建立了数据库连接。
- 我按 F5,工人 22429 再次处理它,一切都很好。
- 我再次按 F5,现在工人 22430 处理它。它会引发错误“身份验证失败”。
- 我再次按 F5,工人 22430 处理它。现在一切都很好,因为那个工人已经建立了联系。
- 现在,我所有的工人(我只有两个)都连接到数据库。无论我按 F5 多少次,每个请求都会成功完成。
但为什么它发生在第一次请求上?来自 pymongo:“如果 connect=False,则在第一次操作时连接。”。如果我理解正确,pymongo 应该在实际第一次操作之前连接,成功完成连接,然后才执行该操作。
那为什么我的第一个数据库操作对每个工人都失败了?我应该如何处理这个?
实际上,问题出在 Mongoengine,而不是 PyMongo。在 Mongoengine GitHub 上,我没有找到与此问题相关的任何内容。我用 Mongoengine 尝试了很多方法来解决这个问题,但没有任何效果。唯一的解决方案是拒绝 Mongoengine 并仅使用 PyMongo。
我重写了我的项目以仅使用 PyMongo。幸运的是,这只是项目的开始,所以并没有花太多时间。重写后,一切都按预期工作,配置完全相同。
与此问题无关:
对于那些考虑使用 Mongoengine 或 PyMongo 的人。使用 PyMongo。直接使用 PyMongo 要容易得多,因为它实现了实际的 MongoDB 文档。Mongoengine 改变了一些概念,最终很难阅读官方 MongoDB 文档和 Mongoengine 文档,后者以不同的方式实现了 MongoDB 文档中的一些概念。是的,通过拒绝更多的抽象,您将避免像我的问题这样的问题。