docs: concurrency, read consistency, Long/BigInt, and resource status codes#534
docs: concurrency, read consistency, Long/BigInt, and resource status codes#534kriszyp wants to merge 1 commit into
Conversation
…e status codes Four clarifications surfaced by the QA-explorer campaign: - Resource API: a 'Concurrency and Safe Concurrent Writes' section — atomic deltas are the only concurrency-safe write primitive; compare-and-set/ read-then-write patterns are not re-validated at commit. - Database API: a 'Read Consistency' section — per-request snapshot scope, and why a cross-request search-then-fetch can disagree (two snapshots, not a bug). - Schema: clarify that Long is bounded by 2^53 (not true 64-bit); use BigInt (sent as an actual bigint) for larger integers. - Resource API: a 'Returning Errors and Status Codes' section — which throw/ return shapes set the HTTP status and the common footguns that do not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request adds documentation sections regarding read consistency, large integer types (Long vs BigInt), concurrency/safe concurrent writes, and returning errors/status codes. The review feedback points out two technical inaccuracies in the documentation: first, the term 'read-your-own-write consistency' is incorrectly used to describe a read-only sequence and should be changed to 'repeatable read consistency'; second, the JavaScript safe integer limit is incorrectly stated as 2^53 instead of 2^53 - 1.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
|
||
| Across separate requests there is no shared snapshot. A common pattern — querying for matching rows in one request, then fetching each by id in a later request — runs the two reads at two different points in time. If the data changes in between (a row is deleted or expires, say), the second read can disagree with the first; a query may return an id whose subsequent fetch returns a 404. This is expected: it is two independent snapshots, not a consistency bug. | ||
|
|
||
| For read-your-own-write consistency across a query and a fetch, perform both reads within a single resource method or `transaction()` callback. |
There was a problem hiding this comment.
The term read-your-own-write consistency refers to a client being able to immediately see writes they have just made. Since the scenario described here is a read-only sequence (a query followed by a fetch) where we want to avoid non-repeatable or phantom reads, repeatable read consistency or consistent reads is the correct terminology.\n\nSuggested change:\nmarkdown\nFor repeatable read consistency across a query and a fetch, perform both reads within a single resource method or `transaction()` callback.\n
|
|
||
| ### Large integers: `Long` vs `BigInt` | ||
|
|
||
| Despite the name, `Long` is bounded by the JavaScript safe-integer range — values must satisfy `|value| <= 2^53` (9,007,199,254,740,992). A `Long` attribute rejects integers beyond that range, so it is not a full 64-bit type. |
There was a problem hiding this comment.
In JavaScript, the safe integer range is defined by Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER, which is -(2^53 - 1) to 2^53 - 1 inclusive. Therefore, the maximum safe integer is 9,007,199,254,740,991 (not 9,007,199,254,740,992, which is 2^53 and is unsafe). The condition should be strictly less than 2^53 or less than or equal to 2^53 - 1.\n\nSuggested change:\nmarkdown\nDespite the name, `Long` is bounded by the JavaScript safe-integer range — values must satisfy `|value| < 2^53` (9,007,199,254,740,991). A `Long` attribute rejects integers beyond that range, so it is not a full 64-bit type.\n
There was a problem hiding this comment.
@kriszyp wdyt about this? Should you adjust the line to be < instead of <= ?
🚀 Preview DeploymentYour preview deployment is ready! 🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-534 This preview will update automatically when you push new commits. |
| ```javascript | ||
| // throw an error with an explicit statusCode | ||
| const err = new Error('Not found'); | ||
| err.statusCode = 404; | ||
| throw err; | ||
|
|
||
| // return a Response | ||
| return new Response(body, { status: 201 }); | ||
|
|
||
| // set it on the context | ||
| this.getContext().response.status = 202; | ||
| ``` |
There was a problem hiding this comment.
nit: I prefer that all code blocks are actually "valid" javascript, even if incomplete. I think this can sometimes be confusing when you have what is three separate implementations all in what seems as a single block. Especially with throw/return where you really shouldn't have anything after it (since its unreachable). Also, this is somewhat vague on how it applies with promises. Like "returning a Response" is actually only valid if the user is implementing an async function. Otherwise, I think the more correct terminology is "resolve a Response".
Anyways, its honestly fine I'm being a little pedantic here I think.
Summary
Four documentation clarifications surfaced by the QA-explorer exploratory campaign. Each is a defensible-but-surprising behavior where the gap was documentation, not correctness — these write down the contract so developers don't have to discover it the hard way.
reference/resources/resource-api.mdaddTo/subtractFrom) are the only concurrency-safe write primitive; compare-and-set / read-then-write patterns (ifVersion, create-only,If-Match, read-decide-write) are not re-validated at commit, so concurrent writers can both pass. Covers theaddTono-return / no-floor limitations and the cluster replication-window caveat.reference/database/api.mdtransaction()for read-your-own-write.LongvsBigIntreference/database/schema.mdLongis bounded by 2^53 despite the name (not true 64-bit); useBigInt(sent as an actual bigint via CBOR/MessagePack, not a JSON number) for larger integers, including@indexedrange queries.reference/resources/resource-api.mderror.statusCode,return new Response(...),context.response.status) and the footguns that silently don't (throw {status}, a thrownResponse,return {status}withoutheaders).No behavior changes — documentation only. Diff is +61 lines across three files.
🤖 Generated with Claude Code