Tema representativo de entrevista

Entrevista de System Design: Diseñar un sistema de venta de boletos de alta demanda

Diseño de sistemasDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Diseña un sistema de venta de boletos de alta demanda para un evento con 50,000 asientos asignados. Dos millones de usuarios llegan en los primeros cinco minutos de la venta, con picos en el edge de 50,000 solicitudes por segundo. Admite hasta 10,000 compradores concurrentes en el mapa de asientos y gestiona 2,000 intentos de reserva y 1,000 autorizaciones de pago por segundo en el pico. La reserva de un asiento dura cinco minutos. Explica la sala de espera virtual, las APIs, el modelo de datos, las máquinas de estados de asientos y órdenes, la consistencia, la expiración de reservas, los fallos de pago, el escalamiento y la verificación, garantizando que un asiento se venda a lo sumo una vez.

Enunciado y contexto aplicable

Diseña un sistema de reserva de boletos para conciertos, eventos deportivos o exposiciones de alta demanda. Un evento tiene 50,000 asientos asignados. Dos millones de usuarios llegan en los primeros cinco minutos de la venta y el tráfico en el edge alcanza picos de 50,000 solicitudes por segundo. El sistema no admite más de 10,000 compradores concurrentes en el mapa de asientos y gestiona hasta 2,000 intentos de reserva y 1,000 autorizaciones de pago por segundo. Una reserva dura cinco minutos; un asiento reservado que no se compra debe volver a estar disponible para la venta.

Asume que el proveedor de pagos admite autorización y captura por separado. El objetivo de p99 para el mapa de asientos es inferior a 500 milisegundos, la creación de reservas inferior a 300 milisegundos y las lecturas del estado de la orden inferiores a 200 milisegundos. El volumen de llegada, los objetivos de latencia, la duración de la reserva y la tasa de pago son supuestos de la entrevista, no métricas de referencia publicadas por ninguna empresa de venta de boletos. La regla de corrección fundamental es que un asiento en un evento pertenece a lo sumo a una orden vendida válida. Cuando la disponibilidad se degrada, el servicio puede pausar la admisión o rechazar reservas; no debe suponer el inventario y continuar vendiendo.

El alcance incluye la exploración de eventos, una sala de espera virtual, el mapa de asientos, reservas atómicas de múltiples asientos, el checkout, la confirmación previa a la emisión, la expiración y la recuperación. La creación de eventos, los precios dinámicos, la reventa secundaria, la contabilidad de reembolsos y un sistema completo de detección de bots están fuera del alcance, pero la respuesta debe definir cómo se conectan con los límites del inventario y del pago. El material público de system design de 2026 sigue presentando la venta de boletos como un problema combinado de concurrencia y avalanchas de tráfico masivas. El material público de Ticketmaster también describe un flujo real en el que los compradores entran en una sala de espera virtual y acceden a la selección de asientos y al checkout a un ritmo controlado.

Qué evalúa el entrevistador

La primera señal es si el candidato separa la equidad de la cola de la corrección del inventario. Una sala de espera virtual protege el origen y controla el ingreso a la venta. No puede evitar que dos compradores admitidos soliciten el mismo asiento. La transacción autoritativa de inventario debe proporcionar la exclusión final.

La segunda señal es la distinción entre un bloqueo de fila en la base de datos y una reserva de negocio. Un bloqueo de fila en PostgreSQL pertenece al interior de una transacción corta y se libera cuando la transacción finaliza. Mantener una conexión y una transacción abiertas durante los cinco minutos en los que un comprador ingresa sus datos y paga es inseguro. El significado de negocio más prolongado debe ser un estado duradero con un hold_id y un expires_at.

La tercera señal son las transiciones de estado conscientes del tiempo y de los reintentos. Un worker de expiración retrasado no debe liberar un asiento que un nuevo comprador tiene reservado en ese momento o que ya ha sido vendido. Un timeout del cliente no debe crear una segunda reserva o un segundo pago. Cada transición verifica el estado actual, el propietario, la versión y la fecha límite, mientras que una clave de idempotencia recupera el resultado original.

La cuarta señal es un límite de pago correcto. La autorización puede tener éxito mientras que la confirmación local falla; una transacción de venta puede confirmarse (commit) mientras su respuesta se pierde; la captura puede tener éxito antes de que llegue su webhook. Las respuestas sólidas preservan los registros de la orden y de la operación de pago y convergen mediante consultas de búsqueda, webhooks y conciliación. No tratan una respuesta faltante como un fallo.

Por último, la capacidad y la verificación deben abordar el cuello de botella real. La CDN, el almacenamiento en caché y las réplicas de lectura pueden absorber la exploración. Las escrituras de asientos para un evento de alta concurrencia aún deben residir en un único shard transaccional autoritativo. Dividir los asientos de un mismo evento entre shards de escritura demasiado pronto convierte una reserva común de tres asientos en una transacción distribuida.

Preguntas para clarificar antes de responder

  • ¿Los asientos son asignados o de admisión general? Los asientos asignados necesitan un estado exclusivo por asiento. La admisión general se modela mejor como una cantidad disponible por nivel de boleto con un decremento condicional que no puede volverse negativo.
  • ¿Una solicitud de múltiples asientos debe ser de tipo todo o nada? Este enunciado requiere reservas de tipo todo o nada de uno a seis asientos. El éxito parcial cambia la fijación de precios, el comportamiento de liberación y la experiencia del comprador.
  • ¿Cuál es la política de equidad de la cola? FIFO premia la llegada temprana; la liberación aleatoria reduce el efecto de las diferencias de red a escala de milisegundos. La política debe divulgarse y permanecer estable en lugar de cambiar a mitad de la venta sin dejar de proclamar una equidad ordenada.
  • ¿Se pueden separar la autorización y la captura del pago? En caso afirmativo, autoriza, confirma los asientos y luego captura. La captura inmediata requiere compensación cuando se toma el dinero pero falla la confirmación del asiento.
  • ¿Cuál es el límite de boletos por cuenta? Aplícalo tanto en la reserva como en la confirmación de la orden. Una restricción exclusiva del navegador es eludible.
  • ¿Pueden varias regiones escribir en el mismo evento? El diseño base asigna una sede de escritura a cada evento y sirve la exploración y la gestión de colas en otros lugares. Las escrituras multirregionales concurrentes requieren consenso entre regiones o inventario preasignado y cambian tanto la latencia como el comportamiento ante fallos.
  • ¿Qué tan actualizado debe estar el mapa de asientos? Si unos segundos de desfase son aceptables, utiliza una instantánea (snapshot) más deltas. Todo estado AVAILABLE mostrado sigue siendo orientativo hasta que la transacción de reserva tenga éxito.
  • ¿Qué sucede si una reserva expira mientras la autenticación del pago aún se está ejecutando? Define un único período de gracia PAYMENT_PENDING acotado. No renueves indefinidamente ni revendas mientras se desconozca el resultado del pago.

Estructura de respuesta de 30 segundos

“Mantendría a los dos millones de personas que llegan en una sala de espera virtual delimitada por evento y emitiría tokens de admisión firmados de corta duración únicamente al ritmo que puedan soportar el shard de inventario, el proveedor de pagos y el emisor de boletos. La exploración y el mapa de asientos utilizan CDN, caché y deltas versionados; las escrituras de reservas se dirigen al único shard de base de datos autoritativo del evento. Una transacción corta bloquea los asientos seleccionados en orden secuencial y escribe un hold_id con una expiración de cinco minutos basada en la hora de la base de datos solo si todos los asientos están disponibles o expirados. Ningún bloqueo de base de datos permanece abierto durante el checkout. Una cola retrasada acelera la limpieza de expiraciones, pero la liberación aún coincide condicionalmente con la reserva, el estado y la fecha límite. El pago utiliza un ID de operación estable para autorizar primero. Tras la autorización, una transacción revalida la reserva y confirma (commits) los asientos, la orden y la outbox como vendidos antes de la captura. Cualquier timeout pasa a ser desconocido y converge mediante webhooks, búsquedas y conciliación. Probaría el diseño con contención por el mismo asiento, carreras entre expiración y pago, respuestas perdidas, mensajes duplicados y fallos de shards.”

Análisis detallado paso a paso

Paso 1: Convertir el volumen en un objetivo de control de admisión

Dos millones de llegadas en 300 segundos promedian unas 6,667 por segundo. El pico en el edge de 50,000 solicitudes por segundo muestra que el instante de apertura y las actualizaciones del navegador son mucho más irregulares y concentrados que el promedio. El inventario está presupuestado para solo 2,000 intentos de reserva y 1,000 autorizaciones de pago por segundo, por lo que los reintentos en el edge no pueden llegar directamente al servicio de inventario.

Cada reserva puede contener hasta seis asientos, por lo que 2,000 solicitudes por segundo pueden provocar hasta 12,000 decisiones sobre filas de asientos por segundo antes de conflictos y rollbacks. Las pruebas de capacidad necesitan una distribución sesgada en secciones populares; repartir esas 12,000 decisiones de manera uniforme entre 50,000 asientos ocultaría la contención.

La sala de espera virtual está aislada por event_id y puede aceptar visitantes antes de la venta. Registra una cohorte de llegada, la hora de entrada, la cuenta y señales de riesgo. Al ser admitido, un visitante recibe un admission_token firmado y de corta duración vinculado al evento y a la cuenta. El ingreso a la venta verifica el token, la expiración y una única sesión activa; el tráfico no válido se rechaza en el edge. La tasa de admisión responde a la espera por bloqueos de inventario, el p99 de reservas, los errores de pago, los compradores activos y la tasa de finalización. Cuando la base de datos se ralentice, reduce la admisión en lugar de ocultar la contención de escritura detrás de más réplicas de lectura.

La documentación pública de salas de espera de Cloudflare describe el orden FIFO a partir de la marca de tiempo en la que un visitante llega por primera vez a una cola activa y un modo aleatorio que selecciona visitantes en espera. Estas son políticas de producto, no mecanismos de consistencia de base de datos. Incluso FIFO necesita reglas explícitas para la granularidad temporal, reconexiones, múltiples dispositivos, la autoridad del reloj y los bots. No puede prometer un orden estricto solicitud por solicitud a través de una red global.

Paso 2: Separar la exploración, la vista de asientos y el inventario autoritativo

Los detalles del evento, el mapa estático del recinto y las explicaciones de precios pueden residir en una CDN o caché. La vista de asientos combina una instantánea versionada con deltas de estado. Un cliente recibe primero un snapshot_version, y luego aplica los cambios que contienen un seat_id, el nuevo estado y una versión superior. Recarga la instantánea tras una desconexión o una brecha de versión. Si 10,000 compradores activos consultan cada cinco segundos, generan alrededor de 2,000 lecturas por segundo. La entrega por deltas reduce las lecturas repetidas del mapa completo, pero no se convierte en la fuente de la verdad del inventario.

El mapa de asientos puede estar brevemente desactualizado. Un comprador puede ver AVAILABLE y aun así perder la carrera por la reserva. Una caché no puede confirmar una venta ni aceptar una reserva a partir de datos obsoletos mientras la base de datos no esté disponible. Esto separa una ruta de exploración de alta disponibilidad de una ruta de escritura de inventario fuertemente consistente.

Asigna cada evento a un shard de escritura primario y utiliza transacciones relacionales dentro de él. Los diferentes eventos escalan horizontalmente por event_id, mientras que un evento de altísima demanda puede recibir un shard o grupo de recursos dedicado. Cincuenta mil filas de asientos no son el problema de capacidad. Las esperas de bloqueo, el agotamiento de conexiones y las tormentas de reintentos causadas por muchas solicitudes dirigidas a los mismos pocos asientos sí lo son.

Paso 3: Definir la API, el estado y los invariantes

La API principal puede mantenerse pequeña:

text
POST /events/{eventId}/holds
  { seatIds, idempotencyKey, admissionToken }
  -> { holdId, status, expiresAt, seats }

POST /holds/{holdId}/checkout
  { paymentMethodToken, idempotencyKey }
  -> { orderId, paymentStatus, nextAction }

