Skip to content

data: downloading a tenant export whose archive is no longer stored answers 410 and expires it (#8590) - #8591

Merged
iliyan-velichkov merged 1 commit into
masterfrom
issue-8590-missing-export-archive-410
Nov 12, 2026
Merged

iliyan-velichkov merged 1 commit into
masterfrom
issue-8590-missing-export-archive-410

Conversation

@iliyan-velichkov

Copy link
Copy Markdown
Contributor

Cause

A tenant export's row lives in the database and its archive in the CMS, and the two can part (the storage of a replaced instance, a restore from an older backup, a file removed by hand). The row was still listed as COMPLETED and its download answered 500: TenantExportArchives.open looked the archive up with getObjectByPath, which every CMS backend (internal, S3, SharePoint) answers for a missing path with the same plain IOException it throws for a storage it cannot read. There was no way to tell the two apart at that call.

Change

  • TenantExportArchives.open now walks the folders with getChildren(), as the class already does for its other lookups (deleteExportFolder, pack). It answers Optional.empty() when there is no archive at the path, and a CMS that cannot be read still throws IOException.
  • New TenantExportService.openArchive(export): when the archive is missing, it sets the export's expiresAt to now (its own transaction) and answers 410 "The export's archive is no longer available". The list then stops offering the export as downloadable, and the hourly housekeeping removes it on its next pass. A real read failure stays 500.
  • TenantExportEndpoint.archive opens the archive through the service before it sets any status or header, so a 410 never follows a started download. Its unused TenantExportArchives dependency is gone.
  • DELETE /{id} of such an export was already 204, because deleteExportFolder treats finding nothing as success. The new IT pins this.
  • data-processes takes chemistry-opencmis-commons-api in test scope, the same as engine-document: CmisFolder names an OpenCMIS enum, so mocking it needs the class.
  • .claude/docs/tenants.md (Expiry bullet) records the rule.

Verification

  • TenantExport*Test: 29 green. That includes three new service cases (archive present; archive missing → 410 + expiresAt = clock now, then expire() removes the row; read failure → IOException and no expiry) and three new archive cases (present; missing folder, missing file, or a folder of the archive's name → empty; storage failure → IOException). The full data-processes unit suite is green.
  • TenantExportIT: 7 green, including the new anExportWhoseArchiveIsNoLongerStoredIsGoneAndCanBeDeleted. It completes an export, deletes its archive from the tenant's CMS, then checks that GET .../archive is 410 with the reason, that expiresAt is not in the future, that DELETE is 204 and that the export is then 404. The log shows the WARN and no stack trace.
  • mvn -T 1C formatter:validate with the formatter cache wiped: green.
  • Release-profile javadoc build of data-processes: green.
  • Not run: the PostgreSQL leg and the full IT suite.

Docs: dirigible-io/dirigible-io.github.io#289 (the missing archive under 410 of "Download the archive").

Fixes #8590

🤖 Generated with Claude Code

…nswers 410 and expires it (#8590)

The export's row lives in the database and its archive in the CMS, and the
two can part: the storage of a replaced instance, a restore from an older
backup, a file removed by hand. The row was then still listed as ready and
its download answered 500, because TenantExportArchives.open looked the
archive up with getObjectByPath, which every CMS (internal, S3, SharePoint)
answers for a missing path with the same plain IOException as for a storage
it cannot read.

TenantExportArchives.open now walks the folders, as the class already does
for its other lookups, and answers empty when there is no archive at the
path, while a CMS that cannot be read still throws. The new
TenantExportService.openArchive turns an empty answer into 410 "The export's
archive is no longer available" and sets the export's expiresAt to now, so
the list stops offering it and the hourly housekeeping removes it. The
endpoint opens the archive through it before it sets any status or header.
Deleting such an export was already 204: deleteExportFolder treats finding
nothing as success.

Verified: TenantExport*Test (29, three new service cases and three new
archive cases - present, missing folder/file, storage failure) and the
data-processes unit suite; TenantExportIT (7, including the new case that
deletes a completed export's archive from the CMS and asserts 410, an
expiresAt not in the future, then DELETE 204); formatter:validate with the
cache wiped; release-profile javadoc of data-processes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iliyan-velichkov
iliyan-velichkov merged commit c54a2ba into master Nov 12, 2026
16 checks passed
@iliyan-velichkov
iliyan-velichkov deleted the issue-8590-missing-export-archive-410 branch November 12, 2026 13:39
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.

Tenant data export: downloading an export whose archive is missing answers 500 instead of 410

1 participant