Caching is an essential mechanism in providing efficient usage of resources in many systems.
Data management using JDO is no different and provides a definition of caching at 2 levels.
Caching allows objects to be retained and returned rapidly without having to make an extra call to
the datastore. The 2 levels of caching available with DataNucleus are
-
Level 1 Cache - mandated by the JDO specification, and represents
the caching of instances within a PersistenceManager
-
Level 2 Cache - represents the caching of instances within a
PersistenceManagerFactory (across multiple PersistenceManager's)
You can think of a cache as a Map, with values referred to by keys.
In the case of JDO, the key is the object identity (identity is unique in JDO).
The
Level 1
Cache is always enabled.
There are inbuilt types for the Level 1 Cache available for selection.
DataNucleus supports the following types of
Level 1
Cache.
-
weak
- uses a weak reference backing map. If JVM garbage collection clears
the reference, then the object is removed from the cache.
This is the default Level 1 Cache
-
soft
- uses a soft reference backing map. If the map entry value object is
not being actively used, then garbage collection
may
garbage collect the reference,
in which case the object is removed from the cache.
-
strong
- uses a normal HashMap backing. With this option all references are
strong meaning that objects stay in the cache until they are explicitly removed by calling
remove() on the cache.
You can specify the type of
Level 1
Cache by providing the PMF property
datanucleus.cache.level1.type
. You set this to the value of the type required.
If you want to remove objects from the L1 cache programmatically you should use
pm.evict()
or
pm.evictAll()
.
Objects are placed in the L1 cache (and updated there) during the course of the transaction.
This provides rapid access to the objects in use in the users application and is used to
guarantee that there is only one object with a particular identity at any one time
for that PersistenceManager.
The Level 1 cache is a DataNucleus plugin point allowing you to provide your own cache
where you require it.
By default in DataNucleus AccessPlatform 2.0,
Level 2
Cache is enabled.
The user can configure the
Level 2
Cache if they so wish.
This is controlled by use of the persistence property
datanucleus.cache.level2.type
.
You set this to "type" of cache required.
With the
Level 2
Cache you currently have the following options.
-
none
- turn OFF Level 2 caching.
-
weak
- use the internal (weak reference based) L2 cache. Provides support for
the JDO 2 interface of being able to pin objects into the cache, and unpin them when
required. This option does not support distributed caching, solely running within the
JVM of the client application. Weak references are held to non pinned objects.
-
soft
- use the internal (soft reference based) L2 cache. Provides support for
the JDO 2 interface of being able to pin objects into the cache, and unpin them when
required. This option does not support distributed caching, solely running within the
JVM of the client application. Soft references are held to non pinned objects.
-
EHCache - a simple wrapper to EHCache's caching
product. Provides basic support for adding items to the cache and retrieval from the cache.
Doesn't support pinning and unpinning.
-
EHCacheClassBased - similar to the EHCache option
but class-based.
-
OSCache - a simple wrapper to OSCache's caching
product. Provides basic support for adding items to the cache and retrieval from the cache.
Doesn't support pinning and unpinning.
-
SwarmCache - a simple wrapper to SwarmCache's
caching product. Provides basic support for adding items to the cache and retrieval from the
cache. Doesn't support pinning and unpinning.
-
Oracle Coherence - a simple wrapper to Oracle's
Coherence caching product. Provides basic support for adding items to the cache and retrieval
from the cache. Doesn't support pinning and unpinning. Oracle's caches support distributed
caching, so you could, in principle, use DataNucleus in a distributed environment with
this option.
-
javax.cache - a simple wrapper to javax.cache's
caching product. Provides basic support for adding items to the cache and retrieval from the
cache. Doesn't support pinning and unpinning.
-
memcache - a simple wrapper to Memcache's
caching product. Provides basic support for adding items to the cache and retrieval from the
cache. Doesn't support pinning and unpinning.
The EHCache, OSCache, SwarmCache, Coherence, javax.cache, and Memcache caches are available in the
datanucleus-cache plugin.
Objects are placed in the L2 cache when you commit() the transaction of a
PersistenceManager. This means that you only have datastore-persisted objects in that
cache. Also, if an object is deleted during a transaction then at commit it
will be removed from the L2 cache if it is present.
The Level 2 cache is a DataNucleus plugin point allowing you to provide your own cache
where you require it. Use the examples of the EHCache, Coherence caches etc as reference.
The majority of times when using a JDO-enabled system you will not have to take control over
any aspect of the caching other than specification of whether to use a
Level 2
Cache or
not. With JDO2 and DataNucleus you have the ability to control which objects remain in the cache.
This is available via a method on the
PersistenceManagerFactory
.
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props);
DataStoreCache cache = pmf.getDataStoreCache();
The
DataStoreCache
interface
provides methods to control the retention of objects in the cache. You have 3 groups of methods
-
evict
- used to remove objects from the Level 2 Cache
-
pin
- used to pin objects into the cache, meaning that they will not get removed by
garbage collection, and will remain in the Level 2 cache until removed.
-
unpin
- used to reverse the effects of pinning an object in the Level 2 cache.
This will mean that the object can thereafter be garbage collected if not being used.
These methods can be called to
pin
objects into the cache that will be much used. Clearly
this will be very much application dependent, but it provides a mechanism for users to exploit
the caching features of JDO. If an object is not "pinned" into the L2 cache then it can typically
be garbage collected at any time, so you should utilise the pinning capability for objects that
you wish to retain access to during your application lifetime. For example, if you have an object
that you want to be found from the cache you can do
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props);
DataStoreCache cache = pmf.getDataStoreCache();
cache.pinAll(MyClass.class, false); // Pin all objects of type MyClass from now on
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
pm.makePersistent(myObject);
// "myObject" will now be pinned since we are pinning all objects of type MyClass.
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.close();
}
}
Thereafter, whenever something refers to
myObject
, it will find it in the Level 2 cache.
To turn this behaviour off, the user can either
unpin
it or
evict
it.
JDO allows control over which classes are put into a Level 2 cache. You do this
by specifying the
cacheable
attribute to
false
(defaults to true).
So with the following specification, no objects of type
MyClass
will be put in the L2 cache.
Using XML:
<class name="MyClass" cacheable="false">
...
</class>
Using Annotations:
@Cacheable("false")
public class MyClass
{
...
}
JDO allows you control over which fields of an object are put in the Level 2 cache.
You do this by specifying the
cacheable
attribute to
false
(defaults to true).
This setting is only required for fields that are relationships to other persistable objects.
Like this
Using XML:
<class name="MyClass">
<field name="values"/>
<field name="elements" cacheable="false"/>
...
</class>
Using Annotations:
public class MyClass
{
...
Collection values;
@Cacheable("false")
Collection elements;
}
So in this example we will cache "values" but not "elements".
If a field is
cacheable
then
-
If it is a persistable object, the "identity" of the related object will be stored
in the Level 2 cache for this field of this object
-
If it is a Collection of persistable elements, the "identity" of the elements will be
stored in the Level 2 cache for this field of this object
-
If it is a Map of persistable keys/values, the "identity" of the keys/values will be
stored in the Level 2 cache for this field of this object
-
If a persistable related object is embedded or serialised then it
will not be cached
.
It is hoped to support this in a later release.
When pulling an object in from the Level 2 cache and it has a reference to another object
Access Platform uses the "identity" to find that object in the Level 1 or Level 2 caches
to re-relate the objects.
DataNucleus provides a simple wrapper to
javax.cache's caches.
To enable this you should set the persistence properties
datanucleus.cache.level2.type=javax.cache
datanucleus.cache.level2.cacheName={cache name}
DataNucleus provides a simple wrapper to
Oracle's Coherence caches.
This currently takes the
NamedCache
interface in Coherence and instantiates a cache of a
user provided name. To enabled this you should set the following persistence properties
datanucleus.cache.level2.type=coherence
datanucleus.cache.level2.cacheName={coherence cache name}
The
Coherence cache name
is the name that you would normally put into a call to
CacheFactory.getCache(name). As mentioned earlier, this cache does not support the
pin/unpin
operations found in the standard JDO 2 interface. However you do have the
benefits of Oracle's distributed/serialized caching. If you require more control over the
Coherence cache whilst using it with DataNucleus, you can just access the cache directly via
DataStoreCache cache = pmf.getDataStoreCache();
NamedCache tangosolCache = ((TangosolLevel2Cache)cache).getTangosolCache();
DataNucleus provides a simple wrapper to
EHCache's caches.
To enable this you should set the persistence properties
datanucleus.cache.level2.type=ehcache
datanucleus.cache.level2.cacheName={cache name}
datanucleus.cache.level2.configurationFile={EHCache configuration file (in classpath)}
The EHCache plugin also provides an alternative L2 Cache that is class-based.
To use this you would need to replace "ehcache" above with "ehcacheclassbased".
DataNucleus provides a simple wrapper to
OSCache's caches.
To enable this you should set the persistence properties
datanucleus.cache.level2.type=oscache
datanucleus.cache.level2.cacheName={cache name}
DataNucleus provides a simple wrapper to
SwarmCache's caches.
To enable this you should set the persistence properties
datanucleus.cache.level2.type=swarmcache
datanucleus.cache.level2.cacheName={cache name}
DataNucleus provides a simple wrapper to
Memcache caches.
To enable this you should set the persistence properties
datanucleus.cache.level2.type=memcache
datanucleus.cache.level2.memcache.servers=...
datanucleus.cache.level2.memcache.keyprefix=...
datanucleus.cache.level2.memcache.expireSeconds=...
datanucleus.cache.level2.memcache.servers
is a space separated list of memcache
hosts/ports, e.g. host:port host2:port.
datanucleus.cache.level2.memcache.keyprefix
is a string to prefix all keys with
to ensure there are no clashes with other application uses of memcache.
datanucleus.cache.level2.memcache.expireSeconds
if not set or set to 0 then no expire