Cómo Juega el Pensamiento Lateral en la Arquitectura de Software

 Hace  unas semanas atrás metí un post sobre Edward de Bono y sus ideas acerca de Pensamiento Lateral. Lo metí en la categoría "Algo de Ocio – Reflexiones" ya que en el mismo no hablaba directamente de Arquitectura de Software sino que, ante todo, sentaba una base de discusión más amplia
 
A cualquiera que haya leído eso sin darse directamente por aludido, pensando que es una curiosidad pero que no se aplica a la realidad de cada uno -y en particular a la arquitectura de software-, espero con este post poder mostrarle mejor el valor de este tipo creativo de enfoque para la resolución de problemas
 
Recordemos lo básico: el Pensamiento Lateral lleva ese nombre porque pone una al lado de la otra distintas alternativas para resolver un problema, sin calificarlas previamente según la viabilidad. No busca entonces encontrar la solución más rápida ni la mejor, sino una solución al fin y al cabo. Al contrario del Pensamiento Lógico -al que no se propone reemplazar sino complementar- no analiza las alternativas en profundidad desde el inicio sino que busca enlistar varias de arranque para, en un segundo paso, recorrer los distintos caminos a ver qué tendencia presentan. Ahora sí, conozcamos los casos:
 
Caso 1: La Fusión de los Bancos
La situación es la siguiente: dos bancos habían fusionado sus carteras de clientes y pretendían ahora fusionar sus operaciones. Para ello enlistaron sendos portfolios de procesos de negocio para determinar los sistemas duplicados, y decidir cuáles discontinuar y cuáles promover a la nueva plataforma tecnológica
Particularmente se detectó que ambas entidades contaban con un módulo de cotizaciones, al que se accedía con un código de divisa y una fecha, para conocer a cuánto cotizó la divisa ingresada en la fecha dada. Se resolvió determinar cuál promover y cuál discontinuar en función de quién presentase el mejor rendimiento
Así, sometieron a ambos sistemas a una prueba de carga y constataron que el sistema A presentaba un mayor consumo de CPU que el sistema B, y también un mayor consumo de memoria, en tanto que el sistema B pasaba grandes intervalos de tiempo ocioso
Aunque inicialmente llegaron a creer que el sistema B era más liviano y eficiente que el sistema A, los resultados estadísticos finales mostraron que el throughput (cantidad de requerimientos resueltos por unidad de tiempo) del sistema A era marcadamante mayor que el de B
Por consiguiente el sistema B fue discontinuado. Te animás a descubrir por qué? (Ayuda: qué podía tener al sistema A ocupando CPU y memoria, comparado con la pasividad del sistema B?). La respuesta va a salir publicada mañana como comentario en este mismo post
 
Caso 2: La Optimización Fallida
Una compañía de turismo en línea venía sufriendo críticas caídas de ventas en temporada alta ya que el alto volumen de visitas al sitio para consultar promociones terminaba agotando los recursos del servidor. Como los visitantes recibían frecuentes timeouts, se terminaban desanimando y desistían de continuar en el sitio
La organización se dispuso a medir tiempos individuales de las consultas para determinar cuáles causaban los cuellos de botella. Encontraron así que la consulta de promociones dado el destino turístico era muy requerida en temporada alta, y acarreaba una demora de 10 (diez) segundos, no cacheaba las consultas más frecuentes a promociones por lo que siempre iba hasta la base de datos, formateaba los resultados, etc
Se realizó un proyecto de optimización para cachear aquellas consultas más frecuentes, entre otras mejoras, y se logró bajar el tiempo de 10 segundos a un promedio de 2 segundos. Además, para evitar colapsar la memoria mediante el uso del caché, se adicionó memoria al servidor, y de paso se le agregó una CPU. Terminado este proyecto de mejoras, se hizo el pase a producción
Sin embargo, y para sorpresa de la organización y de quienes trabajaron en las mejoras, aunque los síntomas en horas de alto tráfico demoraban unos minutos más en comenzar a manifestarse, no dejó de ocurrir que en determinado momento los visitantes comiencen a recibir sistemáticamente timeout, y la aplicación se torne inusable. Qué pudo haber fallado, a pesar de todo? La respuesta la publico en dos días más
 
