Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #31335 -- Fixed removing composed Meta constraints/indexes on foreign keys on MySQL. #15254

Merged

Conversation

GeyseR
Copy link
Contributor

@GeyseR GeyseR commented Dec 28, 2021

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from afcf204 to 49b5eff Compare December 28, 2021 22:37
@GeyseR GeyseR changed the title Fixes #33392 - Fix failure on removing composed indexes on MySQL Fixed #33392 - Fix failure on removing composed indexes on MySQL Dec 28, 2021
@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch 2 times, most recently from 725843a to 7347a05 Compare December 28, 2021 23:04
Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GeyseR Thanks for this patch 👍 I left initial comments, please also check the comments from the previous PR.

django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
with editor_with_post_cleanup('indexes') as editor:
editor.add_index(Book, index)
editor.remove_index(Book, index)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add test for renaming a ForeignKey included in the composed index?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test that actually? As stated in the ticket, for the field renaming Django adds 3 operations to the migration file: remove an index, rename the field, add the index (back). I believe it should be at least a separate ticket, that changes behavior to one operation instead of three...

@felixxm felixxm changed the title Fixed #33392 - Fix failure on removing composed indexes on MySQL Fixed #31335 - Fix failure on removing composed indexes on MySQL Dec 30, 2021
@GeyseR
Copy link
Contributor Author

GeyseR commented Jan 14, 2022

hi @felixxm, I've added two commits to the PR.
The first one (dc79f87) addresses your comments in this PR.
With the second one (3b02904) I try to apply a smarter logic for checking already existing various types of indexes, do they cover the FK index or not. In this commit, I also moved tests to tests/migrations as it is easier to apply migration operations with both state and DB updates instead of emulating the same behavior in the schema editor tests. For sure, these changes add complexity to the solution, but it behaves in a more optimized manner.

@GeyseR
Copy link
Contributor Author

GeyseR commented Jan 14, 2022

hey @felixxm,
could you please help me with debugging failed MySQL CI tests?
they successfully passed on my local MySQL DB v8 and with official django-docker-box docker image.
Here is the output from the latter:

$ export MYSQL_VERSION=8 # also works for 5.7
$ export DJANGO_PATH=<local django path>
$ export PYTHON_VERSION=3.10
% docker-compose run --rm mysql migrations
Creating django-docker-box_mysql_run ... done
wait-for-it.sh: waiting 20 seconds for mysql-db:3306
wait-for-it.sh: mysql-db:3306 is available after 0 seconds
Testing against Django installed in '/tests/django/django' with up to 6 processes
Found 630 test(s).
.....
Ran 630 tests in 22.102s

OK (skipped=3)

Do I use the correct MySQL version that matches the version on the CI server? Do you see any other reasons for the test's failure?

@felixxm
Copy link
Member

felixxm commented Jan 14, 2022

Do I use the correct MySQL version that matches the version on the CI server? Do you see any other reasons for the test's failure?

CI uses MySQL 5.7 (see the wiki page). This can be an issue with caching docker images, try to remove images before switching to MySQL 5.7, e.g.

$ docker-compose stop
$ docker-compose rm
$ export MYSQL_VERSION=5.7
$ docker-compose run --rm mysql migrations

@GeyseR
Copy link
Contributor Author

GeyseR commented Jan 14, 2022

thanks @felixxm, that helped. At least now tests fail locally :)

@GeyseR
Copy link
Contributor Author

GeyseR commented Jan 15, 2022

I think the PR is ready to review. The single failed test looks like an unstable one and is not related to the proposed changes.

@GeyseR
Copy link
Contributor Author

GeyseR commented Jan 28, 2022

@felixxm did you have a chance to review the updated PR

@jacobtylerwalls
Copy link
Member

Hi @GeyseR, if you've accounted for all feedback to date you should uncheck "Needs tests" and "Patch needs improvement" on the ticket so that there's an indication to re-review it. Rebasing from main would help also. Thanks.

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from 509bef9 to a82479a Compare February 21, 2022 08:48
@GeyseR
Copy link
Contributor Author

GeyseR commented Feb 21, 2022

@jacobtylerwalls thanks for the heads up, done

Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GeyseR Thanks for updates 👍

tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from a82479a to 433684d Compare June 3, 2022 00:18
@GeyseR
Copy link
Contributor Author

GeyseR commented Jun 3, 2022

hey @felixxm, I tried to simplify the tests code, how does it look now?

regarding the parts that you've mentioned as covered by other tests, you are partially right - deletion of unique/index_together was covered but by other tests from the schema/tests.py file (the deletion was partially fixed a long time ago). You can find those tests as removed in my commits, I thought that it might be a good idea to keep all the tests cases in one place

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from 433684d to e11f1b7 Compare June 3, 2022 00:34
@felixxm
Copy link
Member

felixxm commented Jun 3, 2022

hey @felixxm, I tried to simplify the tests code, how does it look now?

Many thanks 🚀 I have a week off, so I'm going to review it after the holidays (June, 13th+).

@GeyseR
Copy link
Contributor Author

GeyseR commented Jun 3, 2022

no problem, anyway I need to fix the failed tests :)

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch 3 times, most recently from 7114373 to 6c1d515 Compare June 3, 2022 12:37
Copy link
Member

@David-Wobrock David-Wobrock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍
I have a few remarks here and there, but mostly questions to confirm my understanding :)

(I didn't have time to look in details at the unit tests yet)

tests/schema/tests.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
@GeyseR
Copy link
Contributor Author

GeyseR commented Jun 6, 2022

hi @David-Wobrock, thanks for the review. After some thinking I decided to change how I check for duplicate FK index: instead of checking model's definition I check the DB table structure. It looks like is much more straightforward way and also more reliable as we can find some unmanaged manual indexes that could be enough for index deletion.

After that we don't need to extract get_first_index_field_name as a helper function anymore :)

Copy link
Member

@David-Wobrock David-Wobrock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a more subjective matter, I find the unit tests with their nested functions a bit hard to read. But since I don't have anything better to suggest at the moment, that's fine to me :)

tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/schema/tests.py Outdated Show resolved Hide resolved
@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from 764ee07 to d6c1a4e Compare September 6, 2022 16:20
@GeyseR
Copy link
Contributor Author

GeyseR commented Sep 7, 2022

@felixxm the PR is ready to review 🙄
Should I squash all commits in one?

@felixxm
Copy link
Member

felixxm commented Sep 7, 2022

Thanks 👍

@felixxm the PR is ready to review roll_eyes Should I squash all commits in one?

No need, I can squash with final edits.

Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GeyseR Thanks for updated 👍 We're getting closer.

tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/schema/tests.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
django/db/backends/mysql/schema.py Outdated Show resolved Hide resolved
@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch 2 times, most recently from 3cf0e73 to 7d7f784 Compare September 8, 2022 09:34
@GeyseR
Copy link
Contributor Author

GeyseR commented Sep 8, 2022

@felixxm
regarding this one (GH, for some reason, doesn't allow replying in the discussion thread)

This is an issue in schema so I'd keep these tests in schema/tests.py and also add new tests here

these two tests are now covered by test_composed_indexes_create_implicit_fk_index_when_deleting. Why would we need to keep redundant tests and spend time (and electricity :)) for running them in each build?

I've tried to move tests from operations tests to the schema tests, and this movement also requires adding utility code (apply_operations in particular) to the SchemaTests class. This looks like too much additional knowledge for this class: it should become aware of migrations, the project's state, etc. To avoid that, we need to operate on more low-level schema-related code (which is how I made the first version of the patch, and it looked ugly and confusing)

@felixxm
Copy link
Member

felixxm commented Sep 8, 2022

these two tests are now covered by test_composed_indexes_create_implicit_fk_index_when_deleting. Why would we need to keep redundant tests and spend time (and electricity :)) for running them in each build?

I was asking for keeping these two and moving new one to schema/tests.py, not about creating redundant tests.

I've tried to move tests from operations tests to the schema tests, and this movement also requires adding utility code (apply_operations in particular) to the SchemaTests class. This looks like too much additional knowledge for this class: it should become aware of migrations, the project's state, etc. To avoid that, we need to operate on more low-level schema-related code (which is how I made the first version of the patch, and it looked ugly and confusing)

