pub async fn get_reapable(
conn: &mut PgConnection,
) -> ModelResult<Vec<ReapableUpload>>Expand description
Answer uploads past their origin’s retention window that no submission was ever made from,
oldest first, at most REAP_BATCH_LIMIT of them.
FROM exercise_answer_uploads is the safety property of this whole feature, not an
optimisation: file_uploads also holds CMS media, organization images and certificates, none
of which are bound here, so the host cannot tell whether one is still needed. Widening this
query to file_uploads would silently destroy course media. Never do it.
Files referenced only from a spec blob (e.g. ones a teacher attaches in the CMS editor) must be
uploaded through the POST /api/v0/files/{exercise_service_slug} route instead: the host never
inspects spec contents, so such a file never gets an exercise_task_submission_files row and a
binding here would have it reaped a week later. Their lifecycle is
crate::exercise_spec_uploads’, which reclaims them against declarations rather than
submissions.
Progress is tracked by file_uploads.deleted_at, not by the binding’s: the binding is retired
first and the object removed afterwards, so a row whose object delete failed still has a live
file_uploads row and comes back on the next run. Filtering on u.deleted_at IS NULL instead
would make every transient object-store error orphan its object permanently.