Skip to content

add_object can write a duplicate copy when the existing copy is on a sleeping disk #76

Open

Description

@ctengel

Split out from the #72 sleeping-disk timeout work. This is the write-path correctness analogue of #75 (#72 = read 503, #75 = read false-404, this = write false-"doesn't-exist").

Problem

Before choosing a write target, add_object (PUT /{bucket}/{key} on the locator) fans out a HEAD to every server to confirm the key doesn't already exist (locator_api.py:97):

async def check_exists(server):
    try:
        result = await client.head(server + object_path, timeout=1)
        return server, result.status_code
    except httpx.HTTPError:
        return server, None
...
for server, status in exist_results:
    if status is None:
        candidates.pop(server, None)
    elif status != 404:
        raise HTTPException(status_code=409)

A sleeping HDD takes ~15s to spin up (per #72, "even 15 seconds is normal"), so the timeout=1 HEAD against it raises and returns status is None. The code treats that as "no answer, drop this server from the write candidates" and proceeds — it never learns the object already exists there.

If the only existing copy of that key is on the sleeping server, the locator picks a different server and writes the object there. The result is two copies of a key the system treats as unique and immutable — objects are O_CREAT | O_EXCL, never overwritten, and the whole design assumes a key resolves to one logical object. The duplicate is silent (no 409), and the two copies can later diverge in how readers/replication see them.

This is a correctness symptom (a present object reported absent, leading to a duplicate write), distinct from the availability symptoms in #72.

Notes / options

  • The intended 409-on-conflict path only fires for an explicit non-404 status. A timeout currently can't yield 409 because we can't tell "absent" from "asleep" in 1s.
  • Lengthening the HEAD timeout to tolerate spin-up (~16s) would let the existence check actually see the sleeping copy, at the cost of slower PUTs when a disk is cold.
  • Alternatively, distinguish "timed out / unreachable" from "confirmed 404": if any server's existence check is merely unreachable (not a confirmed 404), refuse to write (e.g. 503 + Retry-After) rather than risk a duplicate — fail safe instead of duplicating.
  • Metadata caching / a "disk asleep" health signal would let the locator answer without waking the disk (see locator API gives 503 if even one obj server takes 2 seconds #72 discussion, Locator performance enhancement on GET #1, Locator periodically read whole list of files #3).

Related

Activity

  1. added this to the Mid-term milestone on May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions