← Todo el blog

Deja que la base de datos diga no: evitar dobles reservas

Dos personas, un hueco, el mismo milisegundo. Las comprobaciones en la aplicación pierden esta carrera — Postgres puede ganarla.

  • PostgreSQL
  • Concurrencia
  • Backend

Comprobar‑y‑luego‑insertar es una condición de carrera con una invitación de calendario adjunta. Bajo carga, dos peticiones leen «el hueco está libre» y ambas lo reservan tan contentas. Aprendí a dejar de arreglarlo en el código y a dejar que la base de datos lo rechace.

La trampa de comprobar‑y‑actuar

SELECT ... WHERE hueco libre y luego INSERT tiene un hueco entre la lectura y la escritura. Dos peticiones concurrentes pueden pasar la comprobación antes de que cualquiera inserte. Ningún código cuidadoso cierra esa ventana de forma fiable — solo la estrechas, no la eliminas.

Haz la restricción física

Las restricciones EXCLUDE de Postgres con un tipo de rango rechazan cualquier fila que se solape con otra existente — al confirmar, dentro del motor. La segunda reserva no recibe un «lo siento» amable de tu código; es físicamente imposible de almacenar. Eso es corrección que no puedes olvidar escribir ni saltarte sin querer.

Las zonas horarias son el otro monstruo

«9:00» no significa nada sin zona, y el horario de verano hace que algunas horas locales no existan y otras ocurran dos veces. Calcula la disponibilidad con la Temporal API contra zonas reales, guarda los instantes en UTC y muéstralos en la zona del visitante. La aritmética de fechas casera es donde mueren las apps de reservas.

Los efectos secundarios necesitan un outbox

El correo de confirmación no debe desaparecer si la petición se cae justo tras confirmar. Escribe la intención en una tabla outbox dentro de la misma transacción y entrégala de forma asíncrona. «Confirmado, se enviará» le gana a «enviado, quizá confirmado».

El hilo común: empuja la corrección a la capa donde se pueda garantizar, no solo esperar.