GET /orders/{orderId}
  -> { orderStatus, paymentStatus, seats }

Un modelo de datos mínimo es:

text
SeatInventory(event_id, seat_id, price_version, status,
              hold_id, hold_expires_at, order_id, version)
Hold(hold_id, event_id, account_id, status, expires_at,
     idempotency_key, request_digest, created_at)
Order(order_id, hold_id, account_id, amount_minor, currency,
      status, payment_operation_id, created_at)
PaymentOperation(operation_id, order_id, provider_reference,
                 kind, status, idempotency_key, updated_at)
Outbox(event_id, aggregate_id, event_type, payload, published_at)

Los invariantes críticos son:

text
one (event_id, seat_id) has at most one valid SOLD order
all seats in one hold share the event, account, and expiry
only the current hold_id may move HELD to SOLD or release it
order confirmation revalidates price version, quantity, limit, and total
one idempotency key describes one immutable request; changed parameters are rejected

El dinero es un entero en la unidad menor de la moneda. Un total enviado por el cliente no es autoritativo; el servidor lo recalcula a partir de la versión del precio bloqueada. Un admission_token otorga el ingreso a la venta, no la propiedad de ningún asiento.

Paso 4: Crear una reserva atómica de múltiples asientos con una transacción corta

Una solicitud selecciona a lo sumo seis asientos. La transacción ordena por seat_id, y luego bloquea las filas con SELECT ... FOR UPDATE en ese orden fijo, reduciendo los deadlocks causados por órdenes de bloqueo opuestos. Si cada fila es AVAILABLE o un HELD expirado, la transacción escribe el mismo hold_id y expires_at = database_now + 5 minutes en todas las filas e inserta el Hold más el evento en la outbox. Si una fila está vendida o pertenece a otra reserva no expirada, toda la transacción hace rollback.

Un UPDATE condicional con predicados de estado y fecha límite es otra implementación válida, siempre que el servicio compruebe que el recuento de filas actualizadas coincida con la cantidad solicitada. En cualquiera de los dos enfoques, la decisión y la escritura deben compartir una única transacción autoritativa. Adquirir un bloqueo en Redis y escribir en la base de datos de forma asíncrona crea dos fuentes de la verdad cuando una tiene éxito y la otra falla. Leer "disponible" y luego escribir incondicionalmente presenta la condición de carrera original.

PostgreSQL documenta que los bloqueos de fila impiden que otros escritores y bloqueadores actúen sobre las mismas filas y se liberan al finalizar la transacción. Por lo tanto, el checkout de cinco minutos del comprador es un estado de datos duradero, no una transacción de base de datos de cinco minutos. Haz commit y libera la conexión de inmediato; luego abre una nueva transacción corta para cada transición posterior.

Haz commit del registro de idempotencia junto con el resultado de la reserva. Si el servicio hace commit de la reserva y se cae antes de responder, un reintento con el mismo idempotencyKey devuelve el holdId original. Una nueva clave entra en una nueva carrera. Almacena un resumen criptográfico (digest) de la solicitud para que la misma clave no pueda reutilizarse para un grupo diferente de asientos.

Paso 5: Garantizar una expiración correcta incluso cuando los trabajos se retrasan

Genera hold_expires_at a partir de la hora de la base de datos. Una lectura puede mostrar una reserva expirada como disponible, pero reclamarla aún comprueba la fecha límite en la transacción de escritura. Crear una reserva programa un mensaje retrasado, y un escaneo periódico proporciona compensación. Ambos aceleran una transición explícita de vuelta a AVAILABLE; ninguno de los dos es la única fuente de corrección de la expiración.

La operación de liberación debe asemejarse a:

text
UPDATE seat_inventory
SET status = 'AVAILABLE', hold_id = NULL, hold_expires_at = NULL
WHERE event_id = :eventId
  AND hold_id = :holdId
  AND status = 'HELD'
  AND hold_expires_at <= database_now;

