Lo que admite la base de datos de tu sitio: transacciones, disparadores y concurrencia
Actualizado: 2026-10-19 · YaDominios
Cada sitio de YaDominios Cloud con plan de pago tiene su propia base SQLite, que tu código usa como env.DB. Admite disparadores con RAISE(ABORT), claves foráneas activas por defecto, vistas, índices únicos y parciales, CHECK, EXISTS, INSERT … SELECT y UPDATE … RETURNING. Las transacciones se hacen con env.DB.batch([...]): si una sentencia falla, se deshace el lote entero. BEGIN y COMMIT sueltos no se admiten. La base atiende una consulta a la vez, así que dos pedidos simultáneos nunca se pisan: con UPDATE … WHERE … RETURNING o con un índice UNIQUE solo uno gana. Límites por base: 10 GB, 30 segundos por consulta, 100 parámetros por consulta y 1.000 consultas por petición. No hay tareas programadas ni Durable Objects dentro de la plataforma.
Para quién es: para quien programa una app sobre YaDominios Cloud —una persona o una IA— y necesita saber, antes de diseñar, qué puede confiarle a la base de datos de su sitio. Todo lo que dice esta página lo comprobamos con pruebas automáticas sobre el mismo motor que corre en producción.
En resumen
| Pregunta | Respuesta |
|---|---|
| ¿Qué base es? | SQLite, una por sitio, en tu código como env.DB. Sin intermediarios: tu código habla directo con ella. |
| ¿Transacciones? | Sí, con env.DB.batch([...]): todo o nada. |
¿BEGIN / COMMIT a mano? | No. Se rechazan. Usa batch(). |
¿Disparadores (CREATE TRIGGER)? | Sí, con RAISE(ABORT, 'mensaje'), dentro de la misma transacción. |
| ¿Claves foráneas? | Sí, activas por defecto. No hace falta PRAGMA foreign_keys = ON. |
| ¿Dos pedidos a la vez? | La base atiende una consulta a la vez. Con UPDATE … RETURNING o UNIQUE, solo uno gana. |
| ¿Tareas programadas o Durable Objects? | No dentro de la plataforma. Ver más abajo. |
Transacciones: el lote es todo o nada
Agrupa las escrituras que tienen que ir juntas en un batch(). Si cualquiera falla, se deshacen todas, también las que ya habían corrido. Lo probamos con un lote de cinco sentencias donde falla la tercera: no queda guardada ninguna. Las mismas cinco sentencias sueltas, sin batch(), sí dejan guardadas las dos primeras.
// Crear el pedido y descontar el inventario: o las dos cosas, o ninguna.
await env.DB.batch([
env.DB.prepare("insert into pedidos (id, producto_id, cantidad) values (?, ?, ?)").bind(id, producto, 2),
env.DB.prepare("update productos set stock = stock - 2 where id = ? and stock >= 2").bind(producto),
]);
Lo que no funciona: mandar BEGIN TRANSACTION y COMMIT como sentencias separadas. La base lo rechaza con un error que pide usar batch().
Disparadores
CREATE TRIGGER funciona con BEFORE y AFTER sobre INSERT, UPDATE y DELETE. Un disparador que hace RAISE(ABORT, 'mensaje') cancela la sentencia, y lo que el disparador ya había escrito también se deshace: corre en la misma transacción. El mensaje le llega a tu código dentro del error.
create trigger pedido_entregado_no_cambia
before update of estado on pedidos
when old.estado = 'entregado'
begin
select raise(abort, 'un pedido entregado no cambia de estado');
end;
Los disparadores pueden ir en el schema.sql de tu repositorio: al publicar respetamos los bloques BEGIN … END; aunque lleven punto y coma por dentro.
Claves foráneas
Están activas siempre. Una fila que apunta a un registro que no existe se rechaza con FOREIGN KEY constraint failed. Si en una migración necesitas romperlas un momento, usa PRAGMA defer_foreign_keys = on: lo que siga roto al terminar la transacción da error. PRAGMA foreign_key_check también funciona.
Dos pedidos al mismo tiempo
La base de cada sitio atiende una consulta detrás de otra. Dos solicitudes que tocan la misma fila nunca se pisan: una espera a la otra. Para asignar algo que solo puede tener un dueño —un turno, un cupo, un repartidor— usa una sola sentencia que compruebe y tome a la vez:
const { results } = await env.DB
.prepare("update repartidores set libre = 0 where id = ? and libre = 1 returning id")
.bind(repartidor)
.all();
if (results.length === 0) {
// Otro pedido lo tomó primero: elige otro.
}
Lo probamos con dos solicitudes simultáneas sobre el mismo registro: una gana y la otra recibe cero filas. Con un índice UNIQUE pasa lo mismo: de dos inserciones con el mismo valor, una entra y la otra se rechaza.
Para tener en cuenta: como las consultas hacen fila, una consulta lenta retrasa a las demás de esa base. Usa índices, mantén cortas las consultas frecuentes y, si una parte de tu app escribe muchísimo (por ejemplo, ubicaciones en vivo), ponla en una base aparte del mismo sitio.
Funciones de SQLite
Todas estas están disponibles y comprobadas una por una:
CREATE VIEWCREATE UNIQUE INDEXe índices parciales conWHERECHECK(un valor fuera de regla se rechaza)EXISTSyNOT EXISTSINSERT … SELECTUPDATE … RETURNINGdatetime('now')(en hora universal)
Límites
| Límite | Valor |
|---|---|
| Tamaño de cada base | 10 GB |
| Duración de una consulta | 30 segundos |
| Parámetros por consulta | 100 |
| Columnas por tabla | 100 |
| Tamaño de una fila, texto o archivo binario | 2 MB |
| Largo de una sentencia SQL | 100 KB |
| Consultas por cada petición a tu sitio | 1.000 |
Lo que permite tu plan (almacenamiento total, cantidad de bases y sitios) se ve en la página de planes y en tu panel.
La consulta por HTTP es para mantenimiento
Además de env.DB, puedes consultar la base desde fuera con la API de consultas o con el Acceso para IA. Esa puerta corre una consulta por llamada, cada una en su propia transacción, y suma el tiempo de ida y vuelta por internet. Sirve para revisar datos, migraciones y soporte. Para lo que tu app hace a cada momento, usa env.DB desde el código del sitio.
Tareas programadas y tiempo real
YaDominios Cloud no ejecuta tareas programadas ni admite Durable Objects propios dentro del sitio. La receta que funciona:
- Haz el trabajo dentro de la petición cuando se pueda: por ejemplo, asignar en el momento en que entra el pedido, con
UPDATE … RETURNINGdentro de unbatch(). - Lo que necesita reloj (vencer una oferta, reintentar algo cada minuto) lo dispara un servicio externo que llama por internet a una ruta de tu sitio, protegida con un secreto que guardas en las variables del panel.
- Para que dos llamadas no hagan lo mismo a la vez, guarda una fila de «turno» y tómala con
UPDATE … WHERE vence < ? RETURNING: solo una llamada la consigue.
Los detalles están en la guía Publicar en YaDominios Cloud.
Preguntas frecuentes
¿Puedo usar BEGIN y COMMIT?
No. Agrupa las sentencias en env.DB.batch([...]): si una falla, se deshacen todas.
¿Tengo que activar las claves foráneas?
No. Están activas siempre, sin PRAGMA.
¿Dos usuarios pueden comprar el último producto al mismo tiempo?
No, si lo descuentas con una sola sentencia que compruebe y descuente a la vez (UPDATE … WHERE stock >= 1 RETURNING). La base atiende una consulta a la vez y solo una de las dos lo consigue.
¿Funcionan los disparadores?
Sí, BEFORE y AFTER, con RAISE(ABORT) para cancelar. Lo que el disparador escribió se deshace junto con la sentencia.