Archivos de etiquetas: JVM

Algo de tuning con grandes cantidades de documentos (II)

Este es el segundo artículo sobre tuning cuando se encuentran muchos documentos dentro del mismo nivel o espacio de trabajo y es continuación del artículo anterior: http://fegor.blogspot.com/2011/03/algo-de-tuning-con-grandes-cantidades.html

Seguimos con la configuración de un sistema con 60 mil documentos (muy pequeños) en el el mismo nivel de espacio de trabajo. Se me olvidó decir que las pruebas se han realizado sobre una máquina virtual en Virtual Box.

Partimos de la última cadena de configuración que usamos en el artículo anterior añadiendo también los parámetros para poder conectar vía JMX con la utilidad jconsole que es la usada para monitorizar los arranques:

export IP_ADDR=»`hostname -i`»
export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms720m -Xmx720m -Xss128k -XX:NewSize=256m -XX:MaxPermSize=128m»
export JAVA_OPTS=»${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80″
export JAVA_OPTS=»${JAVA_OPTS} -Dalfresco.home=${ALF_HOME} -Dcom.sun.management.jmxremote»
export JAVA_OPTS=»${JAVA_OPTS} -Djava.rmi.server.hostname=${IP_ADDR}»

Seguidamente se «optimiza» más la propia configuración de Alfresco ECM añadiendo al fichero alfresco-global.properties lo siguiente:

# Desconectar auditorías
audit.enabled=false
audit.useNewConfig=false

# Conexiones máximas a la BD
db.pool.max=275

# Reducción de conexión (8 por defecto)
db.pool.idle=-1

# Tamaño de filas para consultas (10 por defecto)
hibernate.jdbc.fetch_size=150

# Límite de tiempo mínimo para indexar en background
lucene.maxAtomicTransformationTime=0

# Tiempo para el tracking
index.tracking.cronExpression=0/5 * * * * ?

Cortesía de las Alfresco Master Classes y Toni  de la Fuente.

Los resultados han sido los siguientes: (Tiempo de arranque: 565879 ms)

Ahora cambiamos en contentModel.xml en el tipo cm:content la propiedad «atomic» a «false», y remodificamos los parámetros de la JVM y volvermos a probar por última vez…

export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms720m -Xmx720m -Xss128k -XX:NewSize=128m -XX:MaxPermSize=128m»
export JAVA_OPTS=»${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=50″
export JAVA_OPTS=»${JAVA_OPTS} -XX:MaxGCPauseMillis=1500 -XX:GCTimeRatio=9″
export JAVA_OPTS=»${JAVA_OPTS} -Dalfresco.home=${ALF_HOME} -Dcom.sun.management.jmxremote»
export JAVA_OPTS=»${JAVA_OPTS} -Djava.rmi.server.hostname=${IP_ADDR}»

El resultado final es: (Tiempo de arranque: 520523 ms)

Bien, Pero ¿que pasa si queremos introducir 150 mil documentos o más por nivel? Para esto antes modificaremos los valores de ehcache-context.xml para que puedan almacenar valores muy elevados así como en el fichero ehcache-transactional.xml subiremos el número de elementos en memoria  (maxElementsInMemory=»150000″) y el parámetro de JVM «-XX:NewSize» lo ajustaremos según las necesidades.

Por ejemplo:

Fichero: ehcache-transactional.xml

[…]

     
        
     
     
        
     
     
         org.alfresco.cache.contentDataTransactionalCache
     
     
         150000
     
  
[…]

     
        
     
     
        
     
     
         org.alfresco.storeAndNodeIdTransactionalCache
     
     
         150000
     
  
[…]

Un script para crear documentos de prueba podría ser:

Fichero: crear_documentos.js
var doc;
for(var i=1; i<150000; i++)
{
    doc = companyhome.createFile(«doc-» + i + «.txt»);
    doc.content = «Texto-» + i + «. Esto es una prueba de creación de documento para comprobar la capacidad de indexación, recuperación, etc. ABCDEFGHYJKLMNÑOPQRSTUVWXYZ0123456789 ABCDEFGHYJKLMNÑOPQRSTUVWXYZ0123456789 «;
    doc.save();
}

El principal problema que nos encontramos aquí es que al crearse objetos dentro de un bucle, estos no son liberados tan rápidamente y pasan a la parte de «CMS Old Gen» de la heap que es la que recibe más carga por lo que habrá que ajustar tanto los tiempos de pasadas del GC como el tamaño de la memoria «a corto plazo» o «New Gen», esto puede establecerse con el parámetro -XX:NewSize.

También hay que tener en cuenta la configuración y optimización de MySQL.

Finalmente, una cadena de arranque para la JVM que es bastante útil en el caso de cargas masivas para plataformas 32 bit (para 64 bit se puede incrementar la heap al doble o al triple) sería:

export IP_ADDR=»`hostname -i`»
export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms1384m -Xmx1384m -Xss128k -XX:MaxPermSize=128m -XX:NewRatio=2″
export JAVA_OPTS=»${JAVA_OPTS} -XX:+OptimizeStringConcat -XX:+UseLargePages -XX:+UseFastAccessorMethods -XX:+AggressiveOpts -XX:-UseParallelGC»
export JAVA_OPTS=»${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80″
export JAVA_OPTS=»${JAVA_OPTS} -XX:GCTimeRatio=9″
export JAVA_OPTS=»${JAVA_OPTS} -Dalfresco.home=${ALF_HOME} -Dcom.sun.management.jmxremote»
export JAVA_OPTS=»${JAVA_OPTS} -Djava.rmi.server.hostname=${IP_ADDR}»
export JAVA_OPTS=»${JAVA_OPTS} -Djava.io.tmpdir=${ALF_HOME}/tomcat/temp»

La memoria asignada a la máquina virtual (Virtual Box) es de 1GB. y la de la JVM es más de 1GB. (1385 MB.) por lo que esta hará «swaping» a disco.

En resumen, el tema de optimización de la JVM y Alfresco ECM con cargas masivas de documentos o creación de estos y más en el mismo nivel no es trivial pero siempre se puede «forzar» para que al menos realice el trabajo inicial y posteriormente ir descargando los espacios de trabajo en estructuras de más profundidad para disminuir la carga en memoria de tantas referencias.

Algo de tuning con grandes cantidades de documentos (I)

Cuando se tienen grandes cantidades de documentos a un mismo nivel de espacio de trabajo, del orden de 60000 por ejemplo, a la hora de indexar completamente o de requerir un listado, la JVM hace un uso intensivo de la memoria, sobre todo de la heap y en concreto de la llamada «Old Gen» (o «Tenured Gen», según casos) ya que son bucles donde se crean muchos objetos que tardan en ser liberados.

Las pruebas se han realizado en una máquina virtual con 1GB de RAM con CentOS 5 (i386), JVM 1.6.0_22 y Alfresco Enterprise 3.3.4

Todos los arranques se hacen borrando previamente el directorio «lucene-indexes» y estableciendo en alfresco-global.properties la propiedad «index.recovery.mode=FULL».

La primera prueba es arrancar con los parámetros normales que se suelen poner:

export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms128m -Xmx512m -XX:MaxPermSize=256m»

El resultado es el siguiente: (Tiempo de arranque: 630614 ms)

Se pueden observar las caídas en las gráficas cuando es lanzado el recolector de basura…

Además, al poner tantos elementos obtenemos avisos de la ehcache al sobrepasar los elementos posibles, bien, usamos la configuración que explico en el artículo http://fegor.blogspot.com/2011/01/avisos-de-alfresco-sobre-saturacion-de.html para evitar estos avisos y poder cargar más en la caché.

Además vamos a mejorar los parámetros de memoria, por un lado comprobamos que la «Perm Gen» está demasiado dimensionada con lo que la vamos a acortar para tener más posibilidades de asignarla a la heap. Además usaremos un menor tamaño de «New Gen» para adaptar mejor el tamaño a la parte «Tenured Gen», y dejaremos el tamaño de heap como está…

export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms512m -Xmx512m -XX:NewGen=128m -XX:MaxPermSize=128m»

 El resultado mientras se arranca y reindexa, estando al rededor del 70% de reindexación completa es el siguiente: (Tiempo de arranque: 702462 ms)

Como observamos, no se aprecian grandes diferencias con respecto a la primera configuración, simplemente hemos eliminado los avisos de llenado de la ehcache pero ha tardado más en arrancar. Bien, sigamos, ahora vamos a realizar una configuración más «agresiva» usando los siguientes parámetros:

export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms640m -Xmx640m -Xss128k -XX:NewSize=320m -XX:MaxPermSize=128m»
export JAVA_OPTS=»${JAVA_OPTS} XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80″

Este es el resultado:(Tiempo de arranque: 670572 ms)

Se observa un mejor comportamiento de la memoria, si bien, la parte de «Old Gen» está algo elevada. También se ha recogido la muestra mientras indexaba sobre el 70%.

Por último vamos a retocar un poco los parámetros de la JVM y eliminar la modificación sobre el fichero hibernate-context.xml que se ha seguido en el artículo anterior.

Los parámetros establecidos quedan como:

export JAVA_OPTS=»-server»
export JAVA_OPTS=»${JAVA_OPTS} -Xms720m -Xmx720m -Xss128k -XX:NewSize=256m -XX:MaxPermSize=128m»
export JAVA_OPTS=»${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80″

Y el resultado es: (Tiempo de arranque: 568476 ms)

En este último caso, la reindexación y arranque ha tardado menos tiempo si bien la memoria parece que está más aprovechada ya que se observa una mayor carga de instancias y ocupación de esta sin llegar en ningún momento a colapsarse.

Como se observa, existen múltiples configuraciones y aunque a priori parezca que no haya grandes diferencias, en muchas ocasiones puede optimizar el trabajo tanto a la hora de reindexar muchos documentos como de respuesta hacia el interface gráfico.

También se observa que Alfresco ECM cada vez se comporta mejor con grandes cantidades de documentos (obsérvese que se han creado con un tamaño pequeño y tipo texto solamente) dentro del mismo espacio de trabajo.

En la segunda parte se tocarán no solo parámetros de la JVM sino también de Alfresco ECM para acortar estos tiempos de arranque así como mejorar la gestión de la memoria.

Para obtener más información se pueden consultar los siguientes enlaces:

http://wiki.alfresco.com/wiki/JVM_Tuning#Maximum_JVM_Heap_Size_32.2F64bit
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
http://fegor.blogspot.com/2011/01/avisos-de-alfresco-sobre-saturacion-de.html

Avisos de Alfresco sobre saturación de la Cache (EHCache / Hibernate)

En algunas ocasiones, como migraciones, reindexaciones completas, etc. pueden salir determinados avisos como el siguiente:

… WARN [org.alfresco.repo.cache.TransactionalCache.org.alfresco.storeAndNodeIdTransactionalCache] Transactional update cache ‘org.alfresco.storeAndNodeIdTransactionalCache’ is full (10000).

Este tipo de avisos se refiere a que se ha llenado la memoria de intercambio o caché de segundo nivel (también llamada L2 Cache).

** SOLUCIÓN **

La solución pasa por agrandar el tamaño de esta tocando los parámetros del fichero cache-context.xml

Unos valores más acorde con situaciones de instalaciones en producción podrían ser los siguientes:

[…]
  
     
        
     
     
        
     
     
         org.alfresco.userToAuthorityTransactionalCache
     
     
         10000
     
  
[…]
  
     
        
     
     
        
     
     
     
         org.alfresco.personTransactionalCache
     
     
         25000
     
  
[…]
  
     
        
     
     
        
     
     
         org.alfresco.storeAndNodeIdTransactionalCache
     
     
         100000
     
  

[…]

Cada uno de estos valores hay que adaptarlo al número de elementos que tengamos, como son asignaciones de autorizaciones (roles en espacios de trabajo), usuarios y ficheros o documentos.

** CONSIDERACIONES **

Esto juega negativamente sobre el recolector de basura (GC) de la máquina Java (JVM) ya que tardará más en intentar recoger los objetos obsoletos.

Para esto podemos usar dos soluciones alternativas y parciales pero que impactarán sobre nuestra instalación, una es desactivar directamente la caché.

Para desconectar la caché de segundo nivel (L2 cache) podemos poner el siguiente valor en alfresco-global.properties:

hibernate.cache.use_second_level_cache=false

La otra forma de intentar resolver el tiempo de ralentización será hacer que se guarden los valores de la cache de forma más continuada. Para ello podemos reajustar los valores de los beans sessionSizeResourceInterceptor y sessionSizeResourceManager en el fichero hibernate-context.xml como sigue:

[…]
  
     
        
           
        
     
     
         10000
     
     
         100
     
  
  
     
        
     
     
         100
     
     
         100
     
     
         0
     
  
[…]

Esta última opción solo es recomendable para usarla durante una actualización, una reindexación completa, y cualquier operación que conlleve mucha carga de memoria de intercambio o cache. Una vez finalizada la operación, deberían restaurarse los valores originales.

No hay que olvidarse de hacer copia de estos ficheros para poder restaurarlos posteriormente a los valores originales.

Es recomendable además que se copien los ficheros ehcache-context.xml y hibernate-context.xml del directorio de despliegue al de configuración (extension) como custom-ehcache-context.xml y custom-hibernate-context.xml. De esta forma se pueden dejar los ficheros originales.

Más información y otras fuentes:

http://wiki.alfresco.com/wiki/Repository_Cache_Configuration
http://issues.alfresco.com/jira/browse/ETHREEOH-3294

La siguiente web es muy recomendable, tiene una serie de artículos sobre migraciones en los que se trata este tema.

http://alfrescoshare.wordpress.com/category/alfresco-dm/

Revisiones:

17/08/2011: En el caso de cluster, al usar ehcache-custom.xml en /extension, hay que modificar los valores ahí, no hace falta copiar el fichero ehcache.xml