Planteamiento y alcance
Un cliente envía un comando para crear una orden. El servidor puede confirmar la orden y luego perder la conexión antes de enviar una respuesta. El cliente no puede saber el resultado y reintenta con el mismo Idempotency-Key. Diseñe el contrato de la solicitud, el registro duradero, el control de concurrencia y el flujo de recuperación, e indique qué fallas es seguro reintentar. Las habilidades principales son los límites de los efectos secundarios, la atomicidad y los estados de falla explicables, por lo que esto pertenece al backend.
Qué evalúa el entrevistador
Una respuesta sólida separa “la solicitud nunca llegó”, “se completó pero la respuesta se perdió” y “aún procesando”, para luego elegir la reproducción, la búsqueda de estado o una respuesta de en progreso. También cubre misma-clave/diferentes-parámetros, primeras solicitudes concurrentes, fallas de almacenamiento, expiración, enrutamiento multirregión y efectos posteriores en lugar de limitarse a decir “poner la clave en Redis”.
Preguntas para clarificar primero
- ¿La clave la genera el cliente para un comando lógico o la deriva el servidor a partir de campos de negocio?
- ¿Qué campos de la solicitud están vinculados a la clave y qué error representa una discrepancia?
- ¿La escritura de la orden y el registro de idempotencia están en una sola transacción de base de datos? ¿El pago tiene su propio contrato de idempotencia?
- ¿Cuánto tiempo puede esperar el cliente, cuánto tiempo se retiene la clave y la expiración puede crear una segunda orden?
- ¿El servicio es de región única o multirregión, y qué almacenamiento es autoritativo para cada ruta de ingreso?
Estructura de respuesta en 30 segundos
“El cliente crea una clave estable para un comando lógico y la reutiliza en los reintentos. El servidor impone una restricción de unicidad duradera sobre tenant, operación y clave, almacenando una huella digital de la solicitud, el estado y la respuesta final. La primera solicitud reclama atómicamente processing y ejecuta la transacción de la orden; las solicitudes con misma-clave/mismos-parámetros esperan o se reproducen, mientras que una discrepancia se rechaza. Después de una caída, recupérese a partir de la transacción y la evidencia posterior. Consulte los efectos secundarios desconocidos antes de compensar, nunca reintente a ciegas. La retención debe cubrir la ventana de reintento y las métricas deben demostrar cero efectos secundarios duplicados”.
Solución paso a paso
El contrato debe requerir una clave no vacía y acotada que permanezca sin cambios a través de los reintentos de un comando lógico. El servidor almacena un (tenant_id, operation, idempotency_key) único junto con un hash normalizado de la solicitud, estado, ID del recurso, código de respuesta, cuerpo de respuesta y expiración. El hash previene la reutilización accidental de una clave para un comando diferente.
La primera solicitud debe insertar un registro duradero de processing y crear la orden en una sola transacción local, o usar un log de transacciones que vincule de forma inequívoca ambos elementos. Ante un conflicto de unicidad, lea el registro existente: reproduzca la respuesta guardada para succeeded, devuelva el mismo error de negocio para failed y espere brevemente o devuelva en progreso para processing. Un bloqueo solo en memoria falla al reiniciar y entre réplicas.
La concurrencia se decide mediante la restricción de unicidad y las actualizaciones condicionales. Solo la solicitud propietaria del registro de creación puede mover processing a succeeded; incluya una versión o estado esperado en la actualización para que dos workers no puedan confirmar. Si la transacción de la orden tuvo éxito antes de una caída del proceso, un reintento lee succeeded y lo reproduce. Si la transacción se revirtió, un reintento puede ejecutarse de forma segura.
Los efectos posteriores crean una ventana donde el resultado local es desconocido mientras que el efecto remoto puede haber tenido éxito. Los pagos, envíos y mensajes deben usar el mismo ID de operación de negocio con un contrato de idempotencia aguas abajo, y persistir la solicitud y el resultado. Sin idempotencia aguas abajo, consulte la conciliación o publique a través de un outbox y reintente desde un worker; nunca vuelva a cobrar solo porque la llamada local dio timeout.
Un registro en progreso necesita recuperación. Almacene un lease y un heartbeat; un worker de recuperación verifica la transacción de la orden, el estado aguas abajo o el log de transacciones antes de avanzar a un estado terminal. Si la evidencia es insuficiente, marque unknown y envíelo a conciliación en lugar de tratarlo como una falla. La máquina de estados no debe mover succeeded de vuelta a processing.
La expiración debe coincidir con el riesgo del negocio. Retenga el registro durante el reintento máximo del cliente, la cola de reintentos de red y la ventana de compensación. Reutilizar una clave expirada debe devolver un key_expired explícito, no crear silenciosamente una segunda orden. Los cuerpos de respuesta antiguos pueden compactarse solo si se conservan un resumen de auditoría y la unicidad a nivel de recurso.
En despliegues multirregión, enrute una clave lógica a un almacenamiento autoritativo o imponga una restricción de unicidad global con replicación sincrónica. No ejecute nuevamente en otra réplica simplemente porque una lectura de processing se retrase. Mida conflictos de claves, conflictos de parámetros, timeouts en progreso, respuestas reproducidas, estados desconocidos, bloqueos por recursos duplicados y diferencias de conciliación.
Respuesta modelo de alta calidad
“Defino una clave de idempotencia como la identidad estable de un comando lógico de creación, no como un nuevo ID de solicitud en cada reintento HTTP. El servidor impone una restricción de unicidad duradera sobre tenant, operación y clave, almacenando el hash de la solicitud, estado, ID del recurso, respuesta y expiración. La primera solicitud reclama atómicamente processing y crea la orden; las solicitudes con misma-clave/mismos-parámetros se reproducen o esperan, y una discrepancia se rechaza.
Mantengo la escritura de la orden y el registro de idempotencia en una sola transacción y paso el mismo ID de operación al pago o a otros efectos posteriores. Si la respuesta se pierde, un reintento lee el resultado almacenado; si el estado local es desconocido, consulto los registros posteriores y de conciliación en lugar de adivinar con otro POST. Un worker con lease recupera los processing atascados, y las transiciones condicionales conducen únicamente a succeeded, failed o unknown. La retención cubre reintentos y compensaciones. Inyecto primeras solicitudes concurrentes, caídas de procesos, respuestas perdidas y retrasos entre regiones, y compruebo que haya cero órdenes o cobros duplicados”.
Errores comunes
- Generar una nueva clave para cada reintento → el servidor no puede identificar un único comando lógico → reutilice la clave original.
- Usar solo un bloqueo en memoria o de host único → el reinicio y las réplicas pierden la deduplicación → imponga unicidad duradera.
- Reproducir un resultado para un payload diferente → oculta un error del cliente → almacene una huella digital y rechace la discrepancia.
- Llamar aguas abajo inmediatamente después de registrar
processing→ una caída deja el efecto incognoscible → use una transacción, outbox o idempotencia aguas abajo. - Cobrar de nuevo después de un timeout → el cobro remoto puede haber tenido éxito → consulte y concilie primero.
- Tratar un registro atascado como fallido y volver a ejecutarlo → crea un segundo recurso → recopile evidencia en la recuperación.
- Expirar claves demasiado rápido → los reintentos retrasados crean duplicados → alinee la retención con el riesgo y las ventanas de reintento.
- Probar solo llamadas secuenciales → las condiciones de carrera aún provocan escrituras duplicadas → pruebe concurrencia con la misma clave, caídas y enrutamiento multirregión.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Por qué no usar el ID de la orden como clave única?
El ID de la orden generalmente existe solo después de que el servidor crea el recurso, por lo que no puede cubrir la ventana previa a la primera respuesta. La clave de idempotencia existe antes del efecto secundario y vincula los reintentos a un único comando lógico.
Pregunta de seguimiento 2: ¿Qué sucede si el payload cambia con la misma clave?
Calcule el hash del cuerpo normalizado y los encabezados relevantes. Una huella digital diferente devuelve un error de conflicto de parámetros sin un nuevo efecto secundario; el cliente debe crear una nueva clave para un nuevo comando.
Pregunta de seguimiento 3: ¿Qué sucede si la primera solicitud se queda en processing para siempre?
Utilice leases, heartbeats y escaneos por timeout. Un worker de recuperación verifica transacciones locales, resultados aguas abajo y logs de mensajes; solo la evidencia suficiente avanza el estado, de lo contrario se convierte en unknown para conciliación.
Pregunta de seguimiento 4: ¿Puede ser Redis el único almacenamiento de idempotencia?
Si la base de datos es propietaria del efecto secundario, la expulsión de claves en Redis, las fallas o el retraso de replicación pueden eliminar el límite de seguridad. Redis puede coordinar trabajo de corta duración, pero el estado final y la unicidad pertenecen al almacenamiento duradero consistente con la escritura de negocio.
Pregunta de seguimiento 5: ¿Qué debería suceder tras la expiración de la clave?
No acepte silenciosamente la clave antigua. Devuelva un error de expiración y dirija al cliente a consultar la orden original o crear un nuevo comando; la unicidad de negocio a nivel de recurso debe proporcionar otra protección.
Pregunta de seguimiento 6: ¿Cómo demuestra que no hay efectos duplicados?
Envíe la misma clave de forma concurrente, elimine el proceso antes y después del commit, pierda respuestas y genere timeouts en llamadas posteriores. Verifique la unicidad del recurso, IDs de operación, respuestas reproducidas, logs de transición de estado y conciliación. La aserción es a lo sumo un efecto secundario exitoso por clave lógica.
Pregunta de seguimiento 7: ¿Es una clave de idempotencia un procesamiento exactamente una vez?
No. Hace que un solo servicio reconozca comandos duplicados; no hace que una red entre servicios sea de procesamiento exactamente una vez. Todavía se requieren transacciones, entrega mediante outbox, idempotencia aguas abajo, consultas y conciliación, con resultados explícitos de unknown.