Skip to content

Isolate per-file extraction failures in canvas and edX content syncs - #3752

Closed
mbertrand wants to merge 12 commits into
mainfrom
mattbert/canvas-per-file-fault-isolation
Closed

Isolate per-file extraction failures in canvas and edX content syncs#3752
mbertrand wants to merge 12 commits into
mainfrom
mattbert/canvas-per-file-fault-isolation

Conversation

@mbertrand

@mbertrand mbertrand commented Aug 11, 2026

Copy link
Copy Markdown
Member

What are the relevant tickets?

Closes #3750

Description (What does it do?)

One PDF that crashes the OCR converter currently aborts an entire canvas course sync before any contentfiles load — course 33842-7.06 is stuck at 0 contentfiles on production because a single answer-key PDF crashes opendataloader (RasterFormatException). The same per-file loop (process_olx_path) serves edX archive ingestion and canvas tutor problem files, so any extractor exception kills those syncs the same way.

This branch makes a failing file cost exactly that one file:

  • An OCR crash falls back to tika (logged at warning — the fallback succeeded, so no Sentry error).
  • Any remaining per-file exception — including pypdf-invalid/encrypted PDFs, which previously took a silent drop path — is logged to Sentry with the source path and run id, recorded, and skipped.
  • Skipped files are excluded from every "not seen this run" cleanup, so their existing records survive published with their previous content: canvas's hard-delete set, load_content_files' staleness/unpublish pass (canvas and edX), and the tutor problem orphan delete. Files that fail are simply absent this run — no empty placeholder rows.
  • Checksum gating: a partial failure still stamps run.checksum, so a deterministic crash isn't re-downloaded and re-OCR'd weekly (failed files retry when the archive next changes, or via --overwrite). A total failure does not stamp, so the course retries next sync instead of looking legitimately empty. Same on edX: an all-failed archive is no longer mistaken for an empty one.

No new database queries — the exclusions chain onto existing lazy querysets, and with no failures the generated SQL is identical to main.

How can this be tested?

Use the course from the issue — its current archive contains the PDF that crashes the converter, so it exercises the real failure path. With env pointed at the canvas course bucket (CANVAS_COURSE_BUCKET_NAME etc.) and OCR enabled:

  1. On main, run ./manage.py backpopulate_canvas_courses --canvas-ids 33842 and watch it die on 7.06_Fall2025_Exam1_answers.pdf (FileNotFoundError for the converter output JSON) with the course left at 0 contentfiles.
  2. On this branch, run the same command. Expect the sync to complete: an OCR extraction failed ... falling back to tika warning for the answer-key PDF(s), the course going from 0 to ~158 contentfiles (the fallback text may be sparse), and run.checksum set.
  3. Run it a second time without --overwrite — it should log Checksums match ... skipping load instead of re-downloading and re-extracting.
  4. Retention check: pick a course with existing contentfiles (e.g. 39194 from the same incident) and sync it — no existing contentfiles should be deleted or flipped to published=False because of extraction failures.

mbertrand and others added 11 commits August 10, 2026 16:24
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d archives empty

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
load_content_files no-ops for non-course resources, so the randomized
factory type made the test order-dependent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

content_files_loaded_actions fires search indexing gated by randomized
resource fields; when the draw enables it, the eager task hits the
nonexistent test opensearch and the resulting Retry is swallowed by
process_course_archive's bare except, leaving the checksum unstamped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbertrand mbertrand closed this Aug 11, 2026
@mbertrand
mbertrand deleted the mattbert/canvas-per-file-fault-isolation branch August 11, 2026 11:04
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.

Canvas ingest: one bad PDF crashes the OCR converter and kills the whole course sync

1 participant