Fine tuning your Jahia server

November 14, 2023

After having implemented all your templates and you are satisfied with your website, there may be some modifications to be done to enhance the performance of your server.

Before changing any values on your production server, you should ask yourself the following questions:

  • How many editors do you have working simultaneously on the system?
  • What is the number of authenticated users that can log into your system (in general, not necessarily at the same time)?
  • What is the number of pages that you have in your system, and if they contain a lot of resources (PDF files, etc.)?

As a general rule, in order to test the performance of any system running Jahia, here are the issues that need to be addressed:

  1. Tomcat and the amount of virtual memory (typically the -Xmx part in the setenv.sh/setenv.bat file)
  2. The database and its default settings
  3. Jahia properties configuration

The values given here are the high values and have been tested, but that does not mean that this corresponds to the values you should set. The way to find the proper values that will fit your system is to increase progressively, and set the values here one at a time (except for the server.xml and database pool size, they go by pair). Then run a load test (bearing in mind the answers to the questions at the beginning of this section) to see if it corresponds to your expectations.

Tomcat

bin/setenv.sh or bin/setenv.bat

We usually recommend raising the amount of virtual memory (-Xms and -Xmx parameters) in your bin/setenv.sh (non-Windows OS) or bin/setenv.bat (on Windows) file to 3072, 4096 or even higher.

It is not necessarily true that the more virtual memory you give to your system, the faster you get, as sometimes having a lot of memory can benefit you in the beginning, but then garbage collection may take longer, which will make your server unavailable for a longer period of time.

Starting with DX 7.3.0.1, if you are using the Apache Tomcat, installed with Jahia installer, you can pass environment variables (either set them globally on your system or in the tomcat/bin/setenv.sh or bat files) to adjust the JVM heap size or provide additional JVM options for Jahia. The following example sets the heap size to 4 gigabytes and passes additional Java system property:

export JAHIA_JAVA_XMS=4g
export JAHIA_JAVA_XMX=4g
export JAHIA_JAVA_OPTS=-Dorg.jahia.ehcachemanager.maxBytesLocalHeap=400M

conf/server.xml

Here you can increase the amount of maxThreads as well as the amount of acceptCount. These settings are the ones handling the connections to your server. maxThreads is the maximum number of threads processing requests in Tomcat, whether serving pages from Jahia cache or not. If this one is exceeded, then errors will be sent to the client. In case you need to modify those settings, do it in the HTTP connector, the AJP connector or both, depending how you access your application server.

On the other hand, raising this number may not bring the wanted effect. For example, if you leave maxModulesToGenerateInParallel at 50 in jahia.properties, as no more than that number will do the real work, while the other threads will queue. For more information about that configuration, see Module generation queue.

Database

As we have increased the amount of threads in Tomcat, we have to tune the database connection pool on Jahia side and also eventually the maximum number of connection your DBMS is allowing.

Note please that the maximum number of active DB connections in your pool should be in any case higher than maximum number of HTTP or AJP threads, your application server is processing at a time. And in turn your DBMS server should allow that maximum number of DB connections (also considering other applications, which access the same DBMS).

Cache configuration

Caches are separated into two memory spaces. One will hold the big caches (HTML, dependencies and files), the other one will hold all the other caches (users, groups, ACLs, etc.).

This decision about the split was taken after intensive performance tests to achieve the best results and the easiest configuration. This campaign of performance tests will help us guide you through the configuration of your cache behavior.

First thing to know is by default your Jahia configuration is a generic one, this means we try to find the best compromise for every configuration. As is by default your caches are configured using a percentage of your memory:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="false" monitoring="autodetect"
         name="org.jahia.ehcachemanager"
         dynamicConfig="true"
         maxBytesLocalHeap="15%">

and

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="false" monitoring="autodetect"
         name="org.jahia.ehcachemanager.big"
         dynamicConfig="true"
         maxBytesLocalHeap="20%">

Which on a default minimum production configuration of 4Gb give us 1.4Gb of cache (35% of heap). First, we will give you some guidelines on how to set up those limits depending on your JVM memory, and then we will see how to check the status of your caches so that you can best adapt those sizes.

How to configure and size your caches

The HTML cache is the cache that can grow very fast depending on how many pages and authenticated users you have and ACLs. So, this is the one that will be the one to increase mostly.

Jvm Memory General Cache HTML Cache %
4Gb 400M 1600M 50
6Gb 500M 2500M 50
8Gb 1G 5G 75

Starting with DX 7.3.0.1, the default maximum cache size can be configured via Jahia properties, Java system properties or even environment variables. By adding the following entries into your jahia.*.properties file, the cache size can be specified:

org.jahia.ehcachemanager.maxBytesLocalHeap=400M
org.jahia.ehcachemanager.big.maxBytesLocalHeap=800M

This will dedicate 400M to the general cache and 800M for the HTML cache.

In case you are using earlier Jahia version, please follow the steps below to modify cache configuration directly in the ehcache-*.xml files of your Jahia.

To update your configuration, you need to update two files in your Jahia installation, they are by default located in tomcat/webapps/ROOT/WEB-INF/classes. Those files are ehcache-jahia.xml and ehcache-jahia-html.xml or ehcache-jahia-cluster.xml and ehcache-jahia-cluster-html.xml if you are in cluster. Look up for the <ehcache> tag near the top of the file and update the value of maxBytesLocalHeap, save and restart the Jahia. This is only a general guideline to give you a head starts when you increase the amount of RAM available for Jahia, you need to adapt the cache first following those generic guidelines then we will see how to refine them. However, the important part is that if you do increase your memory available to your Jahia, you need to increase the limit for those Ehcache managers otherwise the increase in performance might not be the one expected.

How to monitor and tune caches

To monitor your caches behavior, go to: http://localhost:8080/tools/cache.jsp

There you should see something like this:

Once the page is displayed click on “show size in bytes” to display the sizes of all the caches.

At the bottom line of each table you will see the overall size of each manager. This is already a very good indicator if your caches are near their maximum size or not. If they are not, keep an eye on them on a regular basis for the first few weeks after launching your platform.

Monitoring those numbers will give you the best size needed, if you do have 8Gb of RAM but not that many pages but a lot of users and ACLs you might need more space for the users/groups cache manager than for the html one.

The main rule to follow here is that the HTML caches will deal with whatever space they have, if too little you will regenerate more fragments than needed which will have an impact on rendering but maybe not so much as most of the shared fragments will still be in memory. On the other side the general cache manager need to have plenty of space to keep all objects in memory, if you start to see a lot of misses on eternal caches in this manager you need to increase the memory limit (to see if a cache is configured as eternal click on “show config details” then on the “?” next to the cache name). As some of those caches have to be eternal Ehcache should never evict them.

List of eternal caches

  • ApplicationCache
  • ApplicationContextCache
  • ApplicationEntryPointCache
  • ExternalIdentifierMapping
  • FileLastModifiedCache
  • HTMLNodeUsersACLs (50M limit by default)
  • HTMLRequiredPermissionsCache
  • JCRGroupCache (100M limit by default)
  • JCRGroupMembershipCache (100M limit by default)
  • RenderService.TemplatesCache (10M limit by default)
  • WorkflowRuleCache
  • org.jahia.security.matchingPermissions (1M by default)
  • org.jahia.security.privilegesInRolesCache
  • org.jahia.services.usermanager.JahiaGroupManagerService.groupPathByGroupNameCache
  • org.jahia.services.usermanager.JahiaGroupManagerService.membershipCache
  • org.jahia.services.usermanager.JahiaUserManagerService.userPathByUserNameCache
  • org.jahia.sitesService.siteDefaultLanguageBySiteKey
  • org.jahia.sitesService.siteKeyByServerNameCache
  • org.jahia.sitesService.sitesListCache
  • vanityUrlByUrlCache

Behavior of HTML Caches

The HTMLCache contains all the generated fragments of your pages/users. The HTMLDependenciesCache and HTMLREGEXPDependenciesCache contain a mapping of dependencies (nodes or regular expression) and the fragment keys linked to them, so that when you publish a node we know which fragments need to be regenerated.

Those caches have a strong interdependency, so when memory is needed and Ehcache will have to evict/expire some entries in one of the “HTML*DependenciesCache” a listener will remove the dependent entries in HTMLCache. So that everything in cache is always in sync.

Module generation queue

The queue can be configured in:

<digital-factory-config>/jahia/jahia.properties

Here you should increase the following value for your server:

######################################################################
### Concurrent processing options ####################################
######################################################################
# This variable controls how many threads are allowed to do heavy weight
# processing (module creation not served from the cache)
maxModulesToGenerateInParallel = 50

This value controls how many parallel threads will be allowed to start rendering modules not coming from cache, meaning that they will open JCR and DB connections to obtain the content from there. maxModulesToGenerateInParallel in jahia.properties should not be bigger than the maxThreads value in server.xml. The factor between maxModulesToGenerateInParallel and maxThreads (HTTP or/and AJP) should be around 2-3, meaning:

maxThreads = maxModulesToGenerateInParallel * (2-3)

For example:

maxModulesToGenerateInParallel = 100, maxThreads = 300
maxModulesToGenerateInParallel = 200, maxThreads = 600

Operating mode

Setting the operating mode to “production” enhances the performance of your server as when set to “development”, we check more often, which resources (templates, rules) on the server changed to redeploy or reinitialize them. The Development Mode will also write more debug information or not compress certain data to have it readable. The Distant Publication Server Mode provides similar performances as the Production Mode, but deactivates some authoring features, as you are not supposed to perform authoring actions directly on this server. This mode is configured in digital-factory-config/jahia/jahia.properties:

# This setting can be used to activate particular profile:
# - development
# - production
# - distantPublicationServer
operatingMode = development

Maintenance mode

You can enable the maintenance mode to disable rendering of any pages except for the tools section of your Jahia instance. This allows system administrators to perform operations on the instance without running into possible interference from users’ actions. You can enable the maintenance mode from the System Maintenance page of the Tools section of your Jahia instance: http://localhost:8080/tools/maintenance.jsp As noted on that page, the setting will not persist across server restarts. If you want to stay in maintenance mode after a server restart, you will need to set the maintenanceMode property to true in digital-factory-config/jahia/jahia.properties:

# Set this to true to enable maintenance mode, i.e. no requests will be
# served except to /tools/
#maintenanceMode = false

Read-only mode

It is possible to disable any editing operations on a specific Jahia instance. This is particularly useful in a clustering scenario to create “pure” browsing nodes. You can enable the read-only mode from the System Maintenance page of the Tools section of your Jahia instance: http://localhost:8080/jahia/tools/maintenance.jsp. As noted on that page, the setting will not persist across server restarts. If you want your Jahia instance to stay in read-only mode even after a server restart, you will need to set the readOnlyMode property to true in digital-factory-config/jahia/jahia.properties:

# Set this to true to enable read-only mode, where access
# to edit/studio/administration modes is disabled
#readOnlyMode = false

JCR DataStore garbage collector

The goal of the JCR DataStore garbage collector is to clean the DataStore up by removing the no longer referenced binaries, i.e. entries which are no longer referenced from any workspace (live, default and versioning). As the nature of the DataStore is append-only (meaning it does not update or delete binaries automatically), this maintenance task should be run periodically (once a week, month or quarter).

