Chapter 10. Caching

10.1. Data Cache
10.1.1. Data Cache Configuration
10.1.1.1. Configuring the Data Cache Name
10.1.1.2. Configuring the Basic Data Cache
10.1.1.3. Configuring the LRU Caching Option
10.1.1.4. Configuring the Data Cache Size
10.1.1.5. Configuring the Eviction Schedule
10.1.2. Data Cache Definition
10.1.2.1. Setting the Data Cache Name
10.1.2.2. Setting the Data Cache Timeout Value
10.1.3. Data Cache Usage
10.1.4. Query Cache
10.1.5. Third-Party Integrations
10.1.5.1. Tangosol Integration
10.1.5.2. GemStone GemFire Integration
10.1.6. Cache Extension
10.1.7. Important Notes
10.1.8. Known Issues and Limitations
10.2. Query Compilation Cache

Kodo utilizes several configurable caches to maximize performance. This chapter explores Kodo's data cache, query cache, and query compilation cache.

10.1. Data Cache

The Kodo data cache is an optional cache of persistent object data that is designed to significantly increase performance, with no changes to your code. When enabled, the cache is checked before accessing the data store. Data is stored in the cache when objects are committed and when persistent objects are loaded from the datastore. Kodo's data cache is designed to provide significant performance increases over cacheless operation, while guaranteeing that behavior will be identical in both cache-enabled and cacheless operation.

Kodo's data cache is not related to the EntityManager or PersistenceManager caches dictated by the JPA and JDO persistence specifications. These specifications mandate behavior for the EntityManager and PersistenceManager caches aimed at guaranteeing transaction isolation when operating on persistent objects. The Kodo data cache operates at the EntityManager / PersistenceManager level and maintains full compliance with the JPA and JDO persistence standards.

There are five ways to access data via the Kodo APIs, as defined in the following table. Kodo's cache plugin accelerates three of these mechanisms. It does not provide any caching of large result set relations or Extent iterators. If you find yourself in need of higher-performance Extent iteration, see Example 10.23, “Query Replaces Extent”.

Table 10.1. Data access methods

Access methodUses cache
Standard relation traversal Yes
Large result set relation traversal No
QueryYes
Lookups by object id Yes
Iteration over an Extent No

Kodo's data cache can be used in both single-JVM and multi-JVM environments. Multi-JVM caching is achieved through the use of the distributed event notification framework described in Section 11.3, “Remote Event Notification Framework”, or through one of Kodo's integrations with third-party distributed caches (see Section 10.1.5, “Third-Party Integrations”).

The single JVM mode of operation maintains and shares a data cache across all EntityManager or PersistenceManager instances obtained from a particular EntityManagerFactory or PersistenceManagerFactory. This is not appropriate for use in a distributed environment, as caches in different JVMs or created from different factory objects will not be synchronized.

[Note]Note

If you mix and match EntityManagers and PersistenceManagers in the same application, they will use the same data cache as long as you obtain every manager from the same factory, or as long as your EntityManagerFactory and PersistenceManagerFactory share the same underlying BrokerFactory.

10.1.1. Data Cache Configuration

The kodo.DataCache configuration property accepts a plug-in string describing the data caches to use. Plugin properties have a syntax very similar to that of Java 5 annotations. Plugin properties take a class name, followed by a comma-separated list of values for the plugin's public fields or bean properties in parentheses. For more information about plugin configuration, see Section 2.4, “Plugin Configuration”.

Many of Kodo's built-in options for plugins have short alias names that you can use in place of the full class name. The kodo.DataCache configuration property accepts the class name aliases described in the following table.

Table 10.2. Aliases for the DataCache Property

AliasDescription
true Sets the basic concurrent data cache.
lru Sets the least-recently-used (LRU) data caching option.
tangosolIntegrates with Tangosol's Coherence caching system. See Section 10.1.5, “Third-Party Integrations”.
gemfire Integrates with GemStone's GemFire v5.0.1 caching system. See Section 10.1.5, “Third-Party Integrations”.
false Disables data caching.

[Note]Note

The default concurrent cache does not fully index its contents by class. Rather, it tracks which clasess are in the cache. It services requests to drop given classes by checking to see if any instances of that class might be in the cache, and then clearing the entire cache. You can work around this inefficiency with careful cache partitioning. The LRU cache, however, does not suffer from this limitation. Dropping a class from the LRU cache only drops entries for that class.

