Cache Service Design
The Cache Service provides a high-speed, thread-safe repository of information to a
JVM without incurring the cost of a remote call. A client of the Cache Service is
able to perform only very simple functions such as getting a value or requesting
that the cache be refreshed. However, behind the scenes, the cache may need to
periodically refresh the underlying data from the backing store. All of this
should be accomplished in a thread-safe manner with as much efficiency as possible
to produce a solution that is fast and accurate.
A total cache, is a cache which needs to be refreshed, as a
whole, on a periodic basis. The intended use of this cache is to provide
fast access to a small set of data, such as lookup values. The data
all must become stale at the same time, as the cache is refreshed as a whole.
There are two different implementations of total caches: read-only and writable.
A read-only cache implementation is supplied for cases where data is cached
for read-only purposes. With the excpetion of cache refreshes, the data cannot
be modified. This is faster than writable caches because synchronization
restrictions are not as stringent. A writable cache is supplied for cases
wherein the cached data can be changed by clients of the cache. Note that
this implementation is not cluster-aware, so changes are only visible within
the cache's local JVM. Also, this is not a write-through cache (the backing
data-store is not affected on updates) so changes will be lost when the cache
The MRU Cache (Most Recently Used Cache) is a cache which
has a set of data that is larger than the cache itself. The cache operates
in what is known alternately as a Most Recently Used (MRU) or uses a Least Recently
Used (LRU) algorithm. What this means is that the most recently requested items
are kept in the cache, while the least recently used are cached only briefly,
and tend to be fetched on each request. An MRU cache is very useful for data
which it is even slightly expensive to retrieve, and which conforms to the 80/20
rule - 20% of the data is used 80% of the time. It is ideal if you wish
to cache large amount of data, but for performance reasons you don't want to
have a very large Map in the memory footprint.
There are two different implementations of MRU caches: single-get and multi-get.
A single-get cache will only retrieve one datum at a time. A multi-get cache
allows for more that one datum to be retrieved at once. This is valuable when
it is expensive to load data, the application may require more than one datum
at a time, and there are efficiencies to be gained in loading data in bulk. An
example is a stock quoting service. A user can ask for quotes for serveral
securities at once. It is more efficient to ask the quoting service for all the
quotes at once than it is to get them individually.
Each cache uses a data-loader to retrieve data from a data store. Data-loaders are
components with custom built FunctionalImplementation classes. This is how the
cache is filled with application-specific data. Data-loaders are free to get data
in any way they need, e.g. direct database-access, EJB calls, Web-Services calls, etc.
Each type of cache uses a different type of component as its functional interface.
- Total cache: org.sape.carbon.services.cache.total.TotalCacheDataLoader.
- MRU cache: org.sape.carbon.services.cache.mru.MRUCacheDataLoader.
- Multi-get MRU cache: org.sape.carbon.services.cache.mru.MultiGetMRUCacheDataLoader.
Strictly speaking, all that is required to configure a data-loader, is the type of
loader it is (defined by the FunctionalInterface you choose) and the
FunctionalImplementation class written by you to perform your custom
data-retrieval logic. This is the minimum required information to configure
any component. If there is specific configuration that your custom implementation
of the data-loader requires, you must create that configuration interface
and have your loader implement the Configurable lifecycle method.
If there is no additional configuration required, a configuration similar
to the following should suffice.
The data loader configuration is a component configuration that is linked to in
the main cache configuration. The configuration can either be specified
as a component reference or a nested configuration. In the case of the cache
service, since a dataloader configuration is generally unique to a cache,
it usually makes sense to specify it as a nested configuration.
Note that the FunctionalInterface could also be
and FunctionalImplementationClass should be the fully qualified name of the
implementation of your data-loader.
Carbon provides the following data loader implementations for use:
There are two things that need to be specifed in a total-cache configuration:
what kind of total cache (read-only or writable) and the name of the data-
loader component. The former is specified by the FunctionalImplementation
you choose. All total caches use the
Example read-only cache configuration
Example writable cache configuration
There are four things that must be specifed in an MRU-cache configuration:
what kind of MRU cache (multi- or single-get), the name of the data-
loader component, the capacity of the cache and the expiration interval of
individual elements of the cache. The type of cache is specified by the
ConfiguraitonInterface you choose. Single-caches use the
configuration interface and multi-get caches use the org.sape.carbon.services.cache.mru.MultiGetMRUCacheConfiguration
configuration interface. The capacity setting tells the cache the maximum
number of entries is can have before dropping the least recently used items.
The expiration interval tells the cache that items that have been in the cache
longer than a certain amount of time should be reloaded regardless of when they
were last accessed. If this value is -1, this function is disabled. The
Expiration interval is an optional value, defaulting to -1.
Example single-get MRU cache configuration
Example multi-get MRU cache configuration
It is often required (especially for total caches) that a cache be refreshed on a periodic
basis. For this reason, all caches are schedulable components. See the Scheduling Service for details.