Consejo Windows Azure #2: No inicie threads desde los Web Roles

,

Suponga que Ud. tiene un proceso “long running” (que tarda más de lo que el usuario está dispuesto a esperar), que se dispara desde una página, la cual se sirve desde un Web Role en Windows Azure. Allá por el 2004/2005, Microsoft recomendaba utilizar ThreadPool.QueueUserWorkItem, o en el mejor de los casos, utilizar IAsyncResult. En esa época, ASP.NET era utilizado principalmente para aplicaciones de Intranet, por lo que los threads no se agotaban fácilmente.

Estas dos “soluciones” poseían los siguientes inconvenientes:

  • ThreadPool.QueueUserWorkItem consumía un thread del ThreadPool. Resulta que ASP.NET usa el mismo ThreadPool por lo que ambos compiten por los mismos threads, reduciendo la cantidad de páginas que puede servir por unidad de tiempo.
  • IAsyncResult permitía al programador crear sus propios threads, sin competir con ASP.NET. El problema de este diseño es que la cantidad de threads que se pueden crear es ilimitada, por lo que se puede saturar fácilmente el CPU.

Ambos problemas limitan fuertemente la capacidad de servir páginas en alta demanda.

La solución

  • Evite crear Threads desde los Web Roles. Es preferible demorar ese pedido de la página o realizarlo mediante AJAX.
    • Imagine que 100 solicitudes concurrentes (100 usuarios pidiendo la página) requieren 200 threads, por lo que estaría pagando el doble en costo de CPU de servidores en Web Role.
  • Si el pedido es lo suficientemente largo, y se toleran demoras de unos pocos segundos desde que se envía hasta que se procesa, hágalo mediante Queues (colas) y Worker Roles. Eugenio Pace escribió una serie de artículos en Inglés (1, 2, 3) que explican la teoría y la práctica de cómo se procesa.
    • La ventaja más importante es que la cantidad de servidores es regulable, no por el pico de demanda de páginas sino por la demora que estemos dispuestos a ofrecer. Haciendo números, si el promedio de solicitudes por segundo (traducidos a mensajes a la cola) es de 200 con un pico de 1000, si cada servidor maneja 100 solicitudes por segundo, debería alcanzar con 2 servidores para sostener el promedio, pero la latencia en hora pico sería quizás prohibitiva. Agregando servidores mejoraría la respuesta en hora pico; incluso puedo hacer esto de forma automatizada y dinámica.
  • Si está diseñando la aplicación desde un principio, considere un diseño pensando en CQRS, el cual contempla más que los long running processes :)

 

Espero que les sea útil.

Gabriel

Consejo Windows Azure #1: El log es su amigo

,
Como parte de mi trabajo tengo que mantener una aplicación de alta concurrencia en Windows Azure. Sabiendo que las máquinas virtuales de Azure son cajas negras  (no me puedo conectar y ver qué ocurre "en vivo") tengo que confiar en los Logs y en los Performance Counters para ver la "salud" de la aplicación.

Una explicación bastante detallada de qué tipos de Logs hay en Azure puede ser accedida en este link.

El consejo es aprovechar los Logs y a la vez ser prudentes en su utilización:

  • Si se detecta un error, registrarlo mediante Trace.TraceError, utilizando la mayor cantidad de detalles que ayuden a su diagnóstico.
  • Si se detecta una validación fallida o un error recuperable, registrarlo mediante Trace.TraceWarning, siendo específico en el evento.
  • Para cualquier evento útil de ser notificado (se inicia el server, cambio de configuración, ocurrió un evento importante que no representa error), registrarlo mediante Trace.TraceInformation, intentando no excederse en la cantidad de información y en la cantidad de mensajes.
Teniendo esto en cuenta vamos a registrar sólo los eventos que representan un error o nos interesan por su importancia. Ahora, ¿qué ocurre con los datos históricos? Windows Azure no tiene un proceso establecido para expirar los datos por fecha, y si quiero eliminar entradas en el Log manualmente, voy a estar abonando cada operación (centavos, pero igual las estaría abonando). Si no elimino entradas viejas en el log, voy a abonar no sólo las entradas que se agreguen al Log como parte de la vida de la aplicación sino que también voy a pagar el almacenamiento de las entradas "viejas". A fin de evitar este costo "escondido", un consejo es realizar el siguiente procedimiento:
  • Crear una cuenta de "storage" en donde temporalmente se guardarán los Logs. Esto no implica un costo adicional.
  • Modificar la configuración de la aplicación para apuntar a los nuevos Logs. Esperar a que todas las máquinas virtuales se reinicien con la nueva configuración.
  • Eliminar las tablas que comiencen con WAD (WADLogsTable, WADPerformanceCountersTable, etc.). La eliminación de tablas completas es un proceso que se realiza automáticamente en diferido. Eliminar un par de Gbs puede llevar un par de horas. Eliminar un par de Tbs puede levar días. No se deje estar! :)
  • Para verificar que se puede utilizar el "storage" anterior, intentar crear a mano la tabla WADLogsTable. Si arroja un error 409 (Conflict), esperar e intentar luego.
  • Modificar la configuración de la aplicación para apuntar a los "viejos" Logs. Esperar a que todas las máquinas virtuales se reinicien con la nueva configuración.
  • Eliminar la cuenta de "storage" de Logs temporales.
Espero que les sea útil.

Social Apps en Windows Azure (4/9/2010) - Material de la charla

,

Aprovecho para inaugurar el blog para pasarles el material de la charla que dimos con @gvilarino en CodeCamp el 4 de Septiembre pasado.

Social Apps en Windows Azure

Update: Aquí les dejo el material.

Espero que les sirva!

Gabriel