Django在芹菜任务和比赛条件中选择_进行_更新,并且一起唯一

Django在芹菜任务和比赛条件中选择_进行_更新,并且一起唯一,django,celery,django-celery,Django,Celery,Django Celery,应用程序具有以下相关模型、公司、节点和日志 class VCompany(models.Model): company_name = models.CharField(max_length=50, ...) class VNode(models.Model): company = models.ForeignKey(VCompany, ...) name1 = models.CharField(...) name

应用程序具有以下相关模型、公司、节点和日志

class VCompany(models.Model):
    company_name = models.CharField(max_length=50,  ...)

class VNode(models.Model):
            company = models.ForeignKey(VCompany, ...)
            name1 = models.CharField(...)
            name2 = models.CharField(...)

class Log(models.Model):
    node = models.ForeignKey(VNode, ... )
VNode必须是唯一的。不幸的是,没有简单的unique/unique组合。如果已知名称1,则公司和名称1的组合是唯一的。如果已知名称2,则公司和名称2的组合是唯一的。名称1或名称2或两者都已知

问题1:是否可以为上述情况构造一个唯一的子句?怎么做

芹菜任务用于通过处理文件中的一些数据在日志中创建记录。有许多芹菜任务同时运行以创建日志记录。在创建日志记录时,可能需要创建一个vnode。这将导致创建重复的vnode记录。为了解决这个问题,我正在这样做:

with transaction.atomic():
    # Get the company for update as a sync method.
    company = VCompany.objects.select_for_update().get(company_name=company)
    if name1:
        node, create = VNode.objects.get_or_create(company=company, name1=... )
        ... set other node data ...
        node.save()
        return node
    if name2:
        node, create = VNode.objects.get_or_create(company=company, name2=... )
        ... set other node data ...
        node.save()
        return node
这个代码显然有效。但是,如果我用transaction.atomic()删除,它将可靠地失败

问题2:为什么需要transaction.atomic()?需要吗

问题3:django的ORM何时发布select_for_更新?get_或_create是否导致它在创建新节点之前释放,从而导致竞争条件发生


谢谢你的提示

问题1:我不知道

问题2:

需要事务\原子,因为select\ U for\ U update仅在当前事务的持续时间内保持锁。否则,(如果您不在任何其他事务中)它将立即释放锁

问题3:

当前事务关闭(提交或回滚)时释放锁

get_或_create不是原子的,只有在db具有正确的唯一性约束时才是线程安全的。如果get_或_create违反唯一性约束,它将引发IntegrityError


在您的情况下,锁定VCompany应该使VNode上的get_或_create方法不可能失败。但是,只有在尝试创建VNode对象的所有位置都正确锁定时,才会出现这种情况

谢谢你的帮助@joshua。需要说明的是,VCompany select_for_update事务由于随后的VNode get_或_create而关闭?我对此有点怀疑,我承认我对这种行为有点惊讶。无论何时存在对表的ORM引用(而不是原始select\U for\U update表),select\U for\U update事务都会关闭,这是真的吗?如果从实际的角度而不是从芹菜任务的角度来看,行为会改变吗?再次感谢。否,VCompany select_for_update仅锁定VCompany表中的行。它不会影响VNode get_或create查询,因为它们实际上不需要读取或写入VCompany表。代码正常工作的原因是,除非其他线程首先在VCompany上获得锁,否则无法使用VNode查询进入块。另外,为了澄清这一点,当“with”块退出时,将释放select_for_update锁。如果没有“with”块,当“get”返回时,它会释放锁。当然我理解。如果没有“with”块,VNode.objects.get_或_create将导致VCompany.objects.select_释放_更新锁。也就是说,任何ORM get活动都会导致select_for_更新锁释放。如果没有“with”块,锁会立即释放(get返回后)。就你的目的而言,这意味着你永远不会真正得到锁。代码中对VNode的调用与VCompany上的锁根本不相关。