We’d like to use PostHog to see how this site gets used, and Spotify to play the team’s funeral songs. Both set cookies, so they stay off until you allow them; everything else works without them. Privacy notice

Skip to content
death.tech home
All changes

Make optimistic locking work: ETags follow the record version

b64db27fix(customers)1 review thread

Illustrative: based on a real change, edited for clarity and privacy.

src/adapters/primary/fastapi/routes/address/get.py+5 −2

Changes to src/adapters/primary/fastapi/routes/address/get.py. Lines with review comments are numbered.
@@
33from fastapi import APIRouter, Header, Response, status
44
55from src.adapters.primary.fastapi.etag import (
6removed generate_etag,
6added stored_etag,
77 validate_if_none_match,
88)
99from src.adapters.primary.fastapi.models.address_attribute import (
@@ async def _get_attribute(
126128 attr = self._fetch_attribute(customer_ref, attribute_ref)
127129
128130 # Handle conditional GET with If-None-Match
129removed current_etag = generate_etag(attr.updated_at, attr.ref)
131added # The ETag is the row version, the same number If-Match is checked against
132added # on writes, so a read and a later write can never disagree about staleness.
1 line 133, review thread 1added current_etag = stored_etag(attr.version, attr.ref)
130134 if if_none_match is not None and validate_if_none_match(
131135 if_none_match, current_etag
132136 ):
@@ async def _get_attribute(
137141 )
138142
139143 # Build full response
144added response.headers["ETag"] = current_etag
140145 return AddressAttributeResponse.from_dto(attr)
141146
142147 async def _head_attribute(
    1. AI reviewP1

      Relationship changes keep old ETags

      A PATCH that only changes an address label or the primary flag updates the relationship, not the attribute, but this ETag only uses the attribute’s version. A client can get a 304 for stale data, or make a later write with an old If-Match that still passes.

    2. author

      Fixed: a link change now marks the attribute as updated, so changing the label, primary flag or grouping moves its version and ETag. A new test covers a stale If-Match after a label-only change, which now returns 412.

    3. reviewer

      Nice. Can the 412 go in the API docs too, so client teams know to refetch and retry?

    4. author

      Done: the endpoint docs now describe the 412 and what to do about it.