0

我继承了一个完善的基于烧瓶的 API 服务,该服务广泛使用了 mongoengine。我们正在将这个单数据库 API 变成一个多租户服务,并不清楚最佳实践。由于许多原因,租户数据将被物理隔离到不同的数据库中,每个租户一个 db。(对于某些文档,还有一个到“核心”数据库的连接。)

用例工作流程很简单:

  • 收到请求
  • 验证 API 访问令牌并协调用户/租户
  • 将数据库连接切换到正确的租户数据库
  • 做文档操作

似乎实现这一点的最佳方法是使用别名,但是当我进行上下文切换时,我需要disconnect('tenant_db')使用connect(alias='tenant_db'). 这感觉不对。

无论如何,代码确实有效,但现在我遇到了单元测试问题。在测试并使用(每个文档)mongomock://localhost连接时,代码实际上会超时尝试连接到在 localhost 上运行的真实 mongodb。我怀疑这一切都与没有适当范围的模拟连接有关,但我找不到太多关于使用模拟数据库进行测试的文档。

抱歉,两个问题合二为一:

  1. 那么disconnectconnect别名模式是正确的方法吗
  2. pytest+mongoengine+mongomock有没有更好的实践(或更清晰的例子)

不是 pytest 的专业人士,但也不是新手。坚固pymongo但全新mongoengine

谢谢!

4

1 回答 1

0

没有得到 mongoengine 社区的任何回应有点失望——也许它没有我希望的那么活跃。

以下是进一步调查的结果,以及我们决定的方法。

第一个尖峰 - 单个tenant_db别名,并在每个请求上更改底层连接。优点:这允许现有代码在没有太多重构的情况下工作。disconnect缺点:如果没有and就不可能更改注册连接的详细信息reconnect,并且会触发有关默认数据库的警告(没有“默认”,这是每个请求,但需要“默认”。)这很混乱,没有成功.

第二个尖峰 - 咬紧牙关,将每个 Document 实例重构到模式内,为每个租户with switch_db(alias)使用唯一的别名。优点:这种明确的性质使文档操作将在正确的数据库中发生更有信心。Pro #2,关于“默认”数据库的不断警告实际上对我们有利 - 任何不在上下文中的流氓文档操作都会引发错误。缺点:仍然需要和在上下文之前。switch_dbdisconnectreconnectwith

第三个尖峰 - 我们考虑分叉 mongoengine,并将Document类更改为更纯粹 - 允许注入 db 连接而不是依赖于已注册连接的外部堆栈。优点:我们将完全控制数据库连接逻辑。缺点:db 的东西在 mongoengine 的 DNA 中很深——如果不付出很大的努力,这看起来不太可能成功。

我们选择了尖峰二。

由于gunicorn->wsgi->flask为我们提供了可靠的 _per-request 隔离,并且租户的新“默认”连接发生在请求身份验证成功后,然后利用with switch_db(alias)工作。这使我们能够在短期内修复这个单租户代码库以多租户方式运行。

我们也不是 100% 确信我们了解底层pymongo将如何连接池以提高性能。更多的学习要在那里完成。

最后,关于pytest混乱。在 Spike One 中,由于夹具、范围、断开/重新连接等原因,无法使用模拟进行测试。 Spike 2 效果更好,除了我们必须在实际代码中添加一个环境变量,这样它就不会尝试建立真正的连接,如果我们处于“unit_test_mode”。

于 2021-03-09T15:30:04.770 回答