textmachine/platform/internal/pgstore/queries/idempotency.sql

47 lines
2.6 KiB
SQL

-- The storage half of `Idempotency-Key`.
-- name: ReadIdempotencyKey :one
-- `for update` on a MISSING row locks nothing, which is why the caller has an insert-then-re-read
-- path behind this and not a bare insert.
select request_sha256, status, location, body, claimed_at, finished_at, content_sha256
from idempotency_keys
where user_id = sqlc.arg(user_id) and method = sqlc.arg(method)
and path_sha256 = sqlc.arg(path_sha256) and key = sqlc.arg(key) for update;
-- name: InsertIdempotencyClaim :execrows
-- `do nothing` plus a re-read decides between two simultaneous first attempts; a bare insert made
-- the loser raise a unique violation — a 500 on the one case this header exists for.
insert into idempotency_keys (user_id, method, path, path_sha256, key, request_sha256, claimed_at, claim_token)
values (sqlc.arg(user_id), sqlc.arg(method), sqlc.arg(path), sqlc.arg(path_sha256), sqlc.arg(key),
sqlc.arg(request_sha256), sqlc.arg(claimed_at), sqlc.arg(claim_token))
on conflict (user_id, method, path_sha256, key) do nothing;
-- name: RetakeIdempotencyClaim :exec
-- The NEW token is what stops the old attempt — which may still be alive and merely slow — from
-- completing or releasing what is no longer its claim.
update idempotency_keys
set claimed_at = sqlc.arg(claimed_at), request_sha256 = sqlc.arg(request_sha256),
claim_token = sqlc.arg(claim_token)
where user_id = sqlc.arg(user_id) and method = sqlc.arg(method)
and path_sha256 = sqlc.arg(path_sha256) and key = sqlc.arg(key);
-- name: CompleteIdempotencyKey :execrows
-- Only the attempt that still HOLDS the claim may write the receipt.
update idempotency_keys
set status = sqlc.arg(status), location = sqlc.arg(location), body = sqlc.arg(body),
finished_at = sqlc.arg(finished_at), content_sha256 = sqlc.arg(content_sha256)
where user_id = sqlc.arg(user_id) and method = sqlc.arg(method)
and path_sha256 = sqlc.arg(path_sha256) and key = sqlc.arg(key)
and claim_token = sqlc.arg(claim_token) and finished_at is null;
-- name: ReleaseIdempotencyKey :execrows
-- On the token for the same reason as the completion: a slow attempt that finally failed used to
-- delete the row of the successor which had taken the key over and was already doing the work.
delete from idempotency_keys
where user_id = sqlc.arg(user_id) and method = sqlc.arg(method)
and path_sha256 = sqlc.arg(path_sha256) and key = sqlc.arg(key)
and claim_token = sqlc.arg(claim_token) and finished_at is null;
-- name: SweepIdempotency :execrows
-- Forgets records past the window the contract promises ("at least 24 hours").
delete from idempotency_keys where claimed_at < sqlc.arg(before);