Caso 3: Escalabilidad Descendente
Los operadores de un supermercado en línea, precavidamente, habían dispuesto monitoreo y alarmas ante bajos recursos de la granja de servidores para la aplicación de ventas on line. Llegaron a la conclusión de que si bien los requerimientos se resolvían rápido y los accesos a la base de datos estaban optimizados, los cuatro servidores de la granja operaban casi al límite de las sesiones simultáneas que eran capaces de satisfacer. Aunque la granja habilitaba escalabilidad horizontal sin afinidad de sesión (lo que permitía que los requerimientos HTTP de un mismo comprador puedan ser atendidos por cualquier servidor en la granja), los requerimientos concurrentes iban siendo demasiados en la medida que el uso de Internet para la cotidianeidad aumentaba
La decisión de ampliar la granja a 12 (doce) servidores se aprobó sin más trámite, al punto que se decidió manejar las sesiones en memoria en lugar de usar una base de datos, por temor a que ésta pueda volverse el nuevo cuello de botella. Por esta razón, se les subió la RAM a cada servidor de la granja
Se implementó, se hizo el pase a producción de todo esto con la ilusión de los operadores de que ya no iban a recibir alarmas por bajos recursos. Y no se equivocaron: ya no se dispararon más alarmas por bajos recursos. Ahora las alarmas comenzaron a llegar por bajo throughput. Si la aplicación en sí no se tocó, y se agregaron recursos… qué empezó a fallar ahora? Tenés tres días para descubrirla vos, o leerla directamente de mi comentario
Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

