Skip to content

Clean up cancelled assets - #1314

Merged
JackLewis-digirati merged 2 commits into
developfrom
fix/cleanupSynchronousIngests
Sep 24, 2026
Merged

JackLewis-digirati merged 2 commits into
developfrom
fix/cleanupSynchronousIngests

Conversation

@JackLewis-digirati

@JackLewis-digirati JackLewis-digirati commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

What does this change?

Resolves #1287

Makes it so that if an ingest is cancelled, it will be cleaned up in the attached storage, and makes sure that the database records the failure.

@JackLewis-digirati
JackLewis-digirati requested a review from a team as a code owner September 24, 2026 10:16
@JackLewis-digirati

Copy link
Copy Markdown
Collaborator Author

What this PR does

Fixes #1287. When the API cancels a synchronous ingest (POST /asset-ingest) partway through, Engine left files behind on the scratch disk and didn't record the outcome in the DB.

1. Partial downloads left on scratch disk

For "file" channel assets (e.g. the PDFs in the issue), AssetToS3.IndirectAssetCopyBucketToBucket downloads the origin to disk, uploads it to S3, then deletes the local copy in a finally. The file path was only recorded after the download completed, so:

  • Cancelled mid-download: FileSaver.SaveResponseToDisk threw partway through writing and the partial file was orphaned.
  • Asset exceeds storage allowance: the early return happened before the path was recorded, so the whole download was left behind.

Changes:

  • FileSaver.SaveResponseToDisk now deletes the partially written file if writing fails (including cancellation), then re-throws. This is best-effort: a failed delete logs a warning and never replaces the original exception.
  • AssetToS3 records the downloaded file path before the allowance check, so the finally always cleans it up.

Deleting the whole per-asset download folder was avoided because adjunct downloads are nested under it (…/{image}/adjuncts/{id}), so it could remove files belonging to a concurrent adjunct ingest.

Image ingests weren't affected because ImageIngestPostProcessing already deletes the working folder regardless of outcome.

2. Error finalising item <id> in DB

IngestExecutor passed the request's (now cancelled) token into the final DB write, so UpdateIngestedDeliverable failed immediately. The item was left in an "ingesting" state with no error recorded.

Changes in IngestExecutor:

  • CompleteAssetInDatabase / CompleteAdjunctInDatabase always use CancellationToken.None, so the outcome is recorded even if the caller has gone away.
  • AdjustAdjunctStoredSize also uses CancellationToken.None. Otherwise an adjunct could be finalised without its size delta being applied, and the customer's storage totals would drift.
  • The storage pre-checks that run before the workers (GetStorageMetrics, GetImageSize) also use CancellationToken.None. Cancelling during these would otherwise throw before the DB write, with the same "stuck ingesting" result.

Only the workers now honour cancellation. They already catch their own exceptions and record them as a failure on the asset/adjunct.

Tests

  • AssetToS3Tests: the local file is deleted when the download exceeds the allowance and when the S3 upload throws.
  • IngestExecutorTests: for assets and adjuncts, cancelling either during a worker or before the workers run still results in the DB write (and adjunct size adjustment) using CancellationToken.None.

🤖 Generated with Claude Code

@JackLewis-digirati
JackLewis-digirati merged commit 395fecf into develop Sep 24, 2026
8 checks passed
@JackLewis-digirati
JackLewis-digirati deleted the fix/cleanupSynchronousIngests branch September 24, 2026 10:54
@JackLewis-digirati

Copy link
Copy Markdown
Collaborator Author

Note

Merged without review due to annual leave. Reviewed by Claude

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

synchronous ingests don't get cleaned up correctly when cancelled

1 participant