Chapter 10. Caching
Prev | Part 6. Kodo JPA/JDO Reference Guide | Next

## Chapter 10. Caching

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 operates at the `EntityManagerFactory` / `PersistenceManagerFactory` level. This cache is designed to significantly increase performance while remaining in full compliance with the JPA and JDO persistence standards. This means that turning on the caching option can transparently increase the performance of your application, with no changes to your code.

| Important
This feature requires Kodo Enterprise Edition or Kodo Standard Edition with the Performance Pack.

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.

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.

There are five ways to access data via the Kodo APIs: standard relation traversal, large result set relation traversal, queries, looking up an object by id, and iteration over an `Extent`. 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.20, “Query Replaces Extent”.

Table 10.1. Data access methods

Access method | Uses cache
--- | ---
Standard relation traversal | Yes
Large result set relation traversal | No
Query | Yes
Lookups by object id | Yes
Iteration over an `Extent` | No

When enabled, the cache is checked before making a trip to the datastore. Data is stored in the cache when objects are committed and when persistent objects are loaded from the datastore.

Kodo's data cache can 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
If you mix and match `EntityManager`s and `PersistenceManager`s in the same application, they will use the same data cache so long as you obtain ever manager from the same factory, or as long as your `EntityManagerFactory` and `PersistenceManagerFactory` share the same underlying `BrokerFactory`.

### 10.1.1. Data Cache Configuration

To enable the basic single-factory cache set the `kodo.DataCache` property to `true`, and set the `kodo.RemoteCommitProvider` property to `sjvm`:

Example 10.1. Single-JVM 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

To configure the data cache to remain up-to-date in a distributed environment, 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.

Kodo's default implementation maintains a least-recently-used map of object ids to cache data. By default, 1000 elements are kept in cache. This can be adjusted by setting the `CacheSize` property in your plugin string - see below for an example. Objects that are pinned into the cache are not counted when determining if the cache size exceeds the maximum.

Expired objects are moved to a soft reference map, so they may stick around for a little while longer. You can control the number of soft references Kodo keeps with the `SoftReferenceSize` property. Soft references are unlimited by default. Set to 0 to disable soft references completely.

Example 10.2. 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)

You can specify a cache timeout value for a class by setting the timeout metadata extension 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.3. Data Cache Timeout

Timeout `Employee` objects after 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>

See the `kodo.persistence.DataCache` Javadoc for more information on the `DataCache` annotation.

A cache can specify that it should be cleared at certain times rather than using data timeouts. 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, where the `*` symbol (asterisk), indicates match all. The tokens are, in order:

- 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')

It is also possible for different persistence-capable classes to use different caches. This is achieved by specifying a cache name in a metadata extension.

Example 10.4. Named Data Cache Specification

JPA:

    import kodo.persistence.*;

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

JDO:

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

See the `kodo.persistence.DataCache` Javadoc for more information on the `DataCache` annotation.

The metadata above will cause instances of the `Employee` class to be stored in a cache named `small-cache`. This `small-cache` cache can be explicitly configured in the `kodo.DataCache` plugin string, or can be implicitly defined, in which case it will take on the same default configuration properties as the default cache identified in the `kodo.DataCache` property.

Example 10.5. Named Data Cache Configuration

JPA XML format:

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

JDO properties format:

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

### 10.1.2. 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. In fact, Section 10.1.6, “Cache Extension” below has tips on how to use this package to extend Kodo's caching service yourself.

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

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

