Windows Desktop Search, ¿Que has hecho estas semanas?

Hace casi dos semanas que instalé Windows Desktop Search, y después de todo este tiempo, aun no se que ha hecho.


Además de hacer que el ventilador de mi notebook trabaje más de la cuenta, no veo mucho más resultados. Después de instalarlo y dejarlo un rato corriendo, así íbamos:



Sin embargo, algo ocurrió y partimos de nuevo, y vamos agregando archivos para escanear


 


Así que lo dejé tranquilo casi dos semanas y así fue variando







Sin embargo, la gota que rebalsa el vaso es la cantidad de archivos que tengo en mi computador. Sólo 100 mil.



¿De adonde salen los otros 1,9 millones de archivos?
Si lo dejo una semana más, ¿llegará a los 3 millones?


Claramente no son archivos Zip o Rar. Esos los tengo deshabilitados en la configuración de Windows Desktop Search.


[Brockman]

¿Por qué debo definir "debug=false" en web.config?, Parte II

En la primera parte de este artículo revisamos parte del impacto de definir debug=”true” en nuestro archivo de configuración web.config. Dentro de lo estudiado, abarcamos la revisión de la carpeta temporal de asp.net y cómo se generan los archivos de código de las páginas y controles, para finalizar con la generación de los ensamblados que resultan de la compilación.


En ésta oportunidad cubriremos cómo actúa asp.net para manejar las modificaciones de los archivos que ya están compilados. Excluiremos por motivos obvios las modificaciones a archivos de configuración, como de la carpeta bin o configuración de IIS.


Antes de entrar de lleno en la modificación, revisemos todas las opciones disponibles para la compilación en asp.net 1.1.

Opciones de compilación 

En la primera parte, utilizamos elemento de compilación del archivo web.config que es creado por defecto:

<compilation defaultLanguage=”c#” debug=”false”    />
 

Sin embargo, existe una cantidad importante de opciones disponibles para la compilación. Veamos uno más completo para VB.NET y C# respectivamente.

<compilation defaultLanguage=”VB” debug=”true”
numRecompilesBeforeAppRestart=”?cantidad” explicit=”true|false” strict=”true|false”
batch=”true|false” batchtimeout=”?seconds” maxBatchGeneratedFileSize=”?kilobytes

maxBatchSize =”?cantidad” tempDirectory=”?ruta”>
</
compilation>

<
compilation defaultLanguage=”c#” debug=”true”
numRecompilesBeforeAppRestart
=”?cantidad”
batch=”true|false” batchtimeout=”?seconds”
maxBatchGeneratedFileSize=”?kilobytes”
maxBatchSize=”?cantidad” tempDirectory=”?ruta”>
</compilation>

 


El significado de las opciones es a veces intuitivo y otras no. Veamos una por una, excluyendo las dos primeras que ya conocemos:




  • numRecompilesBeforeAppRestart: Se refiere a la cantidad de re-compilaciones dinámicas que se realizan antes de que se produzca un reinicio de la aplicación. Es trascendental aclarar que como re-compilaciones se refiere a cambios en los archivos de páginas, controles y otros recursos, dentro de los cuales definitivamente NO se incluyen los archivos de configuración (web.config, machine.config, etc.), los ensamblados de la carpeta bin y modificaciones al directorio virtual o aplicación web. El valor por defecto es 15. Aunque la documentación no lo dice, sí funciona para aspnet 1.1 (ver más).



  • explicit y strict: Las mismas opciones disponibles para los desarrolladores VB. Para C# no tienen efecto.



  • batch: Si es verdadero, se compilan los archivos agrupados de la mejor forma posible (como hemos visto hasta ahora). En caso de ser falso, se compila un archivo por página o control. No confundir con debug=false. En este caso se compila realizando las optimizaciones apropiadas al código.


  • batchtimeout: tiempo en segundos que se dará para que alcance a compilarse un grupo de archivos. En caso de demorar más tiempo del especificado, para ese requerimiento se cambia la modalidad a por archivo.


  • maxBatchGeneratedFileSize:  Según la ayuda, especifica el tamaño máximo (en KB) de los archivos de código fuente generados en cada compilación por lotes (batch). Me surge la siguiente duda. Si el archivo de código generado de una página de mi sitio es más grande, ¿no lo va a compilar?


  • maxBatchSize: Cantidad de archivos que incluirá como máximo en cada ensamblado.


  • tempDirectory: Directorio donde se copiarán los archivos para la compilación.
