Understanding Alfresco Content Deletion

As part of the work I’m doing for the upcoming Alfresco Summit, where I will be talking about my favorite topic: “Security and Alfresco”, I have written a few lines about Alfresco node deletion, how it works and why is important to take it into account in terms of security control.
I just wanted to clarify how Alfresco works when a content item is deleted and also how content deletion works in Records Management (RM). Basic content deletion is already very well explained in this Ixxus blog post but there are some differences in the database schema between Alfresco 4.1 and 4.2 worth noting, such as the alf_node table has a field named ‘node_deleted in versions 4.0 and earlier.
To develop a deep knowledge about Alfresco security and also how to configure Alfresco backup and disaster recovery, you should first need to understand how the Alfresco repository manages the lifecycle of a content item.
Node creation:
When a node is created,regardless how it is uploaded or created in Alfresco (via the API, web UI, FTP, CIFS, etc.)Alfresco will do the following:

  1. Metadata properties are stored into the Database in the logical store workspace://SpacesStore (alf_node, alf_content_url among others).
  2. The file itself is store and renamed as .bin under alf_data/contentstore/YYYY/MM/DD/hh/mm/url-id-of-the-file.bin
  3. Next, depending on your indexing you chose, its index entries are created within Lucene (alf_data/lucene-indexes/workspace/SpacesStore) or Solr (alf_data/solr/workspace/SpacesStore).
  4. Finally, in most cases, a content thumbnail is created as a child of the file created.

Node deletion:
There are two phases to node deletion:
Phase 1- A user or admin deletes a content item (sending it to the trashcan):

  1. When someone deletes a content item, the content and its children (eg. thumbnails) are moved (archived) within  the DB from workspace://SpacesStore to archive://SpacesStore. Nothing else happens in the DB.
  2. The actual content “.bin” file remains in the same location inside the contentstore directory.
  3. Finally,the indexes are moved from the existing location to the corresponding archive alf_data/lucene-indexes/archive/SpacesStore) or Solr (alf_data/solr/archive/SpacesStore) depending on your index engine selection.

NOTE: A deleted node stays in the trashcan FOREVER, unless the user or admin either empties the trashcan or recovers the file. This default” behavior can be changed by using third party modules that empty the trashcan automatically on a custom schedule. See below for more information on these modules.
The trashcan may be found at these locations:
 Alfresco Share: User -> My Profile -> Trashcan (admin user will see all users deleted files, since 4.2 all users can also see and restore their own deleted files).
Alfresco Explorer: User Profile -> Manage Deleted Items (for all users).
Phase 2- Any user or admin (or trashcan cleaner) empties the trashcan:
That means the content is marked as an “orphan” and after a pre-determined amount of time elapses, the orphaned content item ris moved from the alf_data/contentstore directory to alf_data/contentstore.deleted directory.
Internally at DB level a timestamp (unix format) is added to alf_content_url.orphan_time field where an internal process called contentStoreCleanerJobDetail will check how many long the content has been orphaned.,f it is more than 14 days old, (system.content.orphanProtectDays option) .bin file is moved to contentstore.deleted. Finally, another process will purge all of its references in the database by running nodeServiceCleanupJobDetail and once the index knows the node has bean removed, the indexes will be purged as well.
NOTE: Alfresco will never delete content in alf_data/contentstore.deleted folder. It has to be deleted manually or by a scheduled job configured by the system administrator.
By default, the contentStoreCleanerJobDetail runs every day at 4AM by checking how the age of an orphan node and if it exceeds system.content.orphanProtectDays (14 days) it is moved to contentstore.deleted.
Additionally, the nodeServiceCleanupJobDetail runs every day at 9PM and purges information related to deleted nodes from the database.
Now, that we understand how Alfresco works by default, let’s learn how to modify Alfresco’s behavior in order to clean the trashcan automatically:
There are several third party modules to achieve this, but I recommend the Alfresco Trashcan Cleaner by Alfresco’s very own Rui Fernandes. Tt can be found at https://code.google.com/p/alfresco-trashcan-cleaner/.
Once the amp is installed, you can use this sample configuration  by copying it to alfresco-global.properties:

[bash]
trashcan.cron=0 30 * * * ?
trashcan.daysToKeep=7
trashcan.deleteBatchCount=1000

[/bash]

