Tema representativo de entrevista

¿Cómo deberían funcionar la propagación de plazos (deadlines) y la cancelación a través de llamadas RPC?

BackendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Una solicitud tiene un presupuesto de 2 segundos y llama a varios servicios. ¿Cómo propagas los plazos y la cancelación para que el trabajo downstream no sobreviva a la solicitud?

Pregunta y alcance

Estás diseñando una ruta de solicitud en la que el servicio A llama a B y B llama a C. El cliente permite 2 segundos desde la aceptación hasta una respuesta. A puede gastar 500 ms antes de llamar a B; B puede llamar a C y a un servicio de auditoría no crítico. Explica el contrato para plazos, cancelación, reintentos y observabilidad.

Asume que cada salto puede fallar de forma independiente, los relojes no están perfectamente sincronizados y una RPC de solo lectura que ha agotado el tiempo de espera solo se puede reintentar cuando la operación es idempotente. El objetivo es detener el trabajo condenado al fracaso preservando al mismo tiempo suficiente presupuesto para la serialización y el transporte de la respuesta.

Qué está evaluando el entrevistador

La rúbrica de diseño de sistemas a nivel de staff de InterviewStack espera que los candidatos aclaren las restricciones de latencia y fallas, identifiquen cuellos de botella y justifiquen las compensaciones (trade-offs). Una respuesta sólida convierte esas señales en un contrato de presupuesto explícito:

  • Se establece un plazo de solicitud absoluto en el borde (edge); cada salto deriva su presupuesto restante en lugar de inventar un nuevo tiempo de espera (timeout).
  • La cancelación sigue el árbol de llamadas, incluidas las ramas protegidas (hedged) o paralelas, de modo que una respuesta completada detiene el trabajo que ya no puede ayudar.
  • Los reintentos consumen el mismo presupuesto restante y están restringidos por la idempotencia y un límite de intentos.
  • Las métricas distinguen el agotamiento del plazo, la cancelación por parte del emisor (caller), el retraso en cola, el rechazo downstream y el trabajo útil completado después de la cancelación.

Preguntas aclaratorias antes de responder

  1. ¿El límite de 2 segundos es un SLO visible para el usuario o un plazo estricto de corrección? Un plazo estricto invalida los resultados tardíos; un SLO permite una ruta de finalización en segundo plano cuidadosamente delimitada.
  2. ¿Qué llamadas son críticas para la respuesta? Las llamadas no críticas de auditoría o recomendación se pueden desacoplar, muestrear o devolver con una respuesta degradada; la autorización de facturación no.
  3. ¿Las escrituras son idempotentes? Un presupuesto de reintentos es seguro para una lectura o un comando con clave de idempotencia, pero no para un efecto secundario desprotegido.
  4. ¿La pila de RPC propaga el contexto automáticamente? Si no es así, define un contrato de interceptor o middleware y pruébalo en cada límite de lenguaje.
  5. ¿Necesitamos que el trabajo finalice después de la cancelación, como una exportación con puntos de control (checkpoints)? Esa excepción necesita un contrato de trabajo duradero en lugar de ignorar silenciosamente la cancelación.

Estructura de respuesta de 30 segundos

“Establezco un único plazo en el punto de entrada (ingress) y transporto el presupuesto restante a través del contexto RPC. Cada servicio comprueba la cancelación antes de encolar y durante el trabajo prolongado, y luego pasa un presupuesto secundario más corto que deja margen para la respuesta y la red. Los reintentos usan únicamente el tiempo restante, están limitados y requieren idempotencia. Cuando el emisor obtiene un resultado o cancela, la señal de cancelación se propaga en abanico (fan-out) a cada rama. Mido la tasa de plazos excedidos, el presupuesto restante en cada salto, el tiempo en cola, la latencia de cancelación y el trabajo tardío, y luego realizo pruebas de carga en la cadena e inyecto dependencias lentas o canceladas”.

Respuesta detallada paso a paso

1. Establecer y representar el presupuesto

El borde registra un plazo absoluto, por ejemplo t0 + 2s. Tratarlo como un punto en el tiempo evita que cada salto agregue silenciosamente otros 2 segundos. Un servicio calcula remaining = deadline - now y reserva un pequeño margen local para la serialización y el tránsito de red. El margen es una política, no una constante universal; valídalo con datos de latencia por percentiles.

La guía de gRPC distingue un plazo de un tiempo de espera y recomienda plazos de cliente explícitos. También convierte los plazos propagados en tiempo restante para que el desajuste de reloj (clock skew) no haga que un servidor downstream espere más allá del presupuesto original. Por lo tanto, el contrato de cable (wire contract) debe transportar el contexto del plazo a través de la biblioteca RPC, no mediante un encabezado de aplicación de formato libre que los emisores puedan olvidar.

2. Gastar el presupuesto según la ruta crítica

Supongamos que A gastó 500 ms. Reenvía como máximo 1.5 segundos a B. Si B necesita 400 ms de trabajo local y llama a C, B reenvía el menor entre su presupuesto restante menos un margen de respuesta de 100 ms y el límite superior específico del servicio de C. Las llamadas paralelas comparten el mismo plazo principal; no reciben cada una un total de 1.5 segundos.

La admisión a cola comprueba el presupuesto restante. Si el retraso estimado en cola ya lo supera, rechaza rápidamente o devuelve una respuesta degradada documentada. Esto evita que una solicitud ocupe un worker después de que el éxito sea imposible.

3. Propagar la cancelación, no solo la expiración

La expiración del plazo es una razón de cancelación; que el emisor cierre la conexión o que otra rama gane una solicitud hedged es otra. Cada RPC secundaria recibe el contexto de cancelación principal. Los workers lo comprueban antes de pasos costosos y a intervalos delimitados durante los bucles, y luego liberan permisos y cierran flujos (streams).

