Skip to content

LDP write path: If-Match conditional write is check-then-write, not atomic — concurrent writers can silently lose a write #600

Description

@melvincarvalho

What happens

The host honors If-Match / If-None-Match correctly in the sequential case (stale If-Match PUT/DELETE → 412; If-None-Match: * on an existing resource → 412; If-None-Match GET → 304). But the check is check-then-write, not atomic: two concurrent PUTs carrying the same currently-valid If-Match ETag both return 2xx, and the loser is silently overwritten.

Both callers are told success; one write is gone. The same two PUTs run sequentially 412 correctly — this is purely a race between the precondition check and the write.

Measured

Witnessed live in a test (two overlapping conditional PUTs → 204/204, one body lost; sequential → 204/412 as expected):

Why it matters

Any client using ETags for optimistic concurrency (remoteStorage clients, DAV clients, the sparql UPDATE bridge, anything doing GET→transform→PUT-If-Match) can lose writes under load while believing the protocol protected it. This is the one of the plugin experiment's findings with real data-loss consequences, and it is unfixable from outside the host.

Suggested fix

Make the precondition check and the write atomic on the LDP write path — e.g. take a per-path lock (or serialize via a per-path promise queue) around stat→compare→write, or write-to-temp + atomic rename with the ETag re-checked under the lock.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcachingHTTP caching, ETags, Cache-Controlpriority: highP1 - Do soon, significant improvement

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions