【问题标题】:Prevent race conditions in sending data to endpoint and storing in database in Django REST API防止将数据发送到端点并存储在 Django REST API 中的数据库中的竞争条件
【发布时间】:2020-10-06 21:18:43
【问题描述】:

我正在用 Django REST API 编写一个 Web 应用程序,其中我有一个接受 POST 请求的端点:

https://website.com/api/receive-data

具有以下主体:

{
'value': 1,
}

还有我的代码:

# models.py

class MyModelA(models.Model):
status = models.BooleanField(default=False)

class MyModelB(models.Model):
value = models.IntegerField()
model_a_pk = models.IntegerField()

# views.py

class ModelBRecordData(APIView):
def post(self, request, format=None):
    serialzier = ModelBSerializer(data=request.data)
    if serializer.is_valid():
      model_b = MyModelB.objects.create(value=value, model_a_pk=serializer.data.get('model_a_pk'))
      model_b.save()
      model_bs = MyModelB.objects.filter().count()
      if model_bs > 100000:
          model_a = MyModelA.objects.filter(pk=serializer.data.get('model_a_pk')).first()
          model_a.status = True
          model_a.save()

这里的问题是我可能同时收到 1000 个端点请求。这将使该应用程序进入竞争状态,因此在 model_a 仍在更新时可能会创建超过 100,000 条记录。

如何在这样的应用中防止出现这种竞争情况?

【问题讨论】:

    标签: python django rest django-rest-framework race-condition


    【解决方案1】:

    您应该在要更新的 model_a 上设置一个数据库锁,但是当数据库事务完成时会释放一个锁(默认情况下,每个创建/更新数据库查询都是一个单独的小事务),因此您需要手动创建一个数据库您应该在其中执行所有数据库更新的事务:

    from django.db import transaction
    
    class ModelBRecordData(APIView):
    def post(self, request, format=None):
      with transaction.atomic(): # start a DB transaction manually
        serialzier = ModelBSerializer(data=request.data)
        if serializer.is_valid():
          # select model_a for update, and by doing it you set a lock on it, so all
          # parallel requests that also try to select it for update
          # will wait until the lock is released
          model_a = MyModelA.objects.select_for_update().get(pk=serializer.data.get('model_a_pk'))
          model_b = MyModelB.objects.create(value=value, model_a_pk=serializer.data.get('model_a_pk'))
          model_bs = MyModelB.objects.filter().count()
          if model_bs > 100000:
            model_a.status = True
            model_a.save()
    

    更多关于 django 交易的信息:https://docs.djangoproject.com/en/3.1/topics/db/transactions/

    关于select_for_update方法:https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-for-update

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-22
      • 2011-09-22
      • 2017-07-24
      • 2012-04-08
      • 2022-01-10
      • 1970-01-01
      • 2021-05-31
      • 2013-08-12
      相关资源
      最近更新 更多