Saltar al contenido

El Problema de la Concurrencia en E-commerce

Si administras una tienda en línea con Laravel, probablemente hayas enfrentado (o temas enfrentar) uno de los problemas más comunes en el e-commerce: la sobreventa de productos debido a accesos concurrentes.

Imagina este escenario: tienes un producto muy popular con solo 5 unidades en stock. Durante una campaña promocional, 10 clientes intentan comprar este producto exactamente al mismo tiempo. Sin los mecanismos adecuados de control de concurrencia, todos podrían completar sus compras, resultando en 5 clientes decepcionados cuando descubras que has vendido más de lo que tienes disponible.

Este es un problema real que puede afectar seriamente la reputación de tu negocio y la experiencia del cliente. Afortunadamente, Laravel ofrece herramientas poderosas para manejar estas situaciones.

Modelando Nuestro Sistema

Para ilustrar la solución, modelaremos un sistema básico de inventario con Laravel. Tendremos dos modelos principales:

Y crearemos un servicio para manejar las órdenes:

El Problema de Concurrencia

A simple vista, nuestro servicio parece funcionar correctamente, pero no es así:

Ambas solicitudes leen que hay 5 unidades disponibles, y ambas proceden con sus órdenes, resultando en un stock negativo y productos vendidos que no existen.

Solución 1: Transacciones de Base de Datos

El primer paso para solucionar este problema es envolver nuestras operaciones en transacciones de base de datos:

Esto garantiza que si alguna de las operaciones falla, se revertirán todas las operaciones anteriores. Sin embargo, todavía no resuelve nuestro problema de concurrencia.

Solución 2: Bloqueos Pesimistas

Aquí es donde entra en juego lockForUpdate(). Este método nos permite bloquear la fila que estamos consultando para que ninguna otra operación pueda leerla o modificarla hasta que completemos nuestra transacción:

Con esta implementación, lo que sucederá en caso de solicitudes concurrentes es:

¿Qué Hace Exactamente lockForUpdate()?

Según la documentación de Laravel:

Un bloqueo "para actualización" evita que los registros seleccionados se modifiquen o se seleccionen con otro bloqueo hasta que se confirme la transacción.

Esto significa que cuando usamos lockForUpdate(), cualquier otra consulta que intente seleccionar el mismo registro con lockForUpdate() o sharedLock() deberá esperar hasta que nuestra transacción se complete.

¿Y Qué Hay de sharedLock()?

El método sharedLock() es ligeramente diferente:

Un bloqueo compartido evita que se modifiquen las filas seleccionadas hasta que se confirme la transacción.

A diferencia de lockForUpdate(), las consultas con sharedLock() pueden compartir el bloqueo con otras consultas que también usen sharedLock(). Esto permite múltiples lecturas simultáneas, pero impide cualquier modificación.

¿Cuándo Usar lockForUpdate() vs sharedLock()?

  • Usa lockForUpdate() cuando planeas modificar los registros que estás consultando y necesitas asegurarte de que no se modifiquen mientras realizas tus operaciones.

  • Usa sharedLock() cuando solo necesitas leer los registros y quieres asegurarte de que no se modifiquen mientras los lees, pero no te importa que otros procesos también los lean.

Caso Práctico Extendido: Reserva Temporal de Inventario

En sistemas de e-commerce más sofisticados, a menudo se implementa un sistema de "reserva temporal" para mantener productos apartados durante un tiempo mientras el cliente completa su pago.

Podemos extender nuestro ejemplo para manejar este caso:

Esta implementación más compleja demuestra cómo lockForUpdate() sigue siendo crucial incluso en sistemas sofisticados.

Rendimiento e Impacto en la Base de Datos

Es importante señalar que los bloqueos tienen un impacto en el rendimiento de la base de datos. Al usar lockForUpdate(), estás esencialmente forzando a las transacciones a ejecutarse en serie para las filas bloqueadas, lo que puede afectar el rendimiento si tienes un alto volumen de transacciones.

Algunas recomendaciones:

  1. Mantén tus transacciones lo más cortas posible.

  2. Bloquea solo las tablas y filas que realmente necesitas.

  3. Considera implementar una solución de cola para manejar picos de tráfico.

  4. Para sistemas de muy alto rendimiento, considera soluciones complementarias como Redis para el manejo de inventario en tiempo real.

Conclusión

Los bloqueos en Laravel, específicamente lockForUpdate() y sharedLock(), son herramientas poderosas para manejar la concurrencia en aplicaciones web. En sistemas críticos como la gestión de inventario, son esenciales para mantener la integridad de los datos y evitar problemas como la sobreventa.

Si bien los ejemplos en este artículo se centran en un sistema de inventario, los mismos principios se aplican a cualquier sistema donde la concurrencia pueda ser un problema: sistemas de reservas, plataformas de subastas en línea, aplicaciones de venta de entradas, y muchos más.

Espero que este artículo te haya ayudado a entender mejor cómo implementar correctamente los bloqueos en Laravel para garantizar la integridad de tus datos.

Nota: Para una implementación más robusta en producción, considera también manejar timeouts de bloqueo y estrategias de reintento, así como utilizar colas para operaciones que puedan resultar en bloqueos prolongados.

school Curso completo

Curso Laravel 12
Completo 2026

El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje