Django原子地限制多个表的检查和增量计数器

时间:2017-02-14 22:13:37

标签: sql django orm

我有2个模型/表Cluster和Node,可能还有更多,每个都有" volume_limit"和" volume_count"其中的字段/列。

我想在自己的表中原子地检查计数器的限制并增加计数器。 要仅对Cluster进行原子检查和增量,我将执行以下操作

updated = Cluster.objects.filter(id=cluster_id,     volume_count__lt==F('volume_limit')).update(volume_count = F('volume_count')+1)
if not updated:
    raise Validation('limit reached ......')

如果我想增加节点和群集,我会添加以下内容,如果我不能递增节点计数器,这将撤消群集增量。如下所示

updated = Node.objects.filter(id=node_id, volume_count__lt==F('volume_limit')).update(volume_count = F('volume_count')+1)
if not updated:
    Cluster.objects.filter(id=cluser_id).update(volume_count = F('volume_count')-1)
    raise Validation('limit reached ......')

我想知道是否有办法原子地做这两件事?

基本上我想在查询/更新操作中执行以下操作

  1. 为群集和节点对象以及可能更多的对象创建一个查询,这些对象都具有" volume_count"和" volume_limit",需要同时检查/增加。
  2. 检查查询是否转换为每个查询的一个对象。即每个对象/表的计数都在限制范围内。那就是如果我正在检查Cluster和node,那么查询应该转换为只有2个对象,
  3. 对所有查询结果(即对象/表格)执行volume_count = F(' volume_count')+ 1。
  4. 我该怎么做?

1 个答案:

答案 0 :(得分:1)

使用transactions可以解决您的问题。你没有指定你的数据库,但是在具有默认事务隔离级别的PostgreSQL上,我相信这会有效:

with transaction.atomic():
    n_updated = Node.objects.filter(
                    id=node_id,
                    volume_count__lt==F('volume_limit')
                ).update(volume_count=F('volume_count') + 1)
    c_updated = Cluster.objects.filter(
                    id=cluster_id,
                    volume_count__lt==F('volume_limit')
                ).update(volume_count=F('volume_count') + 1)

    if not (n_updated and c_updated):
        # trigger rollback
        raise ValidationError("Limit reached")

关键是您要定位特定的行,并且如果可能的话更新它们,如果他们尝试更新相同的行,这将导致其他事务阻止。由于它是一个交易,要么两个更新都会成功,否则都不会。