Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 1 | /** |
| 2 | * Reading a service's D1 database near the caller with D1's Sessions API, | |
| 3 | * and saying how long an RPC took: the TypeScript side of | |
| 4 | * crates/kit/src/d1.rs, with the same rules. | |
| 5 | * | |
| 6 | * The caller chooses, per request, with the `x-d1-bookmark` header: | |
| 7 | * | |
| 8 | * | Header | Reads go to | | |
| 9 | * | --- | --- | | |
| 10 | * | absent | the primary, with no session (as always) | | |
| 11 | * | `first-primary` | the primary first, then any copy at least as new | | |
| 12 | * | `first-unconstrained` | the nearest copy | | |
| 13 | * | a bookmark | any copy at least as new as the bookmark | | |
| 14 | * | |
| 15 | * Writes always go to the primary. The session's latest bookmark comes | |
| 16 | * back in the response's `x-d1-bookmark`. docs/PERFORMANCE.md says who | |
| 17 | * sends what. | |
| 18 | */ | |
| 19 | ||
| 20 | /** The header that carries a session's constraint or bookmark, both ways. */ | |
| 21 | export const BOOKMARK_HEADER = "x-d1-bookmark"; | |
| 22 | ||
| 23 | const MAX_BOOKMARK = 256; | |
| 24 | ||
| 25 | /** | |
| 26 | * What an `x-d1-bookmark` header asks for: null for no session, otherwise | |
| 27 | * what `withSession` is given. Anything malformed starts on the primary. | |
| 28 | */ | |
| 29 | export function sessionConstraint(header: string | null | undefined): string | null { | |
| 30 | const value = header?.trim(); | |
| 31 | if (!value) return null; | |
| 32 | if (value === "first-primary" || value === "first-unconstrained") return value; | |
| 33 | return value.length <= MAX_BOOKMARK && /^[0-9A-Za-z-]+$/.test(value) ? value : "first-primary"; | |
| 34 | } | |
| 35 | ||
| 36 | /** A D1 binding, as far as sessions need it. */ | |
| 37 | type SessionCapable = { withSession(constraintOrBookmark?: string): { getBookmark(): string | null } }; | |
| 38 | ||
| 39 | /** | |
| 40 | * The database for an RPC `request`: a session, seen as the binding, when | |
| 41 | * the caller asked for one (it answers `prepare` and `batch`, all a request | |
| 42 | * path uses), the binding itself otherwise. `finish` adds the bookmark and | |
| 43 | * the time taken to the answer. | |
| 44 | */ | |
| 45 | export function openD1<D extends SessionCapable>(db: D, request: Request): { db: D; finish(response: Response): Response } { | |
| 46 | const started = Date.now(); | |
| 47 | const asked = sessionConstraint(request.headers.get(BOOKMARK_HEADER)); | |
| 48 | const session = asked ? db.withSession(asked) : null; | |
| 49 | return { | |
| 50 | db: (session ?? db) as D, | |
| 51 | finish(response) { | |
| 52 | const answered = new Response(response.body, response); | |
| 53 | answered.headers.append("server-timing", `svc;dur=${Date.now() - started};desc="${session ? "session" : "primary"}"`); | |
| 54 | const bookmark = session?.getBookmark(); | |
| 55 | if (bookmark) answered.headers.set(BOOKMARK_HEADER, bookmark); | |
| 56 | return answered; | |
| 57 | }, | |
| 58 | }; | |
| 59 | } |