Repository navigation
fix(instructor): bulk-enroll auto-async, chunking, duplicate guards - #100
Merged
Merged
Conversation
…-submit guards Instructor-dashboard batch enrollment broke as the learner count grew: - sync + "notify by email" costs ~2 s/learner, so ~60 learners exceed the proxy's 125 s; the browser errors, the instructor re-clicks, and the same learners are mailed again (up to 8x observed); - the async path refused task_input > 10,000 chars with an uncaught AttributeError -> HTTP 500. Changes (each divergence marked `# NELC:` for the next upgrade re-port): - TASK_INPUT_LENGTH 10000 -> 60000 (task_input is a 65,535-byte TEXT column; no migration). Over-limit raises TaskInputTooLongError (an AttributeError subclass) which the view turns into a 400 JSON message; nothing truncated. - StudentsUpdateEnrollmentView: a batch above BATCH_ENROLLMENT_SYNC_MAX_NOTIFY (30, email on) / BATCH_ENROLLMENT_SYNC_MAX (100, email off) switches to async and says so in the JSON (auto_switched_to_async, sync_limit); a batch larger than one task input is split into several tasks (task_ids). Only the dashboard POST is guarded; the bulk_enroll REST API keeps its contract. - Sync path takes a cache.add lock per (course, action, identifiers) held for the request (BATCH_ENROLLMENT_LOCK_TIMEOUT_SECONDS=900, released in finally); a second identical request gets 409. - A learner whose latest ManualEnrollmentAudit row (same course) already reached the requested state within BATCH_ENROLLMENT_EMAIL_DEDUPE_SECONDS (900) is not mailed again; enrollment itself still runs. - membership.js disables the enroll/unenroll buttons while a request is pending, shows the server's 400/409 message, and explains an automatic switch to background processing / chunking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…static-assets-check The runner's pip 26 builds the loremipsum sdist against setuptools>=82, which removed pkg_resources, so 'Install Full Python Deps for Collection' fails whenever the pip cache misses. Install requirements/pip.txt first, as the other workflows already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
Why
Bulk enrollment from the instructor dashboard (Membership → Batch Enrollment) fails in prod in three measured ways (2026-09-06→10-06):
task_input> 10,000 chars (~330–380 emails) with an uncaught 500 "Task was not created".What (each divergence marked
# NELC:for the Dec-2026 re-port)instructor_task/models.py:TASK_INPUT_LENGTH10,000 → 60,000 (task_inputis a TEXT column, no migration). Over-limit raisesTaskInputTooLongError(subclass ofAttributeError, so existing callers keep working).instructor_task/api.py:split_enrollment_identifierschunks a batch so each task fits; raises instead of truncating.instructor/views/api.py(StudentsUpdateEnrollmentView, dashboard POST only):BATCH_ENROLLMENT_SYNC_MAX_NOTIFY(30, notify on) /BATCH_ENROLLMENT_SYNC_MAX(100, notify off); response carriesauto_switched_to_async,sync_limit;task_ids);cache.addlock on (course, action, md5 of sorted identifiers) → 409 if already running, released infinally(BATCH_ENROLLMENT_LOCK_TIMEOUT_SECONDS=900).instructor/utils.py: skip re-sending the notify email when the learner's latest audit row for this course already reached the requested state withinBATCH_ENROLLMENT_EMAIL_DEDUPE_SECONDS(900); result row getsemail_skipped: true.membership.js: buttons disabled while a request is pending; server 400/409 messages shown; auto-switch/split explained to the instructor./bulk_enrollREST API unchanged (guarded by aguard_ui_requestflag) so its response contract does not break.Testing
Container (
overhangio/openedx:20.0.5+ this fork, sqlite + mongo): baseline 348 passed → 378 passed in the touched modules (30 new tests); 856 passed acrossinstructor,instructor_task,bulk_enroll. pylint/pycodestyle clean on changed files;node --checkon the JS.Known limits / to verify on stage
Related: nelc/eox-nelp
feat/pooled-smtp-email-backend(email speed), nelc/nassaufeat/pooled-smtp-email-backend-stage.🤖 Generated with Claude Code