10.1.1.1. Configuring the Data Cache Name

You can set the data cache name by specifying the Name property in your plugin string. This value is used to retrieve the data cache. If not specified, the name defaults to default.

You can configure multiple data caches. Each data cache must have a unique name. One data cache with the default name, default, must exist.

Example 10.1. Configuring the Data Cache Names

The following example defines two caches: the required default cache (by not specifying the Name property, the name defaults to default) and a cache named secondary-cache.

JPA XML format:

<property name="kodo.DataCache" value="true, true(Name=secondary-cache)"/>

JDO properties format:

kodo.DataCache: true, true(Name=secondary-cache)

10.1.1.2. Configuring the Basic Data Cache

To enable the basic single-factory cache set the kodo.DataCache property to true and the kodo.RemoteCommitProvider to sjvm (single JVM).

To configure the data cache to remain up-to-date in a distributed environment, you should set the kodo.RemoteCommitProvider property appropriately, or integrate Kodo with a third-party caching solution. Remote commit providers are described in Section 11.3, “Remote Event Notification Framework”. Section 10.1.5, “Third-Party Integrations” enumerates supported third-party caching solutions.

Example 10.2. Configuring the Basic Data Cache

JPA XML format:

<property name="kodo.DataCache" value="true"/>
<property name="kodo.RemoteCommitProvider" value="sjvm"/>

JDO properties format:

kodo.DataCache: true
kodo.RemoteCommitProvider: sjvm

10.1.1.3. Configuring the LRU Caching Option

Kodo offers a least-recently-used (LRU) caching option in addition to the default concurrent cache. To enable the LRU data cache, set the kodo.DataCache property to lru. Note that while the LRU cache's eviction scheme is more optimal than the default cache's random evictions, it also requires much more synchronization. Generally, the default cache's higher concurrency results in better performance than the LRU cache's smarter eviction scheme.

Example 10.3. Configuring the LRU Caching Option

JPA XML format:

<property name="kodo.DataCache" value="lru(CacheSize=5000, SoftReferenceSize=0)"/>

JDO properties format:

kodo.DataCache: lru(CacheSize=5000, SoftReferenceSize=0)

10.1.1.4. Configuring the Data Cache Size

Kodo's default implementation maintains a concurrent map of object ids that are used to cache data. By default, 1000 elements are maintained in cache. Objects that are pinned to the cache are not counted when determining if the cache size exceeds the maximum.

You can adjust the data cache size by specifying the CacheSize property in your plugin string.

When the maximum cache size is exceeded, random entries are moved to a soft reference map, so they are maintained for awhile longer. You can control the number of soft references Kodo maintains with the SoftReferenceSize property. By default, an unlimited number of soft references are maintained. Set the property to 0 to disable soft references completely.

Example 10.4. Configuring the Data Cache Size

JPA XML format:

<property name="kodo.DataCache" value="true(CacheSize=5000, SoftReferenceSize=0)"/>

JDO properties format:

kodo.DataCache: true(CacheSize=5000, SoftReferenceSize=0)

10.1.1.5. Configuring the Eviction Schedule

You can clear a data cache at specific times. The EvictionSchedule property of Kodo's cache implementation accepts a cron style eviction schedule. The format of this property is a whitespace-separated list of five tokens. You can use the * symbol (asterisk) as a wildcard to match all values. The following lists the tokens in the order that they must be specified:

  • Minute

  • Hour of Day

  • Day of Month

  • Month

  • Day of Week

For example, the following kodo.DataCache setting schedules the default cache to evict values from the cache at 15 and 45 minutes past 3 PM on Sunday.

true(EvictionSchedule='15,45 15 * * 1')

10.1.2. Data Cache Definition

By default, all persistence instances are stored in the default cache and are subject to the timeout defined in the kodo.DataCacheTimeout configuration property. You can change the cache and timeout for instances of a specific class, however, using the following methods: Define the data cache and timeout value for each class within your application using the following methods:

10.1.2.1. Setting the Data Cache Name

You can specify the name of the data cache to use for a class. You can configure different caches for each persistence-capable class.

[Note]Note

When you specify a data cache for a class, its subclasses use the data cache as well, unless it is overridden by the class itself.

Example 10.5. Setting the Data Cache Name

The following example shows how to store instances of the Employee class in a cache named secondary-cache. If the data cache name is not explicitly specified, the default data cache is used.

JPA:

