{"_self":{"principle":"Self-explaining payload — no external context required. This _self block describes what you are reading and where to look next.","widget":"article_topology","feature":"topology","name":"Article topology","what":"Claims, sources, anecdotes, user reports, related embeds, question graph slice — for ask/ROUTER.","contains":"claims, sources, anecdotes, question_graph slice","slug":"cloudflare-os-d1","urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/topology"},"how_to_use":"Claims, sources, anecdotes, user reports, related embeds, question graph slice — for ask/ROUTER.","write":null,"imessage":null,"router_tag":null,"proof_chain":[{"step":1,"claim":"Articles are voxel graphs of tiered claims, not prose blobs.","verify":"https://miscsubjects.com/api/articles/constitution"},{"step":2,"claim":"Claims link to hash-chained sources via source_ids.","verify":"https://miscsubjects.com/api/articles/cloudflare-os-d1/sources"},{"step":3,"claim":"Ask reads topology; ingest/claim append to ledger.","verify":"https://miscsubjects.com/api/protocol"},{"step":4,"claim":"Models queue growth: populate → collaborate → repair → reflex.","verify":"https://miscsubjects.com/api/protocol/grow"},{"step":5,"claim":"Graph proves its own shape (reflex) and $/claim (yield).","verify":"https://miscsubjects.com/graph.html?layer=reflex"},{"step":6,"claim":"Full feature index + _explain on every API response.","verify":"https://miscsubjects.com/api/articles/system-map"}],"related_features":[{"id":"ask","name":"Ask protocol","what":"Answer only from topology; creates question_node with gaps and ingest_hint.","urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/prompts","write":"https://miscsubjects.com/api/protocol/ask"}},{"id":"graph_topology","name":"Cross-article graph","what":"Merged claims/sources across condition+stack slugs for one question.","urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/graph-topology?question=..."}},{"id":"question_graph","name":"Question graph","what":"Ask nodes (questions + gaps) and evidence_ingest nodes (pasted model output).","urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/question-graph","write":"https://miscsubjects.com/api/protocol/ask"}},{"id":"voxels","name":"Voxel graph","what":"Claims as atoms, sources as edges (supported_by, posted_by). Per-claim provenance.","urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/voxels","write":"https://miscsubjects.com/api/protocol/claim"}}],"system_map":"https://miscsubjects.com/api/articles/system-map","system_map_markdown":"https://miscsubjects.com/api/articles/system-map?format=markdown","not_medical_advice":true},"_explain":{"feature":"topology","name":"Article topology","what":"Claims, sources, anecdotes, user reports, related embeds, question graph slice — for ask/ROUTER.","why":"Every feature is auditable collective intelligence","how":"Claims, sources, anecdotes, user reports, related embeds, question graph slice — for ask/ROUTER.","model":null,"verifies":null,"urls":{"read":"https://miscsubjects.com/api/articles/cloudflare-os-d1/topology"},"imessage":null,"router":null,"related":[{"id":"ask","what":"Answer only from topology; creates question_node with gaps and ingest_hint."},{"id":"graph_topology","what":"Merged claims/sources across condition+stack slugs for one question."},{"id":"question_graph","what":"Ask nodes (questions + gaps) and evidence_ingest nodes (pasted model output)."},{"id":"voxels","what":"Claims as atoms, sources as edges (supported_by, posted_by). Per-claim provenance."}],"not_medical_advice":true},"slug":"cloudflare-os-d1","title":"D1 bills rows, not queries, and serial round trips decide the architecture","register":"essay","tags":["cloudflare","architecture","d1","cloudflare-os","sqlite","database","durable-objects","performance","migrations"],"updated_at":"2026-07-26T05:40:40.830Z","body_excerpt":"D1 is Cloudflare's managed SQLite. Bind a database to a Worker in `wrangler.toml`, get `env.DB`, write ordinary SQL. No connection string, no pool, no instance to size. That pitch is accurate.\n\nThe shape underneath is what decides whether you should build on it: a single SQLite file inside a single Durable Object in a single Cloudflare location, billed by the row rather than by the query, capped at 10 GB per database and 2,000,000 bytes per stored value. Every surprise below follows from one of those four facts.\n\nSiblings: [the platform index](/a/cloudflare-os), [Workers and Durable Objects](/a/cloudflare-os-workers), [R2 for the fields that do not fit](/a/cloudflare-os-r2).\n\n## Evidence status\n\n**Observed** marks first-party measurements or runtime receipts from the named environment.\n**Derived** marks arithmetic calculated from cited inputs. **Specified** marks vendor or standards\ndocumentation. **Implemented** and **deployed** name code and live-state evidence, respectively.\n**Reproduced** means the stated procedure was rerun. **Externally attested** marks operator reports;\nthose reports show that an experience occurred, not that it is universal.\n\n## Cloudflare's own Workers architect calls D1 a wrapper\n\nKenton Varda, who built the Workers runtime, wrote this on Hacker News in June 2026:\n\n> I'll let you in on a sort of dirty secret:\n>\n> It's almost always better to use Durable Objects storage, rather than D1. Even if you only want a single global database, it's better to implement that as a singleton Durable Object, than by using D1. Because that's all D1 itself actually is: a singleton Durable Object that exposes an API to its SQLite database. It's just a wrapper.\n\nHis decision rule, in the same comment:\n\n> If your app does no more than one DB query per request, then D1 is fine: the Worker runs near the end user, and talks over the long-haul network to D1 just once. Whereas with Durable Objects, your Worker would talk over the long-haul network to the Durable Object. No difference.\n>\n> But if your app ever does two or more queries in series for a single request, then Durable Objects becomes vastly better, because you get to move that query-chaining code to happen directly where the database lives, rather than have multiple round trips.\n\nAnd the reason D1 exists at all: \"Really, though, the only reason D1 exists is for comfort. Once you know how to use Durable Objects, there's no reason to use D1.\" He names one exception — D1's read replication is not yet available to raw Durable Objects.\n\nA **Durable Object** is a single-instance JavaScript class with private storage, addressed by name, that Cloudflare guarantees exists exactly once globally. A SQLite-backed one carries its own embedded SQLite database with the same SQL limits as D1, and your code runs in the same process as it — `sql.exec()` is a local function call, not a network request. That is the entire latency difference.\n\n**Verdict.** Choose D1 when all three hold: the data is one global relational set, the request path makes one or two queries, and you want the operational surface D1 has and raw Durable Objects do not — `wrangler d1 execute` against production, versioned migration files, Time Travel point-in-time restore, and read replicas. Choose a SQLite Durable Object when the data partitions naturally per user, tenant, room or document, or when a single request chains three or more dependent queries. Those two rules cover almost every case; when they conflict, the query-chaining rule wins, because round trips are the thing you cannot optimise away later.\n\n## The limits, fetched today, are the actual specification\n\nEvery number below is from `https://developers.cloudflare.com/d1/platform/limits/`, last updated 21 April 2026 per the page itself.\n\n| Limit | Workers Paid | Workers Free |\n| --- | --- | --- |\n| Databases per account | 50,000 (raisable by request) | 10 |\n| Maximum database size | 10 GB — cannot be raised | 500 MB |\n| Maximum storage per account | 1 T","ranking":"safety-first (interaction_risk/limitations), then quote-gated effective_weight","claims":[{"id":"c8","text":"A Uint8Array near 200 KB triggered SQLITE_TOOBIG in a public reproduction while the same bytes as ArrayBuffer and a 2 MB string succeeded.","tier":"anecdotal","section":"SQLITE_TOOBIG is the 2,000,000-byte value cap, and serialization gets you there first","interaction_risk":false,"status":"active","source_ids":["p6"],"why_material":"Serialization shape can reach the SQLite limit before raw data size suggests.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.3,"quote_gated":false},{"id":"c11","text":"Public D1 latency reports conflict: operators report 400 ms and 300–3000 ms paths, while another reports acceptable production performance after Smart Placement.","tier":"anecdotal","section":"Latency: two production reports, both true, measuring different things","interaction_risk":false,"status":"active","source_ids":["p1","p2","p7","s14"],"why_material":"The disagreement is explained by placement and serial call count, not flattened.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.3,"quote_gated":false},{"id":"c12","text":"A separate production operator reports D1 queries hanging for seconds or double digits over periods of weeks.","tier":"anecdotal","section":"Latency: two production reports, both true, measuring different things","interaction_risk":false,"status":"active","source_ids":["p5"],"why_material":"Reliability failures are distinct from normal query latency.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.3,"quote_gated":false},{"id":"c16","text":"A public operator reports multi-million monthly-active-user traffic on SQLite Durable Objects at low cost.","tier":"anecdotal","section":"Per-tenant sharding is documented, and impractical for the reason nobody mentions","interaction_risk":false,"status":"active","source_ids":["p9"],"why_material":"It provides positive operator evidence for the proposed alternative.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.3,"quote_gated":false},{"id":"c22","text":"A Durable Object alarm loop was reported to peak near 930 billion row reads per day and produce a $34,895 invoice with zero users.","tier":"anecdotal","section":"The arithmetic","interaction_risk":false,"status":"active","source_ids":["p10","s3"],"why_material":"It demonstrates the downside of an unbounded row-read loop.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.3,"quote_gated":false},{"id":"c1","text":"D1 exposes managed SQLite through a network API while a SQLite-backed Durable Object co-locates application code with the database.","tier":"system","section":"Cloudflare's own Workers architect calls D1 a wrapper","interaction_risk":false,"status":"active","source_ids":["p8","s1","s11"],"why_material":"This physical shape determines latency and transaction choices.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c2","text":"D1 is the stronger fit for a global relational set with one or two calls per request; a Durable Object is stronger for entity partitions or serial query chains.","tier":"system","section":"Cloudflare's own Workers architect calls D1 a wrapper","interaction_risk":false,"status":"active","source_ids":["p8","s11"],"why_material":"It is the article's architecture verdict.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c3","text":"A paid D1 database is capped at 10 GB and a single string, BLOB or row at 2,000,000 bytes.","tier":"fact","section":"The limits, fetched today, are the actual specification","interaction_risk":false,"status":"active","source_ids":["s2"],"why_material":"Both limits are hard design boundaries.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c4","text":"D1 bills rows scanned and written rather than query count or row byte size.","tier":"fact","section":"Rows are the billing unit, and an unindexed predicate bills the whole table","interaction_risk":false,"status":"active","source_ids":["s3"],"why_material":"The meter changes indexing from an optimization into a cost control.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c5","text":"The 50,000-row scratch benchmark reduced rows_read from 50,000 to 95 and duration from 5.7362 ms to 0.2432 ms after adding one index.","tier":"measurement","section":"The same query, measured with and without an index","interaction_risk":false,"status":"active","source_ids":["r5","s6"],"why_material":"It quantifies the index mechanism on this workload.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c6","text":"At one query per second, the worked unindexed scan costs $104.60 per month after the allowance while the indexed form stays inside the allowance.","tier":"calculation","section":"The arithmetic","interaction_risk":false,"status":"active","source_ids":["r5","s3"],"why_material":"It translates rows_read into operating cost.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c7","text":"The production D1 endpoint accepted five UNION ALL terms and rejected six with SQLITE error code 7500.","tier":"measurement","section":"One undocumented ceiling: five terms in a compound SELECT","interaction_risk":false,"status":"active","source_ids":["r2"],"why_material":"Generated compound statements must be chunked at five on this database.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c9","text":"Fields whose size grows with history should move to R2 while D1 retains a pointer and verification hash.","tier":"system","section":"A worked example, with the numbers","interaction_risk":false,"status":"active","source_ids":["r6","s2"],"why_material":"It preserves append-only data without making a D1 row unwritable.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c10","text":"D1 batch executes statements sequentially in one call, but it does not provide an interactive transaction spanning multiple requests and application logic.","tier":"system","section":"A transaction cannot span two requests, and the workaround is a deliberate parse error","interaction_risk":false,"status":"active","source_ids":["p3","s4"],"why_material":"It bounds which invariants can live in D1 SQL.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c13","text":"Smart Placement can take 15 minutes to decide and requires consistent multi-location traffic.","tier":"fact","section":"Latency: two production reports, both true, measuring different things","interaction_risk":false,"status":"active","source_ids":["s8"],"why_material":"A benchmark immediately after deploy may not measure placed execution.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c14","text":"D1 read replication requires the Sessions API; otherwise queries continue to use the primary.","tier":"fact","section":"Latency: two production reports, both true, measuring different things","interaction_risk":false,"status":"active","source_ids":["s7"],"why_material":"Enabling replicas without Sessions does not distribute reads.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c15","text":"D1 documents per-tenant databases, but static Worker bindings make a database per dynamic tenant impractical.","tier":"system","section":"Per-tenant sharding is documented, and impractical for the reason nobody mentions","interaction_risk":false,"status":"active","source_ids":["p4","s2"],"why_material":"The advertised database count is not the same as runtime addressability.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c17","text":"D1 migrations are ordered files tracked in d1_migrations, and the database name is safer than a mutable binding name.","tier":"fact","section":"Migrations are ordered files, and d1 execute silently desynchronises them","interaction_risk":false,"status":"active","source_ids":["s13","s9"],"why_material":"Bypassing the ledger makes Wrangler's pending list untrustworthy.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c18","text":"The fresh production receipt found 136 applied migrations with 0133_charlie_audit.sql latest.","tier":"measurement","section":"A fresh read-only receipt reproduces the row meter and the five-term ceiling","interaction_risk":false,"status":"active","source_ids":["r4"],"why_material":"It confirms the migration drift measurement still holds.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c19","text":"The fresh event-ledger COUNT returned 401,112 rows, read the same number of rows and reported 1,062,916,096 bytes.","tier":"measurement","section":"A fresh read-only receipt reproduces the row meter and the five-term ceiling","interaction_risk":false,"status":"active","source_ids":["r3"],"why_material":"It connects row-count operations to both rows_read and storage size.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c20","text":"Time Travel restores a whole D1 database to a minute within the retained window; a forward-fix migration is narrower for one schema change.","tier":"system","section":"Migrations are ordered files, and d1 execute silently desynchronises them","interaction_risk":false,"status":"active","source_ids":["s10"],"why_material":"The rollback choice determines blast radius.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c21","text":"Hyperdrive is the preferred comparison when an application already owns Postgres or MySQL and needs pooled Worker connections.","tier":"system","section":"Choosing between D1 and the three things it competes with","interaction_risk":false,"status":"active","source_ids":["s12"],"why_material":"D1 is not the only relational path from Workers.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c23","text":"D1 publishes distinct overload, timeout, reset, network and type errors, so repair must follow the exact error class rather than one generic retry path.","tier":"system","section":"Symptom, cause, fix","interaction_risk":false,"status":"active","source_ids":["s5"],"why_material":"Some failures require indexing or sharding while only transient failures should be retried.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false},{"id":"c24","text":"The fresh primary-key slug lookup read 1 row while the equality filter on the unindexed title read 2,186.","tier":"measurement","section":"A fresh read-only receipt reproduces the row meter and the five-term ceiling","interaction_risk":false,"status":"active","source_ids":["r1"],"why_material":"It reproduces the indexed-versus-scan shape without mutating production.","retracted_at":null,"retraction_reason":null,"challenged_by":[],"effective_weight":0.1,"quote_gated":false}],"sources":[{"id":"s1","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/","title":"Cloudflare D1 overview","quote":"D1 allows you to create thousands of databases at no extra cost for isolation, perfect for scaling out application vibe-coding platforms.","summary":"Vendor overview of D1's managed serverless SQL surface and scale-out model.","claim_ids":["c1"],"hash":"8421f7c951b1b6b9f91b002ccb5cb1e3daa62151e347872d3d67ae971d605deb"},{"id":"s2","type":"specification","url":"https://developers.cloudflare.com/d1/platform/limits/","title":"D1 limits","quote":"D1 is designed for horizontal scale out across multiple, smaller (10 GB) databases, such as per-user, per-tenant or per-entity databases.","summary":"Normative limits for database size, stored values, statements, bindings, query duration and account storage.","claim_ids":["c15","c3","c9"],"hash":"e24e1cedd34c589f64fa47d14121075cac52d8a0f1f80c9eb40617979e521003"},{"id":"s3","type":"specification","url":"https://developers.cloudflare.com/d1/platform/pricing/","title":"D1 pricing","quote":"A query that filters on an unindexed column may return fewer rows to your Worker, but is still required to read (scan) more rows to determine which subset to return.","summary":"The row-read, row-write and storage meters, including the extra written row charged for an index update.","claim_ids":["c22","c4","c6"],"hash":"b59c5d16477943fd6f0f87f12b7fcf494f6e10b15d407c2ac8e59a717a15f6f6"},{"id":"s4","type":"specification","url":"https://developers.cloudflare.com/d1/worker-api/d1-database/","title":"D1Database API","quote":"Sends multiple SQL statements inside a single call to the database. This can have a huge performance impact as it reduces latency from network round trips to D1.","summary":"Binding API contract for prepare, batch, exec, dump and Sessions.","claim_ids":["c10"],"hash":"c12a128be84bce212670195b07768943d098b6a2f1733bb9db182f997263486a"},{"id":"s5","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/observability/debug-d1/","title":"Debug D1","quote":"D1 DB is overloaded. Requests queued for too long.","summary":"Exact D1 error strings with Cloudflare's stated cause and operator action.","claim_ids":["c23"],"hash":"40b7b3301adb9d3430945ba7556b016924b79aa15b404b92a71ec4abf9913cb2"},{"id":"s6","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/best-practices/use-indexes/","title":"Use indexes","quote":"Indexes enable D1 to improve query performance over the indexed columns for common (popular) queries by reducing the amount of data (number of rows) the database has to scan when running a query.","summary":"Index selection, EXPLAIN QUERY PLAN and the scan reduction mechanism.","claim_ids":["c5"],"hash":"691f549cc526479363de2793bd9f8df91315ea93f074389aeb2d279432b4bd1c"},{"id":"s7","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/best-practices/read-replication/","title":"Use read replication","quote":"To use read replication, you must use the D1 Sessions API, otherwise all queries will continue to be executed only by the primary database.","summary":"Read replica activation, Sessions bookmarks and sequential consistency.","claim_ids":["c14"],"hash":"1045b4ffd8f47e2d0fb1ba13afc89161fb9573926b3148226436752fc5f11ed4"},{"id":"s8","type":"publisher_documentation","url":"https://developers.cloudflare.com/workers/configuration/smart-placement/","title":"Smart Placement","quote":"Smart Placement may take up to 15 minutes to analyze your Worker after deployment.","summary":"Placement mechanism, traffic requirements, analysis delay and rollback behavior.","claim_ids":["c13"],"hash":"e63f65a646a215b0e8d96e95486256ddd132358c2bcef22d7774cde442379543"},{"id":"s9","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/reference/migrations/","title":"D1 migrations","quote":"However, the binding name can change, whereas the database name cannot.","summary":"Exact create, list and apply workflow and the migration-ledger behavior.","claim_ids":["c17"],"hash":"42a7015aa2517351f7ea8b2abf090e4d26098fca6a85ff22b4e7effb282bc506"},{"id":"s10","type":"publisher_documentation","url":"https://developers.cloudflare.com/d1/reference/time-travel/","title":"D1 Time Travel","quote":"Time Travel is D1's approach to backups and point-in-time-recovery, and allows you to restore a database to any minute within the last 30 days.","summary":"Point-in-time bookmarks and restore semantics for a whole D1 database.","claim_ids":["c20"],"hash":"9c7d364eaefb088b59fe43efcad93d7f9f1c02496b36ee0e75e346eb3426d8d8"},{"id":"s11","type":"publisher_documentation","url":"https://developers.cloudflare.com/durable-objects/best-practices/access-durable-objects-storage/","title":"SQLite-backed Durable Objects","quote":"Cloudflare recommends all new Durable Object namespaces use the SQLite storage backend.","summary":"Official alternative where application code and SQLite state share the Durable Object.","claim_ids":["c1","c2"],"hash":"90d055d50f766d86552e6ce14248194b650b1e79ac11f92b73149d7a9205d97e"},{"id":"s12","type":"publisher_documentation","url":"https://developers.cloudflare.com/hyperdrive/","title":"Cloudflare Hyperdrive","quote":"Accelerate access to your existing databases from Cloudflare Workers with Hyperdrive's global connection pooling and query caching.","summary":"Official path for keeping Postgres or MySQL while pooling Worker connections.","claim_ids":["c21"],"hash":"133fd89a7a9c714e0ed5ce82f8cbdfede5c34cbaadf57bdc90c47395513ce84c"},{"id":"s13","type":"repository","url":"https://github.com/cloudflare/workers-sdk","title":"Cloudflare workers-sdk","quote":"Home to Wrangler, the CLI for Cloudflare Workers®","summary":"Public Wrangler source repository containing D1 CLI, migration and API implementations.","claim_ids":["c17"],"hash":"1e764519b519d5d92518c9cd18ebef180a0188d1490752d13b5a9d41e94845ba"},{"id":"s14","type":"independent_measurement","url":"https://news.ycombinator.com/item?id=43607264","title":"Independent Workers database latency comparison","quote":"Using CF Workers + DigitalOcean Postgres, I was seeing query responses in the 50-100ms range.","summary":"Named operator comparison: Workers plus DigitalOcean Postgres at 50–100 ms versus Workers plus D1 at 300–3000 ms, both with Smart Placement.","claim_ids":["c11"],"hash":"c9477ca3d6e0dcc2ace8081957baf051bbebe63b753a9fcf38fa468e76590766"},{"id":"p1","type":"hn","url":"https://news.ycombinator.com/item?id=43607561","title":"Journey to Optimize Cloudflare D1 Database Queries","quote":"Using D1 in production for over an year on multiple projects - I can confirm response times to simple queries regularly take 400ms and beyond. On top there's constant network, connection and a plethora of internal errors.","summary":"Over a year of D1 in production across multiple projects. Reports 400ms+ on simple queries plus constant network, connection and internal errors, and explicitly does not recommend D1 for production. Negative.","claim_ids":["c11"],"hash":"ef77f015403fbe7f36664df18308c1a517b258de607cd1a78253c7395dd66fd3"},{"id":"p2","type":"hn","url":"https://news.ycombinator.com/item?id=43607264","title":"Journey to Optimize Cloudflare D1 Database Queries","quote":"I evaluated D1 for a project a few months ago, and found that global performance was pretty terrible.","summary":"Evaluated D1 and rejected it on global TTFB. In a follow-up in the same thread reports Workers+DigitalOcean Postgres at 50-100ms vs Workers+D1 at 300-3000ms, both with Smart Placement on. Negative.","claim_ids":["c11"],"hash":"83bacd078e3af20e6568c5c34af979f1604e8f79e3429491505829e1e92f3653"},{"id":"p3","type":"hn","url":"https://news.ycombinator.com/item?id=43614249","title":"Journey to Optimize Cloudflare D1 Database Queries","quote":"Another fun limitation is that a transaction cannot span multiple D1 requests, so you can't select from the database, execute application logic, and then write to the database in an atomic way.","summary":"Hit D1's lack of multi-request transactions and worked around it by packing a precondition check into the first statement of a batch that deliberately throws a JSON parse error to abort the rest. Says anything more complex would need temp tables and logic pushed into SQL. Negative.","claim_ids":["c10"],"hash":"71aa7517cd44e23fc88645b07bccc56dd0ef399e2de8606bd8d603cf3fd48af3"},{"id":"p4","type":"hn","url":"https://news.ycombinator.com/item?id=43610222","title":"Journey to Optimize Cloudflare D1 Database Queries","quote":"It’s not possible to set up per-user data in D1. Like in theory you probably could, but the DX infrastructure to make it possible is non-existent - you have to explicitly bind each database into your worker.","summary":"Tried the documented per-tenant D1 sharding pattern and found it impractical because every database must be explicitly bound into the Worker. Says a Cloudflare person on Discord confirmed Durable Objects is the only real path to tenancy sharding. Negative.","claim_ids":["c15"],"hash":"0b0e9c14947305ebb868bb43ef2a44206cc6a8e7012d7d9ff98b8ad270c3d76d"},{"id":"p5","type":"hn","url":"https://news.ycombinator.com/item?id=47797766","title":"Cloudflare's AI Platform: an inference layer designed for agents","quote":"D1 reliability has been bad in our experience. We've had queries hanging on their internal network layer for several seconds, sometimes double digits over extended periods (on the order of weeks).","summary":"Production user reporting multi-second to double-digit-second hung D1 queries persisting for weeks, plain network exceptions between Worker and D1 hosts, and hung queries invisible in the observability dashboard. Also cites no transactions. Negative.","claim_ids":["c12"],"hash":"fbcc7f9bf2f2436d1e6b2e70ebc05b030adf5d113f5b011b3a67aef733c73df7"},{"id":"p6","type":"github","url":"https://github.com/cloudflare/workers-sdk/issues/14101","title":"Workflows (local `wrangler dev`): a ~200 KB `Uint8Array` step output fails with `string or blob too big: SQLITE_TOOBIG`, but the same bytes as an `ArrayBuffer` (or a 2 MB string) succeed","quote":"Workflows (local `wrangler dev`): a ~200 KB `Uint8Array` step output fails with `string or blob too big: SQLITE_TOOBIG`, but the same bytes as an `ArrayBuffer` (or a 2 MB string) succeed","summary":"Filed a minimal reproduction where a ~200 KB Uint8Array step output aborts the whole run with SQLITE_TOOBIG under the local Workflows engine, while identical bytes as an ArrayBuffer or a 2 MB string are fine. Negative — the SQLite size ceiling surfacing through the serialization path.","claim_ids":["c8"],"hash":"551fbf82a161b203110c412d319ec4cf8858c734fca14a594781ce5b0e38bcf4"},{"id":"p7","type":"hn","url":"https://news.ycombinator.com/item?id=43608066","title":"Journey to Optimize Cloudflare D1 Database Queries","quote":"I am running 2 production apps on Cloudflare workers, both using D1 for primary storage. I found the performance ok, especially after enabling Smart Placement [1].","summary":"Runs two production apps with D1 as primary storage and finds performance acceptable once Smart Placement is enabled. The dissenting positive voice in an otherwise negative thread.","claim_ids":["c11"],"hash":"9da82063de66ce1ad721b450dc034701ea493e8ac4829e9201e4db1e969a20cb"},{"id":"p8","type":"hn","url":"https://news.ycombinator.com/item?id=48611834","title":"Temporary Cloudflare accounts for AI agents","quote":"It's almost always better to use Durable Objects storage, rather than D1. Even if you only want a single global database, it's better to implement that as a singleton Durable Object, than by using D1.","summary":"The Workers architect states D1 is literally a singleton Durable Object wrapping SQLite, so raw DOs give you code co-located with the database and near-zero query latency. Says D1 exists mainly for familiarity, with read replicas the one remaining D1 advantage. Positive on DOs, blunt about D1.","claim_ids":["c1","c2"],"hash":"aea5afcc0be9d9a0f2c8c1e706e86866718e3b2507366b83794c7b98584e19c1"},{"id":"p9","type":"hn","url":"https://news.ycombinator.com/item?id=48946048","title":"SQLite Is All You Need","quote":"We serve multi million MAU on sqlite orchestrated through durable objects. It's not the most complex thing in the world but it goes further than CRUD. It costs us such a small amount of money for what it does.","summary":"Runs multi-million monthly-active-user traffic on SQLite inside Durable Objects and says their previous Postgres cluster was orders of magnitude more expensive. Positive, at real scale.","claim_ids":["c16"],"hash":"e8a0c1b93f522c2fa2b6e7c87cf4ad90a0982eab4755c1853a482bca49962f03"},{"id":"p10","type":"hn","url":"https://news.ycombinator.com/item?id=47787042","title":"Durable Object alarm loop: $34k in 8 days, zero users, no platform warning","quote":"My DO agent's onStart() handler called this.ctx.storage.setAlarm() on every wake-up without checking whether an alarm was already scheduled.","summary":"Pre-launch solo founder posts a full postmortem: setAlarm() called unconditionally on every DO wake-up, multiplied by 60+ preview deployments each spawning independent DO instances, peaked at ~930 billion row reads/day and produced a $34,895 invoice with zero users. No platform warning fired. Negative.","claim_ids":["c22"],"hash":"e758a6877951d31a0991b0031c2bdbb6ccb428d5e8520c7991317ebeb1468336"},{"id":"r1","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"Fresh first-party indexed versus unindexed lookup receipt","quote":"Primary-key lookup for cloudflare-os-d1 | 1 row | 1 | 0.1485 ms","summary":"Wrangler 4.103.0 read-only production queries compared the primary-key slug lookup with an equality filter on the unindexed title.","claim_ids":["c24"],"hash":"11c8ad10a47f9d251c1341277fe06fe4613af53d5fc447b2e1ed7011fcd05f9f"},{"id":"r2","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"Fresh first-party compound SELECT receipt","quote":"Six `UNION ALL` terms | `too many terms in compound SELECT: SQLITE_ERROR [code: 7500]`","summary":"The same production database accepted five UNION ALL terms and rejected six; no rows changed.","claim_ids":["c7"],"hash":"4112165510fac6f24c4eca7780fc8d3a1f701f8f8544ee290b4e5b29704de643"},{"id":"r3","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"Fresh first-party event-ledger size receipt","quote":"401,112 events; database size 1,062,916,096 bytes","summary":"A read-only COUNT on the event database records current rows, rows_read, SQL duration and size_after.","claim_ids":["c19"],"hash":"0387ed14616a6594c2b3fbb26b74d9f3a9460e718021b681b2d8e62456b52b76"},{"id":"r4","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"Fresh first-party migration-ledger receipt","quote":"136 applied; latest `0133_charlie_audit.sql`","summary":"A read-only query of d1_migrations confirms the live ledger count and newest recorded migration.","claim_ids":["c18"],"hash":"ff9fe9433d6fe4d19e724697d88cde081a09f63d1679e78111cfa05602ee7c5c"},{"id":"r5","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"First-party 50,000-row index benchmark receipt","quote":"Same query, same answer, 526 times fewer rows read and 23.6 times faster.","summary":"Preview-only scratch-table harness: run the same tenant equality query before and after CREATE INDEX, then drop the table.","claim_ids":["c5","c6"],"hash":"cdd467ba01bc307a3dac2db006d06b8137fe4095be80969dbc627fe33c4b4bd2"},{"id":"r6","type":"runtime_receipt","url":"https://miscsubjects.com/api/articles/cloudflare-os-d1","title":"First-party D1-to-R2 revision offload receipt","quote":"`meta` fell from 2,068,258 bytes to 206,362 and the row returned HTTP 200.","summary":"Measured repair: full revision snapshots moved to R2 while D1 retained the index and hash chain.","claim_ids":["c9"],"hash":"10a0bdf5ac3f47f7c447e2f7ede897c9fe5387ea22ae069287395cf8fdd9ffa2"}],"anecdotal_sources":[],"scientific_sources":[],"user_reports":[],"related_articles":[],"question_graph":{"slug":"cloudflare-os-d1","questions":[],"evidence":[],"edges":[],"counts":{"questions":0,"evidence":0,"edges":0}},"honesty":{"active_claims":24,"retracted_claims":0,"cut_claims":0,"challenges":0,"scrub_events":0,"note":"Retracted/cut claims stay on ledger but are excluded from ask unless ?include_inactive=1"},"counts":{"claims":24,"claims_total":24,"sources":30,"anecdotal":0,"scientific":0,"user_reports":0,"questions":0,"evidence_ingests":0}}