I'm not sure why apply_operations hook is needed and what makes the new tests "ugly" and "confusing" 🤔 This bug is related with a specific model that we can create in schema tests and then perform remove_index()/remove_constraint() on it to see if it works. Am I missing something? For example:

    def test_meta_unique_constraint_with_fk(self):
        with connection.schema_editor() as editor:
            editor.create_model(Author)
            editor.create_model(Book)
        constraint = UniqueConstraint(
            fields=["author", "title"], name="book_author_title_uniq"
        )   
        Book._meta.constraints = [constraint]
        with connection.schema_editor() as editor: 
            editor.add_constraint(Book, constraint)
        constraints = self.get_constraints(Book._meta.db_table)
        self.assertIn(constraint.name, constraints)
        Book._meta.constraints = []
        with connection.schema_editor() as editor:
            editor.remove_constraint(Book, constraint)
        constraints = self.get_constraints(Book._meta.db_table)
        self.assertNotIn(constraint.name, constraints)

    def test_meta_index_with_fk(self):
        with connection.schema_editor() as editor:
            editor.create_model(Author)
            editor.create_model(Book)
        index = Index(
            fields=["author", "title"], name="book_author_title_idx"
        )
        Book._meta.indexes = [index]
        with connection.schema_editor() as editor:
            editor.add_index(Book, index)
        indexes = self.get_indexes(Book._meta.db_table)
        self.assertNotIn("author_id", indexes)
        Book._meta.indexes = []
        with connection.schema_editor() as editor:
            editor.remove_index(Book, index)
        indexes = self.get_indexes(Book._meta.db_table)
        self.assertIn("author_id", indexes)

are proper regression tests for removing Index() and UniqueConstraint() with a foreign key.

@GeyseR
Copy link
Contributor Author

GeyseR commented Sep 8, 2022

I was asking for keeping these two and moving new one to schema/tests.py, not about creating redundant tests.

I mean that the new tests cover the cases from these two tests, so they will be redundant as they test the same thing.

Let me make one more attempt with schema tests, I'll try to return soon :)

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch 2 times, most recently from 22c42b6 to bb173bf Compare September 10, 2022 11:01
@GeyseR
Copy link
Contributor Author

GeyseR commented Sep 10, 2022

hey @felixxm, I've pushed another attempt to add tests to tests/schema instead of tests/migrations
I should take my words back about code ugliness, this time it looks good to me :)

@GeyseR GeyseR force-pushed the ticket_33392_mysql_composed_indexes_removal branch from bb173bf to cd80e92 Compare September 10, 2022 12:23
tests/schema/tests.py Outdated Show resolved Hide resolved
@felixxm felixxm force-pushed the ticket_33392_mysql_composed_indexes_removal branch from cd80e92 to f530955 Compare September 12, 2022 08:13
@felixxm
Copy link
Member

felixxm commented Sep 12, 2022

I rebased after ec13e80 and squashed commits.

tests/schema/tests.py Outdated Show resolved Hide resolved
@felixxm felixxm force-pushed the ticket_33392_mysql_composed_indexes_removal branch from c7f5e5a to 5e16b09 Compare September 12, 2022 18:44
@felixxm felixxm changed the title Fixed #31335 - Fix failure on removing composed indexes on MySQL Fixed #31335 -- Fixed removing composed composed Meta constraints/indexes on foreign keys on MySQL. Sep 13, 2022
@felixxm felixxm force-pushed the ticket_33392_mysql_composed_indexes_removal branch from 5e16b09 to b731e88 Compare September 13, 2022 08:39
@felixxm
Copy link
Member

felixxm commented Sep 13, 2022

@GeyseR Thanks 👍 It's ready 🚀

@GeyseR GeyseR changed the title Fixed #31335 -- Fixed removing composed composed Meta constraints/indexes on foreign keys on MySQL. Fixed #31335 -- Fixed removing composed Meta constraints/indexes on foreign keys on MySQL. Sep 13, 2022
@GeyseR
Copy link
Contributor Author

GeyseR commented Sep 13, 2022

@felixxm thanks for you patience with reviewing my code 😅
the last updates look good to me 👍

@felixxm felixxm merged commit b731e88 into django:main Sep 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants