api: Etag is a header now

This commit is contained in:
Matthew Holt 2024-09-06 07:07:46 -06:00
parent 335e086ae0
commit 4eaa500ee2

View file

@ -258,13 +258,13 @@ Caddy's config API provides [ACID guarantees <img src="/old/resources/images/ext
For example, two clients may `GET /config/foo` at the same time, make an edit within that scope (config path), then call `POST|PUT|PATCH|DELETE /config/foo/...` at the same time to apply their changes, resulting in a collision: either one will overwrite the other, or the second might leave the config in an unintended state since it was applied to a different version of the config than it was prepared against. This is because the changes are not aware of each other.
Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the `Etag` trailer and `If-Match` header to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's `/config/...` endpoints concurrently without synchronization. All responses to `GET /config/...` requests have an HTTP trailer called `Etag` that contains the path and a hash of the contents in that scope (e.g. `Etag: "/config/apps/http/servers 65760b8e"`). Simply set the `If-Match` header on a mutative request to that of an Etag trailer from a previous `GET` request.
Caddy's API does not support transactions spanning multiple requests, and HTTP is a stateless protocol. However, you can use the `Etag` and `If-Match` headers to detect and prevent collisions for any and all changes as a kind of optimistic concurrency control. This is useful if there is any chance that you are using Caddy's `/config/...` endpoints concurrently without synchronization. All responses to `GET /config/...` requests have an HTTP header called `Etag` that contains the path and a hash of the contents in that scope (e.g. `Etag: "/config/apps/http/servers 65760b8e"`). Simply set the `If-Match` header on a mutative request to that of an Etag header from a previous `GET` request.
The basic algorithm for this is as follows:
1. Perform a `GET` request to any scope `S` within the config. Hold onto the `Etag` trailer of the response.
1. Perform a `GET` request to any scope `S` within the config. Hold onto the `Etag` header of the response.
2. Make your desired change on the returned config.
3. Perform a `POST|PUT|PATCH|DELETE` request within scope `S`, setting the `If-Match` header to the recent `Etag` value.
3. Perform a `POST|PUT|PATCH|DELETE` request within scope `S`, setting the `If-Match` request header to the stored `Etag` value.
4. If the response is HTTP 412 (Precondition Failed), repeat from step 1, or give up after too many attempts.
This algorithm safely allows multiple, overlapping changes to Caddy's configuration without explicit synchronization. It is designed so that simultaneous changes to different parts of the config don't require a retry: only changes that overlap the same scope of the config can possibly cause a collision and thus require a retry.