Entrevista de diseño de sistemas: resuelve un caso de reservas y justifica tus decisiones
Quick Overview
Diseña una reserva para S9 con retención de treinta segundos y reloj explícito. El modelo Python ejecutado distingue confirmación en 129 de pago tardío en 130, protege la nueva retención de Bruno y trata un segundo pago distinto. Compara una autoridad relacional con un propietario por evento y calcula un presupuesto hipotético de 75 checkouts/s. No demuestra concurrencia de base de datos ni llamadas, devoluciones o disponibilidad de producción.
Ana paga por el último asiento, pero la confirmación llega cuando Bruno ya tiene una nueva reserva temporal. ¿A quién pertenece el asiento? En una entrevista de diseño de sistemas, decir «usaría una cola y Redis» deja sin resolver la decisión principal: qué operación puede confirmar la compra del asiento y qué verá quien pagó demasiado tarde.
Este caso original te ayuda a conectar requisitos, estado, pagos y capacidad. Trabajarás con el asiento S9, una retención de treinta segundos y un reloj explícito. El laboratorio descargable incluye un modelo Python y sus resultados. Para practicar otros contratos de arquitectura, utiliza las preguntas de System Design de PracHub.
Hechos oficiales: las referencias documentan mecanismos de base de datos, API y pagos. Informes de candidatos: no usamos testimonios para afirmar rondas, tiempos o criterios de una empresa. Propuestas propias: el contrato, los números y las decisiones de este ejercicio son hipótesis de preparación. Ejecutamos un modelo secuencial en memoria; no probamos concurrencia PostgreSQL, llamadas al proveedor ni devoluciones reales.

Define qué significa reservar antes de dibujar
Aclara si se reserva un asiento numerado, una unidad de inventario o cualquier plaza dentro de una capacidad total. Un asiento fijo necesita una identidad que no pueda confirmarse para dos compradores. Un cupo agregado requiere controlar cantidades. Una estancia hotelera añade intervalos; no basta con reutilizar la regla de un único asiento.
Nuestro contrato cubre un asiento, un propietario y una retención temporal. No incluye compras de varios asientos, cambios, descuentos ni pagos parciales. Una retención concede la posibilidad de confirmar antes de su vencimiento; no es una compra. Al confirmar, el asiento deja de estar disponible. La pantalla puede mostrar datos antiguos, pero la operación de escritura vuelve a comprobar la autoridad.
Pregunta también qué experiencia se promete. Si la respuesta HTTP se pierde, el cliente debe poder consultar el estado de su intento. Si un pago no puede convertirse en una compra, mostramos «compensación pendiente», no «reserva confirmada». La forma exacta de anular o devolver el cargo depende de la integración elegida y queda fuera del modelo.
En una respuesta oral, presenta primero esa promesa: «La consulta de disponibilidad puede quedar obsoleta; la confirmación protege la propiedad del asiento. Distingo el cobro de la compra para recuperar pagos tardíos». Después podrás justificar la base, los procesos y las rutas.
Pon identidad y tiempo en cada transición
El modelo conserva seat, hold_id, propietario, versión, vencimiento y estado. Al crear otra retención para S9, el modelo aumenta la versión. Así, una tarea antigua no puede interpretar «liberar S9» como permiso para borrar el estado del siguiente comprador. La operación debe referirse a la retención concreta que pretendía liberar.
Elegimos una regla estricta: la confirmación es válida cuando el tiempo de la autoridad es menor que el vencimiento. Si Ana obtiene S9 en el instante 100, su retención vence en 130. Una confirmación evaluada en 129 puede ganar; en 130 ya no es elegible. No usamos el reloj del navegador ni aceptamos como autoridad una fecha enviada por el cliente.
Esta regla es una decisión del ejercicio, no una exigencia universal de diseño. Otro producto podría conceder una extensión al iniciar el pago, pero tendría que reservarla de forma explícita, definir su límite y decidir qué pasa si el proveedor tarda más. «El pago empezó antes» no determina por sí solo quién conserva el asiento.
El laboratorio recibe tiempos enteros y rechaza retrocesos. Esa simplificación permite repetir fronteras sin esperar treinta segundos. En una implementación distribuida tendrías que elegir dónde se evalúa el tiempo, cómo se comportan las transacciones largas y qué precisión necesitas. El reloj inyectado no resuelve esos problemas: los vuelve visibles.
Recorre las dos carreras que cambian el resultado
La primera ejecución confirma a Ana en 129. Después, la tarea de vencimiento en 130 no libera un asiento confirmado. Una repetición del mismo pago conserva el resultado. Sin embargo, una segunda identidad de pago no equivale al mismo evento: nuestro contrato la envía a compensación porque ya existe una compra.
La segunda ejecución recibe el pago de Ana en 130, fuera del plazo. Bruno obtiene una nueva retención en 131. La tarea antigua en 132 debe ser inofensiva y el pago repetido de Ana no cambia al propietario. Bruno confirma en 134. El modelo conserva una única intención de compensación para el pago de Ana.
| Instante y acción original | Decisión esperada | Invariante observado |
|---|---|---|
| Ana obtiene S9 en 100 | Retención S9:1, vence en 130 | Identidad y plazo registrados |
| Pago de Ana evaluado en 130 | Compensación pendiente | Igualdad no cuenta como llegada a tiempo |
| Bruno obtiene S9 en 131 | Nueva retención S9:2 | La generación cambia |
| Vencimiento antiguo en 132 | Ningún cambio | S9:2 permanece |
| Repetición del pago de Ana en 133 | Mismo resultado | Una intención de compensación |
| Pago de Bruno en 134 | Compra de Bruno | El propietario coincide con la retención vigente |
Prueba además el orden contrario: el vencimiento se ejecuta primero en 130 y el pago llega después, también en 130. Con nuestra regla, sigue sin haber compra para Ana. La hora de ejecución del proceso de limpieza no amplía el plazo: una tarea retrasada no concede tiempo extra a Ana. Expirar de forma diferida puede ahorrar trabajo, siempre que adquirir y confirmar evalúen la elegibilidad por sí mismos.

