Caching in HeFQUIN

HeFQUIN supports caching of responses from federation members. By default, HeFQUIN uses a two-level cache consisting of an in-memory L1 cache and a persistent L2 cache. The L1 cache provides fast access within an engine instance, while the L2 cache persists cached responses across engine instances and across JVM restarts.

Overview

When HeFQUIN needs to issue a data retrieval or some other kind of request to a federation member, it first checks the L1 cache. If a matching non-expired response is found, it is returned directly without contacting the federation member. If no such response is found in the L1 cache, HeFQUIN checks the L2 cache. If a matching non-expired response is found there, it is returned and, additionally, copied into the L1 cache. If neither cache contains a usable response, the request is issued to the federation member, and the response is stored in both caches for subsequent accesses.

If the cache reaches its configured cache capacity (the maximum number of cache entries that can be stored at the same time), an existing cache entry is removed according to the configured cache policy to make room for the new entry. Currently, the default cache policy uses least recently used (LRU) eviction.

HeFQUIN supports two implementations for the persistent L2 cache: MapDBCache and ChronicleMapCache. MapDBCache offers faster startup, which is advantageous when using the regular CLI, where JVM startup time has a greater impact. In contrast, ChronicleMapCache is likely to provide better performance for high-throughput workloads, such as when HeFQUIN runs as a service. See the Configuration section below for details on how to configure and switch between these implementations.

Bypassing the Cache

When issuing a query via the HeFQUIN command-line programs, you can explicitly require HeFQUIN to bypass the cache to ensure that requests are sent to federation members instead of using previously cached responses. To this end, use the --ignoreRetrievalCache argument to bypass the cache for data retrieval requests (during the query execution phase of query processing), and use --ignoreCardinalityCache to bypass the cache for cardinality requests during query planning. Even when these options are used, responses obtained from the federation members are still added to the cache.

Configuration

The L1 and L2 cache configurations are specified in the engine configuration file. They are passed as the second and third constructor arguments of FederationAccessManagerWithHierarchicalCache, respectively:

[] a ec:HeFQUINEngineConfiguration ;
   ec:fedAccessMgr [
       a ec:FederationAccessManager ;
       ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.FederationAccessManagerWithHierarchicalCache" ;
       ec:constructorArguments (
           [ ec:argumentTypeName "se.liu.ida.hefquin.federation.access.FederationAccessManager" ;
             ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.AsyncFederationAccessManagerImpl" ;
             ec:constructorArguments ( ec:value:ExecServiceForFedAccess ) ]

           # L1 cache
           _:b1

           # L2 cache
           _:b2
       )
   ] .

In the following example, the L1 cache uses HashMap as its underlying Map implementation. The first constructor argument specifies this implementation, the second specifies the cache capacity (100 in the example), and the third specifies a CachePolicies instance. The constructor argument passed to CachePolicies specifies how long each cache entry is retained before it expires, i.e., the time to live (TTL) in seconds (300 seconds in the example).

# L1 cache
_:b1 ec:argumentTypeName "se.liu.ida.hefquin.base.datastructures.Cache" ;
     ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.cache.CacheLayer" ;
     ec:constructorArguments (
         [ ec:argumentTypeName "java.util.Map" ;
           ec:javaClassName "java.util.HashMap" ;
           ec:constructorArguments () ]
         100   # cache capacity (i.e., max number of cache entries)
         [ ec:argumentTypeName "se.liu.ida.hefquin.base.datastructures.impl.cache.CachePolicies" ;
           ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.FederationAccessManagerWithHierarchicalCache$DefaultHierarchicalMapCachePolicies" ;
           ec:constructorArguments ( 300 ) ]   # time to live (TTL) in seconds
     ) .

In the following example, the L2 cache uses MapDBCache. Its first constructor argument specifies the cache capacity (1000 in the example), while the second specifies a CachePolicies instance with a TTL of 600 seconds.

# L2 cache
_:b2 ec:argumentTypeName "se.liu.ida.hefquin.base.datastructures.Cache" ;
     ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.cache.mapdb.MapDBCache" ;
     ec:constructorArguments (
         1000   # cache capacity (i.e., max number of cache entries)
         [ ec:argumentTypeName "se.liu.ida.hefquin.base.datastructures.impl.cache.CachePolicies" ;
           ec:javaClassName "se.liu.ida.hefquin.federation.access.impl.FederationAccessManagerWithHierarchicalCache$DefaultHierarchicalMapCachePolicies" ;
           ec:constructorArguments ( 600 ) ]   # time to live (TTL) in seconds
     ) .

As described in the Overview, ChronicleMapCache can be used as an alternative to MapDBCache for the L2 cache. To switch to ChronicleMapCache, replace the value of ec:javaClassName in the L2 cache configuration above with se.liu.ida.hefquin.federation.access.impl.cache.chroniclemap.ChronicleMapCache.