6 respuestas a Cómo Juega el Pensamiento Lateral en la Arquitectura de Software

  1. Diego dijo:

    Solución del Caso 1
    El sistema A, al contrario que el B, cacheaba en memoria los resultados de las consultas frecuentes, de manera de evitar ir hasta la base de datos en consultas similares (probado que, cuando una cotización a fecha vencida es ingresada, la probabilidad de que se cambie es casi nula -sería 0 de no ser que hay q prever el error humano (una cotización a fecha vencida puede requerir modificación si se detectó que originalmente se ingresó mal)
    De este modo, el sistema A consumía una memoria similar a la de B en cuanto al overhead para acceder a la base de datos. Pero el sistema A requería adicionalmente memoria para mantener la caché de resultados
    Como el sistema B no cacheaba, cada requerimiento le implicaba acceder a la base de datos, y a raiz de aquello quedaba bloqueado en espera por un evento (disparado por el S.O. al resolverse la consulta a la BD). Durante este período bloqueado, el sistema B no consumía CPU por lo que la consola de monitoreo de aplicaciones lo mostraba como ocioso
    El sistema A, por el contrario, era capaz de resolver sus consultas más frecuentes accediendo a su propia memoria caché. De esta manera, no quedaba bloqueado por el S.O., condiciendo eso a que en consumo de CPU y otros recursos se lo viera como más demandante. Pero era una mayor demanda dentro de los parámetros esperables: el servidor no se congestionaba y los requerimientos frecuentes se satisfacían más rápido
     
    En ese sentido, y por estas razones, el sistema A es preferible a B

  2. Diego dijo:

    Solución del Caso 2
    Lo que falló, a pesar de todas las mejoras realizadas para mejorar la estadística de un requerimiento individual (que de los 10 segundos iniciales cayó a sólo 2) fue que no se contempló en las pruebas de regresión si esta misma estadística se iba a mantener en la concurrencia de requerimientos concurrentes
    El hecho es que al implementar la caché de promociones más frecuentemente consultadas se eligió un modelo thread-safe (esto es, disponible para un único hilo de ejecución a la vez) para evitar que alguien vaya a acceder a una entrada que justo se está actualizando
    En consecuencia, el acceso a la caché quedó disponible para un único requerimiento web a la vez, y siendo que todos los requerimientos para consulta de promociones necesitaban acceder a la misma, comenzaron a encolarse estos requerimientos
    De esta forma, si bien un requerimiento individual se satisfacía en 2 segundos, en el esquema concurrente el requerimiento llegaba, debía esperar un cierto lapso -hasta que todos los requerimientos anteriores hayan tomado y liberado la caché- y recién ahí podía seguir adelante
    Tengamos en cuenta que si requerimientos al servidor llegan en una tasa de 10 por segundo, por poner un valor, el hecho de tener que encolarse para acceder a la caché hace que un requerimiento acceda y los otros 9 esperen. Pero un segundo más tarde, ya son 19 los que están esperando. Supongamos que en el interín la caché se liberó y un segundo requerimiento la lockea. Ok, tenemos 18 requerimientos en espera pero un segundo después ya son 28. Supongamos que entonces se libera la caché y ahora son 27 los que esperan, pero un segundo después hablamos de 37, y así sucesivamente
    En menos de 5 segundos, 37 requerimientos en espera lo que da lugar a dos conclusiones:
    1) Olvidémonos de que cada nuevo requerimiento que llegue esté resuelto 2 segundos después: es lógico que como esto siga creciendo, los browsers de los usuarios van a seguir mostrando la excepción por timeout que motivó este cambió
    2) La tasa de arribo/encolamiento es sensiblemente más alta que la tasa de despacho. No va a pasar mucho para que el Internet Information Services (IIS) alcance su máximo de conexiones concurrentes disponibles, y a partir de allí cada requerimiento nuevo que se reciba va a ser inmediatamente respondido con un HTTP 500 Server Too Busy (Servidor Muy Ocupado)
     
    Programación thread-safe en servicios de alta disponibilidad (high availability) es por lo general desaconsejada siempre que se pueda evitar, aunque en caso de precisarse lo que se sugiere es reducir al mínimo el intervalo crítico y la zona crítica
    Por reducción del intervalo crítico se hace referencia a poner los comandos de lockeo y deslockeo estrictamente entre la/s instrucción/es que accede/n al recurso compartido para minimizar el tiempo de ejecución con ese recurso lockeado
    Por reducción de la zona crítica se hace referencia a no lockear la caché completa, sino unicamente la entrada que se quiere acceder, en base a los argumentos de la consulta del usuario

  3. Diego dijo:

    Solución del Caso 3
    Al quitar el servidor de sesiones manejado desde la base de datos, las sesiones pasaron a ser manejadas bajo el esquema InProc ("en proceso", es decir dentro de la memoria RAM de cada servidor de la granja). Una alternativa hubiese sido manejar las sesiones Out-of Process, es decir, en un servidor aparte en su memoria, pero se desechó por la misma razón por la que se quitó la base de datos como almacén de sesiones: no tener cuellos de botella
    Por ende, la alternativa elegida presenta las ventajas de que no necesita serializar/deserializar sesiones para alojarlas/recuperarlas de servidores de sesiones… pero eso implica volver atrás con la afinidad de sesión: es decir, según rangos de direcciones IP, las sesiones irán a parar a servidores determinísticos y se conservarán en su RAM
    Esto terminó derivando en bajo rendimiento ya que, a pesar de que los servidores tenían más RAM, más CPU, etc, ocasionalmente demasiados requerimientos debían ser resueltos por un puñado de servidores para conservar la afinidad, habiendo otros servidores disponibles (y ociosos) que ya no iban a hacerse cargo de la situación
     
    Una mejor estrategia hubiera sido agrandar la granja y potenciar los servidores (como se hizo) pero sin tocar el esquema del servidor de sesiones basado en la base de datos. Si en las pruebas de stress se detectaba que era cuello de botella, entonces la alternativa hubiera sido probar con el manejo out-of process, que si bien escala menos tiene mejor rendimiento

  4. Jose Luis Barrientos Teran dijo:

    gracias! en una de mis materas tenemos que hacer unas pacticas que son estas!. y bueno solo queria saber si lo tomaste de algun libro y las respuestas son de tu criterio?

  5. chrissystems dijo:

    Gracias, al igual que Jose Luis Barrientos Teran son practicas de una de mis materias
    De nuevo gracias por el aportee

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s