import org.apache.openjpa.persistence.*;

@Entity
@DataCache(name="secondary-cache", timeout=10000)
public class Employee
{
    ...
}

JDO:

<class name="Employee">
    <extension vendor-name="kodo" key="data-cache" value="secondary-cache"/>
    <extension vendor-name="kodo" key="data-cache-timeout" value="10000"/>
    ...
</class>

The secondary-cache cache can be explicitly configured in the kodo.DataCache plugin string, or can be implicitly defined. If implicitly defined, it will assume the same default configuration properties as the default cache identified in the kodo.DataCache property.

Example 10.6. Named Data Cache Configuration

JPA XML format:

<property name="kodo.DataCache" value="true, true(Name=secondary-cache, CacheSize=100)"/>

JDO properties format:

kodo.DataCache: true, true(Name=secondary-cache, CacheSize=100)

10.1.2.2. Setting the Data Cache Timeout Value

You can set a cache timeout value to the amount of time in milliseconds a class's data is valid. Use a value of -1 for no expiration. This is the default value.

Example 10.7. Setting the Data Cache Timeout

The following example shows how to set a timeout value for Employee objects of 10 seconds.

JPA:

@Entity
@DataCache(timeout=10000)
public class Employee
{
    ...
}

JDO:

<class name="Employee">
    <extension vendor-name="kodo" key="data-cache-timeout" value="10000"/>
    ...
</class>

10.1.3. Data Cache Usage

The kodo.datacache package defines Kodo's data caching framework. While you may use this framework directly (see its Javadoc for details), its APIs are meant primarily for service providers. Section 10.1.6, “Cache Extension” below has tips on how to use this package to extend Kodo's caching service.

Rather than use the low-level kodo.datacache package APIs, JPA users should typically access the data cache through Kodo's high-level org.apache.openjpa.persistence.StoreCache facade. This facade has methods to pin and unpin records, evict data from the cache, and more. You get the StoreCache through the OpenJPAEntityManagerFactory.getStoreCache methods.

public StoreCache getStoreCache ();
public StoreCache getStoreCache (String name);

When you have multiple data caches configured as in the secondary-cache example above, the StoreCache can act as a unified facade over all your caches. For every oid parameter to the StoreCache methods, it determines the correct data cache for that oid's corresponding persistent class, and dynamically delegates to that cache.

If you know that you want to access a certain data cache and no others, the OpenJPAEntityManagerFactory.getStoreCache(String name) method returns a StoreCache interface to a particular named data cache.

Example 10.8. Accessing the StoreCache

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf);
StoreCache cache = oemf.getStoreCache ();
...
StoreCache secondaryCache = oemf.getStoreCache ("secondary-cache");
...

The evict methods tell the cache to release data. Each method takes an entity class and one or more identity values, and releases the cached data for the corresponding persistent instances. The evictAll method with no arguments clears the cache. Eviction is useful when the datastore is changed by a separate process outside Kodo's control. Typically in this scenario you have to manually evict the data from the datastore cache; otherwise the Kodo runtime, oblivious to the changes, will maintain its stale copy.

public void evict (Class cls, Object oid);
public void evictAll ();
public void evictAll (Class cls, Object... oids);
public void evictAll (Class cls, Collection oids);

Most caches are of limited size. Pinning an identity to the cache ensures that the cache will not release data for the corresponding instance from the cache unless you manually evict it. Note that even after manual eviction, the data will get pinned again the next time it is fetched from the store. You can only remove a pin and make the data available for normal cache overflow eviction through the unpin methods. Use pinning when you want a guarantee that a certain object will always be available from cache.

public void pin (Class cls, Object oid);
public void pinAll (Class cls, Object... oids);
public void pinAll (Class cls, Collection oids);
public void unpin (Class cls, Object oid);
public void unpinAll (Class cls, Object... oids);
public void unpinAll (Class cls, Collection oids);

Example 10.9. StoreCache Usage

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf);
StoreCache cache = oemf.getStoreCache ();
cache.pin (Magazine.class, popularMag.getId ());
cache.evict (Magazine.class, changedMag.getId ());

See the StoreCache Javadoc for information on additional functionality it provides. Also, Chapter 9, Runtime Extensions discusses Kodo's other extensions to the standard set of JPA runtime interfaces.

