* fix(tiering): make rejected upload cleanup durable
* fix(tiering): close transition upload cancellation gap
* test(tiering): cover failed upload without candidate
* test(tiering): synchronize cancelled cleanup recovery
* test(tiering): stabilize cancelled cleanup recovery
Prefer cancellation when the tier delete journal recovery worker is racing an immediate tick, and build the cancelled-cleanup regression store with an already-cancelled token so production recovery cannot consume the test journal.
Co-Authored-By: heihutu <heihutu@gmail.com>
---------
Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: heihutu <heihutu@gmail.com>
An object transitioned to an unversioned remote tier legally records an
empty remote version_id (per CLAUDE.md: a tier version of `None`/`""`
means the tier bucket is unversioned, so remote GET/DELETE must omit the
versionId). Two recovery paths wrongly treated that empty sentinel as an
incomplete/unrecoverable record while the persist/encode and worker
paths accept it, producing permanent leaks in the crash-recovery window.
- tier_delete_journal::into_jentry rejected entries with an empty
version_id as "incomplete", so unversioned-tier WAL entries could never
be decoded during recovery: the remote object was orphaned and the
journal file leaked forever. Drop the version_id emptiness check; keep
the obj_name/tier_name checks. Truncated payloads are still rejected at
JSON deserialization (all fields are required, non-Option, no default,
with deny_unknown_fields).
- tier_free_version_recovery::is_recoverable_tier_free_version required a
non-empty version_id, silently filtering out free versions of
unversioned-tier objects so a first enqueue failure leaked the remote
object and local free version permanently. Drop the version_id check;
keep free_version + name + tier checks.
Both downstream paths already issue a versionless remote delete for an
empty version_id, so no further changes are needed. Adds regression
tests covering the empty-version_id case and the preserved guards.
Co-authored-by: heihutu <heihutu@gmail.com>