Google SRE advierte que la propagación de plazos por sí sola aún puede filtrar trabajo cuando una llamada más profunda falla prematuramente. La cancelación debe viajar hacia arriba y propagarse a los hermanos. Un trabajo duradero con puntos de control puede finalizar un punto de control seguro, pero ese es un flujo de trabajo asíncrono explícito con su propia API de estado, no una RPC síncrona que ignora la cancelación.

4. Hacer que los reintentos sean conscientes del presupuesto

Calcula attempt_deadline = min(parent_remaining - response_margin, per-attempt_cap). Detente cuando el siguiente intento no pueda finalizar antes del plazo principal. Reintenta únicamente fallas transitorias y solo operaciones que sean naturalmente idempotentes o estén protegidas por una clave de idempotencia. Las solicitudes hedged utilizan el mismo presupuesto principal y cancelan los intentos perdedores tan pronto como llega una respuesta aceptable.

No combines un tiempo de espera de reintento prolongado con un circuit breaker que no tenga conocimiento del presupuesto restante. Registra el número de intento y el plazo original para que un servicio downstream pueda distinguir una solicitud nueva de un reintento casi expirado.

5. Definir una semántica de fallas observable

Devuelve un estado estable para el agotamiento del plazo, como DEADLINE_EXCEEDED de gRPC, y preserva la causa original de la cancelación en los registros. Los campos de traza deben incluir el plazo original, el presupuesto restante al entrar y al salir, el retraso en cola, el recuento de intentos y la latencia desde la cancelación hasta la detención. Alerta sobre el presupuesto gastado en colas y sobre el trabajo útil posterior a la cancelación, no solo sobre la latencia de extremo a extremo.

Prueba el contrato con un entorno de pruebas de integración de tres saltos: retrasa C más allá del presupuesto restante, cancela en A mientras B está en cola, haz que la primera solicitud hedged tenga éxito e inyecta desfases de reloj. Asegura mediante aserciones que ningún hijo se ejecute más allá del plazo principal, excepto un punto de control declarado explícitamente, que los reintentos se detengan y que se liberen los permisos.

Respuesta de muestra de alta calidad

“Haría que el punto de entrada sea el único propietario del plazo de 2 segundos. A lo recibe en el contexto RPC y, después de 500 ms, reenvía el presupuesto restante a B. B resta su propio margen de respuesta antes de llamar a C; las ramas paralelas siguen compartiendo el mismo plazo principal. Cada hijo observa la cancelación antes de encolar y mientras realiza trabajo delimitado, y la cancelación se propaga en abanico cuando el cliente se desconecta o una solicitud hedged gana. Los reintentos se limitan al presupuesto restante y requieren idempotencia. Una llamada de auditoría tardía se puede desacoplar en un trabajo duradero, pero la respuesta crítica no puede fingir que el trabajo tardío es útil. Demostraría el contrato con dependencias lentas inyectadas, retrasos en cola, condiciones de carrera de cancelación, desajuste de reloj y trazas que muestren el presupuesto en cada salto”.

Errores comunes

  • Dar a cada salto un nuevo tiempo de espera → el árbol de llamadas puede ejecutarse durante N veces el presupuesto del usuario → propaga un solo plazo y calcula el tiempo restante.
  • Propagar la expiración pero ignorar la cancelación del emisor → las solicitudes hedged y abandonadas siguen consumiendo workers → propaga el contexto de cancelación en abanico y mide la latencia de detención.
  • Reintentar en cada tiempo de espera agotado → las escrituras no idempotentes pueden duplicar efectos secundarios y los reintentos expirados amplifican la carga → exige idempotencia, clasifica los errores y limita los intentos según el presupuesto restante.
  • Encolar antes de comprobar el presupuesto → una solicitud condenada ocupa capacidad escasa → rechaza o degrada cuando el retraso en cola no quepa en el tiempo restante.
  • Usar un margen fijo sin mediciones → las cargas útiles grandes fallan de forma consistente o se desperdicia el margen de latencia → deriva los márgenes a partir de los percentiles de transporte y serialización.
  • Ocultar el trabajo tardío como éxito → los paneles de control muestran respuestas saludables mientras los recursos se fugan → separa los plazos síncronos de los trabajos con puntos de control explícitos.

Preguntas de seguimiento y respuestas

¿Qué ocurre si los relojes difieren entre servicios?

No compares marcas de tiempo de reloj de pared (wall-clock) en bruto de máquinas independientes. Utiliza la conversión de tiempo restante de la biblioteca RPC o propaga un tiempo de espera tras restar el tiempo transcurrido. Agrega pruebas de desfase de reloj al entorno de pruebas.

¿Se debería extender alguna vez un plazo downstream?

Solo cambiando el contrato del producto a un trabajo asíncrono. Extender un plazo secundario síncrono no puede hacer que la respuesta tardía del padre sea válida; solo incrementa el trabajo desperdiciado.

¿Cómo se reserva presupuesto para una carga útil lenta?

Mide las colas de serialización y red por separado, reserva un margen delimitado y limita el tamaño de la carga útil o utiliza streaming. Si el margen consume repetidamente el presupuesto, cambia el SLO o la estructura de la respuesta en lugar de aumentar silenciosamente cada tiempo de espera.

¿Qué sobrevive a la cancelación?

Únicamente el trabajo explícitamente duradero e idempotente, como una escritura de punto de control o un evento de auditoría. Debe ser observable bajo un identificador de trabajo y no debe retener el worker ni la conexión de la solicitud síncrona.

Fuentes públicas

Preguntas relacionadas