JDO users can access the data cache through the JDO-standard javax.jdo.datastore.DataStoreCache facade. The JDO Overview describes this interface in Chapter 13, DataStoreCache. Kodo extends the standard JDO cache in two ways:

  1. The kodo.jdo.KodoDataStoreCache interface extends JDO's DataStoreCache with many useful methods.

  2. When you have multiple data caches configured as in the secondary-cache example above, Kodo's DataStoreCache implementation acts as a facade over all your data caches. For every oid parameter passed to the DataStoreCache methods, it determines the correct data cache for that oid's corresponding persistent class, and dynamically delegates to that cache.

    If you know that you want to access a certain data cache and no others, the KodoPersistenceManagerFactory.getDataStoreCache(String name) method returns a JDO DataStoreCache interface to a particular named data cache.

Example 10.10. Accessing a Named Cache

import kodo.jdo.*;

...

KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf);
DataStoreCache cache = kpmf.getDataStoreCache ("secondary-cache");
cache.evict (JDOHelper.getObjectId (changedObj));
cache.pin (JDOHelper.getObjectId (popularObj));

You can read more about Kodo extensions to JDO APIs in Chapter 9, Runtime Extensions.

The examples above include calls to evict to manually remove data from the data cache. Rather than evicting objects from the data cache directly, you can also configure Kodo to automatically evict objects from the data cache when you use the OpenJPAEntityManager or PersistenceManager's eviction APIs.

Example 10.11. Automatic Data Cache Eviction

JPA XML format:

<property name="kodo.BrokerImpl" value="EvictFromDataCache=true"/>

JDO properties format:

kodo.BrokerImpl: EvictFromDataCache=true

JPA:

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManager oem = OpenJPAPersistence.cast (em);
oem.evict (changedMag);  // will evict from data cache also

JDO:

PersistenceManager pm = ...;
pm.evict (changedMag);  // will evict from data cache also

10.1.4. Query Cache

In addition to the data cache, the kodo.datacache package defines service provider interfaces for a query cache. The query cache is enabled by default when the data cache is enabled. The query cache stores the object ids returned by query executions. When you run a query, Kodo assembles a key based on the query properties and the parameters used at execution time, and checks for a cached query result. If one is found, the object ids in the cached result are looked up, and the resultant persistence-capable objects are returned. Otherwise, the query is executed against the database, and the object ids loaded by the query are put into the cache. The object id list is not cached until the list returned at query execution time is fully traversed.

Kodo JPA exposes a high-level interface to the query cache through the org.apache.openjpa.persistence.QueryResultCache class. You can access this class through the OpenJPAEntityManagerFactory.

Kodo JDO exposes a high-level interface to the query cache through the kodo.jdo.QueryResultCache class. You can access this class through the KodoPersistenceManagerFactory.

Example 10.12. Accessing the QueryResultCache

JPA:

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf);
QueryResultCache qcache = oemf.getQueryResultCache ();

JDO:

import kodo.jdo.*;

...

KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf);
QueryResultCache qcache = kpmf.getQueryResultCache ();

The default query cache implementation caches 100 query executions in a concurrent map. This can be changed by setting the cache size in the CacheSize plugin property. Like the data cache, the query cache also has a backing soft reference map. The SoftReferenceSize property controls the size of this map. It is disabled by default.

Example 10.13. Query Cache Size

JPA XML format:

<property name="kodo.QueryCache" value="CacheSize=1000, SoftReferenceSize=100"/>

JDO properties format:

kodo.QueryCache: CacheSize=1000, SoftReferenceSize=100

Just as Kodo offers an LRU data caching option, Kodo includes an LRU query cache as well. Once again, it has the same sizing options as the default query cache.

Example 10.14. LRU Query Cache

JPA XML format:

<property name="kodo.QueryCache" value="lru(CacheSize=1000, SoftReferenceSize=100)"/>

JDO properties format:

kodo.QueryCache: lru(CacheSize=1000, SoftReferenceSize=100)

To disable the query cache completely, set the kodo.QueryCache property to false:

Example 10.15. Disabling the Query Cache

JPA XML format:

<property name="kodo.QueryCache" value="false"/>

JDO properties format:

kodo.QueryCache: false

There are certain situations in which the query cache is bypassed:

  • Caching is not used for in-memory queries (queries in which the candidates are a collection instead of a class or Extent).

  • Caching is not used in transactions that have IgnoreChanges set to false and in which modifications to classes in the query's access path have occurred. If none of the classes in the access path have been touched, then cached results are still valid and are used.

  • Caching is not used in pessimistic transactions, since Kodo must go to the database to lock the appropriate rows.

  • Caching is not used when the the data cache does not have any cached data for an id in a query result.

  • Queries that use persistence-capable objects as parameters are only cached if the parameter is directly compared to field, as in:

    JPQL:

    select e from Employee e where e.company.address = :addr
    

    JDOQL:

    select from com.xyz.Employee where company.address == :addr
    

    If you extract field values from the parameter in your query string, or if the parameter is used in collection element comparisons, the query is not cached.

  • Queries that result in projections of custom field types or BigDecimal or BigInteger fields are not cached.

Cache results are removed from the cache when instances of classes in a cached query's access path are touched. That is, if a query accesses data in class A, and instances of class A are modified, deleted, or inserted, then the cached query result is dropped from the cache.

It is possible to tell the query cache that a class has been altered. This is only necessary when the changes occur via direct modification of the database outside of Kodo's control. You can also evict individual queries, or clear the entire cache.

JPA:

public void evict (Query q);
public void evictAll (Class cls);
public void evictAll ();

JDO:

public void evict (Query q);
public void evict (Query q, Object[] params);
public void evict (Query q, Map params);
public void evictAll (Class cls);
public void evictAll ();

For JPA queries with parameters, set the desired parameter values into the Query instance before calling the above methods.

For JDO queries with parameters, pass the parameter values of the query in the params array or map.

Example 10.16. Evicting Queries

JPA:

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf);
QueryResultCache qcache = oemf.getQueryResultCache ();

// evict all queries that can be affected by changes to Magazines
qcache.evictAll (Magazine.class);

// evict an individual query with parameters
EntityManager em = emf.createEntityManager ();
Query q = em.createQuery (...).
    setParameter (0, paramVal0).
    setParameter (1, paramVal1);
qcache.evict (q);

JDO:

import kodo.jdo.*;

...

KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf);
QueryResultCache qcache = kpmf.getQueryResultCache ();

// evict all queries that can be affected by changes to Magazines
qcache.evictAll (Magazine.class);

// evict an individual query with parameters
PersistenceManager pm = pmf.getPersistenceManager ();
Query q = pm.newQuery (...);
qcache.evictAll (q, new Object[] { paramVal0, paramVal1 });

When using one of Kodo's distributed cache implementations, it is necessary to perform this in every JVM - the change notification is not propagated automatically. When using a coherent cache implementation such as Kodo's Tangosol cache implementation, it is not necessary to do this in every JVM (although it won't hurt to do so), as the cache results are stored directly in the coherent cache.

Queries can also be pinned and unpinned through the QueryResultCache. The semantics of these operations are the same as pinning and unpinning data from the data cache.

JPA:

public void pin (Query q);
public void unpin (Query q);

JDO:

public void pin (Query q);
public void pin (Query q, Object[] params);
public void pin (Query q, Map params);
public void unpin (Query q);
public void unpin (Query q, Object[] params);
public void unpin (Query q, Map params);

For JPA queries with parameters, set the desired parameter values into the Query instance before calling the above methods.

For JDO queries with parameters, pass the parameter values of the query in the params array or map.

The following example shows these APIs in action.

Example 10.17. Pinning, and Unpinning Query Results

JPA:

import org.apache.openjpa.persistence.*;

...

OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast (emf);
QueryResultCache qcache = oemf.getQueryResultCache ();
EntityManager em = emf.createEntityManager ();

Query pinQuery = em.createQuery (...).
    setParameter (0, paramVal0).
    setParameter (1, paramVal1);
qcache.pin (pinQuery);
Query unpinQuery = em.createQuery (...).
    setParameter (0, paramVal0).
    setParameter (1, paramVal1);
qcache.unpin (unpinQuery);

JDO:

import kodo.jdo.*;

...

KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf);
QueryResultCache qcache = kpmf.getQueryResultCache ();
PersistenceManager pm = pmf.getPersistenceManager ();

Query pinQuery = pm.newQuery (...);
qcache.pin (pinQuery, new Object[] { paramVal0, paramVal1 });
Query unpinQuery = pm.newQuery (...);
qcache.unpin (unpinQuery, new Object[] { paramVal0, paramVal1 });

Pinning data into the cache instructs the cache to not expire the pinned results when cache flushing occurs. However, pinned results will be removed from the cache if an event occurs that invalidates the results.

