wandres.dev
D1 AVANZADO · migraciones y réplicas

Límites de D1 y cuándo Hyperdrive

Cada tecnología dibuja, con sus límites, la forma de los problemas que resuelve bien. D1 no es una base pequeña: es una apuesta arquitectónica por muchas bases pequeñas en lugar de una gigante. Sus números —diez gigabytes por base como techo infranqueable, un solo hilo de ejecución— no son carencias, sino el contorno de un modelo. Los límites de tamaño y rendimiento de D1, por qué el caudal es el inverso de la duración de la consulta, cómo diseñar con sharding por entidad a favor del grano, y cuándo tu caso pide en realidad un Postgres o MySQL existente acelerado desde Workers con Hyperdrive, su pool de conexiones global y su caché de consultas.

⏱ 17 min

Cada tecnología dibuja, con sus límites, la forma de los problemas que resuelve bien. D1 no es una base pequeña: es una apuesta arquitectónica por muchas bases pequeñas en lugar de una gigante. Sus números —diez gigabytes por base como techo infranqueable, un solo hilo de ejecución— no son carencias que Cloudflare no ha tenido tiempo de arreglar, sino el contorno de un modelo. Entenderlos te dice cuándo D1 es la herramienta perfecta y cuándo tu problema pide, en realidad, un Postgres detrás de Hyperdrive.

🎯 Al terminar esta lección sabrás
  • Conocer los límites de tamaño de D1 y por qué el techo de 10 GB es deliberado.
  • Entender el modelo de un solo hilo y su relación directa con el rendimiento.
  • Diseñar con D1 a favor de su grano: muchas bases pequeñas, no una enorme.
  • Reconocer cuándo el caso pide Postgres o MySQL a través de Hyperdrive.

El tamaño y su techo

Una base D1 almacena hasta 10 GB en el plan de pago, y ese número, a diferencia de casi todos los límites de la plataforma, no se puede subir por solicitud: es un tope de diseño. Lo que sí es enorme es la cantidad de bases: 50.000 por cuenta de partida —ampliable a millones—, con hasta 1 TB de almacenamiento agregado. La asimetría es el mensaje. Cloudflare no te invita a hacer una base grande; te invita a hacer muchísimas pequeñas.

Cada tabla admite hasta 100 columnas, cada fila o BLOB hasta 2 MB, cada sentencia SQL hasta 100 KB y cada consulta hasta 100 parámetros ligados. Son límites holgados para el grano que D1 espera —una base por usuario, por inquilino, por entidad— y estrechos para quien intenta meter un monolito relacional dentro.

Cada uno de estos números es, en realidad, una pregunta disfrazada. El tope de 2 MB por fila pregunta si de verdad ese blob pertenece a la base o a R2; el de 100 columnas, si esa tabla ancha no son en realidad varias entidades sin normalizar; el de 100 KB por sentencia, si esa consulta gigante no debería ser un lote. Los límites de D1 empujan, con suavidad, hacia esquemas y accesos sanos.

ℹ️
Un límite bien puesto es un consejo

Los topes por fila, por columna y por sentencia rara vez estorban a un diseño limpio; cuando los rozas, casi siempre es la señal temprana de un modelo que conviene revisar. Leer un límite como una crítica constructiva a tu esquema, y no como un obstáculo, es una de las marcas de madurez con esta clase de plataformas.

ℹ️
Límites por cuenta, no solo por base

Más allá del tamaño de cada base, la cuenta tiene sus propios topes: 50.000 bases y 1 TB agregado en el plan de pago, ambos ampliables por solicitud hasta millones de bases. La única cifra que no se mueve es la de 10 GB por base. Cuando una plataforma amplía todo menos un número, ese número está diciendo algo sobre cómo quiere que la uses.

Un solo hilo

El hecho técnico que gobierna el rendimiento de D1 es que cada base está respaldada por un único Durable Object y procesa las consultas de una en una. No hay paralelismo dentro de una base: es un hilo. De ahí sale una fórmula sorprendentemente simple para el rendimiento: tu caudal máximo es el inverso de la duración media de tus consultas.

consulta media de 1 ms    ->  cerca de 1000 consultas por segundo
consulta media de 100 ms  ->  cerca de 10 consultas por segundo

Si las consultas se agolpan más rápido de lo que se pueden procesar, D1 las encola; si la cola se llena, devuelve un error de sobrecarga. Por eso el rendimiento en D1 no se compra con más CPU, se gana con consultas más cortas: índices adecuados, sentencias afiladas, lotes bien pensados. La replicación de lectura escala las lecturas repartiéndolas entre réplicas, pero cada réplica es también de un solo hilo, y toda escritura sigue embudada en la primaria.

