Skip to content

perf(grades): optimize database queries for large-scale grade recalcu… - #90

Merged
andrey-canon merged 1 commit into
open-release/verawood.nelpfrom
and/FUTUREX-1744
Aug 5, 2026
Merged

andrey-canon merged 1 commit into
open-release/verawood.nelpfrom
and/FUTUREX-1744

Conversation

@andrey-canon

Copy link
Copy Markdown

Description

This PR addresses significant platform performance degradation caused by inefficient database queries during large-scale course grade recalculations.

The previous implementation relied on SQL OFFSET pagination, which forced the database to perform full index scans and discard hundreds of thousands of rows for high-offset tasks. Additionally, it suffered from an $N+1$ query problem by fetching user data individually for every enrollment in a batch.

Changes

  • Keyset Pagination: Refactored _course_task_args and compute_grades_for_course to use start_id (ID-based seeking) instead of offset. This ensures O(1) database lookup performance regardless of the course size.
  • Database Optimization: Replaced order_by('created') with order_by('id') to leverage the Primary Key clustered index.
  • Eager Loading: Added .select_related('user') to the enrollment QuerySet to fetch user data in a single JOIN query, eliminating $100$ extra queries per batch.
  • Memory Efficiency: Used .values_list('id', flat=True) in the task generator to minimize memory footprint when handling courses with 400k+ enrollments.

How to Test

Run the following script in the Django shell (python manage.py lms shell) on a high-enrollment course:

from common.djangoapps.student.models import CourseEnrollment
from opaque_keys.edx.keys import CourseKey
import time
from django.db import connection, reset_queries

course_key = CourseKey.from_string("your/course/id")
batch = 100
offset_test = 440000 

# Benchmark Legacy Logic
reset_queries()
st = time.time()
enrollments_legacy = CourseEnrollment.objects.filter(course_id=course_key).order_by('created')[offset_test:offset_test + batch]
ids_legacy = [e.user.id for e in enrollments_legacy]
print(f"Legacy Time: {time.time() - st:.4f}s | Queries: {len(connection.queries)}")

# Benchmark Optimized Logic
start_id = CourseEnrollment.objects.filter(course_id=course_key).order_by('id')[offset_test].id
reset_queries()
st = time.time()
enrollments_new = CourseEnrollment.objects.filter(course_id=course_key, id__gte=start_id).select_related('user').order_by('id')[:batch]
ids_new = [e.user.id for e in enrollments_new]
print(f"Optimized Time: {time.time() - st:.4f}s | Queries: {len(connection.queries)}")

Performance Benchmarks

Metric Original (Offset + N+1) Optimized (Seek + Join) Improvement
Execution Time ~0.9614s ~0.0388s ~25x faster
DB Queries 101 1 100 fewer queries

Issue # 1744
Migration pr of #73

@johanseto johanseto left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I didnt understood the last column order of the results in the PR review. However with the other 2 is ok. Seems working.

Image

@andrey-canon
andrey-canon changed the base branch from and/FUTUREX-1331 to open-release/verawood.nelp August 4, 2026 16:55
…lations

Replaced inefficient SQL OFFSET pagination with ID-based keyset pagination to ensure consistent lookup performance, and updated ordering to leverage the primary key index. Resolved an N+1 query issue by eagerly loading user data via `.select_related('user')` and optimized memory footprint using `.values_list()`.

These changes reduce execution time by ~25x and eliminate 100 redundant queries per batch during high-enrollment course processing.
@andrey-canon
andrey-canon merged commit ac3a696 into open-release/verawood.nelp Aug 5, 2026
40 checks passed
@andrey-canon
andrey-canon temporarily deployed to open-release/verawood.nelp August 5, 2026 20:43 — with GitHub Actions Inactive
@andrey-canon
andrey-canon temporarily deployed to open-release/verawood.nelp August 5, 2026 20:43 — with GitHub Actions Inactive

This branch was previously deployed

1 inactive deployment
open-release/verawood.nelp — 5dc09780 Deployed Aug 5, 2026 by andrey-canon via create-jira-issue / create_jira_issue #27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants