Informe experimento HA 01 - martin-hendricks/MISO-PROYECTO-FINAL-SOLVENTA GitHub Wiki

Experimento HA-01 — Informe de resultados

Maestría en Ingeniería de Software — MISW4501 Arquitectura de Software Caso de estudio: Solventa — plataforma insurtech


Título del experimento Caché con actualización oportunista y valor de respaldo para proteger el presupuesto de latencia de la cotización embebida ante un proveedor de Open Finance degradado
Propósito del experimento Evaluar si la distribución de componentes propuesta —:MsCotización → :CacheOF → :AdaptadorOF → proveedor externo, con la llamada al adaptador fuera del camino bloqueante— permite cumplir EC-LAT-01 (p95 ≤ 250 ms) y EC-LAT-02 (p99 ≤ 500 ms) tanto con el proveedor sano como degradado (EC-LAT-09), y determinar cuál es el nivel mínimo de desacople que lo satisface. El montaje no incluye el :ApiGateway, de modo que los umbrales se trasladan al servicio como p95 ≤ 225 ms y p99 ≤ 475 ms.
Resultados obtenidos Resultado 1: el desacople oportunista cumple el ASR en los seis estados del proveedor. El brazo C mantiene 0,00 % de cotizaciones por encima de 475 ms con el proveedor sano, lento, degradado, sin respuesta, caído e intermitente, con un p99 acotado por presupuesto + tarifa (178–190 ms) independientemente de la tasa de acierto de caché. Los brazos A y B no lo consiguen: A llega al 34,10 % de exceso con el proveedor lento y B al 1,28 %, ambos por encima del 1 % que permite el ASR.

Resultado 2: el circuit breaker y el desacople resuelven problemas distintos. Es el hallazgo de mayor valor arquitectónico y no estaba previsto en el diseño. Frente a un proveedor caído, el interruptor abre en segundos y rescata también a la caché simple (B cumple con 0,00 %). Frente a un proveedor lento, el interruptor nunca abre —el proveedor responde dentro del timeout duro de 700 ms— y sólo el desacople cumple. Frente a un proveedor intermitente (30 % de fallos) el interruptor no abre en ningún brazo y aun así B incumple (1,22–1,25 %) y C cumple (0,00 %): la diferencia es atribuible sólo al desacople, sin explicación alternativa. Este último caso se verificó bajo dos modelos de fallo distintos —determinista por cliente y aleatorio por petición— y el resultado se mantiene en ambos.

Resultado 3: EC-LAT-07 no se cumple con el proveedor degradado, y eso refuerza la hipótesis. Medido por fase, el p95 del adaptador con el proveedor lento es de 495,6 ms y el 99,78 % de sus llamadas supera los 120 ms de la restricción. Con el proveedor sano queda al filo (121,8 ms). La dependencia no cabe en su presupuesto cuando se degrada — y por eso el desacople no es una optimización, sino la condición para cumplir el ASR de cotización.

Resultado 4: la latencia no es el único eje del veredicto. Con el proveedor caído, el brazo A también cumple el umbral de latencia, pero lo hace tarifando el 98,9 % de las cotizaciones con el perfil por defecto, sin ninguna señal de Open Finance. B y C, en esa misma condición, resuelven el ~96 % desde caché con datos reales. La proporción resuelta con valor de respaldo es la segunda medida de respuesta de EC-LAT-09.

Resultado 5: dos sub-hipótesis quedan refutadas en su forma publicada. HD-01.3 (el camino degradado es más rápido que el frío) sólo se cumple con el interruptor abierto: con el proveedor lento el degradado cuesta 189 ms contra 159 ms del frío. HD-01.4 se invierte: en C la tasa de acierto no decide el percentil, decide la proporción con respaldo; quien depende del acierto es B, con su mínimo alrededor del 97 %.
Esfuerzo estimado Tiempo: 1 semana — 40 horas
Horas hombre: 10 horas/hombre (4 integrantes)
Menor que HA-08 porque se reutiliza el montaje de contenedores, el stack de observabilidad y el protocolo de corridas ya construidos.

Hipótesis de diseño asociada al experimento

Enunciado.

Si :MsCotización resuelve el perfil de Open Finance leyendo :CacheOF y trata la invocación de :AdaptadorOF como una actualización oportunista —abandonando la espera al agotar el presupuesto por dependencia de 120 ms (EC-LAT-07) y resolviendo con el último valor conocido o con un valor por defecto—, mientras :AdaptadorOF conserva su timeout duro de 700 ms (EC-LAT-08) con circuit breaker y aislamiento de recursos por proveedor,

entonces la cotización embebida alcanzará un p95 ≤ 225 ms y un p99 ≤ 475 ms medidos en el servicio con el proveedor sano y también con el proveedor degradado o caído,

aceptando como contrapartida que una fracción acotada de las cotizaciones se tarifique con señales menos frescas o con valores por defecto.

Punto de sensibilidad Grado de bloqueo de la dependencia externa. Es la decisión arquitectónica central: cuánto puede el camino crítico depender de un tercero impredecible. Se varió en tres niveles —A invocación síncrona bloqueante sin caché, B caché con lectura bloqueante en fallo (cache-aside clásico), C caché con actualización oportunista y valor de respaldo— más un brazo C′ que añade coalescencia de peticiones. Junto con cuatro puntos secundarios: estado del proveedor (seis estados), tasa de acierto de caché (99/98/96/90/50 %), política del interruptor (conteo contra tasa, en tráfico alto y bajo) y tasa de llegada (20/50/100/200 sol/s).
Historia de arquitectura asociada HA-01 — Protección del presupuesto de latencia de la cotización embebida · ASR EC-LAT-01, EC-LAT-02 y EC-LAT-09 (Latencia, prioridad A,A) · Restricciones EC-LAT-07 (≤ 120 ms por dependencia) y EC-LAT-08 (timeout duro de 700 ms)
Nivel de incertidumbre Alto. La separación de los dos timeouts en componentes distintos es una lectura del equipo, no una prescripción del enunciado: el caso da ambos números sin decir a quién pertenecen. Se desconocía (a) cuántas peticiones se sacrifican antes de que el interruptor abra y si ese conjunto rompe el p99, (b) si el umbral por conteo abre a tiempo con tráfico bajo, (c) si las llamadas abandonadas agotan el pool del adaptador bajo carga, y (d) qué tasa de acierto de caché es necesaria. El costo de equivocarse es alto: si el diseño no se sostiene, la cotización embebida —el journey que define la propuesta de valor de Solventa— incumple su ASR frente a un proveedor que Solventa no controla.

Resultado principal en una figura

Bloque 1 — estrategia contra estado del proveedor: p95 y p99 de la cotización en fase degradada, con los umbrales de 225 y 475 ms

La barra verde (C, actualización oportunista) es plana en los cinco estados: 64–65 ms de p95 y 65–186 ms de p99, siempre por debajo de los umbrales. La azul (A, invocación bloqueante) se dispara a 545 ms de p95 con el proveedor lento, y la naranja (B, caché bloqueante) rompe el p99 en ese mismo estado con 490 ms contra los 475 permitidos.

Nótese que en degradado y sin respuesta B vuelve a 65 ms: ahí el interruptor abre y la rescata. Esa diferencia entre estados es el hallazgo que se analiza abajo.


Análisis de los resultados obtenidos

1- Indique si la hipótesis de diseño pudo ser confirmada o no 2- En caso de que la hipótesis se haya confirmado, explique las decisiones de arquitectura que favorecieron el resultado 3- En caso de que los resultados del experimento no hayan sido favorables, explique por qué y cuáles cambios realizaría en el diseño

1. La hipótesis central se confirma, y el experimento además la corrige en un punto sustantivo. El brazo C cumple EC-LAT-01, EC-LAT-02 y EC-LAT-09 en los seis estados del proveedor, con el p99 acotado por presupuesto + tarifa e independiente de la tasa de acierto. Ninguno de los dos brazos alternativos lo consigue en todos los estados.

De las ocho sub-hipótesis: HD-01.1, HD-01.2, HD-01.7 y HD-01.8 se aceptan; HD-01.5 y HD-01.6 se aceptan con una corrección de emplazamiento —hubo que medirlas en el brazo B, no en C, porque en C la política del interruptor es invisible al percentil—; HD-01.3 y HD-01.4 se refutan en su forma publicada y se proponen reescritas.

La corrección sustantiva es la separación entre lo que aporta el interruptor y lo que aporta el desacople, que el diseño no distinguía y la hoja de trabajo de la semana 5 atribuía enteramente a la actualización oportunista. La evidencia los separa limpiamente: contra un proveedor caído basta el interruptor, y de hecho rescata también a la caché simple; contra un proveedor lento el interruptor no llega a abrir nunca y sólo el desacople cumple. El estado intermitente es el que cierra el argumento, porque allí el interruptor no interviene en ningún brazo y la diferencia entre B (1,22–1,25 % de exceso) y C (0,00 %) no admite otra explicación.

2. Decisiones de arquitectura que explican el resultado. La táctica decisiva es acotar el tiempo de respuesta separando dos relojes en dos componentes: :MsCotización abandona la espera a los 120 ms y resuelve con el último valor conocido, mientras :AdaptadorOF conserva su timeout duro de 700 ms y sigue trabajando en segundo plano para repoblar la caché. El adaptador no conoce DEPENDENCY_BUDGET_MS — esa ignorancia deliberada es lo que hace que los dos presupuestos sean independientes y es precisamente lo que la hipótesis ponía a prueba.

La implementación descansa en un detalle que merece registro: el asyncio.shield dentro del wait_for en strategies.py. Sin él, al vencer el presupuesto el wait_for cancelaría la tarea del adaptador y el brazo C degeneraría en un brazo B con timeout más corto — no habría actualización oportunista en absoluto.

Dos tácticas complementarias resultaron tener un propósito distinto del previsto. El circuit breaker protege el presupuesto frente al proveedor caído, pero es inútil frente al proveedor lento; su valor real en el brazo C no es la latencia sino proteger el pool de conexiones y al propio proveedor. La coalescencia de peticiones (brazo C′) no mejora el percentil —183 contra 184 ms— pero evita la saturación del pool bajo estampida: 101 invocaciones en vuelo sobre un pool de 40 sin coalescencia, frente a 20 con ella. Ambas se justifican por protección de recursos, no por latencia.

3. Cambios que se harían en el diseño. El dictamen es adoptar el brazo C, con cuatro ajustes que la evidencia obliga a incorporar:

  • Política del interruptor por tasa sobre ventana deslizante, no por conteo. Con tráfico alto el conteo es la mejor política (abre en 5 s, 0,07 % de exceso); con tráfico bajo es la que rompe el ASR (abre en 15 s, 1,19 % de exceso y p99 de 716 ms). La política por tasa nunca incumple y nunca gana: es la opción robusta y es la que debe quedar en el diseño.
  • Coalescencia (single-flight) como parte del diseño, justificada por el pool. No aporta latencia, pero sin ella un patrón de acceso concentrado satura el pool del adaptador.
  • Reconocer que EC-LAT-07 no se cumple con el proveedor degradado. Es un resultado, no un defecto: la restricción de 120 ms por dependencia es inalcanzable frente a un tercero que se degrada, y el diseño responde a eso abandonando la espera, no intentando cumplirla. La hoja de trabajo debe registrar EC-LAT-07 como cumplido con el proveedor sano, incumplido con el proveedor degradado, y por eso mismo desacoplado.
  • Elevar la proporción con respaldo a medida de respuesta de primera clase en EC-LAT-09. El veredicto de HA-01 no se puede leer sólo sobre el eje de latencia, como demuestra el brazo A: cumple el umbral con el proveedor caído tarifando el 98,9 % con el perfil por defecto.

Límites de estas conclusiones. El más importante es que falta el número que decide: cuánta proporción de cotizaciones resueltas con valor de respaldo considera aceptable actuaría. El criterio de refutación del Anexo B está escrito en función de ese umbral —«se refuta la hipótesis si el brazo C alcanza el umbral sólo degradando la proporción de valor de respaldo a un nivel que actuaría considere inaceptable»— y sin él C gana por definición y el criterio es inaplicable. No se cierra con más corridas; requiere una decisión de negocio.

A ello se suman las amenazas declaradas en el informe detallado: el motor de tarifa es casi toda espera y no reproduce la competencia por CPU de un motor de reglas real; el doble del proveedor es una hipótesis sobre el proveedor real, no una medición contra él; y se ejecutó una repetición en vez de las tres contrabalanceadas del diseño, salvo en la celda cercana al umbral, que sí tiene tres.


Evidencias

1- Presente evidencias de los resultados obtenidos en el experimento.

Implementación y evidencia cruda: experimentos/ha01/ — montaje Docker Compose completo (API con los cuatro brazos seleccionables por QUOTE_STRATEGY, adaptador con interruptor y aislamiento por proveedor, doble del proveedor con latencia log-normal, Toxiproxy, Redis, PostgreSQL y el stack de observabilidad), escenarios k6 y scripts de orquestación.

Datos crudos auditables: results/raw/ — 56 corridas, cada una con api_metrics.txt, provider_metrics.txt, api_info.json (configuración efectiva), env.txt, fases.json (cortes temporales), sanidad.txt (nueve verificaciones), k6_summary.json y stats.csv. Consolidado por fase en results/analysis/resultados_por_fase.csv — 224 filas, una por fase de cada corrida.

Series temporales completas: results/prometheus/ — instantánea consistente de la base de Prometheus (20 MB comprimidos, raspado de 1 s) que cubre la campaña completa. Es lo que permite recalcular cualquier percentil por fase sin reproducir las corridas.

Evidencia visual: seis figuras generadas por el pipeline de análisis acompañan al informe detallado en sus secciones correspondientes — estrategia contra estado del proveedor, latencia por camino, sensibilidad a la tasa de acierto, latencia contra carga, el trade-off de frescura y la saturación del pool. Las once figuras de serie por corrida (fig4_serie_*.png), con el estado del interruptor superpuesto a la latencia segundo a segundo, están en results/analysis/figuras/. Todas se regeneran con graficas.py desde el consolidado.

Informe detallado: Informe de resultados HA-01 — resultados por brazo y estado, las cinco sensibilidades, dictamen por sub-hipótesis, amenazas a la validez y desviaciones declaradas respecto del diseño.

Diseño del experimento: Diseño de experimento HA-01

⚠️ **GitHub.com Fallback** ⚠️