Los mensajes retrasados pueden duplicarse, llegar tarde o fuera de orden. Si un nuevo hold_id ahora posee el asiento, el predicado antiguo ya no coincide. Si el asiento está en estado SOLD, su estado ya no coincide. Liberar basándose únicamente en seat_id puede eliminar la reserva válida de otro comprador.

Una autenticación de pago adicional puede acercarse a la fecha límite de la reserva. Antes de que comience la autorización, este diseño puede mover atómicamente el Hold.status de una reserva válida a PAYMENT_PENDING con una única extensión de gracia acotada. La ocupación en período de gracia aún cuenta para el inventario activo y los límites por cuenta. Un resultado que permanezca desconocido al llegar la nueva fecha límite entra en conciliación; el cliente no puede extender indefinidamente para acaparar asientos.

Paso 6: Incluir los resultados de pago desconocidos en la máquina de estados

El checkout crea un payment_operation_id estable y reutiliza su clave de idempotencia con el proveedor. La documentación pública de la API de Stripe explica que reintentar una solicitud con la misma clave de idempotencia devuelve el resultado guardado de la primera solicitud. Los objetos de tipo PaymentIntent también exponen el estado del ciclo de vida para pagos que puedan necesitar autenticación adicional o finalización asíncrona.

Utiliza esta secuencia:

  1. En una transacción corta, valida la reserva, el límite de boletos, el precio y la fecha límite; luego crea el Order y la operación de autorización. Si se necesita un período de gracia, mueve Hold.status a PAYMENT_PENDING en la misma transacción.
  2. Llama a la autorización de pago fuera de la transacción. Un timeout se convierte en UNKNOWN; no crea una segunda operación.
  3. Tras una autorización confirmada, bloquea los asientos nuevamente, verifica que coincida el hold_id, establece todos los asientos en SOLD, establece la orden en CONFIRMED y escribe un evento en la outbox en una sola transacción.
  4. Realiza la captura después del commit. La captura repetida utiliza el mismo ID de operación. La emisión de boletos consume únicamente el evento de confirmación que haya hecho commit.
  5. Si la autorización tuvo éxito pero la reserva ya no se puede confirmar, anula la autorización. Si la transacción de venta hizo commit y la captura es desconocida, preserva los asientos y la orden mientras convergen los webhooks, la búsqueda activa y la conciliación. No los liberes ni los vendas de nuevo. Si la captura falla definitivamente y no se puede reintentar, cancela la orden y libera sus asientos en una transacción de compensación auditada antes de la emisión de boletos.

Este orden permite un estado temporal de "vendido, captura pendiente", pero nunca entrega el mismo asiento a dos compradores. Si el negocio solo puede capturar inmediatamente, un cobro exitoso seguido de una transacción de venta fallida requiere una anulación o un reembolso auditable. Esa capacidad del proveedor aumenta el riesgo operativo y del cliente, y debe declararse explícitamente.

El estado del asiento y de la orden avanzan de forma monótona. Una redirección del navegador a una página de éxito no puede autorizar la emisión de boletos. Solo una respuesta verificada del proveedor, un webhook o una búsqueda activa hace avanzar el estado del pago. Deduplica los webhooks mediante el ID de evento del proveedor y acepta únicamente transiciones legales.

Paso 7: Diseñar fallos, escalamiento y degradación

  • El shard del evento no está disponible: Detén la admisión y las nuevas reservas para ese evento y retén a los usuarios en la sala de espera. La exploración puede indicar que la reserva no está disponible temporalmente; una réplica desactualizada no puede continuar vendiendo.
  • La entrega de caché o deltas falla: Recurre a instantáneas versionadas de menor frecuencia. La base de datos sigue decidiendo la reserva.
  • Se pierde un mensaje de expiración: La recuperación condicional y el escaneo de compensación aún recuperan los asientos. Monitorea el backlog expirado y la reserva vencida más antigua.
  • El proveedor de pagos se ralentiza: Reduce la admisión y la concurrencia en el checkout y preserva las operaciones desconocidas. No cambies de proveedor automáticamente con el riesgo de generar un cobro duplicado.
  • Se pierde una respuesta tras el commit: La clave de idempotencia recupera la reserva original, la orden o la operación de pago.
  • Un evento se vuelve extremadamente popular: Asígnale recursos de base de datos dedicados, aplica límites de tasa por evento y reduce las lecturas no esenciales. No dividas una transacción de múltiples asientos entre shards.
  • Una región falla: Cada evento tiene una sede de escritura y una conmutación por error (failover) verificada. Antes de que un nuevo primario tome el control, demuestra que el primario anterior ya no puede escribir, evitando la sobreventa activo-activo.

