Developing Applications

Using the Named Cache

The sosscli::NamedCache class and its specialized subclasses are the primary interfaces used interact with your objects in the distributed object store. A NamedCache represents a named collection of objects in the distributed data store.

Named Cache Subclasses

The base sosscli::NamedCache class offers limited functionality since it does not have knowledge of the data types being operated on or the serialization mechanism being used, so client applications will typically use one of the NamedCache’s derived class templates to interact with the ScaleOut StateServer service. Different subclasses provide support for different serialization techniques, and the template parameter provided to these specialized classes indicates the type of the objects that application will be storing in each named cache.

ScaleOut’s C++ Native Client API provides two specialized classes to support different serialization techniques.

NamedProtobufCache
#include "soss_client/named_protobuf_cache.h"

sosscli::NamedProtobufCache<T> is a named cache that expects Google Protocol Buffers message instances as the objects stored in the StateServer service. Objects of type T must be generated by the protoc compiler using .proto files that you define.

The NamedProtobufCache is currently the most feature rich type of named cache—it supports indexing of protobuf fields in the StateServer service and the ability to filter on those fields when performing queries. This guide will focus on the NamedProtobufCache in its examples to illustrate the most typical usage of the C++ Native Client API.

NamedPrimitiveCache
#include "soss_client/named_primitive_cache.h"

sosscli::NamedPrimitiveCache<T> is a named cache that expects primitive C++ types—this type of named cache simply sends the memory occupied by objects of type T to the StateServer service.

Simple structs can also use the NamedPrimitiveCache, but care must be taken to make sure that a cached struct does not contain pointers or references to other objects (only the memory address in a pointer field would be stored in the StateServer service, which would cause serious problems when the object is accessed by other processes). Consider using the NamedProtobufCache if complex object graphs must be stored.

Custom Named Caches

A custom serialization engine can be supported by implementing your own specialized named cache subclass. Inherit from sosscli::TypedNamedCache<T> and override the virtual serialize and deserialize methods to support your custom serializer.

[Note] Note

The full implementation of the NamedPrimitiveCache class is available in the named_primitive_cache.h header, which can serve as a useful starting point for creating your own custom named cache subclasses.

Instantiating a Named Cache

A named cache in the ScaleOut StateServer service can be accessed through the C++ Native Client API by instantiating one of the NamedCache subtypes (typically a NamedProtobufCache or a NamedPrimitiveCache) and providing the cache’s name as a constructor parameter. For example:

//#include "soss_client/named_protobuf_cache.h"
//#include "StockQuote.pb.h"

// A cache named "Stock Quotes", containing protobuf objects
// of type StockQuote:
sosscli::NamedProtobufCache<StockQuote> cache("Stock Quotes");

Multiple client application instances on different machines could instantiate a "Stock Quotes" cache in the manner above and gain access to the same, shared collection of stock quote objects.

Since a NamedProtobufCache is a template class, all objects stored in an NamedProtobufCache will be of the same type. Avoid using the same cache name to instantiate two named caches with different types passed in as the template parameter—this can cause deserialization errors to occur. If different types need to be stored in the StateServer service, create different named caches for each type (that is, pass a different string into the NamedProtobufCache’s constructor to give it a different name).

sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");

// Bad: Don't store different types in a cache with the same name!
// The ScaleOut service will store these different types in the same namespace:
sosscli::NamedProtobufCache<Portfolio> bad_port_cache("Stock Quotes");

// Good: Use different names for caches of different types:
sosscli::NamedProtobufCache<Portfolio> good_port_cache("Portfolios");

Retrieving and Modifying Objects

Objects can be created or updated in ScaleOut StateServer using one of the following methods on a NamedProtobufCache or NamedPrimitiveCache:

Method Description

insert

Inserts an object that does not already exist

insert_and_lock

Inserts an object that does not already exist and locks it

update

Updates an existing object

update_locked_and_retain

Updates an existing object and holds the lock

update_locked_and_release

Updates an existing object and releases the lock

update_optimistic

Performs an optimistic update of an object

put

Inserts or updates an object

Once an object is in the named cache, it can be retrieved by any client using one of the following methods:

Method Description

get

Retrieves an existing object

get_and_lock

Retrieves and acquires an exclusive lock on an existing object

get_locked

Retrieves an existing object and retains the lock that is already held by the caller

Objects can be deleted from the store using the remove family of methods:

Method Description

remove

Removes an object from the named cache

remove_locked

Removes a locked object from the named cache

The C++ Native Client API reference documentation contains detailed documentation, including pre-conditions and post-conditions, for each method listed above. See Locking Objects for details on acquiring exclusive locks on objects.

Memory allocation for objects sent to and returned from a named cache is managed by smart pointers. Objects held in the named cache must be dynamically allocated and wrapped in a reference-counted smart pointer (boost::shared_ptr).

[Note] Note

Whenever possible, use boost::make_shared to create a shared_ptr when creating a new instance of one of your objects. The make_shared call is exception-safe and performs better than if you were to call new yourself.

Creating, Reading, Updating, and Deleting an Object in StateServer. 

#include "soss_client/named_protobuf_cache.h"
#include "StockQuote.pb.h"

using namespace NativeClientSample; // StockQuote class' namespace

int main(int argc, char* argv[])
{
  sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");

  // Create a StockQuote protobuf object and fill out its required fields:
  auto quote = boost::make_shared<StockQuote>();   // 1
  quote->set_ticker("GOOG");
  quote->set_price(1031.89f);
  quote->set_volume(1254310);

  // Create a key to identify the object in the named cache:
  sosscli::SossKey soss_key("GOOG");

  // Add the protobuf object to a ScaleOut named cache:
  quote_cache.put(soss_key, quote);

  // Read the object back out of the StateServer service:
  auto get_result = quote_cache.get(soss_key);
  auto retrieved_quote = get_result.object_ptr(); // a boost::shared_ptr
  std::cout << retrieved_quote->price() << std::endl;

  // Change the price and update the quote in the StateServer service:
  retrieved_quote->set_price(1033.32f);
  quote_cache.update(soss_key, retrieved_quote);

  // Remove the object from the StateServer service:
  quote_cache.remove(soss_key);

  return 0;
}

1

For the definition of the "StockQuote" Protocol Buffer class that is used throughout this guide’s examples, see the section on Using Protocol Buffer Objects.

Keys to Objects

The sample in the prior section illustrates how to create an identifier for an object that is stored in a named cache using a string like "GOOG" as a key:

sosscli::SossKey soss_key("GOOG");

All NamedCache methods that access individual objects (insert, put, get, remove, etc…) take a sosscli::SossKey object as the first parameter to identify the object being accessed.

Key Data Types

The SossKey can be constructed with one of the following types:

  • std::string
  • std::wstring
  • char *
  • wchar_t *
  • std::vector<uint8_t> (a vector of unsigned chars, up to 32 bytes long)
  • int32_t
  • uint32_t
  • int64_t
  • uint64_t
[Note] Note

For integral types, the C++ Native Client API always uses type aliases defined in stdint.h.

Considerations for String Keys

Internally, the ScaleOut StateServer service uses a 32-byte structure to identify objects. So the type of key passed to a SossKey constructor can have performance implications that should be considered, especially when using strings as keys:

  • The StateServer service stores string-based keys as metadata along with the object in the server so that they may be retrieved later by a client application. Using strings to identify your objects, therefore, will increase memory consumption in the service.
  • To fit an arbitrary-length key into the service’s 32-byte structure, the API hashes key strings using the SHA-256 hash algorithm. The resulting digest is then used as the primary identifier for the object be stored.
  • String key metadata is stored as Unicode in the StateServer service, so std::string and char* keys are always converted to wide character strings by the API prior hashing and being sent to the StateServer service. To improve performance, use std::wstring as your string type to avoid this conversion.

Integral types and byte vectors therefore provide better performance than strings and will result in less memory consumption in the server. If you must use string keys, but they are always less than 32 characters long, consider passing them to API calls as a vector<uint8_t> for the best possible performance.

Implicit Conversion to a SossKey

As a convenience, the single-argument SossKey constructors will perform implicit conversion. This allows you to pass your string or integral values directly into NamedCache methods as object identifiers. So in most cases, a SossKey object does not need to be explicitly constructed, leading to fewer lines of code:

// "GOOG" is automatically converted to a SossKey:
quote_cache.put("GOOG", quote);

Object Policies

A number of policies can be associated with objects that can control their lifetime and behavior in the StateServer service. The sosscli::ObjectPolicy class contains extended parameter information for the insert and put methods on the NamedProtobufCache and NamedPrimitiveCache:

ObjectPolicy Field Description

timeout

The lifetime of the object in the named cache.

timeout_type

The type of timeout (sliding, absolute)

preemption_priority

The priority of keeping an object in the cache under low memory conditions

allow_push_repl

