Tecnología de viajes··8 min de lectura

Un proveedor de hoteles no respondió. ¿Por qué se detuvo toda la plataforma de reservas?

Tres proveedores de hotel reciben la misma búsqueda. Dos responden con normalidad. El tercero se queda colgado. Unos segundos después, el viajero ve un error, no los resultados de los proveedores que sí funcionaban.

No es solo un problema del API de aguas arriba. Es un problema de integración con el API del proveedor de hoteles: una dependencia pudo decidir el resultado de todo el flujo.

Un fallo de búsqueda es frustrante. Un fallo de reserva es más serio. Cuando la petición de reserva hace timeout, la reserva puede existir ya, aunque la plataforma no haya recibido la confirmación. Lo que ocurra después decide si el cliente obtiene una respuesta fiable o una segunda reserva.

El objetivo útil no es «no tener nunca un error de API». Es contener los fallos y conservar el estado de negocio correcto cuando la información está incompleta.

El fallo de búsqueda y el fallo de reserva no son el mismo problema

Una búsqueda de hotel es una lectura. Según el producto y lo que se le prometió al cliente, la plataforma puede seguir siendo útil aunque un proveedor no esté disponible. Una petición de reserva cambia el mundo exterior: puede haber una habitación retenida, y ya puede existir una obligación de pago.

Estas operaciones necesitan políticas distintas.

Una llamada fallida no es el mismo evento en la búsqueda y en la reserva. Son decisiones de diseño para una plataforma de reservas, no la descripción de un producto.
SituaciónComportamiento adecuado
SituaciónUno de tres proveedores de búsqueda hace timeoutComportamiento adecuadoDevolver los resultados válidos de los otros si las reglas lo permiten, y marcar la cobertura como incompleta
SituaciónFallan todos los proveedores de búsquedaComportamiento adecuadoExplicar que la comprobación de disponibilidad no pudo completarse. No decir que los hoteles están agotados
SituaciónCaduca un token de tarifa de la búsquedaComportamiento adecuadoRefrescar la disponibilidad o el detalle de la tarifa, en el flujo de ese proveedor
SituaciónLa respuesta de la reserva hace timeoutComportamiento adecuadoMarcar el estado como incierto. Recuperar o conciliar el intento anterior antes de reintentar
SituaciónLa reserva está rechazada con certezaComportamiento adecuadoMostrar el fallo confirmado, con opciones seguras de recuperación

Tratar cada error de API como «no hay disponibilidad» esconde una caída del proveedor a operaciones y puede confundir al viajero.

Que un proveedor lento no se coma toda la plataforma

Un agregador típico reparte una petición entre varios sistemas externos. Sin aislamiento, un proveedor lento puede ocupar hilos, pools de conexión o colas compartidas hasta que los proveedores sanos también se degradan.

Un diseño más robusto fija un plazo, un límite de concurrencia y un seguimiento de salud para cada proveedor. El servicio puede recoger los resultados que terminaron dentro del presupuesto de tiempo, y puede decir qué proveedores no terminaron.

El patrón bulkhead de Microsoft describe pools de recursos aislados, para que el fallo de un componente no agote los recursos que necesitan los demás. Su patrón circuit breaker describe bloquear llamadas que probablemente volverán a fallar, durante un tiempo, dejando una comprobación posterior de que la dependencia se recuperó. Los patrones resuelven problemas distintos y ambos hay que afinarlos. Un circuit breaker protege a quien llama. No hace idempotente una petición de reserva.

Un flujo ilustrativo:

  • El proveedor A responde dentro de la ventana de búsqueda: se incluyen sus ofertas válidas.
  • El proveedor B responde con normalidad: se incluyen sus ofertas válidas.
  • El proveedor C supera su plazo: se registra supplier_unavailable, no no_rooms.
  • La capa de agregación devuelve los resultados disponibles, con una nota interna clara de qué terminó y qué cobertura falta.

Si el cliente debe ver resultados parciales depende de lo que promete el producto de reservas. Es una decisión de negocio, no un accidente de la llamada HTTP que terminó primero.

Arquitectura ilustrativa

Un proveedor caído, la búsqueda sigue

La misma petición de búsquedaEnviada en paralelo. Cada proveedor tiene su propio plazo y su propio límite de concurrencia.

  1. Proveedor ALímite propio

    Responde dentro de la ventana. Se incluyen las ofertas válidas.

  2. Proveedor BLímite propio

    Responde con normalidad. Se incluyen las ofertas válidas.

  3. Proveedor CLímite propio, superado

    Timeout. Registrado como supplier_unavailable, no como no_rooms.

Resultados devueltos

Ofertas de A y B, con la cobertura marcada incompleta porque C no respondió.

Tres llamadas a proveedores corren en paralelo, cada una dentro de su límite. Dos devuelven ofertas. Una hace timeout. La búsqueda devuelve igual los resultados buenos y dice que la cobertura está incompleta.

Una estrategia de reintento en el API de reserva no puede ser «inténtalo otra vez»

La documentación de errores de Expedia Rapid advierte que un timeout de reserva o una respuesta 5xx no invalidan necesariamente una reserva. El fallo puede ocurrir después de creada la reserva. Expedia documenta la recuperación con la referencia original de reserva y los identificadores relacionados, para poder establecer el estado final. Ese comportamiento es de Expedia, descrito en la documentación Rapid.

