2026-10-26

El 409 es la respuesta correcta

El celular reintentó una llamada que seguía en curso. La segunda no puede hacer el trabajo otra vez.

El celular reintentó una llamada que el servidor todavía estaba procesando. La segunda entró igual y volvió a ejecutar el paso lento.

Ese fue el error. Un reintento tratado como un trabajo nuevo.

El usuario esperaba. La red cortó la espera. El cliente, con razón, reintentó. En el servidor las dos llamadas compartían la intención y nada más. Cada una creía que era la primera.

El efecto quedaba duplicado. Cuando la segunda chocaba con la primera a mitad de camino, el usuario veía un error y el sistema quedaba a medias.

Lo intenté así: guardar la llave y, si ya había resultado, devolverlo. Eso cubre el caso fácil. La primera llamada ya terminó. El caso real era el otro. La primera seguía viva. Todavía no había resultado que repetir. La segunda arrancaba el trabajo otra vez.

Dejar la llave bloqueada para siempre tampoco. Si el proceso muere a la mitad, la marca de "en curso" se queda y la acción queda cerrada. Un lock eterno es peor que un duplicado.

La llave la elige el cliente. La misma acción trae la misma llave.

Si la primera sigue en curso, la segunda no trabaja. Responde 409, con retryable y un retry_after. El cliente espera y pregunta. El servicio de atrás se llama una vez.

Si la primera ya cerró, el reintento devuelve ese mismo resultado. Un intento. Un efecto.

Si el proceso muere sin marcar éxito ni fallo, la marca vence. Pasado el plazo, la misma llave se puede procesar de verdad. Si el servicio de atrás falla, la llave se suelta en ese momento.

La primera respuesta puede no llegar al celular. La segunda no tiene que ser un 200 con un efecto nuevo. Un 409, en ese momento, es la respuesta correcta.

Notas