Whether the object is subject to GeoServer "push" replication

parents

Keys to objects on which the object being added depends

[Note] Note

The ObjectPolicy class, like all options and results classes in the C++ Native Client API, provides accessor methods to get and set its private fields. Getters are simply functions with the field name (i.e., ObjectPolicy::timeout(), which returns a boost::posix_time::time_duration), and setters prefix the field with "set_" (i.e., ObjectPolicy::set_timeout(time_duration), which returns a reference to the ObjectPolicy object to allow the chaining of setter calls).

Policies for Individual Objects

An ObjectPolicy can passed as the third parameter to insert and put calls:

// A policy for an object that will expire after 60 seconds:
sosscli::ObjectPolicy policy;
policy.set_timeout(boost::posix_time::seconds(60));
policy.set_timeout_type(sosscli::ObjectPolicy::Absolute);

sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");
quote_cache.put("GOOG", quote, policy);

You may have noticed that the third policy argument above was not supplied to put calls in earlier examples. This is because the insert and put methods have a default value for the third parameter, a constant called sosscli::ObjectPolicy::CACHE_DEFAULTS. The CACHE_DEFAULTS constant indicates to the API that this insert/put operation does not specify any policies for the object being inserted, and the API should instead use the policies that have been defined for the entire NamedCache instance. (See the next section for details on configuring polices for a named cache).

[Note] Note

Many methods in the C++ Native Client API accept "Options" classes as parameters that allow you to perform fine tuning of the method’s behavior on a call-by-call basis (GetOptions, LockOptions, etc.). These Options classes all follow the same pattern as ObjectPolicy: They provide a CACHE_DEFAULT constant that is used as a default argument to tell the method to fall back to cache-level behavior. The examples in this guide will often avoid passing explicit values into these types of options parameters for the sake of brevity and clarity.

Policies for an Entire Named Cache Instance

Default options and policies for an entire named cache instance can be configured using the sosscli::NamedCache::DefaultCachePolicy class. This nested class is available in every named cache instance, and a reference to this object is available via the NamedCache::default_cache_policy() method:

// By default, all objects inserted into quote_cache should expire after 30 secs:
sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");
quote_cache.default_cache_policy().set_timeout(boost::posix_time::seconds(30));
quote_cache.default_cache_policy().set_timeout_type(sosscli::ObjectPolicy::Absolute);

Note that any changes make to a named cache’s default cache policies are valid for only that instance of the NamedCache object. Default polices will need to be re-set if your NamedCache instance goes out of scope and is re-constructed.

The default polices for a cached object can be overridden on a call-by-call basis by providing a sosscli::ObjectPolicy object as an argument to individual insert and put calls. (See Policies for Individual Objects.)

Named Cache Options

Most individual named cache operations accept "options" arguments that can be used to control the call’s behavior. Methods on the NamedCache and its subclasses accept an options object whose name contains an "Options" suffix. Classes of this nature include the following:

Typical fields on these classes allow you to control how the method should behave in the event of a service error (throw_on_error), or whether CRUD operations should use the local, in-process client cache (use_client_cache). The C++ Native Client API reference documentation covers these Options classes in detail.

sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");

// Perform a get call that doesn't throw an exception and won't use
// the in-process cache:
sosscli::GetOptions<StockQuote> get_options;
get_options.set_throw_on_error(false).set_use_client_cache(false);

auto get_result = quote_cache.get("GOOG", get_options);
auto retrieved_quote = get_result.object_ptr(); // a boost::shared_ptr
std::cout << retrieved_quote->price() << std::endl;

Note that the setters on these options classes return references to the options object, which allows for the chaining of setter calls.

Like the ObjectPolicy class discussed above, all options classes provide a CACHE_DEFAULT constant member. Passing this constant as an argument to a NamedCache method will cause the call to revert to the cache-wide behavior that is configured on the NamedCache instance’s default policies (see Policies for an Entire Named Cache Instance). The CACHE_DEFAULT constant is the default argument for all NamedCache calls that take an options parameter, so options arguments can be omitted in cases where the cache defaults will suffice:

sosscli::NamedProtobufCache<StockQuote> quote_cache("Stock Quotes");
quote_cache.default_cache_policy().set_throw_on_error(false);
quote_cache.default_cache_policy().set_use_client_cache(false);

// Perform get() using cache defaults:
auto get_result = quote_cache.get(soss_key);
auto retrieved_quote = get_result.object_ptr();
std::cout << retrieved_quote->price() << std::endl;