A new version of the POLiTe library focuses on the library performance and on the design of new rich-featured cache layer. The cache layer replaces the ObjectBuffer interface and enhances the concept of indirect memory pointers by adding one more indirection level.


The biggest part of the cache layer represent the provided cache implementations. Cache implementations may derive from one of two interfaces depending on what features they will provide (see the Figure 3.5, “Caches in the POLiTe 2 library”):

  • Cache - an interface providing basic synchronous caching functionality.

  • ExtendedCache - extends the Cache interface with additional methods that are needed for asynchronous maintenance of the cache using the cache manager CacheKeeper (see later).

Concurrent data access strategies as introduced in the original POLiTe library can be used with caches that implement the ExtendedCache interface.

Caches can be grouped together using the ComposedCache class with the help of selectors. Selectors are classes whose instances determine which strategy or which cache should be used based on the given object type, the object itself or based on other custom criteria. The ComposedCache uses CacheSelector to decide which cache should be used for the object being processed and then it passes a StrategySelector to the selected cache as a parameter to each of its operations. Then the cache uses the StrategySelector to modify its behaviour with selected strategies.

Caches that implement the extended interface can be maintained by the cache manager called CacheKeeper[13]. CacheKeeper runs a second thread that scans managed caches and removes instances considered as 'worst'. If dirty, these instances are written back to the database. CacheKeeper acts also as a facade for composing caches and specifying strategies.

Following cache implementations are provided with the POLiTe 2 library:

  • VoidCache - implements the basic Cache interface. This implementation actually does not cache anything, it just holds locked local copies. As the copies are unlocked, changes are propagated immediately to the database and objects are removed from the cache.

  • The LRUCache (multithreaded) and the LRUCacheST (single threaded) implement the ExtendedCache interface. These classes use the LRU[14] replacement strategy. The multithreaded variant can even be used with the CacheKeepers' asynchronous maintenance feature. LRU strategy discards least recently accessed items first. It maintains the items in ordered list. New or accessed items are added or moved to the top of the list. Least recently accessed items are pushed towards the bottom from which they are removed. For more information see [09].

  • The ARCCache (multithreaded) and the ARCCacheST (single threaded) use the ARC[15] replacement strategy. ARC tries to improve LRU by splitting the cached items into two LRU lists - one for items that were accessed only once (recent cache entries) and second for items that were accessed more than once (frequent cache entries). The cache adapts the size ratio between these two lists. ARC performs in most cases better than LRU, espetially in cases where a larger set of items is accessed (for example a query result is iterated). The cache is not polluted as frequently used items remain in the second list. For more information see [10][11].

Transient object instances are in the original POLiTe library created using standard C++ dynamic allocation - using the new operator. After making these objects persistent, pointers to these objects are exchanged for indirect references represented by the Ref<T> template. As the ownership of these objects is transferred to the object cache, the direct pointers may be rendered invalid. In the POLiTe 2 library, the creation and destruction of the objects is managed by the library code and users can access its members by dereferencing indirect pointers.

The indirect pointer template Ref<T> was replaced by the template DbPtr<T>. Its instance may be referred to as a database pointer further in the text. DbPtr<T> is used similarly to the Ref<T> template. Example 3.5, “Persistent object manipulation in the POLiTe 2 library” demonstrates the creation of a transient instance and its manipulation analogously to the Example 3.3, “Persistent object manipulation”.


The DbPtr<T> can point to instances in several states:

  • Transient instances which are present only in memory (and do not have a persistent representation yet), the owner of these instances is the pointer.

  • In-memory instances owned by a cache (which may or may not have a database representation. This represents in fact two states of the object).

  • Persistent representation of the object. The pointer contains only the identity of the object.

Last object state, the locked local copy, is implemented as an additional level of indirection when dereferencing the database pointer. By dereferencing it using the * operator, user receives an instance of a cache pointer (CachePtr<T>). The existence of the cache pointer guarantees that the object is loaded into the memory and that the memory address of the object will not change until the instance of the cache pointer is destroyed (unlocked). Such process locks the objects in the cache so they cannot be removed unless they are unlocked. The transitions between the object states are displayed in the Figure 3.6, “States of the POLiTe 2 objects”.


By using the * operator on a database pointer user receives a reference to a loaded and locked instance in a cache. Constructor of the created cache pointer asks cache to load relevant object from the database (if not already present in the cache) and locks the object in the cache. The cache pointer can be dereferenced again resulting in retrieval of direct pointer to the in-memory instance. This process can be simplified just by using the -> operator on the database pointer. An implicit instance of the cache pointer is created and the -> operator invoked on it. Then the requested member of the instance is accessed. After that, the cache pointer instance is destroyed and cache lock released. (This may result in significant performance degradation if using the VoidCache, because only locked copies are contained in the cache. Calling the -> operator causes a persistent object to be loaded from database into the memory, then desired member is accessed and object - if modified - is written back and removed from cache). The process is almost the same as if dereferencing the Ref<T> references, the difference is the additional level - the cache pointer.

If the variable e is of the DbPtr<Employee> type, the following expression:

e->salary(65000);

may trigger a sequence of actions as displayed in the Figure 3.7, “Dereferencing DbPtr in POLiTe 2”




[13] CacheCacheKeeper