Misterios acerca de Worker Roles que se levantan y caen de inmediato

Esto me ocurrió con mi último desarrollo de un SaaS orientado al campo de laboratorios médicos.

Hace tiempo no desarrollaba sobre Cloud Services; en especial sobre Worker Roles. Esto, dado que luego de la aparición de las Web Apps, la mayoría de veces todo lo he podido resolver usando Web Sites y Web Jobs.

Pero aquí requería establecer una conexión TCP con los clientes y esto solo es posible con VMs en IaaS y Cloud Services; bien sea un Worker Role o un Web Role. Como ninguna interacción adicional era necesaria con un IIS, entonces obviamente escogí un Worker role, al cual le habilité un puerto Input Endpoint de tipo TCP, para que los clientes se pudieran comunicar con éste directamente, sin necesidad de tener un IIS consumiendo recursos de máquina.

Cuando desarrollas sobre un Worker Role, hay puntos en el ciclo de vida de éste que son difíciles de depurar.

Sobretodo cuando se está levantando el Worker Role.

Hay ocasiones en las que el worker comienza a chillar. Y peor que chillar, comienza a levantarse cayendo de inmediato, yéndose de bruces contra tu escritorio, aplastándote las manos, pues ni siquiera alcanzas a quitarlas del teclado cuando ya está caído.

Dios no lo quiera, y que cuando se levante el Worker Role luego de estar tres horas programándole seguido, éste se caiga misteriosamente y al ver el compute emulator, notes que nada está online:

EmptyServiceDeploymet

Ninguna de las líneas de diagnóstico que escribiste dentro del código de tu Worker alcanza a escribirse en la consola. Quedas en la inmunda...

Entonces aquí te voy a dar dos pistas, obtenidas meramente de mi experiencia luchando minutos en el primer caso, pero en el segundo, un par de horas para solucionar éstos inconvenientes.

Primera Pista:
Settings del Worker Role
Si dentro del código de tu Worker Role haces referencia a algún setting de los que pones usando las propiedades del Cloud Service; y dicho setting no se encuentra, de seguro el Worker no arranca bien y se cae antes de que puedas preguntar por qué?
Además recuerda que los settings que se referencian dentro del Worker Role, no son los que están en al App.confing del Worker Role como tal, sino aquellos que están en los settings del Cloud Service project que referencia al Worker:

image

Asegurarte de que los settings contienen todos los elementos que has citado en el worker role a través de instrucciones como

bool TRACING = bool.Parse(CloudConfigurationManager.GetSetting("Tracing"));

garantiza que las fallas de arranque de Worker Role serán minimizadas. También asegúrate de que si estás haciendo parsing del valor del setting, no estés produciendo excepciones que jamás llegarán a tu conocimiento…

Pista número 2:

Ya tenía muy avanzada la solución. De hecho la primera versión en producción ya estaba desplegada. Necesité hacer unos ajustes para comenzar a ofrecer una característica de logging y auditoría de operaciones ejecutadas sobre el worker role.

Estas adiciones, implicaban escribir en el table storage desde el Worker Role. Así que creé una clase que heredaba de TableEntity; clase que se encuentra en la famosa Microsoft.WindowsAzure.Storage.dll.

Esa DLL es de las más evolucionadas en Azure. Hoy día va en la versión 6.1. Recuerdo que cuando trabajé Azure por primera vez, usé la 1.1.

De hecho avanza tan rápido, que Visual Studio 2015 incluye la versión 4.3 cuando creas un proyecto tipo Cloud Service.

Y esto va configurando lo que posteriormente se convertiría en un infierno de incertidumbre y dolor donde no sabía por qué demonios el worker no se podía quedar en pie. Es como si estuviera en una feria de esas en las que aparecen figuritas y la persona apenas las ve tiene que dispararles y tumbarlas antes de que desaparezcan de nuevo.

Pues bien; sucede que también desarrollé un componente de licenciamiento. Licenser, lo llamé; y lo uso para el software que desarrollo. Licenser me permite administrar desde las tablas de Azure, qué instancias de mis desarrollos pueden ejecutarse de acuerdo a si me han pagado oportunamente o no. Lo sé. Es algo paranoico; llámenme desconfiado; pero casos se han visto. Mejor la seguridad que la policía.

Cuando lo desarrollé, usé la versión 6.1 del API del storage.

Y obviamente el worker role para mi cliente, incluía una referencia a este componente. Y funcionaba bien! A pesar de que éste worker tenía una versión desactualizada del API (la 4.3 por defecto de Visual Studio), frente a la que usaba Licenser (6.1).

Pero no fue sino después que tuve que usar el API directamente desde el worker (cuando implementé la funcionalidad de auditoría), que el worker comenzó a desparramarse inclementemente.

Aquí tuve una desventaja y es que como dije antes, desarrollé mucha funcionalidad antes de decidir hacer una ejecución del worker con fines de prueba. Así que cuando lo hice y falló, habían muchos posibles puntos que ocasionaban la falla.

Pasaron cerca de tres horas y no la encontré. Así que tuve que construir una solución de ceros, aislando el problema a ver si se me ocurría algo.

Lo primero que noté es que si creaba un worker desde ceros y llamaba directamente al API de storage, el worker andaba sin queja alguna. Erguido y orgulloso, esperando a ser desplegado en la nube.

Luego lo que hice fue añadirle las referencias de los componentes desarrollados antes; probando cada vez; preciso cuando añadí el de Licenser, boom, estalló el worker y cayó.

Por fin había un cuerpo del delito; y la prueba forense que tuve que hacer se limitó a ver las versiones del api de storage que se estaban usando.

Solo bastó con decirle al Nuguet Package Manger que detectara proyectos en los cuales una actualización del paquete de WindowsAzure.Storage.Client aplicaría; y saltó a la vista el pobre worker con su anciana versión.

image

Un update, un run y luego… la felicidad...