main
tag: launch-2026-06-01
tag: magicmirror-v0.1.1
tag: magicmirror-v0.3.0
tag: mnw-cli-v0.1.2
tag: mnw-cli-v0.1.3
tag: mnw-cli-v0.1.4
tag: pom-v0.4.1
tag: pom-v0.4.2
tag: pom-v0.4.3
tag: pom-v0.4.4
tag: pom-v0.4.5
tag: wam-v0.3.0
tag: wam-v0.3.1
Files
Commits
Tags
Notes
Issues
server: route failed/lost-race upload confirms through the S3 orphan queue
confirm_upload / version_confirm_upload deleted the S3 object directly when the
DB tx rolled back or lost the CAS race. A concurrent double-confirm of the same
key could have committed it onto the live row first, so a blind delete would
destroy the object the winning confirm points at (404ing every fan download).
Enqueue to the orphan queue instead, whose is_s3_key_live check skips any key a
row still references.
Outstanding working-tree changes committed as-is (not authored this session).
Co-Authored-By
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 files changed,
+20 insertions,
-4 deletions
270
270
271
271
match tx_result {
272
272
Err(e) => {
273
-
// tx rolled back — storage counter unchanged. Just clean up S3.
274
-
s3.delete_object(&req.s3_key).await.ok();
273
+
// tx rolled back — storage counter unchanged. A concurrent
274
+
// double-confirm of this same key could have committed it onto the
275
+
// item row before our `try_apply_storage_on` errored (storage cap
276
+
// filling in between), so a blind delete could destroy the live
277
+
// object the winner points at. Route through the orphan queue; its
278
+
// `is_s3_key_live` check skips any key a row still references.
279
+
super::enqueue_s3_orphan(&state.db, &req.s3_key, "item_confirm_failed").await;
275
280
return Err(e);
276
281
}
277
282
Ok(0) => {
213
213
214
214
match committed {
215
215
Err(e) => {
216
-
s3.delete_object(&req.s3_key).await.ok();
216
+
// The tx rolled back, so nothing this request wrote references the
217
+
// key — but a concurrent double-confirm of this same key could have
218
+
// committed it onto the row before our `try_apply_storage_on`
219
+
// errored (e.g. the storage cap filled in between). A blind delete
220
+
// would then destroy the live object the winning confirm points at.
221
+
// Route through the orphan queue, whose `is_s3_key_live` check skips
222
+
// any key a row still references.
223
+
super::enqueue_s3_orphan(&state.db, &req.s3_key, "version_confirm_failed").await;
217
224
return Err(e);
218
225
}
219
226
Ok(false) => {
220
-
s3.delete_object(&req.s3_key).await.ok();
227
+
// Lost the CAS race: a concurrent confirm swapped the version's
228
+
// s3_key out from under us. If it committed THIS key, a direct
229
+
// delete would 404 every fan download of the version the winner
230
+
// just published. The orphan queue's liveness check is the guard.
231
+
super::enqueue_s3_orphan(&state.db, &req.s3_key, "version_confirm_lost_race").await;
221
232
return Err(AppError::BadRequest(
222
233
"Version was modified concurrently. Please try uploading again.".to_string(),
223
234
));