Asynchronous C++ Client

Asynchronous C++ Client

The latest release of the Hazelcast C++ client API features so many changes and there may be only a few APIs we didn’t touch. We kept the familiar structures and names as they were before, but we made numerous changes to take advantage of C++11 capabilities.

Our new API fully supports asynchronous programming with use of `boost::future` and continuations.

Async Client Instantiation

With the new API, you can do other things while the client is starting and query the client initialization completion using the provided future. The `hazelcast_client` constructor is no longer available to library users but it is kept internal. Similar to the rest of the API, you get a `boost::future<hazelcast_client>` and you have the capability to wait for the client start or add some continuation logic to be executed when the client start is completed, etc. The new API is as follows:

auto hz_future = hazelcast::new_client();

//…. do some other work …

auto hz = hz_future.get();

And the version accepting the `client_config` parameter is:

auto hz_future = hazelcast::new_client(std::move(config));

//…. do some other work …

auto hz = hz_future.get();

As you may have already noticed, the `client_config` object is no longer copyable. The config is now move-only. The reason for this is that there are some configuration elements that are specifically set for that hazelcast instance, e,g, listeners, and it is not correct to copy them. This eliminates the difficult to solve unexpected surprises.

Async Hazelcast Structures and Optional Values

With 4.0 API, you always get a shared pointer of structures from the `hazelcast_client` api. For example:

boost::shared_future<std::shared_ptr<hazelcast::client::imap>> my_map_future = hz.get_map(“My Map”);

std::shared_ptr<hazelcast::client::imap> my_map = my_map_future.get();

We always return a future for all the API that requires at least one invocation to the server side and hence requires network communication. Even `get_map` API does remote server invocation to have the map resources created in the cluster appropriately. Hence, it returns a future.

For example, this is how we do get the value of an entry with a key of 5:

boost::future<boost::optional<int32_t>> value_future = my_map->get<int32_t, int32_t>(5);

boost::optional<int32_t> value = value_future.get();

As you already noticed, the values are now returned as `optional` values. If the result returns a no value (null value), then the optional will not have any value in the state, hence it will be a `boost::none` value. Optional is more explicit, communicates the absence of the entry for the key more clearly and it also has small value optimizations for small objects (e.g. primitives, etc.).

Boost Future Continuations

Boost provides a lot of different use cases with the futures. Some of the very interesting capabilities are future continuations, utilities to wait for one or all completions for multiple futures, etc.

Since you always get a boost::future when using the Hazelcast API, you can easily utilize these capabilities. For example, let’s assume that you want to do multiple puts into a map in addition to a get for a key and print the size of the final resulting map, and you can do this in async manner:

// Trigger put of multiple entries

std::vector<boost::future<boost::optional<int>>> all_jobs;

all_jobs.emplace_back(map->put(1, 5));

all_jobs.emplace_back(map->put(2, 10));

all_jobs.emplace_back(map->put(3, 15));

 

// Trigger retrieving value for key 8

all_jobs.emplace_back(map->get<int, int>(8));

 

auto final_future = boost::when_all(all_jobs.begin(), all_jobs.end()).then([=] (boost::future<boost::csbl::vector<boost::future<boost::optional<int>>>> f) {

f.get();

std::cout << “Size of the final map is ” << map->size().get() << std::endl;

});

// The execution did not block at all up to this point

//… do some other work …

// The result will be output to the screen at some point in async manner or on destruction of `final_future` object.

Lambda Friendly Listeners

One of the important features of C++11 is the ability to provide function objects for handlers. Hence, we redesigned our listener APIs to accept such lambda functions. For example, let’s see how an `imap` entry listener prints the added event when a new entry is added.

map->add_entry_listener(hazelcast::client::entry_listener().on_added([](hazelcast::client::entry_event &&event) {

     std::cout << “[added] ” << event << std::endl;

}), true).get();

We provided the `on_added` handler of the listener only, which means the other events (`on_removed`, `on_updated`, `on_evicted`, `on_expired`, `on_merged`, `on_map_evicted`, `on_map_cleared`) will not be handled at all.

The listeners are passed as rvalue, hence we move them into the internal list of listeners.

Conclusion

We hope that you find the new async API useful in your application development and opens new doors for infinite possibilities. Please share your thoughts and experiences.