La sala de espera también necesita resistencia a la copia de tokens, la reproducción (replay) y la elusión (bypass). Los tokens están firmados, son de corta duración y están vinculados al evento y a la cuenta; el servicio limita las sesiones concurrentes y la cantidad de boletos, mientras que el tráfico de alto riesgo recibe verificación adicional. Estos controles reducen la ventaja de los bots. No demuestran de forma independiente que un comprador sea humano ni que la asignación sea perfectamente equitativa.

Paso 8: Verificar invariantes mediante inyección de fallos

Además de las pruebas funcionales, valida propiedades verificables por máquina:

text
count(valid SOLD orders for one seat) <= 1
every CONFIRMED order owns exactly its recorded seats
every SOLD seat points to one CONFIRMED or payment-reconciling order
an expiry action changes only its own hold_id
replaying one idempotent request does not create new business state

Envía miles de clientes concurrentes tras un mismo asiento y espera una sola reserva válida. Luego haz que los clientes soliciten el mismo conjunto de tres asientos en diferentes órdenes para verificar el comportamiento de todo o nada, el orden de bloqueo fijo y los reintentos ante deadlocks. Mueve el tiempo a través del límite de expiración mientras intercalas una nueva reserva, un mensaje de expiración, la autorización del pago y la confirmación. Una tarea de limpieza antigua nunca debe alterar el nuevo estado.

Finaliza forzosamente un proceso o pierde una respuesta inmediatamente después del commit de la reserva, antes y después de que retorne la autorización, después del commit de venta, después del envío de la captura, y antes y después de la publicación en la outbox. Duplica webhooks y mensajes retrasados; desconecta la caché, el proveedor de pagos y el primario de la base de datos. Evalúa el resultado utilizando el recuento de ventas a nivel de evento, la espera por bloqueos, la tasa de conflictos condicionales, la tasa de expiración de reservas, la antigüedad de pagos desconocidos, la espera en cola y la tasa de admisión, y las diferencias de conciliación en la emisión de boletos. Un HTTP 200 por sí solo demuestra poco.

Respuesta de muestra de alta calidad

“Dividiría el problema en protección del tráfico y corrección del inventario. Dos millones de llegadas en cinco minutos promedian unas 6,667 por segundo, con un pico en el edge de 50,000 solicitudes por segundo, mientras que el inventario solo acepta 2,000 intentos de reserva por segundo. Crearía una sala de espera virtual delimitada por evento. Esta absorbe el tráfico de actualización en el edge y emite tokens de admisión de corta duración vinculados a la cuenta según el estado de salud del shard del evento y la ruta de pago. El orden de la cola es una política de producto explícita; no proporciona exclusión de asientos.

La página del evento y el mapa estático del recinto utilizan una CDN. El mapa de asientos utiliza una instantánea versionada más deltas y puede tener unos segundos de desfase; solo la reserva decide. Cada evento tiene un shard de escritura de base de datos relacional, por lo que sus 50,000 asientos no se dividen entre shards. La API de reserva acepta de uno a seis IDs de asientos ordenados y una clave de idempotencia. Una transacción corta bloquea esas filas y escribe un hold_id con una expiración de cinco minutos basada en la hora de la base de datos solo cuando todos los asientos están disponibles o expirados. De lo contrario, todos fallan. La transacción libera sus bloqueos en el commit; el checkout retiene un estado de reserva duradero, no un bloqueo de base de datos.

Una cola retrasada y un escáner limpian las reservas expiradas, pero cada liberación coincide con el hold_id, el HELD y la fecha límite. Por lo tanto, un mensaje antiguo tardío no puede liberar una nueva reserva ni un asiento vendido. La clave de idempotencia y el resumen de la solicitud hacen commit junto con la reserva, por lo que una respuesta perdida tras el commit solo recupera el resultado original.

Para el pago, el enunciado asume autorización y captura por separado. Creo una operación de pago estable y autorizo primero. Un timeout es desconocido y utiliza el mismo ID de operación para búsqueda o reintento. Tras la autorización, una transacción revalida la reserva, el precio, el límite y la fecha límite; luego pasa los asientos a SOLD, la orden a CONFIRMED y escribe en la outbox. La captura y la emisión de boletos ocurren a continuación. Si la autorización tuvo éxito pero la confirmación del asiento falla, anulo la autorización. Si los asientos están confirmados y la captura es desconocida, preservo la orden y converjo mediante webhooks, búsquedas y conciliación; nunca libero ni revendo.

El escalamiento está delimitado por evento: los eventos comunes comparten shards, los eventos de alta demanda reciben recursos dedicados y un evento tiene una única sede de escritura. Si la base de datos, el proveedor de pagos o el emisor se ralentizan, la sala de espera reduce la admisión y puede pausar las reservas. Para la verificación, miles de clientes compiten por un asiento y por conjuntos de asientos superpuestos mientras inyecto expiraciones tardías, caídas tras el commit, respuestas de pago perdidas, webhooks duplicados, pérdida de caché y fallo del primario. El diseño se considera aprobado solo si demuestra a lo sumo una orden vendida válida por asiento, ningún estado nuevo a partir de reproducciones y ninguna reserva nueva modificada por una acción de expiración antigua.”

Errores comunes

  • Bloquear en Redis y escribir el inventario de forma asíncrona → El bloqueo y las escrituras en la base de datos pueden discrepar, produciendo dos autoridades → Ejecuta la condición, la reserva duradera y el resultado idempotente en una sola transacción de base de datos autoritativa.
  • Mantener un bloqueo de fila durante los cinco minutos del checkout → Las transacciones largas consumen conexiones, bloquean a los escritores y se recuperan deficientemente tras el abandono → Usa bloqueos solo para transiciones y representa el checkout mediante un estado de reserva duradero.
  • Asumir que la sala de espera virtual evita la sobreventa → Controla la admisión mientras que los compradores admitidos aún compiten por las mismas filas → Aplica la exclusión por separado en la transacción de inventario.
  • Liberar la expiración solo por el ID del asiento → Un mensaje tardío puede borrar una nueva reserva o un estado vendido → Haz coincidir hold_id, el estado y la fecha límite juntos.
  • Cobrar porque el mapa de asientos muestra disponible → El modelo de lectura puede estar desactualizado y otro comprador puede ganar primero → Adquiere la reserva autoritativa antes del pago.
  • Liberar y reintentar con una nueva clave tras un timeout de pago → La primera operación pudo haber tenido éxito, causando una reventa o un cobro duplicado → Preserva el estado desconocido, reutiliza el ID de operación y converge mediante webhooks, búsquedas y conciliación.
  • Fragmentar aleatoriamente (shard) cada asiento de un evento de alta demanda → Una sola reserva de múltiples asientos cruza shards y pierde la atomicidad simple → Mantén un evento en un shard de escritura hasta que la capacidad medida demuestre que es necesaria una partición de inventario más compleja.
  • Prometer FIFO global estricto → La latencia de red, las reconexiones, el cambio de dispositivos y los bots alteran el orden observable → Define la granularidad de la cola, la identidad, las reconexiones y las políticas de riesgo como un contrato de equidad verificable.
  • Hacer pruebas de carga solo con el rendimiento promedio → La demanda de apertura se concentra en pocos asientos, ocultando esperas de bloqueo y tormentas de reintentos detrás de una distribución uniforme → Prueba asientos populares, grupos superpuestos, ráfagas instantáneas y dependencias ralentizadas.

Preguntas de seguimiento y respuestas

Pregunta de seguimiento 1: ¿Qué cambia para la admisión general cuando solo debe evitarse la sobreventa de una cantidad por nivel de boleto?

