Gentics Portal | java is using the Java Caching System for caching rendered content pages, and Caffeine for caching data like navigation information, or GraphQL queries in memory.
The portal provides a method to create a cache that can be used in custom handlers or helpers.
Implicit caching
When caching is enabled in the server configuration (which is true by default), the portal automatically caches the following:
-
Queries loaded from the filesystem.
Elasticsearch will be cached after they where processed by the
QueryBuilder, and GraphQL queries will be put in the cache after the schema. -
Handlebars templates loaded from the filesystem.
The caching itself is done by Vert.x, but whether or not templates are cached is controlled by the portal configuration.
-
Navigation data loaded from Mesh.
The used cache keys take into account the current language, the current users roles and the branch of the Mesh project that is served by this portal. The cached data has already been postprocessed by the
NavigationTransformer. -
Content loaded from Mesh.
When a page loaded from Mesh is finished rendering, it is put in the cache and further requests to the same page will be served from the cache. The cache key is comprised of the request path and the configured custom parameters.
Note that binary files themselves are not cached by the portal, but their ETag headers are to reduce the number of requests to Mesh.
Another important note is that content for authenticated requests only uses the users roles for the cache key, so the rendered response should not contain user specific data.
Caching content can also be prevented by putting the value
truein the routing context under the keymesh.portal.cache.disablebefore theRouteOrder.CONTENT_ROUTES.
Cache clear endpoint
Creating a custom cache
Caching is useful for resource intensive tasks, remote data fetching and you should decide what to cache and when the cache should expire.
Custom caches can be created by getting a cache builder by calling CacheProvider.cacheBuilder() with the type of
objects to be cached. Caches are identified by their namespace, when the builder already created a cache with the
same namespace, the existing cache will be returned.
When the builder is configured using the methods explained in the following sections, a PortaclCache instance is
created by calling its build() method.
Note that caches can be created even when caching is disabled in the portal configuration, then returned caches will just never actually store anything.
Expiration settings
The cache will always remove old entries once maximumSize (the maximum number of elements in the cache) is reached
and new entries need to be added, but depending on the expireMode entries will also be removed when expireTime
seconds have passed.
The available expireModes are:
-
None: entries will never expire (but can still be removed due to size constraints). -
Write: entries will be removedexpireTimeseconds after they have been created or updated.This mode should be used for data that is costly to retrieve, but will get stale after some time.
-
Access: entries will be removedexpireTimeseconds after they have last been accessed.This is the default when no expire mode is specified.
Automatic cache clear
The created cache can be automatically cleared when one of the clearEvents is received via the
eventbus. Common events to clear caches would be
events about creation, updates or deletion of Mesh nodes.
When no eventFilter is specified, the cache will be cleared when any of the clearEvents is received. The
eventFilter can inspect the body of the event to decide whether the cache should be cleared or not (e.g only
when an updated node has a certain schema).
Key mappers
Most methods of the created PortalCache will create a cache key from the
RoutingContext of the current request.
By default the key is created from the language, a hash of the users
roles and the branch of the current
request.
When the builder is told to useCustomKeyParams the configured
custom parameters will also be added to the created cache key.
A custom keyMapper can also be provided when creating a cache. This mapper can ignore the routing context completely
and return any key appropriate for the custom cache. Note that useCustomKeyParams will have no effect when a custom
keyMapper is specified.
Auxiliary disc cache
When the diskCacheRegion() is specified for the builder, a JCS backed cache is created instead of a Caffeine based
one. This requires a cache.ccf configuration
file with a basic configuration, in the config directory of the portal.
The IsEternal, MaxLife and MaxIdle element properties, as well as the MaxObjects cache property will be
overwritten with the above mentioned values passed to the builder.
Example
The following is an example of how to create a custom PortalCache for JsonObject values.
@Inject
public CacheProvider cacheProvider;
private PortalCache<JsonObject> cache;
/* ... */
public void initCache() {
cache = cacheProvider.cacheBuilder(JsonObject.class)
.namespace("cache.custom.users")
.expireTime(60)
.expireMode(ExpireMode.Write)
.clearEvents(MeshEvent.NODE_UPDATED)
.eventFilter(event -> {
return "portal_users".equals(
event.getBodyAsJson().get("schemaName").textValue());
})
.keyMapper(ignoredParam -> "users")
.build();
}
Using a custom cache
Store and retrieve data
Whenever a costly operation is performed it should be passed to one of the get() methods of a PortalCache, which
will first check if there already is a value in the cache. If not the provided supplier function will be called, and
the result is put in the cache.
For example the following code would check if the loaded user information is already in the cache, and if not call
loadUsersFromDb(), put the result in the cache and then return it.
void doSomethingWithUsers(RoutingContext rc) {
List<String> users = cache.get(rc, this::loadUsersFromDb);
// ...
}
Since communicating with Mesh involves responses using ReactiveX there is also a version of
get() which takes a supplier generating a Single.
When need more control is needed over how things are put in the cache, a value can be stored with the put() method,
and retrieved via getIfPresent() (which returns null when no entry is found in the cache).
Clearing data
All entries in a single PortalCache can be cleared by calling clear(), and all cached values of all caches can
be cleared by calling the CacheProvider's clearAll() method.
As mentioned earlier the elements in a cache can also be cleared automatically by specifying Mesh events which should trigger a cache clear.
Browser caching
Binary content and static files are not cached by the portal itself, but when caching is enabled the portal will use the respective HTTP headers so that the browser can make use of caching.
Binary content
For binary files (which are loaded from Mesh via the webroot or webrootfield endpoint) the request headers
concerning caching will be forwarded to Mesh. When the client sends a request header If-None-Match: "[ETag]", Mesh
will respond with a 304 Not Modified message when the content has not changed and Gentics Portal | java will in turn forward
this HTTP response to the client.
Additionally, a max-age value can be configured for the Cache-Control
HTTP header, that will be set for binaries served from Mesh.
The default value for caching.binaryMaxAge is 86400 (in seconds = one day). This means clients will be told to not ask again for updates and try to load a binary from their client side cache for one day. Setting cahce.binaryMaxAge to 0 means, that each request will be forwarded to Mesh to retrieve the actuall ETag header. With that the client can decide to download a newer version if it is avaliable.
Static files
Resources from the configured static path are served by the Vert.x
StaticHandler which does not
use Etags, but sets the respective HTTP headers such that browsers know they can cache the files themselves. It will
also respond with a 304 Not Modified message, when the request contains the If-Modified-Since header and the
resource has not been changed since then.
