# Concurrency, etags and caching

A site has more than one editor — a person in the admin, your integration, a scheduled
job — and the default outcome when two of them save the same page is that the later write
wins and nobody is told. The `etag` every resource carries is how you opt out of that.

Read it with the resource, send it back in `If-Match`, and a write that lost the race is
refused instead of applied:

```sh title="quickstart.sh"
etag=$(jq -r '.etag' <<<"$page")

curl_json "${auth[@]}" -X PATCH "$site/cms_pages/$page_id" \
  -H "If-Match: $etag" \
  -d '{"meta_description": "Created from the quickstart."}' >/dev/null

# That write minted a new etag, so the one above is now stale. Sending it again is what a
# second editor's Save looks like: 409 ABORTED, with the current resource in the body.
stale=$(curl_json "${auth[@]}" -X PATCH "$site/cms_pages/$page_id" \
  -H "If-Match: $etag" -d '{"title": "Hello again"}' || true)

jq '.error | {code, status, message}' <<<"$stale"
```

The refusal is a `409` with `ABORTED` and **the current resource in the body**, so a client
has everything it needs to show the conflict — or to merge and retry — without a second
request. Without `If-Match` the write is unconditional, which is fine for a field only you
own and wrong for anything a person also edits.

The same value works on the way in: a `GET` with `If-None-Match` answers `304` when
nothing changed, so a poller pays for headers rather than for a body.

## 14. Concurrency and etags

- A resource with concurrent editors MUST expose an `etag`, computed over its
  persisted state.
- A client MAY send `If-Match: "<etag>"`. If the stored etag differs, the write
  fails `ABORTED` (409) and the response body carries the current resource.
- A resource MAY require `If-Match` on Update and Delete. It MUST document
  which.
- `etag` MAY also be echoed as an HTTP `ETag` header. The field is the contract;
  the header is a convenience.

## 16. Caching and conditional reads

- A `GET` with `If-None-Match` matching MUST return `304` with no body.
- Mutating responses are `Cache-Control: no-store`.
