Bump django from 4.2.30 to 5.2.16 - #118
Merged
Merged
Conversation
Bumps [django](https://github.com/django/django) from 4.2.30 to 5.2.16. - [Commits](django/django@4.2.30...5.2.16) --- updated-dependencies: - dependency-name: django dependency-version: 5.2.16 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Django 5.1 dropped support for the Meta.index_together option, which broke app startup on this branch's Django 5.2 bump (TypeError: 'class Meta' got invalid attribute(s): index_together). Replace it with the modern Meta.indexes API. The migration can't just rename the old index in place: the composite index_together index was already silently lost on SQLite because 0003_make_workspace_nonnull's AlterField rebuilds the whole table, and SQLite's table-rebuild path only restores indexes tracked via Meta.indexes, not the legacy index_together. On MySQL (production) the AlterField is an in-place ALTER TABLE, so the old index is untouched and still physically present under its old auto-generated name. The new migration accounts for both cases: it drops the old index by its (deterministic, backend- independent) name if found, then adds the new named index.
models.Index() validates that fields/expressions are non-empty in its constructor, even though remove_index()/remove_sql() only actually need the .name to build the DROP INDEX statement. My local SQLite testing didn't exercise this: the legacy index doesn't physically exist there (see the previous commit's message), so drop_legacy_index() short-circuited before constructing the Index. Production's MySQL DB does still have it, so it hit the construction and raised ValueError on startup. Verified by manually recreating the legacy index on a SQLite DB at migration 0003 and confirming 0004 now drops it and adds the new one.
…ocket script/run launches gunicorn with --preload, which imports simone.urls (and therefore starts the Cron background thread, which opens a DB connection on its first tick) in the master process before workers are forked. fork() duplicates the master's open MySQL socket fd into the worker, so the master's Cron thread and the worker end up sharing one underlying connection. Once both sides use it concurrently they corrupt each other's protocol state on that socket and hang forever waiting on a response that will never arrive — no exception, no log line, just a request that never completes until Slack's ~3s client timeout gives up (nginx logs this as a 499, since upstream never responded). Add a post_fork gunicorn hook that drops any DB connections inherited from the master, forcing each worker to lazily open its own independent connection on first use. This is the standard fix for gunicorn --preload combined with persistent database connections (CONN_MAX_AGE=300 here). Verified locally: gunicorn boots with the new config and post_fork hook runs without error, and a DB-touching request (/adm/login/) completes in ~20-30ms with no hang. Couldn't reproduce the exact master/worker socket race end-to-end without MySQL access in this environment, but the mechanism matches the observed symptoms precisely and this is the documented remedy for it.
…to dependabot/pip/django-5.2.16
.dockerignore excludes everything by default and only allowlists specific paths back in. gunicorn.conf.py (added in the previous commit for the post_fork DB-connection fix) was never added to that allowlist, so it never made it into the image and script/run's --config gunicorn.conf.py failed with "Error: 'gunicorn.conf.py' doesn't exist".
The post_fork connections.close_all() from the last commit turned out to be a no-op for this: Django's ConnectionHandler keys connections by thread-local storage, and post_fork runs on the thread that called fork() (the arbiter's), not the Cron thread that actually opened the connection in the master. So there was nothing in that thread-local slot to close, and the 499s continued exactly as before. Re-examining the actual mechanism: --preload runs `simone.urls` (which starts the singleton Cron thread and, via its first tick, opens a DB connection inside a transaction.atomic() block) in the gunicorn master before workers are forked -- confirmed by the container logs, where "Cron run: starting" always precedes "Booting worker". fork() duplicates whatever that connection has in flight into the worker. With multi- workspace support, essentially every incoming Slack event needs to look up the Workspace row for the team_id, so if the duplicated/corrupted connection leaves Cron's transaction (and any locks it took) stuck open with nothing able to ever commit or roll it back, every subsequent request needing that same table blocks forever waiting on the lock -- nothing logged, until Slack's ~3s client timeout gives up and nginx records a 499. 100% reproducible, matching what's been observed. The robust fix is to stop sharing anything across the fork boundary at all: drop --preload so each worker loads the app (and starts its own Cron thread, opens its own DB connections) fully independently, post-fork, with nothing inherited from a master that did real work first. Verified this reorders app loading to happen after "Booting worker" instead of before, and a DB-touching request still completes in ~30ms. This relies on running a single worker (script/run doesn't pass --workers, so gunicorn defaults to one) so Cron only ever starts once; noted in a comment. Also replaced gunicorn.conf.py's now-defunct post_fork hook with a worker_abort hook that dumps every thread's stack via faulthandler if a worker ever gets SIGABRT'd for timing out, so a future hang is diagnosable from the logs directly instead of another guess-and-redeploy cycle.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps django from 4.2.30 to 5.2.16.
Commits
6c8eee4[5.2.x] Bumped version for 5.2.16 release.d5d60ed[5.2.x] Fixed CVE-2026-53878 -- Prevented newlines from being accepted in Dom...6c66eb8[5.2.x] Fixed CVE-2026-53877 -- Prevented heap buffer over-read when creating...721685a[5.2.x] Fixed CVE-2026-48588 -- Prevented caching of responses that set cooki...61a829d[5.2.x] Added stub release notes and release date 5.2.16.21af510[5.2.x] Avoided breaking sha1sum verification in the generated checksum.txt f...039df55[5.2.x] Replaced the defunct pgp.mit.edu keyserver with GitHub for key import.2b3093f[5.2.x] Fixed #29187 -- Fixed flaky receiver count assertion in signals tests.4abc595[5.2.x] Refs #16281 -- Fixed isolation of admin_views.ViewOnSiteTests.f87add9[5.2.x] Added CVE-2026-6873, CVE-2026-7666, CVE-2026-8404, CVE-2026-35193, an...Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)You can disable automated security fix PRs for this repo from the Security Alerts page.