Explica dónde se arbitra la escritura
Una alternativa inicial es mantener una fila autoritativa por asiento en una base relacional. Adquirir, confirmar y liberar comprueban estado, identidad y plazo dentro de una operación que no permita dos ganadores. Las lecturas de catálogo pueden utilizar caché; las escrituras no confirman desde una copia obsoleta.
Hecho oficial: PostgreSQL documenta que los bloqueos de fila coordinan escritores y operaciones de bloqueo, y que una transacción puede sufrir un interbloqueo. Esto apoya la discusión de una implementación; no demuestra que nuestro modelo Python tenga esas garantías. Si propones bloqueo, explica qué fila protege, cuánto dura y cómo recuperas una transacción abortada.
Hecho oficial: los niveles de aislamiento tienen comportamientos diferentes. No basta con pronunciar «transacción» para garantizar cualquier secuencia de lecturas y escrituras. Define el predicado de confirmación, la restricción de unicidad y la política de reintento, y después prueba el conflicto en la base elegida.
La alternativa es asignar un propietario de escritura por evento y procesar sus comandos en orden. Puede facilitar el razonamiento sobre un asiento muy disputado, pero añade preguntas de recuperación, retraso y transferencia de propiedad. El consumidor debe conservar estado duradero y reconocer comandos repetidos; una cola por sí sola no crea ese protocolo.
Compara ambos diseños con el mismo fallo: el proceso muere después de decidir quién gana y antes de responder. En el relacional, consulta el estado comprometido. En el propietario por evento, recupera la decisión duradera y evita que dos propietarios activos escriban a la vez. Explica qué mecanismo evita esa doble autoridad, no esconderlo tras «añadir más servidores».
Separa intento de pago, evento y compra
Guarda la identidad del intento de pago y su relación con la retención. Repetir el mismo intento debe recuperar una decisión compatible; reutilizar su identidad con otra retención se rechaza en nuestro modelo. La identidad del evento recibido sirve además para deduplicar su procesamiento, pero un proveedor puede producir varios eventos relacionados con un mismo objeto de pago.
Hecho oficial: Stripe explica el tratamiento de eventos webhook, incluida la posibilidad de eventos repetidos y la verificación de su firma. También documenta sus solicitudes idempotentes. Son reglas de una integración concreta, no una garantía de que el asiento y el cobro compartan una transacción.
Si el proveedor cobra y el servidor pierde la respuesta, consulta y concilia el intento conocido antes de crear otro. Si el pago es válido pero la retención ya no lo es, registra una acción de recuperación duradera. Nuestra etiqueta «compensación pendiente» solo identifica ese trabajo: no ejecuta un reembolso ni certifica su resultado.
Una intención duradera necesita identidad, estado y observabilidad. El proceso puede fallar después de que el proveedor acepte la devolución y antes de guardar el éxito local. Define cómo consultar o repetir esa operación sin multiplicarla. En el modelo, un conjunto en memoria evita repetir una intención durante la ejecución; al reiniciar perdería esa protección.
Conecta las rutas con la incertidumbre del cliente
Propón una ruta de retención que reciba el asiento y una clave de solicitud; devuelve identidad, vencimiento y estado. Una ruta de confirmación relaciona el intento con esa retención. Una consulta de estado permite distinguir pendiente, confirmado y compensación pendiente cuando una respuesta no llega.
Hecho oficial: las recomendaciones de diseño de API de Microsoft relacionan recursos, métodos y contratos HTTP. En este caso proponemos respuestas de conflicto cuando la retención ya no es elegible. La elección de códigos no reemplaza la documentación del estado ni la autorización de la solicitud.
Una clave idempotente tampoco permite leer reservas de otra persona. Vincula la solicitud al usuario autorizado y define qué ocurre al reutilizar la clave con datos distintos. El laboratorio no implementa autenticación, almacenamiento de claves de solicitud ni endpoints HTTP; estos son requisitos para una siguiente versión, no pruebas superadas.
Para la interfaz, devuelve una información que pueda sostener la conversación con soporte. «No se pudo confirmar; estamos resolviendo el pago» comunica algo diferente de «inténtalo otra vez». El cliente necesita un identificador estable y una consulta, no una invitación a iniciar cobros repetidos por cada timeout.
Usa la capacidad para elegir una acción concreta
Supongamos, solo para comparar diseños, un presupuesto de 500 operaciones de inventario por segundo. Reservamos el 40% de margen y estimamos cuatro operaciones por checkout. El cálculo es 500 × 0,60 / 4 = 75 checkouts por segundo. No medimos esa capacidad en PostgreSQL ni en un servicio real.
Si la demanda supera ese presupuesto hipotético, limita las admisiones al camino de escritura y separa la navegación del checkout. Un token de entrada no promete un asiento; concede permiso para intentarlo. Si la gente repite solicitudes durante un conflicto, la amplificación puede invalidar la estimación de cuatro operaciones.
Pide una prueba con compradores concentrados en S9, no solo tráfico repartido entre miles de asientos. Observa espera de bloqueos, abortos, latencia y compras confirmadas. En el diseño de propietario por evento, observa también antigüedad de comandos y tiempo de recuperación. Una media baja puede esconder una cola que supera el plazo de retención.
Defiende la disponibilidad con una consecuencia para el usuario. Si la autoridad de escritura no está disponible, conserva la consulta informativa y evita prometer una confirmación. Aceptar un comando pendiente podría ser válido con otro contrato; tendría que comunicar que todavía no existe una compra y limitar cuánto espera.
Termina con decisiones que puedan ponerse a prueba
Descarga el ejercicio y ejecuta python3 holds.py. Sus 22 comprobaciones incluyen identidad, igualdad en el vencimiento, los dos órdenes de la carrera, pago repetido, segundo pago y reloj hacia atrás. El resultado conserva entradas esperadas y observadas. No incluye múltiples hilos, broker, base de datos ni benchmarks.
Antes del mock, escribe tu decisión en una frase y el requisito que la cambiaría. «Una autoridad por asiento simplifica la exclusión; reconsideraría el reparto si un evento caliente supera el presupuesto medido». Después provoca el fallo correspondiente y explica qué queda confirmado, qué queda pendiente y quién puede consultarlo.
Estas preguntas verificadas permiten variar el contrato. Sus títulos originales se conservan; son material de práctica reportado en PracHub, no predicciones de una próxima entrevista ni prueba de un formato uniforme.
| Pregunta PracHub verificada | Decisión que conviene practicar |
|---|---|
| Model Flights, Passengers, Bookings, and Seat Holds | Separar pasajero, intento y propiedad temporal del asiento. |
| Match Bookings to Payments | Conciliar identidades sin convertir cada cobro en una compra válida. |
| Design ticketing system with seat hold | Explicar la frontera exacta entre retención y confirmación. |
| Design Room-Level Hotel Reservations Without Double Booking | Cambiar del asiento fijo a intervalos y revisar el invariante. |
| Design a payment system with holds and batching | Distinguir estados del pago y recuperación de una retención. |
Continúa en la categoría System Design. En cada respuesta, elige una carrera, recórrela en ambos órdenes y conecta el resultado con una alternativa de arquitectura. Esa prueba permite defender tus compromisos con más precisión que una lista de componentes.
Comments (0)