Reutilización de ensamblados

Algo que no habíamos comentado antes y que es muy importante. El resultado de la compilación (ensamblados) no se ve afectado por reinicios de proceso, de servicio o de servidor. Cuando se procesa un requerimiento se revisa si el recurso (aspx, ascx) está compilado en el directorio temporal, y en caso de ser así, se utiliza el ensamblado sin consumir tiempo y recursos compilando nuevamente.


La siguiente imagen fue capturada de mi equipo el 10 de Mayo después de hacer un cambio a un archivo y podrán observar que se han mantenido los otros ensamblados con fecha 07 de Mayo.



Si notan con cuidado, verán un archivo que tiene como extensión DELETE. Pasemos a revisar como ocurre eso.

Modificaciones de archivos

Para no distorsionar cualquier prueba, he limpiado mi carpeta temporal por lo que ahora cuento con archivos generados el día de hoy (22 de Mayo). La siguiente imagen muestra el estado inicial de la carpeta temporal.



Para complementar lo anterior, se despliega ahora una imagen con las DLLs cargadas en memoria para el proceso aspnet_wp.exe (worker process de IIS 5.1 de Windows XP 32 bits). Para monitorear procesos, pueden utilizar las herramientas disponibles de SysInternals.



Después de realizar una modificación en el archivo WebForm1.aspx, y volver a ejecutarlo, hacer otra modificación y volver a ejecutarlo, se pueden observar los siguientes cambios en la carpeta temporal y en el proceso aspnet_wp.exe.




En la primera modificación se generó un ensamblado para almacenar el resultado de la compilación de este archivo modificado (wdjnsrvz.dll). Como luego se volvió a modificar el mismo, el primer compilado (wdjnsrvz.dll) ya no era necesario y se generó uno nuevo (5jbqtzyx.dll). El ensamblado que ya no se utiliza, se marca con la extensión .delete.


Adicionalmente, en el proceso aspnet_wp.exe se continúan cargando ensamblados a medida que se van requiriendo páginas. Recordemos que los ensamblados NO pueden ser descargados de un proceso, a menos que se descargue el dominio. Para el caso de una aplicación web, el dominio es la misma aplicación, lo que hará que no se descarguen los ensamblados hasta que la aplicación se recicle.


Limpiamos nuevamente nuestra carpeta temporal, definimos debug=”true” y procedemos a modificar y forzar re-compilaciones.

Después de muchas modificaciones

Como era de esperarse, después de muchos cambios, la situación en el directorio temporal y el proceso no es de las mejores. Haz click en las imágenes para agrandar.








Una vez que modificamos los archivos más de las veces especificadas en numRecompilesBeforeAppRestart, el worker process (aspnet_wp.exe) se recicla, como era esperado.


Después del reciclado del proceso, este es el panorama en el directorio temporal y el proceso. Haz click en las imágenes para agrandar.







Con debug=”false”

Si repitiésemos todo los pasos anteriores, pero con debug=”false”, el resultado sería como el de la siguientes imágenes. Podrán ver que hay una menor cantidad de archivos, optimizados durante la compilación.














Si se revisan con detalle las imágenes anteriores, hay 4 ensamblados que no han sido modificados desde un poco más de 1 hora. Estos ensamblados son los que contienen los archivos Global.asax, WebUserControl2.ascx, WebUserControl1.ascx y WebForm5.aspx, que corresponden a los 4 archivos que no se han modificado en estas pruebas. Sólo se modificaron los WebFormN.aspx, con N desde 1 a 4.


A propósito, hemos comprobado que la opción numRecompilesBeforeAppRestart  sí funciona en asp.net 1.1.

