Opened 27 hours ago
Last modified 8 hours ago
#37287 assigned Cleanup/optimization
Add Index on LogEntry.action_time to optimize admin dashboard performance — at Version 1
| Reported by: | Ali Rafiei | Owned by: | |
|---|---|---|---|
| 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.