Reemplaza el estado por asiento con un InventoryBucket(event_id, tier_id, available, held, sold, version). Una transacción de reserva requiere condicionalmente available >= quantity, decrementa una vez y crea la reserva. La expiración y la confirmación aún mueven los recuentos de forma idempotente mediante hold_id. Un nivel con alta contención puede usar varios depósitos (buckets) para distribuir las escrituras, pero un plano de control de cuotas debe asignar las capacidades de los depósitos y mantener su suma dentro del inventario real. Eliminar la selección atómica de asientos simplifica el almacenamiento; las carreras de expiración y pagos desconocidos se mantienen.

Pregunta de seguimiento 2: La autorización promedia 90 segundos y la autenticación adicional puede superar los cinco minutos. ¿Cómo detienes las reservas de larga duración?

Comprueba el tiempo restante antes de que comience el pago y rechaza un nuevo flujo de autenticación cuando quede muy poco tiempo. Una vez iniciado, mueve la reserva a un estado de gracia PAYMENT_PENDING acotado de única vez, con límites por cuenta y para todo el evento sobre ese inventario. Al expirar la gracia, deja de iniciar tareas y consulta al proveedor. Libera solo después de confirmar que no hubo autorización; completa o anula una autorización confirmada. Utiliza los datos de conversión y rotación de inventario para ajustar la reserva de cinco minutos y el período de gracia en lugar de permitir que los clientes renueven repetidamente.

Pregunta de seguimiento 3: La región de escritura primaria desaparece durante la venta. ¿Debería otra región tomar el control de inmediato?

No inicies un segundo escritor basándote únicamente en un timeout. Pausa la admisión y las nuevas reservas para el evento. Utiliza una concesión (lease) por consenso, conmutación por error de la base de datos o el aislamiento del primario anterior para demostrar que existe un único escritor; luego permite que el nuevo primario se reanude desde una posición de log confirmada. La gestión de colas y la degradación de solo lectura pueden continuar durante el lapso intermedio; las reservas cuya exclusividad no se pueda demostrar, no. Concilia órdenes, asientos y operaciones de pago antes de aumentar la admisión nuevamente.

Pregunta de seguimiento 4: ¿Cómo mejoras la equidad y limitas los bots sin hacer que los controles de fraude sean una dependencia para la corrección?

Utiliza cuentas verificadas, límites de compra, límites de tasa, señales de dispositivo y comportamiento, y desafíos adicionales para sesiones de alto riesgo. Los tokens de la cola están firmados, son de corta duración y están vinculados al evento y a la cuenta; las sesiones duplicadas se fusionan o se rechazan según la política. La venta puede usar FIFO de tiempo de llegada aproximado o asignar posiciones aleatoriamente a los usuarios en espera previa. Ya sea que la detección de riesgos pase por alto un bot o no, la base de datos aplica los mismos invariantes de reserva, límite y orden. Un fallo en el control de riesgos afecta la equidad en la asignación; no debe causar sobreventa.

Pregunta de seguimiento 5: ¿Por qué no proteger cada asiento con un servicio de bloqueo distribuido existente?

Cuando el inventario autoritativo ya reside en una base de datos relacional con escrituras condicionales y transacciones cortas, un servicio de bloqueo independiente crea un segundo estado que debe coincidir con el inventario, además de límites de expiración de arrendamiento y fencing. Los bloqueos de fila o las actualizaciones condicionales sitúan la exclusión y la escritura duradera en una sola transacción y son más simples. Considera un bloqueo distribuido solo cuando el inventario abarque almacenes de datos que no puedan realizar transacciones conjuntas o cuando la sección crítica proteja un recurso externo. Incluso en ese caso, el almacenamiento duradero aún necesita una versión o un token de fencing para rechazar a un poseedor retrasado.

Fuentes públicas

Preguntas relacionadas

Herramienta de entrevista relacionada

Usa Resolver para una respuesta de diseño de sistemas

Aclara primero los requisitos y luego avanza a través de la escala, la arquitectura, la elección de componentes y las compensaciones (trade-offs).

Ver la herramienta