¿Cuál es el valor adecuado para numRecompilesBeforeAppRestart?

Lamentablemente esta no es una pregunta sencilla de responder. En éste KB (http://support.microsoft.com/kb/319947/en-us) recomiendan definir un valor mayor la cantidad de archivos que actualmente se copian. Me imagino que hay algunos escenarios donde una recomendación de ese tipo hace sentido, pero en mi experiencia, la cantidad de archivos a subir a producción siempre es variable.

Otra implicancia de alto impacto

Existe una posibilidad de que una página pueda correr indefinidamente si  debug=”true”. De acuerdo al documento ASP.NET Performance Monitoring, and When to Alert Administrators, si está en true, el valor de <httpRuntime executionTimeout=/> y Server.ScriptTimeout serán ignorados, y dependerá del valor de <processModel responseDeadlockInterval=/> si se presenta la condición o no.

 

Espero en una tercera entrega, poder mostrar optimizaciones a nivel de código. Espero que ya estés medio convencido.


Desde Quito, Ecuador
Patrick

numRecompilesBeforeAppRestart sí funciona en Asp.Net 1.1

Mientras trabajaba en la segunda parte del artículo ¿Por qué debo definir “debug=false” en web.config?, Parte I, leyendo la documentación de MSDN en el link de más abajo, me encuentro con lo siguiente:


Link: http://msdn2.microsoft.com/en-us/library/system.web.configuration.compilationsection.numrecompilesbeforeapprestart(VS.80).aspx


El extracto del documento donde dice claramente que es una propiedad que es nueva para el Framework 2.0.





.NET Framework Class Library
CompilationSection.NumRecompilesBeforeAppRestart Property
Note: This property is new in the .NET Framework version 2.0.
Gets or sets the number of dynamic recompiles of resources that can occur before the application restarts.
Namespace: System.Web.Configuration
Assembly: System.Web (in system.web.dll)
 

Sin embargo, al utilizar esta propiedad en un sitio desarrollado en 1.1, es aceptada sin problemas en la configuración.


Más aún, haciendo una investigación más profunda, podemos ver con [Reflector] el código de System.Web y encontraremos este código en el método que procesa la sección de compilación:





    HandlerBase.GetAndRemoveBooleanAttribute(node, “debug”, ref result._debug);
    HandlerBase.GetAndRemoveBooleanAttribute(node, “strict”, ref result._strict);
    HandlerBase.GetAndRemoveBooleanAttribute(node, “explicit”, ref result._explicit);
    HandlerBase.GetAndRemoveBooleanAttribute(node, “batch”, ref result._batch);
    HandlerBase.GetAndRemovePositiveIntegerAttribute(node, “batchTimeout”, ref result._batchTimeout);
    HandlerBase.GetAndRemovePositiveIntegerAttribute(node, “maxBatchGeneratedFileSize”, ref result._maxBatchGeneratedFileSize);
    HandlerBase.GetAndRemovePositiveIntegerAttribute(node, “maxBatchSize”, ref result._maxBatchSize);
    HandlerBase.GetAndRemovePositiveIntegerAttribute(node, “numRecompilesBeforeAppRestart”, ref result._recompilationsBeforeAppRestarts);
    HandlerBase.GetAndRemoveStringAttribute(node, “defaultLanguage”, ref result._defaultLanguage);

 


Efectivamente la propiedad se lee del archive de configuración, y como era de esperarse, tiene su valor por defecto puesto en duro en la clase.





private CompilationConfiguration()
{
    this._batch = true;
    this._batchTimeout = 15;
    this._maxBatchGeneratedFileSize = 0xbb8;
    this._maxBatchSize = 0x3e8;
    this._recompilationsBeforeAppRestarts = 15;
}

 


Bueno, nada más que decir. Para tus proyectos en Framework 1.1, aún es tiempo de usarla. Para los de 2.0, 3.0 y 3.5, también puedes ya que la documentación dice que es de 2.0 en adelante.


Patrick.
Santiago

¿Por qué debo definir "debug=false" en web.config?, Parte I

Uno de los atributos menos comprendidos del archivo web.config es el relacionado con la compilación (compilation). Cuando yo desarrollaba y estaba encargado de manejar sitios en producción, siempre me preocupaba de que el atributo estuviese en false en producción, ya que había leído que así debía ser y no me lo había cuestionado mayormente, fundamentado en lo que yo creía que significaba.


La principal confusión, al menos para mí, venía por lo que yo entendía por compilación. Cuando uno realiza un proyecto web en Visual Studio 2003, se puede generar código en dos modalidades. Una de ellas de Code Behind que corresponde a un archivo del lenguaje (VB.NET o C# por ejemplo) donde incluimos el código asociado a la página, y la otra modalidad es incluir directamente el código dentro de la página.


Siempre he sido un fanático del primer tipo de proyecto y su compilación, la cual fue drásticamente eliminada en Visual Studio 2005 y reemplazado por una cantidad casi infinita de posibles formas de compilar y generación de ensamblados (assemblies), no repetibles y que son bastante más engorrosas para subir a producción. Más adelante veremos esto y cómo Microsoft enmendó el camino.


Con la primera metodología para trabajar proyectos en VS 2003, al compilar, se genera un ensamblado en la carpeta bin del sitio web. Este ensamblado tiene todo el código de los archivos Code Behind compilado en su interior. Utilizando la otra forma, se genera también un ensamblado en la carpeta bin, pero éste contiene información mínima de la aplicación, como las referencias. Todo el código queda en las páginas y sube al sitio (con todos los riesgos que esto implica).


Bueno, volviendo a mi confusión, yo creí, al igual que varios que conozco, que esta opción debug=”true” del atributo compilation aplicaba sólo para los proyectos generados en la segunda modalidad y no para los con Code Behind, ya que todo estaba compilado en el ensamblado.


Ahí estaba mi error. Repasemos antes cual es el atributo al que estoy haciendo referencia. En el archivo web.config existe el siguiente bloque de configuración.

    < compilation defaultLanguage =”c#” debug =”false” />

Además de la compilación que se realiza al presionar Build Solution o Rebuild Solution, y que genera este ensamblado en la carpeta bin, existe otra compilación que se realiza cuando la aplicación es requerida.








Tenemos una aplicación que tiene 1 página, igual a la que por defecto se nos propone al crear un nuevo proyecto web. Este proyecto se ve como el de la siguiente imagen.


 


Si genero el proyecto con build, se genera un ensamblado y su archivo de símbolos (.pdb) como vemos ahora:



Primer requerimiento


Antes de que mi aplicación sea requerida por un browser, nada ha ocurrido ni se ha compilado (además de lo que ya compilamos en la carpeta bin). Eso lo podemos ver examinando la siguiente carpeta:





%windir%\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files







Seguramente encontrarás cosas que no pensabas que existían, pero en mi caso, no hay nada porque la limpié hace unos momentos. Entonces, si le hago un requerimiento a mi sitio http://localhost/WebCompilation/WebForm1.aspx, en esta carpeta aparece lo siguiente, después de expandir todos los directorios.



 


Las únicas carpetas que tienen información son:






…\Temporary ASP.NET Files\webcompilation\f7e6afd0\61bb37a7
…\Temporary ASP.NET Files\webcompilation\f7e6afd0\61bb37a7\assembly\dl2\a61b0a98\2a9e47df_4890c701
 

El contenido de la primera carpeta se despliega en la imagen de más arriba a la derecha. Obviamente los nombres con números de las carpetas se generan automática y azarosamente. La segunda carpeta tiene una copia del ensamblado generado por la compilación del proyecto, ese de la carpeta bin y un archivo ini con información adicional. Es importante notar que hay una cantidad importante de archivos y de diversos tipos, los que pasamos a revisar a continuación:




  • aluvtjbz.0.cs: Archivo de código c# generado a partir del archivo global.asax.


  • aluvtjbz.cmdline: archivo con las instrucciones de compilación del archivo interior. Muy interesante.


  • aluvtjbz.dll: ensamblado con el resultado de la compilación del archivo aluvtjbz.0.cs


  • aluvtjbz.err: en mi caso, vacío. Podría deducir que se almacenan lo errores de compilación.


  • aluvtjbz.out: Salida generada por pantalla cuando se compiló el archivo aluvtjbz.0.cs, producto de la ejecución del comando escrito en aluvtjbz.cmdline.


  • aluvtjbz.pdb: Símbolos asociados al ensamblado generado en la compilación.

Notar que cuando digo el archivo de código generado, no me refiero al archivo global.asax.cs  sino que al archivo que se genera para la compilación del archivo global.asax (sin .cs) propiamente tal. Complementariamente, los mismos archivos existen para el archivo WebForm1.aspx, almacenados en los archivos de nombre amyrvbql.


Adicionalmente hay algunos archivos más, que almacenan información de dependencias de archivos. Estos son:




  • hash.web que almacena algún hash identificador del sitio web


  • global.asax.xml, almacena las dependencias de global.asax (el mismo archivo global.asax y el ensamblado de la carpeta bin)


  • de1daaf3.web información al menos desconocida para mi (archivo de tamaño 0)


  • WebForm1.aspx.de1daaf3.xml dependencias de WebForm1.aspx (contiene una referencia a el mismo archivo WebForm1.aspx y al ensamblado de la carpeta bin)

Después del primer requerimiento se han compilado los archivos requeridos para servir el contenido necesario. Como se solicitó el archivo WebForm1.aspx, fue generado éste y el global.asax, utilizado siempre en cada requerimiento.





Ésta compilación es la que es modificada de acuerdo al valor de la configuración. En inglés lo podrán encontrar como Batch Compilation.
 

Antes de ver qué sucede si cambiamos la configuración a debug=”false”, agregaremos algunas páginas más a la aplicación, y otras carpetas con más páginas. Además, he agregado un par de controles de usuario en cada carpeta, los cuales son usados por los archivos WebForm1.aspx y WebForm4.aspx.


 








Después de limpiar la carpeta temporal y acceder  WebForm1.aspx (la que tiene el control de usuario), el contenido de la nueva carpeta temporal es:



 


También se han generado archivos de dependencias para todos los archivos compilados, con nombres similares a los anteriores. Si se fijan además, no se ha compilado nada de WebForm2.aspx ni ninguno de los archivos de la carpeta newfolder1.







Archivos en otras carpetas


Si ahora accedo a los archivos WebForm3.aspx y WebForm4.aspx, considerando que esta última tiene incrustado el control de usuario WebUserControl2.ascx, ocurre que ahora tengo muchos archivos en esta carpeta, los cuales han sido compilados cada uno en un ensamblado diferente. Pueden probar abriéndolos con [Reflector], como se muestra en la imagen de arriba a la derecha, para el archivo nmxwjakq.dll.


Cambiemos la configuración


Al cambiar la configuración a debug=”false” y al hacer el primer requerimiento a la página WebForm1.aspx, nuestro directorio se limpia automáticamente y se generan nuevos archivos, pero esta vez son muchos menos.







Además del hecho de que son menos archivos y con optimizaciones (que veremos en otra oportunidad), aspnet ha compilado todos los archivos del directorio, independientemente si eran o no requeridos para servir la página WebForm1.aspx.

 

Una peculiaridad del resultado de éste proceso es ver cómo se han agrupado los archivos para la compilación. Si recordamos, el archivo WebForm1.aspx tiene una instancia del control WebUserControl1.ascx pero no así el archivo WebForm2.aspx. Sin embargo, si vieron con detenimiento la imagen donde se mostraba la vista desde Reflector, habrán notado que el compilado de WebForm1.aspx va en un ensamblado (fqv6no_s.dll) y todo el resto de archivos aspx y ascx (menos  global.asax) se han compilado en otro ensamblado (uwsjh005.dll). El archivo global.asax se compiló en m9mdpgdl.dll. Agregué un archivo WebForm5.aspx sólo para comprobar que agregaba todos los archivos restantes.


Notemos también que no se han compilado los archivos de la carpeta NewFolder1. No obstante, al acceder WebForm3.aspx (que no contiene una instancia del control WebUserControl2.ascx, se compilan todos los archivos de esa carpeta, en 2 ensamblados. Uno de los ensamblados contiene a WebForm3.aspx y WebUserControl2.ascx, y el otro ensamblado a WebForm4.aspx. Mostraremos más abajo una imagen desde Reflector.


No estoy seguro de porque los agrupa así, pero me inclino con casi total seguridad que se debe a las dependencias. Para poder compilar un archivo que contiene un control incrustado, necesitará primero compilar el control y una vez que éste fue compilado, compilar la página que lo contiene.


Referencias a otras carpetas


¿Qué sucede si agregamos en WebForm1.aspx (que ya contiene una referencia a WebUserControl1.ascx) una referencia a WebUserControl2.ascx?


El resultado obtenido es el esperado aunque con una pequeña sorpresa. Lo esperado es que se compilan todos los archivos ya que el requerimiento de la página la WebForm1.aspx hace que se compile toda la carpeta donde está este archivo, como también que se compilen todos los archivos de la carpeta donde está WebUserControl2.ascx, el cual ahora era utilizado desde WebForm1.aspx. La sorpresa, al menos para mí, vino del hecho de que se utiliza el mismo patrón de agrupación de archivos para la compilación.


Si se fijan en las imágenes de más abajo, a la izquierda está el resultado de requerimientos por separado para cada WebForm1.aspx y WebForm3.aspx, ocurrida en dos momentos diferentes. La de la derecha, sólo para el llamado de WebForm1.aspx. Las agrupaciones de archivos son equivalentes.






 

Por último, ¿qué sucede si ambas páginas, o mejor dicho, los tres formularios de la raíz (WebForm1.aspx, WebForm2.aspx y WebForm5.aspx) tienen incrustado una instancia de WebUserControl1.aspx?







Antes de hacer la prueba, voy a dar mi vaticinio. Ustedes podrán confiar o no de que estoy dando mi vaticinio sin haber el resultado [;)], en el caso de apuntarle correctamente. Creo que pueden pasar 2 cosas. O bien se compilan todos pos separado o se compilan los aspx todos juntos (ya que no tienen dependencias entre ellos). Mi apuesta va por la segunda.


Mi intuición fue acertada. Efectivamente agrupó a todos los WebForm*.aspx en un solo ensamblado, el WebUserControl1.ascx en otro, y por último, el global.asax en otro. La imagen de Reflector nos muesta el resultado a la derecha.



Para la segunda parte, dejaremos el estudio de las opciones disponibles en el ítem de compilación en el archivo web.config, como también las conclusiones de porque no debo definir el atributo debug=”true” en producción. Aún nos falta ver qué sucede con las modificaciones a los archivos y las re-compilaciones.


Diferencias con Visual Studio 2005


Como ya había mencionado, en Visual Studio 2005, la forma de compilación y entrega para proyectos web cambió considerablemente. Sin entrar en justificaciones como tampoco en detalles, sólo agregaré que Microsoft enmendó el rumbo proveyendo algo similar a como funcionaba en Visual Studio 2003. Éste se llama Visual Studio 2005 Web Application Projects, disponible en la dirección http://msdn2.microsoft.com/es-cl/asp.net/aa336618.aspx. Si ya instalaste Service Pack 1, ya lo tienes.


Si te interesa además saber todos los detalles sobre la compilación en Visual Studio 2005 y formas de hacer entregas (deployment) te recomiendo el siguiente artículo de Rick Strahl, llamado Compilation and Deployment in ASP.NET 2.0 disponible en http://www.code-magazine.com/Article.aspx?quickid=0609061


Tareas para la casa


Prueben cambiando el lenguaje de compilación y vean que ocurre. Háganlo eso sí con debug=”true” para que vean los archivos de código generados.

Patrick.
Sao Paulo, Brasil.