mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
docs(api): correct two comments that describe behaviour the code does not have
The single-record write ceiling justified itself with "every one of these routes is already wrapped in `withIdempotency`". Ten of the eleven are. `POST /api/medications/intake` is not, and it is the one of the eleven a native client actually loops — it is replay-tolerant for its own reason, being an update of a named `intakeId` rather than a create. The comment now says which is which, and adds the clause it was missing: the check runs before the body is read, so a refused request spends the allowance too. The custom-symptom wrapper said a replay "answered the 409 that uniqueness raises". There is no such 409. The row's key is minted fresh per request and the label lives in an AES-GCM column with a per-write IV, which can carry no unique index, so an un-keyed replay wrote a second row with the same label — a silent duplicate in the person's own vocabulary, up to the fifty-row cap. The wrapper is worth more than the old sentence claimed, not less.
This commit is contained in:
1 parent
b2c8fe8567
commit
68964b4bac
2 files changed
+17
-7
No files matched your search
@@ -70,10 +70,15 @@ export const GET = apiHandler(async () => {
|
||||
|
||||
/**
|
||||
* Wrapped in `withIdempotency`: a custom symptom is minted from the client's
|
||||
* offline outbox under the same `Idempotency-Key`, and a replay after a lost
|
||||
* success response would otherwise answer the 409 the label's unique key
|
||||
* raises rather than the 201 the first attempt produced — a create the client
|
||||
* has no record of, reported as a conflict it cannot act on.
|
||||
* offline outbox under the same `Idempotency-Key`, and nothing else here would
|
||||
* catch the replay. The row's key is minted fresh per request
|
||||
* (`custom:<uuid>`), and the label it would duplicate lives in
|
||||
* `labelEncrypted` — AES-GCM with a per-write IV, so no unique index can be
|
||||
* put on it. A replay after a lost success response therefore wrote a SECOND
|
||||
* row carrying the same label, up to the fifty-row cap: a silent duplicate in
|
||||
* the person's own symptom vocabulary rather than a conflict anything refused.
|
||||
* The wrapper answers the first attempt's 201 instead, and the in-flight 409 it
|
||||
* defines is the only 409 this route can produce.
|
||||
*/
|
||||
export const POST = apiHandler(
|
||||
withIdempotency<[NextRequest]>(postCustomSymptom),
|
||||
|
||||
@@ -132,9 +132,14 @@ export async function checkAnalyticsReadRateLimit(
|
||||
* `checkAnalyticsReadRateLimit` gives: a loop that rotates across ten creates
|
||||
* should still meet a cap. Three hundred a minute is five writes a second
|
||||
* sustained for a full minute — no hand-driven session and no sync burst
|
||||
* reaches it, and every one of these routes is already wrapped in
|
||||
* `withIdempotency`, so an honest retry storm is deduplicated before it is
|
||||
* counted. It exists to cut off the pathological loop, not to shape traffic.
|
||||
* reaches it. Ten of the eleven are wrapped in `withIdempotency`, so an honest
|
||||
* retry storm is deduplicated before it is counted; `POST
|
||||
* /api/medications/intake` is not, because it updates a named `intakeId`
|
||||
* rather than creating a row, so a replay writes the status that is already
|
||||
* there. It exists to cut off the pathological loop, not to shape traffic.
|
||||
*
|
||||
* A refused request is counted too: the check runs before the body is read,
|
||||
* so a client looping on a 422 spends its allowance without writing a row.
|
||||
*
|
||||
* Keyed on the ACTING account rather than the record, the frozen precedent from
|
||||
* the delegable medication routes: a delegate burns their own allowance rather
|
||||
|
||||
Reference in new issue
Block a user