| 1 | // Probe for .github/workflows/d1-replication.yml: checks the D1 Sessions API on a |
| 2 | // throwaway database with read replication on, the way apps/diffinery uses it |
| 3 | // (src/lib/d1.ts): write in one session, then read in a new session started from |
| 4 | // the first one's bookmark, and expect to see the write every time. |
| 5 | export default { |
| 6 | async fetch(req, env) { |
| 7 | const url = new URL(req.url); |
| 8 | if (url.pathname !== "/check") return new Response("not found", { status: 404 }); |
| 9 | const rounds = Number(url.searchParams.get("rounds") ?? 20); |
| 10 | const out = { rounds, stale: 0, replicaReads: 0, primaryReads: 0, regions: {}, errors: [] }; |
| 11 | for (let i = 1; i <= rounds; i++) { |
| 12 | try { |
| 13 | const w = env.DB.withSession("first-primary"); |
| 14 | const value = `${Date.now()}-${i}-${crypto.randomUUID()}`; |
| 15 | await w.prepare("INSERT INTO probe (k, v) VALUES ('latest', ?) ON CONFLICT (k) DO UPDATE SET v = excluded.v").bind(value).run(); |
| 16 | const bookmark = w.getBookmark(); |
| 17 | // A later request: new session from the bookmark (as a browser cookie would carry it). |
| 18 | const r = env.DB.withSession(bookmark); |
| 19 | const row = await r.prepare("SELECT v FROM probe WHERE k = 'latest'").first(); |
| 20 | const res = await r.prepare("SELECT v FROM probe WHERE k = 'latest'").run(); |
| 21 | const meta = res.meta ?? {}; |
| 22 | if (row?.v !== value) out.stale++; |
| 23 | if (meta.served_by_primary === false) out.replicaReads++; |
| 24 | else out.primaryReads++; |
| 25 | const region = meta.served_by_region ?? "unknown"; |
| 26 | out.regions[region] = (out.regions[region] ?? 0) + 1; |
| 27 | // An unconstrained session may read an older value, but must never fail. |
| 28 | await env.DB.withSession("first-unconstrained").prepare("SELECT v FROM probe WHERE k = 'latest'").first(); |
| 29 | } catch (e) { |
| 30 | out.errors.push(String(e)); |
| 31 | } |
| 32 | } |
| 33 | out.ok = out.stale === 0 && out.errors.length === 0; |
| 34 | return Response.json(out, { status: out.ok ? 200 : 500 }); |
| 35 | }, |
| 36 | }; |