As the process could be resource intensive, the operation should be planned for times when the processing node is not under stress. The job can be triggered manually from the Jahia Tools Area -> JCR DataStore garbage collection (http://localhost:8080/ tools/jcrGc.jsp).

Starting from the 7.3.7.0, the task is executed automatically the first day of each month at midgnight.

Storing binary files

During the installation process when setting the database connection settings an option allows you to either check or uncheck the box “Store binary data in the database”. According to the Apache Jackrabbit wiki (https://wiki.apache.org/jackrabbit/DataStore), “FileDataStore is guaranteed to be consistent after a crash (unlike the BundleFsPersistenceManager). It is usually faster than the DbDataStore, and the preferred choice unless you have strict operational reasons to put everything into a database.” We recommend you leave the “Store binary data in the database” checkbox unchecked. You cannot switch between the store implementation at a later time, unless one makes an export-import of the repository data. When using a FileDataStore in cluster, a shared file system needs to be used, where all cluster-nodes point to. By default the datastore is located at digital-factory-data/repository/datastore. You can override that location (and move the folder to any other place, when the Jahia server is shut down), if needed, by changing the path value for jackrabbit.datastore.path in the jahia.properties file, for example:

# JCR file datastore path in case of the file-based binary storage
jackrabbit.datastore.path = /opt/DigitalExperienceManager-7/share/datastore

Increasing bundleCacheSize

Another recommendation is to increase the value of the bundleCacheSize settings. There are three PersistenceManagers using bundle caches: one for default workspace, one for live workspace and one for the version space. Each is on default just 8MB small. For large production systems you should increase the values, so that they together occupy around 1/10th of the JVM maximum heap space. More information can be found at this link. At that linked article you get some information how to read the bundleCache related log output in the console. Based on the miss to access ratio in your environment you can decide whether you should dedicate more or less memory to either default, live or the version bundle cache. In cluster, it also depends whether a cluster node is used for authoring/processing content or just for serving the published live content. So, you should adapt the setting to the cluster node role, and if for instance it is just used to serve live content, then the live bundle cache should get most of the 1/10th of heap. Usually the versioning bundleCache can be 2-4 times smaller than the default/live bundleCache, but it depends on the environment and usage, so you can decide on your own by checking the bundleCache lines in the console output. Let’s take as example that we have a system using 3GB of heap, we may set the bundleCacheSize to the following values: default: 128MB, live: 128MB, version: 64MB.

To increase the bundleCacheSize parameter of the different PersistenceManagers you can provide the following settings in the <digital-factory-config>/jahia/jahia.properties file:

jahia.jackrabbit.bundleCacheSize.workspace=128
jahia.jackrabbit.bundleCacheSize.versioning=64

The first entry sets the bundle cache size of the persistence manager for live and default workspaces to 128 MB. The second one – the size of the cache for versioning persistence manager. Starting form 7.3.1.0,  you can also set different cache size for default and live workspace by using dedicated properties : 

jahia.jackrabbit.bundleCacheSize.workspace.default=128 
jahia.jackrabbit.bundleCacheSize.workspace.live=256
jahia.jackrabbit.bundleCacheSize.versioning=64

JCR indexing configuration

If you would like to override the indexing configuration of the JCR repository, you could place the files, named indexing_configuration.xml and indexing_configuration_version.xml into the digital-factory-config/jahia folder. Jahia will detect them on startup and use them (instead of the digital-factory-data/repository/indexing_configuration.xml and digital-factory-data/repository/indexing_configuration_version.xml files).

Logging

Modifying the logging level

The following instructions apply to modify logging levels permanently. If you want to only change the level for a short time, you can use the runtime tool, described in Logging. When you install a release of Jahia, the logging level is set to the minimum to avoid slowing down the platform. If you need to increase it for debugging purpose, you need to modify the file log4j.xml which is in the following directory:

<digital-experience-manager-web-app-dir>/WEB-INF/etc/config

Log4j defines the logging levels as follows (from the more to the less verbose): ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF. At the bottom of the file, you have the <root>... </root> part. Change the:

<level value="info"/>

To

<level value="debug"/>

for example, to have more debugging information in the console. You can also change this parameter for some specific part of Jahia like Jackrabbit or Lucene. You can even add your own logger on a specific set of classes, for example:

<logger name="org.quartz">
    <level value="info"/>
</logger>

By default, logs are redirected to the standard out, which is normally the console. Under Windows, logs will be displayed in the DOS window where Tomcat is running. On Linux, logs will be redirected to the catalina.out file. As Jahia uses Apache Log4j for its logging system, you can use tools like Chainsaw (part of the Log4j project) to better work with logging messages. You can change the log-level of Jahia “on-the-fly” without having to shutdown and restart it. This is very useful when you need to have extra logs on a production server, but do not want to restart it just for this. Jahia watches for changes in the log4j.xml file every 60 seconds, so once you have changed the log level, you will need to wait a few seconds before the changes will be effective. Do not forget to change the values of INFO back, as the DEBUG log level has a pretty important impact on performance.

Logging configuration location

If you would like to override the WEB-INF/etc/config/log4j.xml file completely or change its location, you have several options:

  1. By placing a file, named log4j.xml into the <digital-factory-config>/jahia folder. Jahia will detect it on startup and use it (instead of the WEB-INF/etc/config/log4j.xml file)
  2. By providing a Java system property named jahia.log4j.config you could specify the resource location for the Log4j configuration (using Spring resource's format), e.g. in the tomcat/bin/setenv.sh:
CATALINA_OPTS="$CATALINA_OPTS -Djahia.log4j.config=file:///opt/DigitalExperienceManager-7/log4j.xml"

Rolling over the log file (daily) on Windows

When running Jahia instance of a Windows-based machine, there is a know issue with the DailyRollingFileAppender (Bug 29726) that prevents the jahia.log from rolling over properly. The solution is to switch to the RollingFileAppender from the Apache Extras for Apache log4j project with an appropriate rollingPolicy configuration. Note, please, due to a special classloading on JBoss EAP this solution does not apply to a Jahia instance, running on JBoss EAP server.

The apache-log4j-extras-1.2.17.jar is already included into the Jahia Web application.

Modify the file <dx-webapp-dir>\WEB-INF\etc\config\log4j.xml and replace the DailyRollingFileAppender with the RollingFileAppender with an rollingPolicy element as follows:

    <appender name="ROLL" class="org.apache.log4j.rolling.RollingFileAppender">
        <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
            <param name="FileNamePattern" value="${jahia.log.dir}jahia.%d{yyyy-MM-dd}.log" />
        </rollingPolicy>
        <param name="File" value="${jahia.log.dir}jahia.log" />
        <param name="Threshold" value="debug" />
        ...

    <appender name="profilerLogs" class="org.apache.log4j.rolling.RollingFileAppender">
        <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
            <param name="FileNamePattern" value="${jahia.log.dir}jahia_profiler.%d{yyyy-MM-dd}.log" />
        </rollingPolicy>
        <param name="File" value="${jahia.log.dir}jahia_profiler.log" />
        <param name="Threshold" value="debug" />
        ...
    <appender name="hitAccess" class="org.apache.log4j.rolling.RollingFileAppender">
        <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
            <param name="FileNamePattern" value="${jahia.log.dir}jahia_access.%d{yyyy-MM-dd}.log" />
        </rollingPolicy>
        <param name="File" value="${jahia.log.dir}jahia_access.log" />
        <param name="Threshold" value="debug" />
        ...

Note, please, there are three different appenders, logging into different files. For all of them rollover, you need to apply the changes

A Jahia instance restart is needed for changes to be effective.