You can disable caching on a per-EntityManager, per-PersistenceManager or per-Query basis:

Example 10.18. Disabling and Enabling Query Caching

JPA:

import org.apache.openjpa.persistence.*;

...

// temporarily disable query caching for all queries created from em
OpenJPAEntityManager oem = OpenJPAPersistence.cast (em);
oem.getFetchPlan ().setQueryResultCache (false);

// re-enable caching for a particular query
OpenJPAQuery oq = oem.createQuery (...);
oq.getFetchPlan ().setQueryResultCache (true);

JDO:

import kodo.jdo.*;

...

// temporarily disable query caching for all queries created from pm
PersistenceManager pm = ...;
KodoFetchPlan fetch = (KodoFetchPlan) pm.getFetchPlan ();
fetch.setQueryResultCache (false);

// re-enable caching for a particular query
Query q = pm.newQuery (...);
KodoFetchPlan fetch = KodoJDOHelper.cast (pm.getFetchPlan ());
fetch.setQueryResultCache (true);

10.1.5. Third-Party Integrations

Kodo includes built-in integrations with Tangosol Coherence and GemStone GemFire caching products.

10.1.5.1. Tangosol Integration

The Kodo data cache can integrate with Tangosol's Coherence caching system. To use Tangosol integration, set the kodo.DataCache configuration property to tangosol, with the appropriate plugin properties for your Tangosol setup. For example:

Example 10.19. Tangosol Cache Configuration

JPA XML format:

<property name="kodo.DataCache" value="tangosol(TangosolCacheName=kodo)"/>

JDO properties format:

kodo.DataCache: tangosol(TangosolCacheName=kodo)

The Tangosol cache understands the following properties:

  • TangosolCacheName: The name of the Tangosol Coherence cache to use. Defaults to kodo.

  • TangosolCacheType: The type of Tangosol Coherence cache to use (optional). Valid values are named, distributed, or replicated. Defaults to named, which means that the cache is looked up via the com.tangosol.net.CacheFactory.getCache(String) method. This method looks up the cached by name as defined in the Coherence configuration.

    [Note]Note

    If you encounter problems using a Tangosol Coherence 1.2.2 distributed cache type with the Apple's OS X JVM, try using their replicated cache instead.

  • ClearOnClose: Whether the Tangosol named cache should be completely cleared when the EntityManagerFactory or PersistenceManagerFactory is closed. Defaults to false.

The Kodo query cache can also integrate with Tangosol's Coherence caching system. To use Tangosol query cache integration, set the kodo.QueryCache configuration property to tangosol, with the appropriate plugin properties for your Tangosol setup. For example:

Example 10.20. Tangosol Query Cache Configuration

JPA XML format:

<property name="kodo.QueryCache" value="tangosol(TangosolCacheName=kodo-query)"/>

JDO properties format:

kodo.QueryCache: tangosol(TangosolCacheName=kodo-query)

The Tangosol query cache understands the same properties as the data cache, with a default Tangosol cache name of kodo-query.

10.1.5.2. GemStone GemFire Integration

The Kodo data cache can integrate with GemStone's GemFire v5.0.1 caching system and later. To use GemFire in Kodo you will need to ensure that the copy-on-read attribute of the region element is set to false.

By default, the GemFire data cache will use a GemFire region of root/kodo-data-cache and the GemFire query cache will use a region of root/kodo-query-cache. This can be changed be setting the optional property GemFireCacheName.

Example 10.21. GemFire gemfire.xml example

The following example is of a gemfire.xml file that uses the default names for the query and data caches:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Gemfire cache configuration for Kodo testing -->
<!DOCTYPE cache PUBLIC
  "-//GemStone Systems, Inc.//GemFire Declarative Caching 5.0//EN"
  "http://www.gemstone.com/dtd/cache5_0.dtd">
<cache search-timeout="60" lock-lease="300" copy-on-read="false">
    <region name="root">
        <region-attributes scope="local"/>
        <region name="kodo-data-cache">
            <region-attributes scope="local"/>
        </region>
        <region name="kodo-query-cache">
            <region-attributes scope="local"/>
        </region>
    </region>
</cache>

Example 10.22. GemFire Cache Configuration

JPA persistence.xml:

<property name="kodo.DataCache" 
    value="gemfire(GemFireCacheName=/root/my-kodo-data-cache)"/>
