fix(ecstore): handle free-version migration races

This commit is contained in:
overtrue
2026-08-23 10:52:13 +08:00
parent dfbe165882
commit 3be5efab67
4 changed files with 108 additions and 34 deletions
@@ -198,6 +198,9 @@ boundary.
The source record is physically removed only after the target write quorum has
committed and the source cleanup preflight still matches the exact inventory.
If the lifecycle worker has already completed the remote delete and removed the
source record before decommission acquires the source lock, decommission records
that identity as already consumed and treats the missing source record as safe.
If target capacity, metadata validation, lock fencing, or quorum fails, the
source record remains and the entry records `state = "free_version_retained"`
with reason `tier_free_version_migration_failed`; the worker retries the
@@ -218,13 +221,15 @@ migrated unchanged rather than discarded: the lifecycle worker retains them if
remote identity validation cannot make a delete request.
Each migrated record emits `state = "free_version_migrated"` with reason
`tier_free_version_migrated`. Each failed record emits the retained state and
failure reason above. The entry also emits a disposition summary with migrated,
retained, and total counts. The final decommission sweep uses the exact loader,
counts free records still present, and emits one retained record/reason for each
unresolved free version before failing the sweep. This makes both successful
migration and retained cleanup obligations visible instead of silently omitting
free records.
`tier_free_version_migrated`. A record consumed before migration emits
`state = "free_version_consumed"` with reason
`tier_free_version_already_consumed`. Each failed record emits the retained state
and failure reason above. The entry also emits a disposition summary with
migrated, consumed, retained, and total counts. The final decommission sweep uses
the exact loader, counts free records still present, and emits one retained
record/reason for each unresolved free version before failing the sweep. This
makes successful migration, completed cleanup, and retained cleanup obligations
visible instead of silently omitting free records.
No new S3-visible version or admin response field is needed: free versions remain
internal and are never counted as user-visible versions. The structured