The options above configure the cleaner to run every hour at thethe half hour and it will remove content from the trashcan and mark them as orphan if a content has been in the trashcan for more than 7 days. It will do this in batches of 1000 deletions every time it runs. To delete from the trashcan without waiting any grace period set the trashcan.daysToKeep property value to -1.
Can I configure Alfresco to avoid using contentstore.deleted and ensure it really deletes a file after the trashcan is cleaned?
Yes, this is possible by setting system.content.eagerOrphanCleanup=true in alfresco-global.properties and once the trashcan is emptied, the file will not be moved to contentstore.deleted but it will be deleted from the file system (contentstore). After that, nodeServiceCleanupJobDetail will purge any related information from the database. Using sys:temporary aspect it also perform same behavior.
So, what is the recommended configuration for a production server?
This is something you have to figure out based on your backup and disaster recovery strategy. See my  Alfresco Summit presentation and white paper here: http://blyx.com/2013/12/04/my-talk-about-alfresco-backup-and-recovery-tool-in-the-alfresco-summit/.
If you have a proper l backup strategy, you can offer your users a grace period of 30 days to recover their own deleted documents from the trashcan and after the grace period delete them simultaneously from the trashcan and the filesystem. This can be achieved by installing the previously mentioned trashcan-cleaner and with this configuration in alfresco-global.properties:

[bash]
system.content.eagerOrphanCleanup=true
trashcan.cron=0 30 * * * ?
trashcan.daysToKeep=30
trashcan.deleteBatchCount=1000

[/bash]

And what about Alfresco Records Management, does it work in the same way? How a record destruction works?
In the Records Management world you don’t tend to delete documents as often it is done in Document Management. When a content item is deleted from the RM file plan, it is considered to be a regular delete operation. This is rarely used and only done by RM admins when there is some justifiable reason such as correcting  a mistake that requires a record to be removed.
The only difference is that the deleted record by-passes the archive store, hence it never goes to the trashcan, it is marked as orphan once it is deleted. Then it will be moved to contentstore.deleted after orphanProtectDays or it is truly deleted if eagerOrphanCleanup is set as true.
Destruction of a record works in the same way that a record is removed, this will by-pass the archive and immediately trigger the clean-up (eagerOrphanCleanup) process so the content does not stay in the file system contentstore or contentstore.deleted.
As far as the meta-data goes, there are two options; the first is that all the meta-data (and hence the node itself) are completely deleted, the alternative method cleans out all the content but the node remains with only the meta-data (called ghosting). In Alfresco RM versions before 2.2 this was a global configuration value (rm.ghosting.enabled=true), in 2.2 it can be defined on the destroy step of the disposition schedule: “Maintain record metadata after destroy”.

Alfresco content deletion graph
Alfresco content deletion

Some final words on content deletion:
As we have seen, Alfresco offers different ways to delete content. It is important to remember, even if Alfresco completely deletes content such as when using the destroy option in RM or by using eagerOrphanCleanup, Alfresco will not wipe the removed content from the physical storage, it therefore can be recovered by file system recovery tools. Wiping a deleted content item may vary depending on multiple factors, since filesystem type to hardware configuration, etc. If you want to guarranty a real physical wipe of a file in your file system, a third party software must be used to “zero out” the corresponding disk sectors. The specific tools depend on the operating system type, hardware, etc.
Thanks to my colleagues at Alfresco Kevin Dorr, Roy Wetherall for the Records Management section and Luis Sala for the document syntax review.

