As mentioned in earlier postings, JCR observation is a very cool concept, is it allows you to react on changes in the repository; this allows you to change any written data after the write in a centralized manner, instead of attaching a post processing step to each and every method before it is doing the save to the repository.
A good example for this are the CQ DAM Asset Update workflows. There are many ways how you can ingest assets into your CQ: direct upload via browser, webdav, sync from Creative Cloud, uploads via your own bulk ingestions methods, the Sling POST servlet, … And instead of attaching the metadata extraction, the rendition generation and all the other stuff to each of these methods, a single centralized process takes care of it. It doesn’t matter that this process is asynchronous to the ingestion itself. In this case it’s a workflow, but the workflow triggers are also directly based on JCR observation.
So JCR observation is a powerful concept, and is widely used for many different purposes.
But as it is a very fundamental concept and basis for a lot of features within CQ, you can do also a lot of harm in there. So I want to discuss the most important feature of JCR observation.
JCR observation is single-threaded
In Jackrabbit 1 and Jackrabbit 2 the JCR observation is a single-threaded mechanism. So for every event each registered listener is evaluated, and if there’s a match, the onEvent method of that listener is called by that thread.
The code executed in that onEvent method should be really fast. No long-standing calculation, no network access.
If you have a long-standing calculation or if you need to do network access, do it in a dedicated thread and run it asynchronously to the JCR observation.
Be careful when your event listener is writing to the repository as well. It there’s already a heavy write load, your event listener might block when it comes to writing to the repository. This will add additional delay to the JCR observation processing.
If your JCR observation handler needs to be active only on certain instances, make the register process configurable. Based on run mode or OSGI configuration do not do the “addEventListener” call. This avoids the small overhead of executing an Event listener, which is not required on this instance.
If you get the WARN statement in your log files, that you have more than 200k pending events in the observation queue, your alarm clocks should ring. In that case all save() operations are delayed until the queue goes below that limit again (see http://www.docjar.com/html/api/org/apache/jackrabbit/core/observation/ObservationDispatcher.java.html); if you encounter this message, your first priority should be to analyze your observation listeners and speed them up.
If you want to know which observation listener consumes how much time, you should set the class “org.apache.jackrabbit.core.observation.EventConsumer” to DEBUG; it will then print for each event (!), which listener consumed how much time.
So, as a conclusion, if you use use JCR observation, be aware, that you the code fast you want to execute. Everything which exceeds a few milliseconds should be offloaded to a separate thread. And check, that you run only the observation listeners which are really required to run.
2 thoughts on “CQ development patterns – JCR observation”
Instead of relying on low-level JCR observation, picking the OSGi event based pattern (http://sling.apache.org/documentation/tutorials-how-tos/how-to-manage-events-in-sling.html) is often the better choice (it comes already with powerful filtering mechanisms and you don’t have to care about registering the event listener).
Konrad, thanks for your comment. Yes, in many cases Sling events are sufficient for the usecase; but I often see in practice and projects, that developers tend to use JCR observation, because it’s documented quite well and part of a specification.
On the other hand side if you are not familiar with Sling and all of its modules, you hardly find this page.
Comments are closed.