Skip to content

id(web): PR #1696's upload-name uniquifier is check-then-act — concurrent same-name uploads still silently un-name the first #2363

Closed

Description

@dezren39

id(web): PR #1696's upload-name uniquifier is check-then-act — concurrent same-name uploads still silently un-name the first

Finding

PR #1696 (merged) fixed #1549 for the sequential case: upload_handler resolves a collision-free stored name with uniquify_upload_name (pkgs/id/src/web/routes.rs:1473 on current main 5a34fa70) and only afterwards sets the blob tag under that name (routes.rs:1616). The check and the claim are two separate awaited store operations with nothing serialising them — no lock in the handler or in AppState, and iroh-blobs tags().set is an unconditional write, not a create-if-absent.

Two uploads of the same filename with different bytes that are in flight at the same time can therefore interleave as: both call upload_name_owner (routes.rs:1455) and see the intended name free → both are handed the intended name → both tags().set it, the second overwriting the first. The first image loses its name exactly as in #1549 (its response even reported that name as its stored name and URL), and the filename-keyed metadata below the tag write is then set-if-absent under the same contested name, so the surviving name's metadata can describe the losing upload. The PR's tests exercise strictly sequential uploads, so the interleaving is untested.

Evidence

Why it matters

Pasting images is the upload path's normal use, and two pastes/uploads racing (two tabs, two collaborators on a shared node, a retry overlapping the original) is ordinary, not adversarial. When it happens, the failure is the precise silent data loss #1549 was filed to eliminate — one image quietly un-named while its uploader was told the name was theirs — just narrowed to the concurrent window the fix left open.

Acceptance criteria

  • Name resolution and tag claiming are atomic in effect: two concurrent uploads of the same filename with different bytes both end up listed and resolvable under distinct stored names (e.g. the resolve+set sequence is serialised, or the claim is retried/re-resolved when the tag changed underneath), and each response reports the name that actually names its blob.
  • A regression test drives two same-name different-bytes uploads concurrently (interleaved at the resolve/set boundary) and pins that neither un-names the other; the existing sequential id(web): image upload stores by filename with no collision handling — a second paste named 'image.png' silently un-names the first #1549 tests stay green, including the idempotent identical-bytes re-upload.
  • Filename-keyed metadata for a raced name cannot mix the two uploads (the surviving name's created/content-type records describe the blob the name resolves to).

Agent: pr-diff-review-b
State: filed
Source PR: #1696

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. 9 remaining items

  6. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — queue task 5250 (lane C) — picked up for implementation (resume)

    Branch queue/5250-2363-upload-name-atomic already exists at main 0f4731ea0 with no fix commits yet — resuming from stage branch_created. Plan: make upload-name resolve+claim atomic in pkgs/id/src/web/routes.rs (AppState upload-name mutex across uniquify+set, or claim-and-verify), add the interleaved concurrent same-name regression test, keep sequential #1549 semantics unchanged.

  7. added a commit that references this issue on Nov 1, 2026
  8. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — queue task 5250 (lane C) — done: fixed and merged

    PR #3425 merged as 7d0c1a82595d507db3181d67d1df99d273b3c5a4 (head 23299301d).

    • Fix: AppState.upload_name_lock (Arc<Mutex<()>>) held in upload_handler across uniquify-resolve, the tags().set claim, and all filename-keyed metadata writes — resolve+claim is now atomic for handlers sharing one AppState. Sequential id(web): image upload stores by filename with no collision handling — a second paste named 'image.png' silently un-names the first #1549 semantics unchanged.
    • Regression test concurrent_uploads_keep_names_and_metadata_consistent interleaves two same-name different-bytes uploads at the resolve/set boundary and asserts distinct stored names, per-blob responses, listing/resolvability, and metadata consistency.
    • Verification: laptop cargo test --features web --lib upload_router_tests — 13 passed, 0 failed (first attempt failed only on the fresh worktree missing the gitignored web/dist RustEmbed folder; copied from the primary clone and rerun green). Codex review on 23299301d: completed, no findings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/pr-diff-reviewFiled by the pr-diff-review jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions