operaciones
Un solo stock para seis canales: dónde se rompe en realidad
Vender en web + Telegram + ChatGPT + WhatsApp + voz con un único pool de inventario suena simple. Se rompe siempre igual.
El sueño es: una tabla de productos, seis superficies de venta, y el stock simplemente funciona. El problema real es la unidad de consistencia, no las superficies.
La race condition que sufre toda tienda multi-canal
Dos clientes, dos canales, última unidad.
- 00:00.000 — Cliente A en la web abre la PDP. El servidor lee
stock = 1. - 00:00.040 — Cliente B en Telegram pregunta "¿sigue disponible?". El bot lee
stock = 1. Responde "sí". - 00:00.180 — A clica Comprar.
- 00:00.220 — B dice "lo tomo". El bot llama
add_to_cart. - 00:00.310 — Checkout de A escribe
stock -= 1(ahora 0). - 00:00.350 — Checkout de B escribe
stock -= 1(ahora -1).
Tienes overselling y uno recibirá un email de disculpa.
Lo que funciona: bloqueo pesimista en la fila del SKU
Cada add-to-cart o checkout que quiere consumir stock toma un row-level lock en la fila del SKU, lee dentro del lock, decide, escribe, libera. La web bloquea 50ms mientras el bot termina; el segundo ve la verdad y rechaza. Ambos clientes reciben la respuesta correcta.
En Prisma:
await prisma.$transaction(async (tx) => {
const product = await tx.product.findUnique({ where: { id }, select: { stock: true, trackStock: true } });
if (product?.trackStock && product.stock < qty) throw new Error("OUT_OF_STOCK");
await tx.product.update({ where: { id }, data: { stock: { decrement: qty } } });
});
SQLite serializa por file-lock; Postgres por row-lock; en cualquier caso la segunda transacción espera el commit de la primera.
Lo que no funciona: cachés
Réplicas de lectura, cachés Redis, contadores denormalizados por canal — todo gana latencia en lectura y pierde corrección en escritura. La escritura del stock debe ser autoritativa; lecturas pueden cachearse, pero el caché debe invalidarse síncronamente en cada escritura o le mentirá al próximo canal.
Lo que se rompe con reservaciones
Muchas plataformas intentan "reservar" stock al iniciar el carrito y "confirmar" en checkout. Funciona hasta que el carrito se abandona y nadie limpia la reserva. Tres meses después, tu stock autoritativo y el mostrado divergen 40%. La cura es un TTL de reserva: tras N minutos de inactividad, libérala automáticamente.
Lo que añaden los canales AI
Cada canal AI (ChatGPT, Telegram, voz) es un tool consumer que habla con tu tienda. La superficie de race-condition se multiplica, pero la cura no cambia. Las tool-calls llegan al mismo decremento atómico. El AI no sabe que está en una carrera; la base sí.
Los comerciantes que nunca tienen overselling tratan el inventario como una cuenta bancaria: cada retiro bloquea primero, lee dentro del bloqueo, decide, escribe. Aburrido, rápido, correcto.