【发布时间】:2015-12-16 04:27:01
【问题描述】:
考虑一个具有单个 RESTful API 的 Django 应用程序,该 API 创建对象(使用 Django REST 框架)。作为此 API 的一部分,我会进行一些验证以确保创建调用是幂等的,这样如果您调用创建 API 两次,第一次将成功,第二次将失败并显示自定义错误代码。
我有一个测试这个 API 的场景,它会以以下方式间歇性失败:
- 第一次 API 调用,成功,返回 201 -> 对象应该已创建
- 响应后立即进行第二次 API 调用
- 验证逻辑调用
MyModel.objects.get(some_field=some_value)来检查这是否是重复调用 - 没有找到这样的对象,尽管在步骤 1 中创建了,因此创建了一个重复的对象
- 检查管理员/查询模型时,可以看到两个对象。
更多数据:
- 此模型上没有显式缓存,或此过程中涉及的任何其他缓存。
- 我无法在本地重现此内容
- 在我的部署设置中,这种可能的竞争条件的失败率约为 5%。
- 本地和部署都使用 PostgreSQL。
- 部署环境确实启用了通用缓存,但是在本地启用缓存时仍然没有复制。
什么可能导致这种竞争状况? Django ORM 是否有任何失败模式,我可能会得到陈旧的数据?有什么方法可以防御性地保护验证免受陈旧数据的影响?
【问题讨论】:
-
对于拥有 72,000 个互联网点的人,我不敢问这个问题,但您会显示步骤 3-4 的代码吗?另外,您确定步骤一和二是正确的,因为第二个调用是在第一个返回响应后开始的?
-
您正在尝试使用检查和插入模式执行 upsert 或 insert-if-not-exists 。这行不通。您需要尝试插入,如果失败则捕获错误。或者使用 PostgreSQL 9.5 的 upsert 支持。见stackoverflow.com/q/17267417/398670
-
@YPCrumble 这里的代码无关紧要,我问了一个非常具体的问题,为什么 QuerySet 可能会返回陈旧的数据,是的,我特别提到第一个请求已经返回了 201回应。
-
@CraigRinger 我知道什么是 upsert,但事实并非如此。我只是在检查一个对象是否存在,以便在它存在时返回一个自定义错误。
-
@YuvalAdam insert-if-not-exists 就像您面临与 upsert 相同的竞争条件和相同的可见性问题。这基本上是 upsert 的退化案例。
标签: django postgresql python-3.x django-models django-rest-framework