Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#24895 closed Bug (fixed)

Migration loader fails to load pair of squashed apps when dependent app's squashed migrations are partially applied

Reported by: Carl Meyer Owned by: Carl Meyer
Component: Migrations Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Consider the following set of migrations (taken directly from the test suite; for clarity I removed the repetitive _auto suffix from the migration names, and renamed the apps A and B instead of app1 and app2):

app A: 1 -> 2 -> 3 -> 4
app B: 1 -> 2

Where A.3 has a dependency on B.2, and there also exist two squashed migrations A.2_squashed_3 and B.1_squashed_2.

If A.2 and A.3 are both applied or unapplied (that is, their replacement by A.2_squashed_3 is allowed to take effect), this migration set will load fine.

But if A.2 is applied and A.3 is not, this migration set will fail to load with "django.db.migrations.exceptions.NodeNotFoundError: Migration A.3 dependencies reference nonexistent parent node ('B', '2')".

When the loader iterates over all "replacing" migrations, it finds B.1_squashed_2. Then it looks at each migration replaced by B.1_squashed_2, and one of those is B.2. It checks what migrations depend on B.2, so it can replace that dependency with a dependency on B.1_squashed_2. It finds that A.3 depends on B.2. So far so good.

But at this point, before replacing A.3's dependency on B.2 with a dependency on B.1_squashed_2, it first checks whether A.3 is itself replaced by anything. If so, it tries to update the dependencies of the replacing migration (that is A.2_squashed_3) and doesn't bother to update the dependencies of A.3 itself. But it fails to check whether, given the already-applied migrations, A.2_squashed_3 will _actually_ be allowed to replace A.2 and A.3 -- it just assumes the replacement will occur. And in the situation where the replacement doesn't occur (because A.2 is applied and A.3 is not), the dependency of A.3 on B.2 is never updated to account for the replacement of B.2 with B.1_squashed_2, and thus there is a dependency error.

I believe this fix should be backported to the 1.8.x branch, since it is a crashing bug.

Change History (5)

comment:1 Changed 5 years ago by Carl Meyer

Pull request: https://github.com/django/django/pull/4732

Rather than trying to detect whether the replacement in question will actually occur (which duplicates logic that will occur when that replacement is itself processed), we just update the dependencies of every migration that depends on this one, replacing or replaced. This means we may update the dependencies of a migration that won't in the end be used, but there's no harm in that; better to keep more state accurate than less.

comment:2 Changed 5 years ago by Carl Meyer

Has patch: set
Owner: changed from nobody to Carl Meyer
Status: newassigned

comment:3 Changed 5 years ago by Tim Graham

Triage Stage: UnreviewedReady for checkin

comment:4 Changed 5 years ago by Carl Meyer <carl@…>

Resolution: fixed
Status: assignedclosed

In 84522c0:

Fixed #24895 -- Fixed loading a pair of squashed migrations with a dependency.

comment:5 Changed 5 years ago by Carl Meyer <carl@…>

In 98b40ffe:

[1.8.x] Fixed #24895 -- Fixed loading a pair of squashed migrations with a dependency.

Backport of 84522c0d165076d01cd034d7c381b75044daec8d from master.

Note: See TracTickets for help on using tickets.
Back to Top