Imagina esta secuencia, también ilustrativa:

  1. La aplicación envía una petición de reserva con un identificador de intento que se puede seguir.
  2. El proveedor crea la reserva.
  3. Se pierde la respuesta HTTP.
  4. La interfaz informa de un error de red.
  5. El cliente pulsa Book otra vez.

Sin un camino de recuperación controlado, ese quinto paso puede repetir una transacción real. Que una petición repetida sea segura depende del contrato de idempotencia documentado por el proveedor y de si ese proveedor puede devolver el estado. No depende de un UUID generado en el backend. Un identificador generado en local no es una garantía de idempotencia del lado del proveedor.

Un timeout no demuestra que la reserva falló. La primera acción correcta tras una reserva incierta suele ser resolver el intento original con el proceso de recuperación o conciliación que soporte el proveedor, no enviar otra vez la misma mutación.

Arquitectura ilustrativa

La reserva existe, pero la respuesta se perdió

  1. Enviar la petición

    La aplicación envía un intento de reserva con un identificador que se puede seguir.

  2. El proveedor crea la reserva

    La habitación queda retenida en el proveedor. Ese hecho ya ocurrió.

  3. La respuesta hace timeout

    El acuse HTTP no llega. La reserva y la respuesta son hechos distintos.

  4. Recuperar la reserva existente

    Buscar el intento original con el proceso de retrieval o conciliación del proveedor.

  5. Confirmar el resultado

    El estado se conoce por esa consulta, no por una segunda mutación de reserva.

El proveedor puede crear la reserva y aun así no devolver la respuesta HTTP. La recuperación trae la reserva que ya existe y confirma su resultado. No vuelve a enviar la mutación dando por hecho que el timeout significó un fallo.

Haz explícito el estado de la transacción

Conviene persistir un intento de reserva antes de enviar la petición externa. Una máquina de estados sencilla, a nivel de aplicación, podría incluir:

requested → submitted → pending_resolution → confirmed | failed

Algunas aplicaciones también necesitan cancellation-pending, amendment-pending o manual-review. Son estados de diseño sugeridos. No es la afirmación de que cada proveedor exponga el mismo modelo de estado.

Un estado pending_resolution es útil. Le dice al software y al equipo de soporte que el resultado es desconocido, en vez de archivar el intento como una compra rechazada.

Un proceso seguro ata los reintentos y la recuperación a la identidad original, comprueba si ya hay una confirmación, registra lo que el cliente aprobó y deja el pago y los datos del viajero fuera de los logs.

La guía de AWS sobre APIs idempotentes explica por qué importan un identificador de petición reconocido y el manejo atómico de una mutación duplicada. Esas propiedades tienen que vivir en el contrato del API, o dentro de un límite de transacción que controlas. No se pueden dar por hechas en cada proveedor de hoteles.

Normaliza los datos del proveedor sin esconder las diferencias

Un modelo canónico de reserva simplifica la plataforma. Normalizar no es permiso para tirar las diferencias útiles.

Un adaptador puede convertir los campos de habitación, régimen y cancelación a una forma común. Aun así debería guardar los identificadores originales, el texto de la política, los códigos de diagnóstico y las referencias de correlación, para poder seguir una reserva discutida hasta su origen.

Un API puede devolver un error de negocio en JSON estructurado. Otra capa puede devolver una página HTML de un gateway. La referencia de errores de Expedia señala que pueden aparecer formatos inesperados. Un parser que asume que todo error es JSON con un campo message puede romperse justo cuando la plataforma más necesita fallar con suavidad.

Valida el formato de la respuesta de forma defensiva, clasifica lo que no reconoces y guarda suficiente procedencia de diagnóstico para investigar. Redacta el pago y los datos del viajero antes de que lleguen a un log.

Vigila la incertidumbre de negocio, no solo el uptime HTTP

Un panel de infraestructura en verde no significa que los clientes tengan reservas confirmadas. Las medidas útiles incluyen:

  • Búsquedas completadas, por proveedor y por operación.
  • Latencia de respuesta y tasa de timeout dentro del presupuesto de tiempo del usuario.
  • Fallos de tarifa y de parseo, por proveedor y por plan de tarifa.
  • Intentos de reserva que siguen sin resolver, y qué antigüedad tienen.
  • Éxito de la conciliación, y el tiempo que tarda en resolverse.
  • Eventos de detección de duplicados y escalados a soporte.

Para lecturas que se pueden reintentar con seguridad, usa un número acotado de reintentos, con backoff y jitter cuando encaje. AWS Well-Architected dice que hay que controlar el tráfico de reintentos. Los reintentos sin límite pueden empeorar un incidente. El mismo hábito de «inténtalo otra vez» no cabe en una mutación de reserva.

La resiliencia es lo que hace el sistema cuando se rompe el camino feliz

Una plataforma de reservas no se vuelve fiable dando por hecho que cada proveedor lo es. Se vuelve fiable aislando dependencias, conservando lo que significa una oferta, distinguiendo un estado incierto de un fallo confirmado y dándoles a los operadores un camino claro de recuperación.

Si la caída de un solo proveedor tumba la búsqueda con regularidad, o si un timeout de reserva lleva a la gente a reintentar una mutación a mano, revisa el aislamiento de fallos y la recuperación del estado de la reserva antes de añadir otra integración.

WebbingHUB Travel trabaja en la conectividad con proveedores y en los flujos de reserva. El arranque útil es una excepción real que el equipo no consigue resolver.

Fuentes y lecturas