Opened 26 hours ago
Last modified 6 hours ago
#37287 assigned Cleanup/optimization
Add Index on LogEntry.action_time to optimize admin dashboard performance
| Reported by: | Ali Rafiei | Owned by: | Ali Rafiei |
|---|---|---|---|
| Component: | contrib.admin | Version: | dev |
| Severity: | Normal | Keywords: | admin, index, logentry, performance |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Problem & Motivation
In high-traffic production environments, the django_admin_log table often grows to millions of rows. Every time a user loads the main Django Admin Index (dashboard) page, the framework displays the "Recent actions" sidebar. This forces the following query to execute:
SELECT ... FROM "django_admin_log" ORDER BY "django_admin_log"."action_time" DESC LIMIT 10;
Because LogEntry explicitly defines ordering = -action_time in its Meta options but lacks any indexing on the action_time field, database engines must perform a highly inefficient sequential table scan to retrieve these 10 rows. This causes severe CPU spikes and long page-load delays on large datasets, as highlighted in recent community discussions (e.g., Django Forum thread https://forum.djangoproject.com/t/strange-behaviour-for-django-5-0-long-loading-times-and-high-postgres-cpu-load-only-admin/41943).
Historical Context & Precedent
The structural performance limitations of django_admin_log are well-documented. Similar bottlenecks regarding missing indexes were flagged in Ticket #17659 and Ticket #36414.
While previous community efforts to index django_admin_log faced roadblocks—specifically regarding the object_id column because MySQL struggles to index unbounded text columns—action_time is a standard DateTimeField. Adding an index here carries no cross-database compatibility issues and is universally supported across PostgreSQL, MySQL, SQLite, and Oracle.
Proposed Solution
To address this bottleneck, I propose adding an explicit descending index on action_time directly to LogEntry.Meta.indexes (e.g., models.Index(fields=-action_time and generate the corresponding core migration for contrib.admin. This targets an un-bypassable query built into Django's default UI and guarantees predictable performance scaling for enterprise installations.
Change History (3)
comment:1 by , 25 hours ago
| Description: | modified (diff) |
|---|
comment:2 by , 25 hours ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
Patch: https://github.com/alirafiei75/django/tree/ticket_37287
Adds a descending index on LogEntry.action_time (admin.0004). The admin
"Recent actions" sidebar always runs ORDER BY action_time DESC LIMIT 10
with no matching index.
Benchmark: SQLite, 200,000 rows, same query as the dashboard: