Repository navigation
fix(api): retry expired cleanup and enforce URL timeout during DNS - #32
Merged
AndreyBegma merged 3 commits intoOct 21, 2026
Merged
AndreyBegma merged 3 commits into
AndreyBegma merged 3 commits into
Conversation
One persistently failing unlink no longer stops the pass: the failure is logged, the row stays for the next pass, later batches still advance, and the removed count reflects what was actually deleted. The loop stops once a batch consists only of failures.
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.
Summary
Two API reliability fixes discussed with Andriy on Discord, based on develop
494ba189629cde828f3b3d5dab278589f3f39246.Keep failed expiration cleanup retryable
Previously, cleanup deleted database rows before unlinking files. A transient
unlink failure therefore left bytes on disk without an expired row for the
next cron pass to retry.
Wait for all unlink outcomes, delete rows only for successful removals, and
retain failed removals for a later pass. Partial success is committed before
the failure is propagated: a single persistent failure must not retain the
entire oldest 500-row batch.
Regression coverage uses the real temporary-file storage with the in-memory
repository: transient unlink failure, database deletion failure after unlink,
and 501 expired files with one persistent unlink failure. The latter confirms
that a subsequent pass reaches the next batch while retaining the failed row.
Apply the URL-upload deadline while DNS is pending
UrlFetcher.open()previously awaited DNS without observing the caller'sAbortSignal, so the existing 30-second timer could fire while the request
continued waiting for DNS.
Pass the signal through resolution and stop waiting when it aborts. Preserve
the abort error for the existing upload handler's 408 mapping. Address checks,
connection pinning and redirect validation remain in place. Both late DNS
fulfilment and rejection have handlers; an already-aborted operation starts
neither DNS nor HTTP.
Local verification
scripts disabled during the scoped dependency installation.
using a local placeholder URL; no database connection was needed.
--runInBand).tsc -p apps/api/tsconfig.json --noEmitpassed.git diff --checkpassed.the same dependencies. A further 501-file counterexample caught batch
starvation in the initial candidate and passes with this revision.
Scope and limits
new tests are injected doubles; cleanup uses disposable local files.
OS DNS lookup itself.
does not add pagination around entirely failing batches.
locally. The existing ts-jest warning about shared compiled JS is unchanged.
Implementation and regression tests were prepared with Codex assistance.