The In-process Client Cache

Introduction

To maximize access performance, ScaleOut’s C++ Native Client API maintains a fully-coherent internal near cache that contains deserialized instances of objects in the store that have been recently accessed. When reading objects, this client-side cache reduces access response time by eliminating data motion and deserialization overhead for objects that have not been updated in the authoritative SOSS distributed store.

The contents of the deserialized cache will automatically be invalidated if the object is changed or removed by any of the servers in the farm, so it will never return a stale version of a cached object. The client libraries always check with the SOSS service prior to returning the object in order to make sure that the deserialized version is up-to-date. While this check requires a round-trip to the service, it is much faster than retrieving and deserializing the entire object.

As a result, a named cache’s get method will possibly return a shared pointer to an object that lives in the API’s client cache if the API has determined that this cached instance of your object is up-to-date. Note, however, that different get operations on different threads may be given a pointer to the same instance of an object. This behavior is safe for common read-only/read-write access patterns but should be avoided in the situations described below in Guidelines for Client Cache Usage.

The client cache is enabled by default and can be disabled for individual calls using the set_use_client_cache() setter that is available on "Options" classes or can be disabled for all operations on a named cache by configuring the cache’s default_options (see Named Cache Options).

Guidelines for Client Cache Usage

The deserialized cache is only intended to be used with either read-only access or in a locked retrieve/update usage pattern. In the retrieve/update usage pattern, an object stored in the SOSS service is retrieved and locked, optionally updated, and then unlocked (either by an unlock call or by an update_locked_and_release operation). This pattern enables multiple processes on the same or different servers to reliably update a shared instance of an object. No changes should be made to the retrieved object outside of the retrieve/update pair because these changes would not be propagated to the distributed cache.

Sometimes a client application may want to retrieve an object from the ScaleOut StateServer service, make some changes to the object, and then discard it after using it without persisting the changes to the SOSS service. This usage model can cause the deserialized cache to get out of sync with SOSS. If an application requires that changes be made to a retrieved object outside of the above read/update usage pattern, steps should be taken to avoid corrupting the deserialized cache. Either the cache should be disabled, or a deep copy of the retrieved object should be made with changes made only to the copy. Doing this will keep the locally cached object from getting out of sync with the authoritative object that is kept in the SOSS service.

Also, direct usage of objects returned from the client cache is not well-suited for optimistic locking access patterns since optimistic updates that fail due to a version mismatch may have made changes to a shared instance of an object whose updates were not persisted to SOSS. See Optimistic Locking and the Client Cache for more information on client cache strategies that can be employed with optimistic locking.