Conviene ver este número no como un castigo sino como un contrato de rendimiento honesto. Una base tradicional oculta su contención tras un pool de hilos y un planificador; D1 la expone: si tus consultas son lentas, tu caudal cae de forma visible y predecible, y la solución está siempre en el mismo sitio, la consulta. Medir la duración media de tus sentencias deja de ser un ejercicio académico para volverse la métrica que gobierna cuántos usuarios concurrentes puede servir una base.

Esa aritmética también explica por qué la replicación de lectura importa tanto justo aquí: no acelera una consulta individual, pero multiplica el número de hilos que pueden atender lecturas a la vez, uno por réplica. El techo de escritura sigue siendo el hilo único de la primaria; el de lectura se reparte. Saber cuál de los dos es tu cuello de botella —lecturas o escrituras— decide si la replicación te ayuda o si tu problema tiene otra forma. En D1, medir precede a optimizar, y casi siempre la optimización es la misma: acortar la consulta.

💡
El límite que más sorprende

Una sola invocación de tu Worker puede abrir como máximo seis conexiones simultáneas a D1, y ejecutar hasta 1000 consultas por invocación en el plan de pago. Si te acercas a esos números en una sola petición, la señal no es pedir más límite: es que tu acceso a datos necesita rediseñarse hacia menos consultas y más gruesas.

Diseñar dentro de los límites

El error clásico al llegar a D1 desde Postgres es tratarlo como un Postgres pequeño y chocar contra el techo de 10 GB. El diseño correcto invierte el grano: si tu SaaS tiene inquilinos, dale a cada uno su base; si tu app es por usuario, una base por usuario. Miles o millones de bases de 10 GB, cada una de un solo hilo, escalan de forma horizontal y sin coste por existir, porque pagas por consultas y almacenamiento, no por base.

Este patrón —sharding por entidad— convierte los límites en virtudes. El aislamiento entre inquilinos es físico, no lógico; el replica lag de un cliente no afecta a otro; una restauración con Time Travel toca a un solo inquilino. Lo que en una base monolítica serían problemas de contención y de radio de impacto, aquí se disuelven porque el grano es fino.

Nótese además que la replicación y Time Travel, de las lecciones anteriores, escalan con este grano: replicar mil bases pequeñas cerca de sus usuarios y restaurar una sola sin tocar a las demás son operaciones baratas justamente porque cada base es pequeña. Los límites de D1 no viven aislados; conspiran a favor del mismo modelo horizontal.

Hay un caso que este diseño no elude, y conviene decirlo con claridad: si tu problema es intrínsecamente una sola entidad enorme —un grafo social único, un libro mayor contable global, un índice compartido por todos—, partirlo en bases por inquilino no tiene sentido, porque no hay inquilinos que separar. Forzar ese caso en D1 te obliga a coser consultas entre bases a mano, reinventando mal lo que un motor relacional grande ya hace bien. Reconocer esa frontera es parte de la competencia.

📝
El grano es una decisión de dominio, no de infraestructura

Elegir entre muchas bases y una grande no se resuelve mirando gráficas de rendimiento, sino entendiendo la forma de tus datos: dónde están las fronteras naturales entre entidades y cuánto se cruzan tus consultas entre ellas. La infraestructura solo confirma lo que el dominio ya decidió.

💡
Una prueba rápida de encaje

Antes de comprometerte con D1 para un caso dudoso, intenta escribir la consulta más cara de tu producto. Si cabe con naturalidad dentro de una sola base por entidad, D1 encaja; si necesita unir datos de muchas entidades a la vez y de forma habitual, esa fricción es tu problema pidiéndote Postgres.

🗃️

D1: muchas y pequeñas

SQLite en el edge, hasta 10 GB por base, un solo hilo. Brilla con sharding por inquilino o usuario y lecturas replicadas cerca del cliente.

🐘

Hyperdrive: una y grande

Tu Postgres o MySQL existente, acelerado desde Workers con pool de conexiones global y caché de consultas. Para una base relacional central y voluminosa.

Cuándo Hyperdrive

Hay problemas que no encajan en el grano de D1: una base relacional que supera los 10 GB y no se deja partir, un esquema que ya vive en Postgres con extensiones y tipos de los que dependes, una carga de escritura que un solo hilo no absorbe, o simplemente un equipo con años de SQL de Postgres que no va a reescribir. Para eso está Hyperdrive: no es una base, es un acelerador que pone tu Postgres o MySQL existente —esté en AWS, Neon, PlanetScale o donde sea— al alcance de un Worker con baja latencia.

import { Client } from "pg";

export default {
  async fetch(request, env) {
    // Hyperdrive mantiene el pool; crear el cliente en cada peticion es barato
    const client = new Client({ connectionString: env.HYPERDRIVE.connectionString });
    await client.connect();
    const { rows } = await client.query("SELECT * FROM productos LIMIT 20");
    return Response.json(rows);
  },
};

