-
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Revert "Use Django DB max age connection setting" #9824
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
Revert "Use Django DB max age connection setting" #9824
Conversation
d4dac7e to
189b278
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9824 +/- ##
=======================================
Coverage 78.62% 78.63%
=======================================
Files 153 153
Lines 19199 19197 -2
Branches 2547 2546 -1
=======================================
Hits 15095 15095
+ Misses 3811 3809 -2
Partials 293 293
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
auvipy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can revert this. but what will be consequences? can we add additional integration tests to make sure there will be no regression at all?
Before the connection was only closed based on a heuristic, now it will always be closed at the end of the task.
I can't see a way to add an integration test without adding postgres and a django application. If you're just looking for a big sign that steers people away from re-implementing something similar again, I can add a unit test that asserts |
|
yeah sure, that would be great |
All set. |
e8ea92f to
88ca764
Compare
|
I suggest try part of the following test cases to improve test coverage. ` --- Edge Case: Simulate SIGALRM or OSError on DB close ---def test__close_database_handles_oserror(self): --- Multi-DB: All connections are forcibly closed ---def test__close_database_multi_db(self): --- Connection already closed and other DB exceptions are handled gracefully ---@pytest.mark.parametrize('exc', [KeyError('closed'), Exception('generic')]) --- DB_REUSE_MAX: Ensure close is NOT called until threshold ---def test_close_database_reuse_max_behavior(self): --- Forbid use of CONN_MAX_AGE ---def test_close_database_ignores_conn_max_age(self): --- Worker/process shutdown closes all connections ---def test_on_worker_process_init_closes_connections(self): --- Concurrency: Multiple tasks finish simultaneously, closing only once ---def test_close_database_concurrent_tasks(self): --- Dead code assertion: 'close_if_unusable_or_obsolete' is not used ---def test_close_database_does_not_call_close_if_unusable_or_obsolete(self): |
88ca764 to
e1fd8ff
Compare
|
@auvipy I added a test case for |
This reverts commit f0c9b40. This reverts PR celery#6134 and stops using the close_if_unusable_or_obsolete API since there are edge cases where it's unable to detect if a connection if actually unusable. This is most obvious when Celery interrupts a query in progress via a time limit handler. Django has marked this issue as wontfix (https://code.djangoproject.com/ticket/30646). Since this is effectively an optimization for Celery that can't be reliably used, Celery ought to close the connection after each task instead of trying to manage connections in a way similar to how the Django application does.
This API can fail to close unusable connections in certain scenarios, namely database failovers and ungraceful terminations (e.g. signal handler for time limit exceeded tasks). This makes close_if_unusable_or_obsolete adequate for HTTP request lifecycle management but inappropriate for use within celery workers. See also: https://code.djangoproject.com/ticket/30646 https://forum.djangoproject.com/t/close-if-unusable-or-obsolete-fails-to-close-unusable-connections/41900
e1fd8ff to
df224a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR reverts a previous optimization that attempted to reuse Django database connections using close_if_unusable_or_obsolete(). The change returns to the safer approach of always closing database connections after each task to avoid edge cases where Django cannot reliably detect unusable connections.
- Removes the
forceparameter from_close_database()method and always callsconn.close() - Updates test cases to reflect the simplified connection handling behavior
- Adds new test to verify connections are always closed without attempting optimization
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| celery/fixups/django.py | Removes conditional connection closing logic and always closes connections |
| t/unit/fixups/test_django.py | Updates tests to match simplified behavior and adds verification test |
auvipy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merging this for now as it is a revert of an old feature which is not valid anymore. the pr add some extra tests to prevent possible regression.
|
btw, do we also need to clean or update the relevant docs as well? |
|
AFAICT, there were never any docs added for this optimization, so I don't think there need to be any additional changes. |
|
ok thanks |
This reverts commit da4a80d.
|
Hi, this is a big performance regression. Celery will now always close DB connections between every task, instead of respecting the django We had to revert our upgrade to 5.6.0 because it was adding a huge load on our database, since creating connections is relatively expensive. |
|
OK. I tried to revert this but seems that is not possible. a manual revert is needed. can you take on this? |
|
after reading the reply of a django core team member, it seems we should not revert this at all. instead we could try different improvement. |
As in, not revert the original code, or not revert this PR? The way I understand the comment from the django forum, it sounds like this PR is not the correct solution. So it would make sense to revert this PR (revert #9824). |
we got a manual revert request here. as per the forum discussion, this PR should not be merged in the first place. so we have to revert it now. thanks for your report |
@auvipy |
|
we also got this PR #10015 an you please take a look? |
This reverts commit f0c9b40 (cc @whtsky @auvipy)
This reverts PR #6134 and stops using the close_if_unusable_or_obsolete API since there are edge cases where it's unable to detect if a connection if actually unusable, see the relevant Django issue which is marked as
wontfix: https://code.djangoproject.com/ticket/30646 (see also https://forum.djangoproject.com/t/close-if-unusable-or-obsolete-fails-to-close-unusable-connections/41900).A celery-specific way to introduce one of these edge cases is to run a task with a timeout that gets interrupted via
SIGALRMwhile a query is executing. The connection will be unusable but considered usable by Django, causing the next invocation of a task on that worker to fail.Since this is an optimization that can't be reliably used, Celery ought to close the connection after each task instead of trying to manage connections in a way similar to how the Django application does.
Description
Sorry for the verbose timeline but this optimization has a bit of a history in Celery.
Timeline
June 2017
#4116 is filed citing that Celery doesn't reuse database connections the same way Django does
Sept 2017
#4292 is added to start reusing Django connections via
close_if_unusable_or_obsoleteJuly 2018 - Apr 2019
#4878, #5483, and celery/django-celery-results#58 crop up as a result of #4292
May 2019
The optimization gets reverted in #5515
June 2020
#6134 reintroduces this optimization to attempt to fix #4116 once and for all