【问题标题】:IntegrityError: foreign key not present when doing a get_or_createIntegrityError:执行 get_or_create 时不存在外键
【发布时间】:2016-12-01 15:21:16
【问题描述】:

我有两个模型

LongJob(models.Model):
    pass

Report(models.Model):
    job = models.OneToOneField('LongJob', related_name='report')

我首先在一个视图中创建一个作业:

job = LongJob.objects.create()

然后我将job 传递给 Celery 任务并获取或创建报告:

@shared_task
def celery_task(job):
    report, created = Report.objects.get_or_create(job=job)

有时,get_or_create 调用会引发以下IntegrityError

IntegrityError: insert or update on table "report" violates foreign key constraint "job_id_c7400e5ba78c00c_fk_longjob_id"
DETAIL:  Key (job_id)=(120057) is not present in table "longjob".

错误发生后,我检查LongJob 表中是否存在ID 应该不存在的作业对象,我确实找到了它。这怎么可能 ?

数据库是 Postgresql 9.4。

【问题讨论】:

    标签: django postgresql celery


    【解决方案1】:

    序言

    Read Committed 是 PostgreSQL 中的默认隔离级别。当一个 事务使用这个隔离级别,一个 SELECT 查询(没有 FOR UPDATE/SHARE 子句)只看到查询开始前提交的数据; 它永远不会看到未提交的数据或期间提交的更改 通过并发事务执行查询。实际上,一个 SELECT 查询 在查询开始的那一刻看到数据库的快照 跑步。但是, SELECT 确实看到了以前执行的更新的效果 在它自己的事务中,即使它们尚未提交。 另请注意,两个连续的 SELECT 命令可以看到不同的数据, 即使它们在单个事务中,如果其他 事务在第一个 SELECT 开始之后和之前提交更改 第二个 SELECT 开始。

    https://www.postgresql.org/docs/9.4/static/transaction-iso.html

    红鲱鱼

    get_or_create
    假设正确使用,正确的数据库,此方法是原子的 基础数据库的配置和正确行为。 但是,如果没有在数据库级别强制执行唯一性 在 get_or_create 调用中使用的 kwargs(参见 unique 或 unique_together), 这种方法容易出现竞争条件,可能导致多个 同时插入具有相同参数的行。

    https://docs.djangoproject.com/en/1.9/ref/models/querysets/#django.db.models.query.QuerySet.get_or_create

    错误原因

    您在 django 设置中为您的数据库关闭了自动提交,或者您在事务中运行以下代码。

    job = LongJob.objects.create()
    

    您在一个线程中插入Job 记录,在另一个线程中插入Report。但是由于事务隔离级别的原因,celery 工作人员实际上无法看到创建了 LongJob 实例的作业记录。

    get_or_create 方法的原子性仅保证不能由第二个线程创建具有相同唯一键的相同报告。它不会影响外键引用的对象。

    解决方案

    在传递到 celery 之前提交事务或让 celery 任务等待正在创建的记录(要提交的主 django 线程中的事务)。

    【讨论】:

    • 这里有一些忍者技能...确实代码在事务中运行,但是由于芹菜调用在代码中运行,我完全忘记了事务。这让我觉得我应该在盲目使用 ORM 之前真正学习 PG 内部工作原理。干杯!
    • 很高兴能帮上忙
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 2018-02-19
    • 1970-01-01
    • 2018-02-12
    • 2011-09-16
    • 1970-01-01
    • 2022-07-22
    相关资源
    最近更新 更多