Alfresco, qué ocurre cuando una carpeta es movida

/ / Blog, Document Management
Alfresco Global Virtual Hack-a-thon 2016
Primera beta de Alfresco SDK 3 disponible

En los últimos días, hemos estado aprendiendo de un caso de uso que requería el movimiento de una única carpeta que contenía a su vez miles de carpetas.

alfresco-move-folder

Cuando se utilizaba la acción Mover sobre la carpeta, se recibía un error de timeout en Alfresco Share y el servidor se caía horas después debido a un OutOfMemoryError.

En primer lugar pensamos que ambos problemas no estaban relacionados, ya que una simple inspección en move-to.post.json.js reveló que la excepción era capturada pero nunca era escrita en las trazas. Modificamos el web script de Alfresco para que mostrase el detalle del error y, sorprendentemente, observamos el mismo resultado que antes. Y la excepción no fue escrita en las trazas.

La única conclusión posible es que el proceso estaba todavía ejecutándose en el servidor de Alfresco, aunque el cliente HTTP de Share había recibido un timeout. Lanzando un kill -3 sobre el proceso Java de Alfresco, encontramos la siguiente traza de ejecución:

"ajp-apr-8009-exec-8" #84 daemon prio=5 os_prio=0 tid=0x00007fabe8015800 nid=0x5fb7 runnable [0x00007fab9f402000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:170)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)

    ...

	at org.alfresco.repo.domain.node.AbstractNodeDAOImpl.getChildAssocs(AbstractNodeDAOImpl.java:3484)

    ...

    at org.alfresco.repo.rule.ruletrigger.OnMoveNodeRuleTrigger.triggerChildrenRules(OnMoveNodeRuleTrigger.java:84)
	at org.alfresco.repo.rule.ruletrigger.OnMoveNodeRuleTrigger.triggerChildrenRules(OnMoveNodeRuleTrigger.java:86)
	at org.alfresco.repo.rule.ruletrigger.OnMoveNodeRuleTrigger.onMoveNode(OnMoveNodeRuleTrigger.java:69)

    ...

    at org.alfresco.repo.model.filefolder.FileFolderServiceImpl.moveOrCopy(FileFolderServiceImpl.java:1115)
	at org.alfresco.repo.model.filefolder.FileFolderServiceImpl.moveFrom(FileFolderServiceImpl.java:997)

    ...
    
    at org.mozilla.javascript.gen.classpath__alfresco_extension_templates_webscripts_org_alfresco_slingshot_documentlibrary_action_move_to_post_json_js_9._c_runAction_19(classpath*:alfresco/extension/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.js:930)

Alfresco estaba tratando de encontrar y lanzar reglas en cada una de las carpetas hijas (quizá un millón) y ese trabajo se estaba realizando dentro de una única transacción de base de datos. Así que al final, Alfresco caía debido a los requisitos de consumo de memoria de esta gigantesca transacción.

Decidimos intentar la operación deshabilitando la ejecución de reglas a nivel global (gracias a Douglas CR Paes por esto) utilizando un script en la JavaScript Console.

var context = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext();
var ruleService = context.getBean('RuleService', Packages.org.alfresco.service.cmr.rule.RuleService);

ruleService.disableRules();
logger.warn("After disabling: " + ruleService.isEnabled());

Una vez deshabilitado, la operación funcionó sin problemas, así que volvimos a restaurar la ejecución de reglas en la JavaScript Console.

var context = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext();
var ruleService = context.getBean('RuleService', Packages.org.alfresco.service.cmr.rule.RuleService);

ruleService.enableRules();
logger.warn("After enabling: " + ruleService.isEnabled());

Después de un rato, algunos errores de tipo AJP timeout comenzaron a aparecer en Apache HTTP. SOLR, que estaba instalado en un servidor independiente, había comenzado a consultar intensivamente Alfresco. Y aquí es dónde hizo aparición el extraño aspecto Cascade Update.

alfresco-cascade-update

Cada vez que un node es movido, este aspecto (que incluye las propiedades cascadeTx y cascadeCRC) es asignado al nodo. Sorprendentemente, este aspecto nunca es eliminado. Así que cualquier carpeta en el sistema sobre la que alguna vez se haya ejecutado una acción Mover tendrá el aspecto asignado y las propiedades informadas.

alfresco-search-cascade

El aspecto es utilizado por SOLR para re-indexar el contenido de una carpeta movida, como puede comprobarse en la clase Java SolrInformationServer. Parece que esta operación no está diseñada para grandes volúmenes, ya que el conector AJP (o incluso el conector HTTP) de Tomcat parece colapsarse durante esta operación.

Y la peor parte es que, si la operación se ha ejecutado correctamente, el infausto Cascade Update no es eliminado. Así que perdura para siempre como la basura espacial orbitando cualquier nodo que alguna vez ha sido movido en Alfresco.

Algunas operaciones rutinarias en Alfresco, como mover una carpeta, pueden parecer tan sencillas como un simple “update” en base de datos. Sin embargo pueden provocar serios problemas en sistemas reales, ya que disparan operaciones que pueden no estar diseñadas para grandes volúmenes. No obstante, gracias al firme compromiso de Alfresco con el open source, pueden encontrarse alternativas para realizar cualquier operación necesaria.

Unidad de negocio, keensoft