#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 by , 9 years ago
comment:2 by , 9 years ago
Has patch: | set |
---|---|
Owner: | changed from | to
Status: | new → assigned |
comment:3 by , 9 years ago
Triage Stage: | Unreviewed → Ready for checkin |
---|
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.