Altering a field with a unique constraint drops and rebuilds FKs to other fields in the table
|Patch needs improvement:
In #28305, a bug was fixed that prevented a MySQL field with a unique constraint from being altered if there was a foreign key constraint on it. However, the fix itself seems to have a more subtle bug in it: instead of dropping and recreating just foreign keys affecting that particular field, it does so for all foreign key constraints on any field in that table. This still yields a valid result, but for large tables with many incoming FK constraints this can dramatically increase the duration of the migration.
In particular, migration 0008 from django.contrib.auth (which changes the length of the username, a field with a unique constraint) now causes all foreign keys to the user ID to be dropped and later recreated throughout the database. I'm working with a MySQL database where this increases the duration of the migration from 10-20 minutes to nearly 48 hours, during most of which writes are blocked on at least one of the tables for which the indexes need to be recreated. Thankfully, we caught it during load testing with a production-sized database before attempting a production deployment. This particular manifestation of the problem only hits installations that were populated with data using a pre-1.10 version of Django but are attempting to go straight to 1.11 or above (which I suspect is actually fairly common given that 1.8 and 1.11 are LTS versions, and 1.10 is no longer being supported).
The problem seems to be in the usage of
_related_non_m2m_objects to enumerate the foreign key constraints; this function returns all relations involving the table of the field provided, not just that specific field. I discovered the bug in 1.11, but it still seems to be present in master.