Resolviendo nuestros problemas de rendimiento

Creado el día 31/01/2008 02:56 por zorry

Bueno, pues hemos conseguido averiguar y solucionar los problemas que teníamos con nuestra aplicación.

Al estresar nuestra aplicación, con 20 usuarios concurrentes, habíamos encontrado que sólo un 10% de las pruebas conseguían finalizar correctamente. Con lo que nos ponemos con la ingeniería forense:

  1. Comprobamos que nuestra aplicación web no sea el causante de los problemas. En principio, no lo parece. El consumo de CPU es del orden del 2% y la de memoria algo alta, pero nuestra aplicación siempre ha sido un poco "hambrienta", unos 100 megas. Pero la máquina debería ser capaz de aguantar la carga de proceso sin problemas.
  2. Descartado nuestro frontal, vamos a comprobar cómo va el servidor de CRM. De nuevo, parece que todo va como debería, muy poco consumo de CPU (2%) y memoria otra vez contenido (aproximadamente 100 megas también).
  3. Descartamos el CRM, con lo que nos queda inspeccionar el servidor de SQL Server. Entramos en la máquina y ya notamos que hay algo que no va todo lo bien que debiera: La CPU está en torno al 98% de carga (de un quad core) y la memoria está disparada, aproximadamente tiene unos 6 Gb de memoria el servicio de SQL. Una vez paramos las pruebas de carga, el consumo de CPU baja al 0%. Parece que tenemos fichado al culpable. Ahora vamos a ver cómo lo solucionamos.

Decidimos volver a lanzar las pruebas de carga, pero esta vez con un SQL Profiler, de manera que podamos trazar las consultas realizadas al servidor SQL. Tras unos minutos capturando tráfico, vemos que hay algunas consultas que saturan la CPU. Con lo que vamos a ver de que manera las optimizamos.

Cargamos Database Engine Tuning Advisor, una herramienta de SQL Server 2005, e introducimos el archivo de traza capturado antes. Le decimos que tiene que inspeccionar la base de datos de CRM, y se pone a la tarea. Tras un rato ejecutando y analizando nuestro conjunto de pruebas, nos sugiere crear una serie de índices y estadísticas sobre bastantes tablas, estimando la mejora en un 90% aproximado. Posteriormente la herramienta nos permite aplicar o salvar los cambios, y elegimos salvarlos en un archivo sql para poder aplicarlo en todos los entornos.

Pero nos queda la preocupación de si será recomendable modificar la base de datos de CRM, ya que no tenemos control sobre ella, al estar la estructura de esta base de datos mantenida por el propio CRM. Pero tras investigar un poco, encontramos en este artículo lo que necesitábamos: Está soportado crear índices siempre y cuando no sean índices únicos.

Así que aplicamos los cambios sobre la estructura de base de datos, y volvemos a realizar la prueba. Y los resultados no pueden ser más esperanzadores, ya obtenemos más de un 90% de pruebas correctas, y ahora el servidor es capaz de procesar más del doble de peticiones http por minuto!


Investigando un problema de SQL Server

Creado el día 30/01/2008 00:39 por zorry

En la aplicación en la que estoy trabajando, tenemos un problema de consumo de CPU muy alta en el servidor de SQL Server. Este post es para recordarme este artículo interesante acerca de tuning de SQL Server.

Vamos a ver cómo lo podemos llevar a cabo (las optimizaciones) puesto que prácticamente no podemos tocar la estructura de la base de datos (la estructura es la generada por CRM 3.0 y no la podremos tocar)

SQL Server Performance


Realizando pruebas de carga de una aplicación Cookieless

Creado el día 09/01/2008 09:31 por zorry

La aplicación web en la que estoy trabajando, trabaja en modo cookieless. Necesito probarla y replicar el funcionamiento del navegador. Una aplicación funcionando en modo cookieless almacena el ID de sesión en la URL, de manera que tras la primera petición del navegador, la aplicación redirige a otra Url, con el ID de sesión embebido, y las consecuentes peticiones contienen este Id de Sesión.

Cuando realizamos una captura de sesión mediante Fiddler (ver artículo anterior), al capturar la sesión, capturamos el SessionID, quedando este en hardcode en las peticiones. Si necesitamos que el SessionID varíe para cada prueba, necesitamos que capturar el SessionID de la primera petición y propagarlo a las siguientes.

Para poder hacerlo, vamos a crearnos un CustomExtractionRule, para ello, nos creamos una clase que herede de ExtractionRule:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using Microsoft.VisualStudio.TestTools.WebTesting;
   5:  
   6: namespace WebTest.Funcional
   7: {
   8:     public class ExtractCookielessSessionId : ExtractionRule
   9:     {
  10:         public override void Extract(object sender, ExtractionEventArgs e)
  11:         {
  12:             if (!e.Response.IsHtml)
  13:             {
  14:                 e.Success = false;
  15:                 e.Message = "The response did not contain HTML";
  16:             }
  17:  
  18:             //Obtiene el SessionID para cookieless
  19:             string beginSessionId = "(S(";
  20:             string endSessionId = "))";
  21:             string url = e.Response.ResponseUri.AbsoluteUri;
  22:             int inicioSID = url.IndexOf(beginSessionId);
  23:             int finSID = url.IndexOf(endSessionId);
  24:  
  25:             if (inicioSID >= 0 && finSID > inicioSID)
  26:             {
  27:                 string sessionId = url.Substring(inicioSID, finSID - inicioSID + endSessionId.Length);
  28:                 e.WebTest.Context.Add(this.ContextParameterName, sessionId);
  29:             }
  30:             
  31:         }
  32:  
  33:         public override string RuleName
  34:         {
  35:             get { return "ExtractCookielessSessionId"; }
  36:         }
  37:  
  38:         public override string RuleDescription
  39:         {
  40:             get { return "Extracts Cookieless Session Id"; }
  41:         }
  42:     }
  43: }

 

Una vez creada esta clase, compilamos el proyecto de pruebas, y agregamos la nueva CustomExtractionRule a la primera petición. Además, a la regla, le ponemos el nombre de parámetro de contexto SESSION (de esta manera, la regla de extracción sacará el SessionID a esta variable de contexto).

addextractionrule

Posteriormente, sólo nos quedará modificar el resto de las Url, sustituyendo el SessionID por el valor {{SESSION}}:

steps

Con esto, al ejecutar el webtest podremos ver cómo se emplea cada vez un SessionID diferente, con lo que conseguimos independizar cada prueba.


Realizando pruebas de carga con Ajax

Creado el día 05/01/2008 06:55 por zorry

En el proyecto en el que trabajo actualmente, tenemos que probar cómo se comporta la aplicación web bajo carga de muchos usuarios. Para realizar la carga, empleo Visual Studio 2005 Team Suite.

En una primera aproximación para realizar las pruebas de carga, realicé una captura web mediante la herramienta nativa de Visual Studio. En principio funciona bien, captura las peticiones http a la aplicación, pero como nuestra aplicación funciona con Ajax, las peticiones que realiza la aplicación de manera asíncrona mediante Javascript no son capturadas, con lo que las pruebas no son completas.

La solución que he encontrado ha sido emplear Fiddler para capturar el tráfico. Se arranca esta herramienta de captura antes de arrancar la aplicación web, se realiza la prueba de navegación desde un navegador, y posteriormente, Fiddler permite salvar todo el tráfico entre nuestro navegador y la aplicación web (incluido Ajax) como prueba de Visual Studio.

Posteriormente, este archivo webtest puede importarse en un proyecto de pruebas de Visual Studio 2005 Team suite para realizar las pruebas de carga.