<property name="kodo.QueryCache" 
    value="gemfire(GemFireCacheName=/root/my-kodo-query-cache)"/>

JDO properties file:

kodo.DataCache: gemfire(GemFireCacheName=/root/My-kodo-data-cache)
kodo.QueryCache: gemfire(GemFireCacheName=/root/My-kodo-query-cache)

If you set GemFire for both kodo.DataCache and kodo.QueryCache you aren't required to specify a kodo.RemoteCommitProvider unless you are registering your own RemoteCommitListeners.

Some notes regarding using GemFire with Kodo:

  • Custom field types mapped with externalizers or custom mappings must be serializable.

  • The kodo.DynamicDataStructs option is not supported.

10.1.6. Cache Extension

The provided data cache classes can be easily extended to add additional functionality. If you are adding new behavior, you should extend kodo.datacache.DataCacheImpl. To use your own storage mechanism, extend kodo.datacache.AbstractDataCache, or implement kodo.datacache.DataCache directly. If you want to implement a distributed cache that uses an unsupported method for communications, create an implementation of kodo.event.RemoteCommitProvider. This process is described in greater detail in Section 11.3.2, “Customization”.

The query cache is just as easy to extend. Add functionality by extending the default kodo.datacache.QueryCacheImpl. Implement your own storage mechanism for query results by extending kodo.datacache.AbstractQueryCache or implementing the kodo.datacache.QueryCache interface directly.

10.1.7. Important Notes

  • The default cache implementations do not automatically refresh objects in other EntityManagers and PersistenceManagers when the cache is updated or invalidated. This behavior would not be compliant with the JPA and JDO specifications.

  • Invoking OpenJPAEntityManager.evict and PersistenceManager.evict does not result in the corresponding data being dropped from the data cache, unless you have set the proper configuration options as explained above (see Example 10.11, “Automatic Data Cache Eviction”). Other methods related to the EntityManager and PersistenceManager caches also do not effect the data cache.

    The data cache assumes that it is up-to-date with respect to the datastore, so it is effectively an in-memory extension of the database. To manipulate the data cache, you should generally use the data cache facades presented in this chapter.

  • You must specify a kodo.event.RemoteCommitProvider (via the kodo.RemoteCommitProvider property) in order to use the data cache, even when using the cache in a single-JVM mode. When using it in a single-JVM context, set this property to sjvm.

10.1.8. Known Issues and Limitations

  • When using datastore (pessimistic) transactions in concert with the distributed caching implementations, it is possible to read stale data when reading data outside a transaction.

    For example, if you have two JVMs (JVM A and JVM B) both communicating with each other, and JVM A obtains a data store lock on a particular object's underlying data, it is possible for JVM B to load the data from the cache without going to the datastore, and therefore load data that should be locked. This will only happen if JVM B attempts to read data that is already in its cache during the period between when JVM A locked the data and JVM B received and processed the invalidation notification.

    This problem is impossible to solve without putting together a two-phase commit system for cache notifications, which would add significant overhead to the caching implementation. As a result, we recommend that people use optimistic locking when using data caching. If you do not, then understand that some of your non-transactional data may not be consistent with the datastore.

    Note that when loading objects in a transaction, the appropriate datastore transactions will be obtained. So, transactional code will maintain its integrity.

  • Extents are not cached. So, if you plan on iterating over a list of all the objects in an Extent on a regular basis, you will only benefit from caching if you do so with a Query instead:

    Example 10.23. Query Replaces Extent

    JPA:

    import org.apache.openjpa.persistence.*;
    
    ...
    
    OpenJPAEntityManager oem = OpenJPAPersistence.cast (em);
    Extent extent = oem.getExtent (Magazine.class, false);
    
    // This iterator does not benefit from caching...
    Iterator uncachedIterator = extent.iterator ();
    
    // ... but this one does.
    OpenJPAQuery extentQuery = oem.createQuery (...);
    extentQuery.setSubclasses (false);
    Iterator cachedIterator = extentQuery.getResultList ().iterator ();
    

    JDO:

    Extent extent = pm.getExtent (Magazine.class, false);
    
    // This iterator does not benefit from caching...
    Iterator uncachedIterator = extent.iterator ();
    
    // ... but this one does.
    Query extentQuery = pm.newQuery (extent);
    Iterator cachedIterator = ((List) extentQuery.execute ()).iterator ();
    

 

Skip navigation bar   Back to Top