Lo hace con dos mecanismos. Un pool de conexiones global elimina el coste de abrir una conexión nueva en cada invocación, que para una base regional accedida desde el edge sería demoledor. Y una caché de consultas, activa por defecto, sirve las lecturas más populares sin ir hasta el origen. Sigues usando tus drivers y tu ORM de siempre; Hyperdrive se interpone y los vuelve viables desde el edge.

El binding se declara en la configuración de Wrangler, y a partir de ahí env.HYPERDRIVE expone la conexión que tu driver consume:

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "tu-id-de-hyperdrive",
      "localConnectionString": "postgres://usuario:clave@localhost:5432/midb"
    }
  ]
}
⚠️
Hyperdrive no borra la física, la reorganiza

Hyperdrive acelera el acceso, pero tu Postgres sigue siendo una base regional con un solo primario de escritura y sus propios límites de conexiones. No convierte a Postgres en una base de borde ni elimina la latencia hasta el origen para escrituras o para lecturas no cacheadas; reduce el coste de conexión y sirve lo cacheable. Elegirlo es elegir la semántica y el ecosistema de Postgres, no huir de sus leyes.

📝
Postgres, MySQL y sus compatibles

Hyperdrive habla con cualquier Postgres o MySQL, y también con bases compatibles con el protocolo de Postgres como CockroachDB o Timescale, estén alojadas en Neon, PlanetScale, AWS o tu propio servidor. No reescribes nada: tu ORM y tus migraciones de Postgres siguen siendo los mismos; Hyperdrive solo cambia el camino por el que el Worker llega a ellos.

La honestidad intelectual aquí vale más que la lealtad a una herramienta: no todo pertenece a D1, y saber cuándo salir de él es tan parte del oficio como saber usarlo bien.

El criterio de elección, al final, se reduce a una sola pregunta sobre la forma del dato:

flowchart TD
Q[como es tu problema de datos] --> A[muchas entidades pequenas y aisladas]
Q --> B[una masa relacional central y grande]
A --> D1[D1 con sharding por entidad]
B --> C[ya vive o debe vivir en postgres o mysql]
C --> HY[Hyperdrive lo acelera desde el edge]
style D1 fill:#a6e3a1,color:#11111b
style HY fill:#89b4fa,color:#11111b
Los límites no son el borde de lo posible, son la forma de una filosofía

Cuando un ingeniero se topa con el límite de 10 GB de D1 y lo lee como una carencia —que todavía no soportan bases grandes—, comete el error de interpretar una decisión de diseño como una limitación técnica pendiente de resolver. Ambas son indistinguibles desde fuera, un número que no puedes exceder, pero significan cosas opuestas. Una limitación técnica es una promesa implícita de que algún día subirá; una decisión de diseño es la declaración de una filosofía, y el hecho de que este límite en concreto sea el único que Cloudflare no amplía por solicitud es la pista de que estamos ante lo segundo. D1 no es una base pequeña que aspira a ser grande: es la encarnación de la tesis de que el escalado de datos en el edge debe ser horizontal y no vertical, muchas bases minúsculas y aisladas en vez de un coloso compartido. Un solo hilo por base no es un cuello de botella que no han paralelizado; es lo que hace que cada base sea un Durable Object razonable, restaurable, replicable y barato, y lo que permite que existan millones de ellas sin que ninguna contamine a las demás. Verlo así reordena la pregunta que te haces frente a un problema de datos. La ingenua es si D1 aguanta tu base, y lleva a forzar un monolito dentro de un molde que lo rechaza. La madura es si tu problema es de muchas entidades pequeñas y aisladas o de una masa relacional central, y su respuesta te dice la herramienta sin más discusión: si es lo primero, D1 con sharding por inquilino es elegante justamente por sus límites; si es lo segundo, ninguna cantidad de ingenio hará que D1 deje de ser el molde equivocado, y Hyperdrive existe precisamente para que no tengas que intentarlo, acelerando el Postgres que tu problema pedía desde el principio. La sabiduría no está en memorizar la tabla de límites, sino en leer, a través de ella, la forma del problema para el que la herramienta fue tallada, y en tener la honestidad de cambiar de herramienta cuando tu problema tiene otra forma.

⚔️ Lee la forma del problema
  1. Enumera los límites de una base D1 que más afectan a tu diseño: tamaño, hilo único, consultas por invocación y conexiones simultáneas.
  2. Estima el caudal máximo de una base cuya consulta media dura 5 ms y razona qué pasaría al duplicar esa duración.
  3. Rediseña un esquema multiinquilino monolítico como sharding por inquilino con una base D1 por cliente; nombra dos ventajas concretas.
  4. Describe un caso real que no quepa en D1 y justifica por qué pide Postgres a través de Hyperdrive.
  5. Explica los dos mecanismos con que Hyperdrive vuelve viable una base regional desde el edge: pool de conexiones y caché de consultas.