You obtain the `StoreCache` through the `KodoEntityManagerFactory.getStoreCache` methods. When you have multiple data caches configured as in the `small-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 `KodoEntityManagerFactory.getStoreCache(String name)` method returns a `StoreCache` interface to a particular named data cache.

Example 10.6. Accessing the StoreCache

    import kodo.persistence.*;

    ...

    KodoEntityManagerFactory kemf = KodoPersistence.cast (emf);
    StoreCache cache = kemf.getStoreCache ();
    ...
    StoreCache smallCache = kemf.getStoreCache ("small-cache");
    ...

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

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. In this scenario, you typically 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 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);

Most caches are of limited size. Pinning an identity to the cache ensures that the cache will mill not kick the data for the corresponding instance out of 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 once again 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, rather than requiring a datastore trip.

Example 10.7. StoreCache Usage

    import kodo.persistence.*;

    ...

    KodoEntityManagerFactory kemf = KodoPersistence.cast (emf);
    StoreCache cache = kemf.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 `small-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.8. Accessing a Named Cache

    import kodo.jdo.*;

    ...

    KodoPersistenceManagerFactory kpmf = KodoJDOHelper.cast (pmf);
    DataStoreCache cache = kpmf.getDataStoreCache ("small-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 `KodoEntityManager` or `PersistenceManager`'s eviction APIs.

Example 10.9. Automatic Data Cache Eviction

JPA XML format:

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

JDO properties format:

    kodo.BrokerImpl: EvictFromDataCache=true

JPA:

    import kodo.persistence.*;

    ...

    KodoEntityManager kem = KodoPersistence.cast (em);
    kem.evict (changedMag);  // will evict from data cache also

JDO:

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

### 10.1.3. 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 `kodo.persistence.QueryResultCache` class. You can access this class through the `KodoEntityManagerFactory`.

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.10. Accessing the QueryResultCache

JPA:

    import kodo.persistence.*;

    ...

    KodoEntityManagerFactory kemf = KodoPersistence.cast (emf);
    QueryResultCache qcache = kemf.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 least-recently-used cache. 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.11. Query Cache Size

JPA XML format:

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

JDO properties format:

    kodo.QueryCache: CacheSize=1000, SoftReferenceSize=100

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

Example 10.12. 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.13. Evicting Queries

JPA:

    import kodo.persistence.*;

    ...

    KodoEntityManagerFactory kemf = KodoPersistence.cast (emf);
    QueryResultCache qcache = kemf.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.14. Pinning, and Unpinning Query Results

JPA:

    import kodo.persistence.*;

    ...

    KodoEntityManagerFactory kemf = KodoPersistence.cast (emf);
    QueryResultCache qcache = kemf.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.15. Disabling and Enabling Query Caching

JPA:

    import kodo.persistence.*;

    ...

    // temporarily disable query caching for all queries created from em
    KodoEntityManager kem = KodoPersistence.cast (em);
    kem.getFetchPlan ().setQueryResultCache (false);

    // re-enable caching for a particular query
    KodoQuery kq = kem.createQuery (...);
    kq.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.4. The Concurrent Data Cache

The concurrent is a new data cache plugin for Kodo. It has not been as thoroughly tested as Kodo's standard data cache. The concurrent cache can, however, offer considerably greater performance and scalability characteristics compared to the standard cache when the following conditions are met:

1. The cache is big enough to hold all of your commonly-used data.

2. Your application is heavily concurrent.

Additionally, this cache does not fully index its contents by class, but rather just keeps track of 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. This inefficiency can easily be worked around with careful cache partitioning.

To use the concurrent data cache instead of the standard data cache, set your `kodo.DataCache` property to `concurrent` instead of `true`. Or, you can mix concurrent and standard caches in a set of named caches.

The concurrent cache has the following configuration parameters:

1. `CacheSize`: The maximum number of objects that this cache will hold. The default is 1000 objects. This setting differs from the setting for the default data cache in that instances that are pinned into cache do count towards this limit.

2. `Name`: The unique name of the cache.

3. `EvictionSchedule`: A cron-style eviction schedule that this cache should follow.

4. `LRUThresholdRatio`: The ratio above which this implementation should start applying its least-recently-used algorithm to prepare for eviction should the need arise. Default is 80%. Values should be integers between 1 and 100.

Example 10.16. Concurrent Data Cache Configuration

The following configuration uses the standard Kodo cache as the primary cache and a named concurrent cache as a secondary cache. Various classes can be configured to use this secondary cache in their metadata.

JPA XML format:

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

JDO properties format:

    kodo.DataCache: true, concurrent(Name=secondary)

### 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.17. 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
  As of this writing, it is not possible to use a Tangosol Coherence 1.2.2 distributed cache type with Apple's OS X 1.3.1 JVM. Use 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.18. 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 v3.5.1 caching system. later. To use GemFire in Kodo you will need to change your `gemfire.properties` to have the property `enable-shared-memory=true`. You will also need to add both Kodo and GemFire to your classpath and then start a GemFire server.

    prompt> gemfire start

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.19. 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)

GemFire `cache.xml`:

    ...
        <shared-root-region name="root">
            <region-attributes>
            ...
            </region-attributes>
            <region name="My-kodo-data-cache">
                <region-attributes>
                </region-attributes>
            </region>
            <region name="My-kodo-query-cache">
                <region-attributes>
                </region-attributes>
            </region>
        </shared-root-region>
        ...

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 `RemoteCommitListener`s.

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 `EntityManager`s and `PersistenceManager`s when the cache is updated or invalidated. This behavior would not be compliant with the JPA and JDO specifications.

- Invoking `KodoEntityManager.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.9, “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.

- `Extent`s 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.20. Query Replaces Extent

  JPA:

      import kodo.persistence.*;

      ...

      KodoEntityManager kem = KodoPersistence.cast (em);
      Extent extent = kem.getExtent (Magazine.class, false);

      // This iterator does not benefit from caching...
      Iterator uncachedIterator = extent.iterator ();

      // ... but this one does.
      KodoQuery extentQuery = kem.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 ();

Prev | Up | Next
9.9. Non-Relational Stores | Home | 10.2. Query Compilation Cache