Idempotencia y efectos duplicados
Un reintento por timeout se convierte en un segundo cobro salvo que el servidor pueda distinguir 'es la primera vez que veo esto' de 'ya procese esto'.
📈 Que cambia para el negocio
Elimina cobros y notificaciones duplicadas por reintentos del cliente, y evita el costo de soporte y devolucion que cada uno genera.
💼 Que demuestra tecnicamente
Muestra que el cliente hace lo correcto al reintentar y que distinguir el reintento es responsabilidad del servidor — y que seis de las siete implementaciones dejan de ser correctas al escalar a dos replicas.
✅ Evidencia que deja
- Contrasta /charge-unsafe y /charge-idempotent sobre los mismos N reintentos de una misma clave.
- Hace visible overcharged_cents: la plata que el negocio tendria que devolver, en la unidad en que se discute.
- Expone /outbox con pendientes y entregados, para ver que el efecto que cruza el boundary no se desincroniza del cargo.
👀 Que mirar al ejecutarlo
- Si charges_applied es igual a attempts sin clave y exactamente 1 con ella.
- Si duplicates_prevented da attempts - 1: la prueba de que los reintentos se reconocieron como tales.
- Si side_effects_emitted baja de N a 1 y el transporte pasa de directo a outbox.
Como esta resuelto en cada stack
Reserva atomica de la Idempotency-Key con la primitiva de cada runtime: INSERT ... ON CONFLICT DO NOTHING modelado con flock en PHP, dict.setdefault bajo Lock en Python, Map con has+set atomico por el modelo de un solo hilo en Node, ConcurrentHashMap.putIfAbsent en Java, ConcurrentDictionary.TryAdd en .NET, sync.Map.LoadOrStore en Go, y HashMap::entry con match exhaustivo en Rust — el unico donde ignorar el resultado no compila. Mas outbox pattern en los siete
| Stack | Health check local | Compose | Detalle |
|---|---|---|---|
| 🐘 PHP 8.3 | http://localhost:8100/16/health | compose.root.yml | README del stack |
| 🐍 Python 3.12 | http://localhost:8200/16/health | compose.python.yml | README del stack |
| 🟢 Node.js 22 | http://localhost:8300/16/health | compose.nodejs.yml | README del stack |
| ☕ Java 21 | http://localhost:8400/16/health | compose.java.yml | README del stack |
| 🔵 .NET 8 | http://localhost:8500/16/health | compose.dotnet.yml | README del stack |
| 🐹 Go 1.23 | http://localhost:8600/16/health | compose.go.yml | README del stack |
| 🦀 Rust 1.83 | http://localhost:8700/16/health | compose.rust.yml | README del stack |
El expediente completo
El caso no empieza en el codigo: empieza en el sintoma y termina en el postmortem.