27 thoughts to “Understanding Alfresco Content Deletion”

  1. Hola Toni,

    antes de todo enhorabuena por el blog en general y por este post en particular, me está siendo muy útil para comprender el ciclo de vida de los documentos en Alfresco.

    Mi duda es que he hecho pruebas en Alfresco (3.4) para ver que un documento que ha sido eliminado de la papelera, pierde sus referencias en la BBDD para que, de forma manual, proceder a borrar los elementos del “contentstore.deleted”.

    He configurado el “alfresco-global.properties” de la siguiente manera:

    system.content.eagerOrphanCleanup=true
    system.content.orphanProtectDays=1
    system.content.deletionFailureAction=IGNORE
    system.content.orphanCleanup.cronExpression=0 0 4 * * ?

    Pero sin embargo, el documento que he eliminado, sigo viendo su referencia en al tabla “alf_node” aunque con el campo “node_deleted” = 1.

    ¿No debería haberse ejecutado el servicio “nodeServiceCleanupJobDetail”? Éste está configurado como sigue:

    org.alfresco.repo.node.cleanup.NodeCleanupJob

    0 0 21 * * ?

    ¡Muchas gracias de antemano!
    Un saludo!
    Jorge

  2. Perdon, en el comentario anterior no ha salido lo que quería poner de la configuración del XML, por lo que lo edito aquí sin los “” por si hubiera algún problema en la visualización.

    bean id=”nodeServiceCleanupJobDetail” class=”org.springframework.scheduling.quartz.JobDetailBean”
    property name=”jobClass”
    value org.alfresco.repo.node.cleanup.NodeCleanupJob /value
    /property
    property name=”jobDataAsMap”
    map
    entry key=”nodeCleanupWorker”
    ref bean=”nodeCleanupRegistry” /
    /entry
    /map
    /property
    /bean
    bean id=”nodeServiceCleanupTrigger” class=”org.alfresco.util.CronTriggerBean”
    property name=”jobDetail”
    ref bean=”nodeServiceCleanupJobDetail” /
    /property
    property name=”scheduler”
    ref bean=”schedulerFactory” /
    /property
    property name=”cronExpression”
    value 0 0 21 * * ?/value
    /property
    /bean

  3. Hola Jorge,

    Sinceramente no recuerdo si este comportamiento es igual en Alfresco inferior a la versión 4.2. Pero el servicio que comprueba ese estado es nodeServiceCleanupJobDetail y es a las 9PM. Si no te está funcionando igual es un problema de esa versión. Has probado con versiones recientes?
    Gracias por seguir el blog. Me alegro que sea útil!

  4. Hola Toni,

    Excelente artículo. Bueno, este y otros tuyos que he leído. Arrojan mucha luz sobre temas para los que a menudo es difícil encontrar información, incluso en los recursos de Alfresco.

    Mi pregunta es muy clara y directa: tenemos Alfresco 3.4.11 y queremos planificar algo para liberar el espacio ocupado por el contentstore.deleted. Las opciones del alfresco-global.properties que mencionas aquí, ¿funcionarían en nuestra versión de Alfresco? Actualmente no tenemos en nuestro alfresco-global.properties ninguna entrada que haga mención a trashcan.*, system.content.orphanProtectDays, ni nada parecido.

    Gracias y un saludo,
    Potele

  5. Hola Jose,
    Gracias por leer el blog.
    No recuerdo bien en esa versión, pero si está en contentstore.deleted puedes borrar el contenido de la carpeta para liberar espacio. De hecho, si tienes un backup-recovery bien configurado deberías borrar contentstore.deleted de forma automática, una vez a la semana por ejemplo, a modo de mantenimiento. Por defecto no hay nada de eso en alfresco-global.properties ya que el comportamiento es como explico en el artículo. En resumen, si quieres liberar espacio borra el .deleted.
    Saludos.

  6. Hola,

    pero aunque esté en el contentstore.deleted aún quedan referencias en la BBDD, como por ejemplo en alf_node y en alf_trasaction. ¿No se debería esperar para borrar hasta que el proceso que purga la BBDD (que se ejecuta por defecto a las 21h) se ejecute?

    Un saludo!
    Jorge

  7. Lo que queda en la DB se limpia en el proceso diario a las 21h. Puedes esperar si quieres pero si lo piensas, recuperar lo que hay en contentstore.deleted sería trabajo de chinos. Alfresco no va a borrar nada salvo que tu lo borres manualmente o en un cron.

  8. Hola Toni,

    Entonces no quedaría inconsistente la BBDD ¿verdad? y se puede borrar sin problemas los “.bin”, ya que como la entrada del “fichero.bin” al estar en el contentstore.deleted es eliminada de alf_content_url, sólo con las referencias en alf_node y alf_transaction no se puede hacer un “recovery” del fichero.

    Pensaba que hasta que no se purgara la BBDD de todo lo relacionado con el fichero que quedaba en el contentstore.deleted no se podía eliminar.

    Una duda que me surge es, ¿y si el servicio de la purga de la BBDD por cualquier motivo no se ejecutara y se elimina manualmente todo del contentstore.deleted?¿

    ¡Gracias de antemano!
    Un saludo!
    Jorge

  9. En teoría, el rastro de los orphans se borran en la limpieza de las 21h, así que no debe haber inconsistencias. Si falla la purga de la DB un día se ejecuta al día siguiente y limpiará lo que no ha limpiado antes.

  10. ¡Muchas gracias Toni!

    Lo preguntaba porque ahora que estamos inmerso en un upgrade desde la versión 3.4.d a la 5.1 estamos echando en cuenta que el contentstore.deleted nunca ha sido vaciado y no tenemos claro incluso que el proceso de las 21h se estuviera ejecutando, de ahí mi duda de vaciar el “deleted” sin tener en cuenta que el proceso de purga de la BBDD funcionara.

    ¡Un saludo y gracias por todos los consejos y las explicaciones que puedo leer en el blog!
    Jorge

  11. Si, recuerda que contentstore.deleted lo tienes que borra a mano salvo que configures alfresco para que no mande ficheros al .deleted y los borre directamente cuando se vacía la papelera.
    Suerte con el upgrade! Me alegro que lo hagáis, la versión 5.1 es muuucho mejor 🙂

  12. Gracias por la respuesta, Toni.

    He mirado en los logs de tomcat y alfresco y no veo ninguna traza que pueda indicar que el proceso nodeServiceCleanupJobDetail se esté ejecutando a las 21:00, y eso me deja un poco mosqueado. Quizás no estoy buscando las trazas en los ficheros correctos, o quizás no haya trazas del proceso.

    Lo que supongo que sí se estará ejecutando es el proceso contentStoreCleanerJobDetail a las 4:00, pero no porque vea trazas, sino porque en el contentstore.deleted todo tiene como hora de modificación las 4:00. Eso sí, no encuentro por ningún lado la opción system.content.orphanProtectDays de 16 días que comentas, por lo que deduzco que en la versión 3.4.11 no es configurable y se ejecuta de forma fija a las 4:00. ¿Tiene sentido esto que estoy diciendo? ¿Hay alguna forma de confirmar esto?

    Ayer eliminé de la papelera un montón de información, pero no veo que contentstore.deleted haya cambiado de tamaño, por lo que supongo que habrá que esperar a que pasen los contenidos a contentstore.deleted a las 4:00 de algún día en los próximos 16 días (supongo…).

    Gracias de nuevo y un saludo,
    Potele

  13. Por cierto, JORGE VALDERRAMA, nosotros también vamos a abordar dentro de poco el -supongo que doloroso- proceso de migración de la 3.4.11 (equivalente a la 3.4.d, pero enterprise, en lugar de community) a 5.x, así que nos vendría bien cualquier consideración al respecto. Te tengo fichado! 😉

  14. Potele, por defecto no se ve rastro de esas tareas en en log, creo que recordar que hay que activar el debug en algunas partes del repo, pero en esa versión no recuerdo. Lo último que comentas es correcto, me imagino que lo verás al día siguiente. Pero actualiza por dios! 😀
    Suerte con la tarea 😉

  15. Toni: sobre: “Alfresco no va a borrar nada salvo que tu lo borres manualmente o en un cron.”
    Quieres decir que puedo ejecutar rm – R contentstore.deleted y alfresco no sufre ningún problema??

  16. Buenas, tengo el mismo problema que los compañeros, solo que en la configuración mia de Alfresco viene en el content-services-context.xml de esta manera:

    ${system.content.orphanProtectDays}

    El valor de esa variable, ¿sabéis de donde viene? o es que lo tengo que poner a pelo o en el alfresco-global.properties…

    gracias!

  17. Buenos días,
    He descargado el módulo Trashcan Cleaner, según veo en internet necesito para instalarlo el .jar y .war de Alfresco y el fichero .amp del módulo, pero este último, el .amp, no lo veo en la carpeta… dónde se esconde?

  18. Hay varios modulos que hacen mas o menos lo mismo, depende del que hayas instalado. No conozco los detalles de cada paquete.

  19. Ya lo encontré Toni, gracias.
    Voy a instalarlo a ver que tal. Pior cierto, viendo las configuraciones ejemplo del Traschcan que facilitas arriba Toni, veo que se puede programar fácilmente el vaciado de la papelera o conteststore, pero el contentstore.deleted con el system.content.eagerOrphanCleanup configurado a False… se podría programar para que se vacie automáticamente cada X días? o habría que hacerlo a mano como comenta @MARY?

    Saludos.
    Y gracias por tu blog, ayuda muchísimo.

  20. Buenas quisiera saber si existe alguna posibilidad de crear un flujo de trabajo y cuando le pongo fecha de vencimiento al llegar ese momento se me envíe un correo avisándome que expiró el mismo.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.