From 2b379d91de221dd3ffb6cd3537c8f2431196f270 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Wed, 31 Oct 2018 15:52:55 +0300 Subject: [PATCH 01/11] Updated the documentation with new document structure and contents. --- README.md | 2962 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 1984 insertions(+), 978 deletions(-) diff --git a/README.md b/README.md index f5d6ddaa3f..502240e18f 100644 --- a/README.md +++ b/README.md @@ -1,724 +1,994 @@ # Table of Contents -* [Generating API Documentation](#generating-api-documentation) -* [Downloading the Project](#downloading-the-project) -* [Building the Project](#building-the-project) - * [Mac](#mac) - * [Linux](#linux) - * [Windows](#windows) -* [Reproducing Released Libraries](#producing-released-libraries) - * [Mac](#mac) - * [Linux](#linux) - * [Windows](#windows) -* [Features](#features) -* [Setting Up the Client](#setting-up-the-client) -* [Installing the Client](#installing-the-client) -* [Compiling the Client](#compiling-the-client) - * [Mac Client](#mac-client) - * [Linux Client](#linux-client) - * [Windows Client](#windows-client) -* [Serialization Support](#serialization-support) - * [Custom Serialization](#custom-serialization) - * [Polymorphic Types Serialization](#polymorphic-types-serialization) - * [Global Serializer](#global-serializer) -* [Invocation And Retry Mechanism](#invocation-and-retry-mechanism) - * [Client Backpressure](#client-backpressure) -* [Client Connection Strategy](#client-connection-strategy) - * [Configure Client Reconnect Strategy](#configure-client-reconnect-strategy) - * [Shuffle Cluster Connection Member List](#shuffle-cluster-connection-member-list) -* [Listener Events](#listener-events) -* [Logger Configuration](#logger-configuration) -* [Raw Pointer API](#raw-pointer-api) -* [Mixed Object Types Supporting HazelcastClient](#mixed-object-types-supporting-hazelcastclient) - * [TypedData API](#typeddata-api) -* [Query API](#query-api) -* [Ringbuffer](#ringbuffer) -* [Reliable Topic](#reliable-topic) -* [PN Counter](#pn-counter) -* [Flake Id Generator](#flake-id-generator) -* [Map Near Cache](#map-near-cache) -* [TLS Feature](#tls-feature) -* [AWS Cloud Discovery](#aws-cloud-discovery) -* [PartitionAware](#partitionaware) -* [Code Examples](#code-examples) - * [Map](#map) - * [Queue](#queue) - * [Entry Listener](#entry-listener) - * [Serialization](#serialization) - * [Continuous Query](#continuous-query) - * [Ringbuffer](#ringbuffer) - * [Reliable Topic](#reliable-topic) - * [Publisher](#publisher) - * [Subscriber](#subscriber) - * [TLS](#tls-example) -* [Mail Group](#mail-group) -* [License](#license) -* [Copyright](#copyright) - -This is the repository of C++ client implementation for [Hazelcast](https://github.com/hazelcast/hazelcast), the open source in-memory data grid. A comparison of features supported by the C++ Client vs the Java client can be found [here](http://docs.hazelcast.org/docs/latest-dev/manual/html-single/index.html#hazelcast-clients-feature-comparison). - - - -# Generating API Documentation - -You can generate API Documentation via Doxygen from root with the following command. - -``` -doxygen docsConfig -``` - -# Downloading the Project - -Clone the project from github: - -git clone --recursive git@github.com:hazelcast/hazelcast-cpp-client.git - -We use --recursive flag for our dependency on googletest framework. - -# Building the Project - -First create a build directory from the root of the project. In the build directory, run the following commands depending on your environment. +* [Introduction](#introduction) +* [1. Getting Started](#1-getting-started) + * [1.1. Requirements](#11-requirements) + * [1.2. Working with Hazelcast Clusters](#12-working-with-hazelcast-clusters) + * [1.3. Downloading and Installing](#13-downloading-and-installing) + * [1.4. Basic Configuration](#14-basic-configuration) + * [1.4.1. IMDG Configuration](#141-imdg-configuration) + * [1.4.2. Hazelcast Client Configuration](#142-hazelcast-client-configuration) + * [1.5. Basic Usage](#15-basic-usage) + * [1.6. Code Samples](#16-code-samples) +* [2. Features](#2-features) +* [3. Configuration Overview](#3-configuration-overview) + * [3.1. Configuration Options](#31-configuration-options) + * [3.1.1. Programmatic Configuration](#311-programmatic-configuration) +* [4. Serialization](#4-serialization) + * [4.1. IdentifiedDataSerializable Serialization](#41-identifieddataserializable-serialization) + * [4.2. Portable Serialization](#42-portable-serialization) + * [4.3. Custom Serialization](#43-custom-serialization) + * [4.4. Global Serialization](#44-global-serialization) +* [5. Setting Up Client Network](#5-setting-up-client-network) + * [5.1. Providing the Member Addresses](#51-providing-the-member-addresses) + * [5.2. Setting Smart Routing](#52-setting-smart-routing) + * [5.3. Enabling Redo Operation](#53-enabling-redo-operation) + * [5.4. Setting Connection Timeout](#54-setting-connection-timeout) + * [5.5. Setting Connection Attempt Limit](#55-setting-connection-attempt-limit) + * [5.6. Setting Connection Attempt Period](#56-setting-connection-attempt-period) + * [5.7. Enabling Client TLS/SSL](#57-enabling-client-tlsssl) + * [5.8. Enabling Hazelcast Cloud Discovery](#58-enabling-hazelcast-cloud-discovery) +* [6. Securing Client Connection](#6-securing-client-connection) + * [6.1. TLS/SSL](#61-tlsssl) + * [6.1.1. TLS/SSL for Hazelcast Members](#611-tlsssl-for-hazelcast-members) + * [6.1.2. TLS/SSL for Hazelcast C++ Clients](#612-tlsssl-for-hazelcast-cpp-clients) + * [6.1.3. Mutual Authentication](#613-mutual-authentication) +* [7. Using C++ Client with Hazelcast IMDG](#7-using-cpp-client-with-hazelcast-imdg) + * [7.1. C++ Client API Overview](#71-cpp-client-api-overview) + * [7.2. C++ Client Operation Modes](#72-cpp-client-operation-modes) + * [7.2.1. Smart Client](#721-smart-client) + * [7.2.2. Unisocket Client](#722-unisocket-client) + * [7.3. Handling Failures](#73-handling-failures) + * [7.3.1. Handling Client Connection Failure](#731-handling-client-connection-failure) + * [7.3.3. Backpressure](#732-backpressure) + * [7.3.4. Client Connection Strategy](#732-client-connection-strategy) + * [7.3.4. Client Reconnect Strategy](#732-client-reconnection-strategy) + * [7.4. Using Distributed Data Structures](#74-using-distributed-data-structures) + * [7.4. Using Distributed Data Structures](#74-using-distributed-data-structures) + * [7.4.1. Using Map](#741-using-map) + * [7.4.2. Using MultiMap](#742-using-multimap) + * [7.4.3. Using Replicated Map](#743-using-replicated-map) + * [7.4.4. Using Queue](#744-using-queue) + * [7.4.5. Using Set](#745-using-set) + * [7.4.6. Using List](#746-using-list) + * [7.4.7. Using Ringbuffer](#747-using-ringbuffer) + * [7.4.8. Using Reliable Topic](#748-using-reliable-topic) + * [7.4.9. Using Lock](#749-using-lock) + * [7.4.10. Using Atomic Long](#7410-using-atomic-long) + * [7.4.11. Using Semaphore](#7411-using-semaphore) + * [7.4.12. Using PN Counter](#7412-using-pn-counter) + * [7.4.13. Using Flake ID Generator](#7413-using-flake-id-generator) + * [7.5. Distributed Events](#75-distributed-events) + * [7.5.1. Cluster Events](#751-cluster-events) + * [7.5.1.1. Listening for Member Events](#7511-listening-for-member-events) + * [7.5.1.2. Listening for Distributed Object Events](#7512-listening-for-distributed-object-events) + * [7.5.1.3. Listening for Lifecycle Events](#7513-listening-for-lifecycle-events) + * [7.5.2. Distributed Data Structure Events](#752-distributed-data-structure-events) + * [7.5.2.1. Listening for Map Events](#7521-listening-for-map-events) + * [7.6. Distributed Computing](#76-distributed-computing) + * [7.6.1. Using EntryProcessor](#761-using-entryprocessor) + * [7.7. Distributed Query](#77-distributed-query) + * [7.7.1. How Distributed Query Works](#771-how-distributed-query-works) + * [7.7.1.1. Employee Map Query Example](#7711-employee-map-query-example) + * [7.7.1.2. Querying by Combining Predicates with AND, OR, NOT](#7712-querying-by-combining-predicates-with-and-or-not) + * [7.7.1.3. Querying with SQL](#7713-querying-with-sql) + * [7.7.1.4. Filtering with Paging Predicates](#7714-filtering-with-paging-predicates) + * [7.8 Raw Pointer API](#raw-pointer-api) + * [7.9 Mixed Object Types Supporting HazelcastClient](#mixed-object-types-supporting-hazelcastclient) + * [7.9.1 TypedData API](#typeddata-api) +* [8. Development and Testing](#8-development-and-testing) + * [8.1. Building and Using Client From Sources](#81-building-and-using-client-from-sources) + * [8.1.1 Mac](#811-mac) + * [8.1.2 Linux](#811-linux) + * [8.1.3 Windows](#811-windows) + * [8.2. Testing](#82-testing) + * [8.2.1 Tested Platforms](#811-tested-platforms) + * [8.3. Reproducing Released Libraries](#83-reproducing-released-libraries) +* [9. Getting Help](#9-getting-help) +* [10. Contributing](#10-contributing) +* [11. License](#11-license) +* [12. Copyright](#12-copyright) + + +# Introduction + +This document provides information about the C++ client for [Hazelcast](https://hazelcast.org/). This client uses Hazelcast's [Open Client Protocol](https://hazelcast.org/documentation/#open-binary) and works with Hazelcast IMDG 3.6 and higher versions. + +### Resources + +See the following for more information on C++ and Hazelcast IMDG: + +* Hazelcast IMDG [website](https://hazelcast.org/) +* Hazelcast IMDG [Reference Manual](https://hazelcast.org/documentation/#imdg) + +### Release Notes + +You can see the release notes for each C++ client release on the [Releases](https://github.com/hazelcast/hazelcast-cpp-client/releases) page of this repository. + + +# 1. Getting Started + +This chapter explains all the necessary things to start using Hazelcast C++ Client including basic Hazelcast IMDG and client +configuration and how to use distributed maps with Hazelcast. + +## 1.1. Requirements + +- Windows, Linux or MacOS +- Java 6 or newer +- Hazelcast IMDG 3.6 or newer +- Latest Hazelcast C++ Client + +## 1.2. Working with Hazelcast Clusters + +Hazelcast C++ Client requires a working Hazelcast IMDG cluster to run. IMDG cluster handles storage and manipulation of the user data. +Clients are a way to connect to IMDG cluster and access such data. + +IMDG cluster consists of one or more Hazelcast IMDG members. These members generally run on multiple virtual or physical machines +and are connected to each other via network. Any data put on the cluster is partitioned to multiple members transparent to the user. +It is therefore very easy to scale the system by adding new members as the data grows. IMDG cluster also offers resilience. Should +any hardware or software problem causes a crash to any member, the data on that member is recovered from backups and the cluster +continues to operate without any downtime. Hazelcast clients are an easy way to connect to an IMDG cluster and perform tasks on +distributed data structures that live on the cluster. + +In order to use Hazelcast C++ Client, we first need to setup an IMDG cluster. + +### Setting Up an IMDG Cluster + +There are multiple ways of starting an IMDG cluster easily. You can run standalone IMDG members by downloading and running jar files +from the website. You can embed IMDG members to your Java projects. The easiest way is to use [hazelcast-member tool](https://github.com/hazelcast/hazelcast-member-tool) +if you have brew installed in your computer. We are going to download jars from the website and run a standalone member for this guide. + +#### Running Standalone Jars + +Go to https://hazelcast.org/download/ and download `.zip` or `.tar` distribution of Hazelcast IMDG. Decompress the contents into any directory that you +want to run IMDG members from. Change into the directory that you decompressed the Hazelcast content. Go into `bin` directory. Use either +`start.sh` or `start.bat` depending on your operating system. Once you run the start script, you should see IMDG logs on the terminal. +Once you see some log similar to the following, your 1-member cluster is ready to use: +``` +INFO: [192.168.0.3]:5701 [dev] [3.10.4] -For compiling with SSL support: +Members {size:1, ver:1} [ + Member [192.168.0.3]:5701 - 65dac4d1-2559-44bb-ba2e-ca41c56eedd6 this +] -- You need to have the openssl (version 1.0.2) installed in your development environment: (i) Add openssl `include` directory to include directories, (ii) Add openssl `library` directory to the link directories list (This is the directory named `tls`. e.g. `cpp/Linux_64/hazelcast/lib/tls`), (iii) Set the openssl libraries to link. - -- You can provide your openssl installation directory to cmake using the following flags: - - DHZ_OPENSSL_INCLUDE_DIR="Path to open installation include directory" - - DHZ_OPENSSL_LIB_DIR="Path to openssl lib directory" +Sep 06, 2018 10:50:23 AM com.hazelcast.core.LifecycleService +INFO: [192.168.0.3]:5701 [dev] [3.10.4] [192.168.0.3]:5701 is STARTED +``` -- Use -DHZ_COMPILE_WITH_SSL=ON +#### Adding User Library to CLASSPATH -- Check the top level CMakeLists.txt file to see which libraries we link for openssl in which environment. - - For Mac OS and Linux, we link with "ssl" and "crypto" - - For Windows, install also the 1.1.x version of the openssl library. An example linkage for 64 bit library and release build: "libeay32MD ssleay32MD libcrypto64MD libSSL64MD". + When you want to use features such as querying and language interoperability, you might need to add your own Java classes to Hazelcast member in order to use them from your C++ client. This can be done by adding your own compiled code to the `CLASSPATH`. To do this, compile your code with the `CLASSPATH` and add the compiled files to `user-lib` folder in the extracted `hazelcast-.zip`. Then, you can start your Hazelcast member by using the start scripts in the `bin` folder. The start scripts will automatically add your compiled classes to the `CLASSPATH`. + Note that if you are adding an `IdentifiedDataSerializable` or a `Portable` class, you need to add its factory too. Then, you should configure the factory in the `hazelcast.xml` in the `bin` folder like the following: + ```xml + + ... + + + > + IdentifiedFactoryClassName + + + + ... + +``` +Similarly, use `` instead of `` if you are using portables. -# Tested Platforms -Our CI tests are run continuously on the following platforms and compilers: -- Linux: CentOs5 gcc 3.4.6, CentOs5.11 gcc 4.1.2, centos 7 gcc 4.8.2 -- Windows: Visual Studio 12 -- Mac OS: Apple LLVM version 7.3.0 (clang-703.0.31) +#### Using hazelcast-member Tool -## Mac +`hazelcast-member` is a tool to make downloading and running IMDG members as easy as it could be. If you have brew installed, run the following commands: +``` +brew tap hazelcast/homebrew-hazelcast +brew install hazelcast-member +hazelcast-member start +``` +In order to stop the member, run the following command: +``` +hazelcast-member stop +``` +Find more information about `hazelcast-member` tool at https://github.com/hazelcast/hazelcast-member-tool -**Release:** +Refer to the official [Hazelcast IMDG Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#getting-started) for more information regarding starting clusters. - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release - cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release +## 1.3. Downloading and Installing +Download the latest C++ client library from [Hazelcast C++ Client Website](https://hazelcast.org/clients/cplusplus/). You need to download the zip file for your platform. For Linux and Windows, 32 and 64-bit libraries exist while for MacOS only has the 64-bit version. -**Code Coverage:** +Unzip the file. Below is the directory structure for "Linux 64-bit ZIP" and the others are similar: - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_CODE_COVERAGE=ON - gcovr -u -e .*external.* -e SimpleMapTest.h --html --html-details -o coverage.html +- cpp/Linux_64/ : + - hazelcast : + - lib : The shared and static library folder. + - tls : Contains the library with TLS (SSL) support enabled. + - include : The include folder you need to include when compiling your project. + - external/include : External folder that you need to include when compiling your project. Currently the only dependency is `boost/shared_ptr.hpp`. + - boost : The external boost files for boost/shared_ptr. -**Xcode Development:** + - examples : There are a number of examples in this folder for each feature. Each example produces an executable which you can run in a cluster. You may need to set the server IP addresses for the examples to run. - cmake .. -G Xcode -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug +### 1.3.1 Compiling Your Project - cmake .. -G Xcode -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=archive -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=library +For compilation, you need to include the `hazelcast/include` and `external/include` folders in your distribution. You also need to link your application to the appropriate static or shared library. -**Valgrind sample run with suppressions:** +If you want to use tls feature, use the lib directory similar to: cpp/Linux_64/hazelcast/lib/tls - valgrind --leak-check=yes --gen-suppressions=all --suppressions=../test.sup ./hazelcast/test/clientTest_STATIC_64.exe > log.t +#### 1.3.1.1 Mac Client -## Linux +For Mac, there is one distribution: 64 bit. -**Release:** +Here is an example script to build with static library: - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release - cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release +`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include cpp/Mac_64/hazelcast/lib/libHazelcastClientStatic_64.a` - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release - cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release +Here is an example script to build with shared library: -**Building the tests:** +`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include -Lcpp/Mac_64/hazelcast/lib -lHazelcastClientShared_64` - Add the -DHZ_BUILD_TESTS=ON flag to the cmake flags. e.g.: - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_BUILD_TESTS=ON +#### 1.3.1.2 Linux Client -**Building the examples:** +For Linux, there are two distributions: 32 bit and 64 bit. - Add the -DHZ_BUILD_EXAMPLES=ON flag to the cmake flags. e.g.: - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_BUILD_EXAMPLES=ON +Here is an example script to build with static library: -**Valgrind:** +`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include + cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_VALGRIND=ON +Here is an example script to build with shared library: -## Windows +`g++ main.cpp -lpthread -Wl,–no-as-needed -lrt -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClientShared_64` -**Release:** +Please add the **__STDC_LIMIT_MACROS** and **__STDC_CONSTANT_MACROS** compilation flags for environments for which the compilation fails with error "INT32_MAX" could not be determined. For example: - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release - cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release - cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release - cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release +`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` - MSBuild.exe HazelcastClient.sln /property:Configuration=Release -**Debug:** +#### 1.3.1.3 Windows Client - cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Debug - cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Debug - cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug - cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug +For Windows, there are two distributions; 32 bit and 64 bit. The static library is located in a folder named "static" while the dynamic library(dll) is in the folder named as "shared". - MSBuild.exe HazelcastClient.sln /property:TreatWarningsAsErrors=true /property:Configuration=Debug +When compiling for Windows environment the user should specify one of the following flags: + HAZELCAST_USE_STATIC: You want the application to use the static Hazelcast library. + HAZELCAST_USE_SHARED: You want the application to use the shared Hazelcast library. -# Reproducing Released Libraries +## 1.4. Basic Configuration +If you are using Hazelcast IMDG and C++ Client on the same computer, generally default configuration just works. This is great for +trying out the client. However, if you run the client on a different computer than any of the cluster members, you may +need to do some simple configuration such as specifying the member addresses. + +The IMDG members and clients have their own configuration options. You may need to reflect some of the member side configurations on the client side to properly connect to the cluster. +This section describes the most common configuration elements to get you started in no time. +It discusses some member side configuration options to ease understanding Hazelcast's ecosystem. Then, client side configuration options +regarding cluster connection are discussed. Configuration material regarding data structures are discussed in the following sections. +You can refer to [IMDG Documentation](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview](#configuration-overview) for more information. + +### 1.4.1. IMDG Configuration +Hazelcast IMDG aims to run out of the box for most common scenarios. However if you have limitations on your network such as multicast being disabled, +you may have to configure your Hazelcast IMDG instances so that they can find each other on the network. Also most data structures are configurable. +Therefore, you may want to configure your Hazelcast IMDG. We will show you the basics about network configuration here. + +There are two ways to configure Hazelcast IMDG. One is to use a `hazelcast.xml` file and the other is to programmatically configure the +instance before starting it from Java code. Since we use standalone servers, we will use `hazelcast.xml` to configure our cluster members. + +When you download and unzip `hazelcast-.zip`, you see the `hazelcast.xml` in `bin` folder. When a Hazelcast member starts, it looks for +`hazelcast.xml` file to load configuration from. A sample `hazelcast.xml` is below. We will go over some important elements in the rest of this section. +```xml + + + dev + dev-pass + + + 5701 + + + 224.2.2.3 + 54327 + + + 127.0.0.1 + + 127.0.0.1 + + + + + + + + 1 + + +``` + +- ``: Specifies which cluster this member belongs to. A member connects only to other members that are in the same group as +itself. You will see `` and `` tags with some pre-configured values. You may give your clusters different names so that they can +live in the same network without disturbing each other. Note that the cluster name should be the same across all members and clients that belong + to the same cluster. `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility +purposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later. +- `` + - ``: Specifies the port number to be used by the member when it starts. Its default value is 5701 You can specify another port number, and if + you set `auto-increment` to `true`, then Hazelcast will try subsequent ports until it finds an available port or `port-count` is reached. + - ``: Specifies the strategies to be used by the member to find other cluster members. Choose which strategy you want to + use by setting its `enabled` attribute to `true` and the others to `false`. + - ``: Members find each other by sending multicast requests to the specified address and port. It is very useful if IP addresses + of the members are not static. + - ``: This strategy uses a pre-configured list of known members to find an already existing cluster. It is enough for a member to + find only one cluster member to connect to the cluster. The rest of the member list is automatically retrieved from that member. We recommend + putting multiple known member addresses there to avoid disconnectivity should one of the members in the list is unavailable at the time + of connection. + +These configuration elements are enough for most connection scenarios. Now we will move onto configuration of the C++ client. + +### 1.4.2. Hazelcast Client Configuration +You can configure Hazelcast C++ Client programatically. + +This section describes some network configuration settings to cover common use cases in connecting the client to a cluster. Refer to [Configuration Overview](#configuration-overview) +and the following sections for information about detailed network configuration and/or additional features of Hazelcast C++ client +configuration. + +An easy way to configure your Hazelcast C++ Client is to create a `ClientConfig` object and set the appropriate options. Then you can +supply this object to your client at the startup. + +**Programmatic configuration** + +You need to create a `ClientConfig` object and adjust its properties. Then you can pass this object to the client when starting it. + +```C++ + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hz(config); // Connects to the cluster +``` -Sometimes you may want to reproduce the released library for your own compiler environment. You need to run the release script and it will produce a release folder named "cpp". +If you run Hazelcast IMDG members in a different server than the client, you most probably have configured the members' ports and cluster +names as explained in the previous section. If you did, then you need to make certain changes to the network settings of your client. -Note: -- The default release scripts require that you have openssl (version 1.0.2) installed in your development environment. +### Group Settings -## Mac +**Programmatic:** +```C++ + hazelcast::client::ClientConfig config; + config.getGroupConfig().setName("group name of your cluster"); +``` -Run releaseOSX.sh. +### Network Settings +You need to provide the ip address and port of at least one member in your cluster so the client finds it. -## Linux +**Programmatic:** +```C++ + hazelcast::client::ClientConfig config; + config.getNetworkConfig().addAddress(hazelcast::client::Address("your server ip", 5701 /* your server port*/)); +``` -Run releaseLinux.sh +## 1.5. Basic Usage -## Windows +Now that we have a working cluster and we know how to configure both our cluster and client, we can run a simple program to use a +distributed map in C++ client. -Run releaseWindows.bat. +The following example first creates a programmatic configuration object. Then, it starts a client. -# Features +```C++ +#include +int main() { + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hz(config); // Connects to the cluster + std::cout << "Started the Hazelcast C++ client instance " << hz.getName() << std::endl; // Prints client instance name + return 0; +} +``` +This should print logs about the cluster members and information about the client itself such as client type and local address port. +``` +24/10/2018 13:54:06.390 INFO: [0x7fff97559340] hz.client_1[dev] [3.10.2-SNAPSHOT] (20181023:5f1a045da7) LifecycleService::LifecycleEvent STARTING +24/10/2018 13:54:06.390 INFO: [0x7fff97559340] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent STARTED +24/10/2018 13:54:06.392 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] Trying to connect to Address[127.0.0.1:5701] as owner member +24/10/2018 13:54:06.395 INFO: [0x7000069c1000] hz.client_1[dev] [3.10.2-SNAPSHOT] Setting ClientConnection{alive=1, connectionId=1, remoteEndpoint=Address[localhost:5701], lastReadTime=2018-10-24 10:54:06.394000, closedTime=never, connected server version=3.11-SNAPSHOT} as owner with principal uuid: 160e2baf-566b-4cc3-ab25-a1b3e34de320 ownerUuid: 7625eae9-4f8e-4285-82c0-be2f2eee2a50 +24/10/2018 13:54:06.395 INFO: [0x7000069c1000] hz.client_1[dev] [3.10.2-SNAPSHOT] Authenticated with server Address[localhost:5701], server version:3.11-SNAPSHOT Local address: Address[127.0.0.1:56007] +24/10/2018 13:54:06.495 INFO: [0x700006cd3000] hz.client_1[dev] [3.10.2-SNAPSHOT] +Members [2] { + Member[localhost]:5702 - 44cf4017-5355-4859-9bf5-fd374d7fc69c + Member[localhost]:5701 - 7625eae9-4f8e-4285-82c0-be2f2eee2a50 +} +24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED +Started the Hazelcast C++ client instance hz.client_1 +``` +Congratulations, you just started a Hazelcast C++ Client. +**Using a Map** -You can use Native C++ Client to connect to Hazelcast cluster members and perform almost all operations that a member can perform. Clients differ from members in that clients do not hold data. The C++ Client is by default a smart client, i.e., it knows where the data is and asks directly for the correct member. You can disable this feature (using the `ClientConfig::setSmart` method) if you do not want the clients to connect to every member. +Let us manipulate a distributed map on a cluster using the client. -The features of C++ Clients are listed below: +Save the following file as `IT.cpp`, compile it using a command similar to (Linux g++ compilation is used for demonstration): +```C++ +g++ IT.cpp -o IT -lpthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64 +``` +Then, you can run the application as +``` +./IT +``` -- Access to distributed data structures (`IMap`, `IQueue`, `MultiMap`, `ITopic`, etc.). -- Access to transactional distributed data structures (`TransactionalMap`, `TransactionalQueue`, etc.). -- Ability to add cluster listeners to a cluster and entry/item listeners to distributed data structures. -- Distributed synchronization mechanisms with `ILock`, `ISemaphore` and `ICountDownLatch`. +**main.cpp** +```C++ +#include +int main() { + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hz(config); // Connects to the cluster + + hazelcast::client::IMap personnel = hz.getMap("personnelMap"); + personnel.put("Alice", "IT"); + personnel.put("Bob", "IT"); + personnel.put("Clark", "IT"); + std::cout << "Added IT personnel. Logging all known personnel" << std::endl; + std::vector > entries = personnel.entrySet(); + for (std::vector >::const_iterator it = entries.begin(); + it != entries.end(); ++it) { + std::cout << it->first << " is in " << it->second << " department." << std::endl; + } + + return 0; +} +``` +**Output** +``` +24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED +Added IT personnel. Logging all known personnel +Alice is in IT department +Clark is in IT department +Bob is in IT department +``` +You see this example puts all IT personnel into a cluster-wide `personnelMap` and then prints all known personnel. -# Setting Up the Client +Now create `Sales.cpp` and compile and run it. +Compile: +```C++ +g++ Sales.cpp -o Sales -lpthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64 +``` +Then, you can run the application as +``` +./Sales +``` -Hazelcast C++ Client is shipped with 32/64 bit, shared and static libraries. You only need to include the boost *shared_ptr.hpp* header in your compilation since the API makes use of the boost `shared_ptr`. +**Sales.cpp** +```C++ +#include +int main() { + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hz(config); // Connects to the cluster + + hazelcast::client::IMap personnel = hz.getMap("personnelMap"); + personnel.put("Denise", "Sales"); + personnel.put("Erwing", "Sales"); + personnel.put("Fatih", "Sales"); + personnel.put("Bob", "IT"); + personnel.put("Clark", "IT"); + std::cout << "Added Sales personnel. Logging all known personnel" << std::endl; + std::vector > entries = personnel.entrySet(); + for (std::vector >::const_iterator it = entries.begin(); + it != entries.end(); ++it) { + std::cout << it->first << " is in " << it->second << " department." << std::endl; + } + return 0; +} +``` +**Output** +``` +24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED +Added Sales personnel. Logging all known personnel +Denise is in Sales department +Erwing is in Sales department +Faith is in Sales department +Alice is in IT department +Clark is in IT department +Bob is in IT department +``` + +You will see this time we add only the sales employees but we get the list all known employees including the ones in IT. +That is because our map lives in the cluster and no matter which client we use, we can access the whole map. + +## 1.6. Code Samples + +Please see Hazelcast C++ [code samples](https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples) for more examples. + +You can also refer to Hazelcast C++ [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/). + +# 2. Features + +Hazelcast C++ client supports the following data structures and features: + +* Map +* Queue +* Set +* List +* MultiMap +* Replicated Map +* Ringbuffer +* Reliable Topic +* Lock +* Semaphore +* Atomic Long +* CRDT PN Counter +* Flake Id Generator +* Event Listeners +* Entry Processor +* Query (Predicates) +* Paging Predicate +* Built-in Predicates +* Listener with Predicate +* Near Cache Support +* Programmatic Configuration +* Fail Fast on Invalid Configuration +* SSL Support (requires Enterprise server) +* Authorization +* Smart Client +* Unisocket Client +* Lifecycle Service +* IdentifiedDataSerializable Serialization +* Portable Serialization +* Custom Serialization +* Global Serialization + +# 3. Configuration Overview + +This chapter describes the options to configure your C++ client. + +## 3.1. Configuration Options + +You can configure Hazelcast C++ client programmatically (API). + +### 3.1.1. Programmatic Configuration + +For programmatic configuration of the Hazelcast C++ client, just instantiate a `ClientConfig` object and configure the +desired aspects. An example is shown below. + +```C++ + hazelcast::client::ClientConfig config; + config.getNetworkConfig().addAddress(hazelcast::client::Address("your server ip", 5701 /* your server port*/)); + hazelcast::client::HazelcastClient hz(config); // Connects to the cluster +``` -The downloaded release folder consists of: +Refer to `ClientConfig` class documentation at [Hazelcast C++ Client API Docs](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/) for details. -- Mac_64/ -- Windows_32/ -- Windows_64/ -- Linux_32/ -- Linux_64/ -- docs/ *(HTML Doxygen documents are here)* +# 4. Serialization +Serialization is the process of converting an object into a stream of bytes to store the object in memory, a file or database, or transmit it through network. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization. Hazelcast offers you its own native serialization methods. You will see these methods throughout the chapter. -Each of the folders above contains the following: +Hazelcast serializes all your objects before sending them to the server. The `unsigned char` (`byte`), `bool`, `char`, `short`, `int32_t`, `int64_t`, `float`, `double`, `std::string` types are serialized natively and you cannot override this behavior. The following table is the conversion of types for Java server side. -- examples/ - There are a number of examples in this folder for each feature. Each example produces an executable which you can run in a cluster. You may need to set the server IP addresses for the examples to run. +| C++ | Java | +|--------------|-------------------------------------| +| byte | Byte | +| bool | Boolean | +| char | Character | +| short | Short | +| int32_t | Integer | +| int64_t | Long | +| float | Float | +| double | Double | +| std::string | String | -- hazelcast/ - - lib/ => Contains both shared and static library of hazelcast. - - lib/tls => Contains the library with TLS (SSL) support enabled. - - include/ => Contains headers of client. +Vector of the above types can be serialized by default as `boolean[]`, `byte[]`, `short[]`, `int[]`, `float[]`, `double[]`, `long[]` and `string[]` for Java server side respectively. -- external/ - - include/ => Contains headers of dependencies. (boost::shared_ptr) +If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [IdentifiedDataSerializable Serialization](#1-identifieddataserializable-serialization) and [Portable Serialization](#2-portable-serialization). -# Installing the Client +On top of all, if you want to use your own serialization type, you can use a [Custom Serialization](#3-custom-serialization). -The C++ Client is tested on Linux 32/64-bit, Mac 64-bit and Windows 32/64-bit machines. For each of the headers above, it is assumed that you are in the correct folder for your platform. Folders are Mac_64, Windows_32, Windows_64, Linux_32 or Linux_64. +When Hazelcast serializes an object into Data (byte array): -# Compiling the Client +1. It first checks whether the object pointer is NULL. -For compilation, you need to include the `hazelcast/include` and `external/include` folders in your distribution. You also need to link your application to the appropriate static or shared library. +2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type id for the built-in types such as int32_t, bool, short, byte, etc. If it does not match the buil-in types, it may match the Hazelcast serialization types IdentifiedDataSerializable, Portable or custom types. The matching is done based on method parameter matching. -If you want to use tls feature, use the lib directory similar to: cpp/Linux_64/hazelcast/lib/tls +3. If the above check fails, Hazelcast will use the registered Global Serializer if one exists. -## Mac Client +If all the above fails, then `exception::HazelcastSerializationException` exception is thrown. -For Mac, there is one distribution: 64 bit. +## 4.1. IdentifiedDataSerializable Serialization -Here is an example script to build with static library: +For a faster serialization of objects, Hazelcast recommends to implement IdentifiedDataSerializable interface. -`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include cpp/Mac_64/hazelcast/lib/libHazelcastClientStatic_64.a` +Here is an example of an object implementing IdentifiedDataSerializable interface: -Here is an example script to build with shared library: +```C++ +class Employee : public serialization::IdentifiedDataSerializable { +public: + static const int TYPE_ID = 100; -`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include -Lcpp/Mac_64/hazelcast/lib -lHazelcastClientShared_64` + virtual int getFactoryId() const { + return 1000; + } -## Linux Client + virtual int getClassId() const { + return TYPE_ID; + } -For Linux, there are two distributions: 32 bit and 64 bit. + virtual void writeData(serialization::ObjectDataOutput &writer) const { + writer.writeInt(id); + writer.writeUTF(&name); + } -Here is an example script to build with static library: + virtual void readData(serialization::ObjectDataInput &reader) { + id = reader.readInt(); + name = *reader.readUTF(); + } -`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include - cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` +private: + int id; + std::string name; +}; +``` -Here is an example script to build with shared library: +IdentifiedDataSerializable uses `getClassId()` and `getFactoryId()` to reconstitute the object. To complete the implementation `DataSerializableFactory` should also be implemented and registered into `SerializationConfig` which can be accessed from `clientConfig.getSerializationConfig().addDataSerializableFactory`. The factory's responsibility is to return an instance of the right `IdentifiedDataSerializable` object, given the class id. -`g++ main.cpp -lpthread -Wl,–no-as-needed -lrt -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClientShared_64` +A sample `IdentifiedDataSerializableFactory` could be implemented as following: -Please add the **__STDC_LIMIT_MACROS** and **__STDC_CONSTANT_MACROS** compilation flags for environments for which the compilation fails with error "INT32_MAX" could not be determined. For example: +```C++ +class SampleDataSerializableFactory : public serialization::DataSerializableFactory { +public: + static const int FACTORY_ID = 1000; -`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` + virtual std::auto_ptr create(int32_t classId) { + switch (classId) { + case 100: + return std::auto_ptr(new Employee()); + default: + return std::auto_ptr(); + } + } +}; +``` -## Windows Client +The last step is to register the `IdentifiedDataSerializableFactory` to the `SerializationConfig`. -For Windows, there are two distributions; 32 bit and 64 bit. The static library is located in a folder named "static" while the dynamic library(dll) is in the folder named as "shared". +**Programmatic Configuration:** +```C++ + ClientConfig clientConfig; + clientConfig.getSerializationConfig().addDataSerializableFactory(SampleDataSerializableFactory::FACTORY_ID, + boost::shared_ptr( + new SampleDataSerializableFactory())); +``` -When compiling for Windows environment the user should specify one of the following flags: - HAZELCAST_USE_STATIC: You want the application to use the static Hazelcast library. - HAZELCAST_USE_SHARED: You want the application to use the shared Hazelcast library. +Note that the id that is passed to the `SerializationConfig` is same as the `factoryId` that `Employee` object returns. -# Serialization Support +## 4.2. Portable Serialization -C++ client supports the following types of object serializations: +As an alternative to the existing serialization methods, Hazelcast offers Portable serialization. To use it, you need to implement `Portable` interface. Portable serialization has the following advantages: -- **Built-in primitive types**: Some primitive types have built-in support for serialization. These are `char`, `unsigned char` (`byte`), `bool`, `short`, `int`, `long`, `float`, `double`, `stl string`and vector of these primitive types. -- **IdentifiedDataSerializable**: This interface enables a fast serialization by providing a unique factory and class IDs. It requires the server side class as well. -- **Portable Serialization**: This serialization carries the meta data for the object structure. If server side deserialization is not needed, you do not need to prepare the server side implementation. -- **Custom Serialization**: This serialization allows you to use an external custom serialization, e.g., Google's Protocol Buffers. It provides serialization support without modifying your existing libraries where object classes exist. +- Supporting multiversion of the same object type +- Fetching individual fields without having to rely on reflection +- Querying and indexing support without de-serialization and/or reflection -## Custom Serialization +In order to support these features, a serialized Portable object contains meta information like the version and the concrete location of the each field in the binary data. This way Hazelcast is able to navigate in the binary data and de-serialize only the required field without actually de-serializing the whole object which improves the Query performance. -If all of your classes that need to be serialized are inherited from the same class, you can use an implementation as shown in the example snippet below: +With multiversion support, you can have two nodes where each of them having different versions of the same object and Hazelcast will store both meta information and use the correct one to serialize and de-serialize Portable objects depending on the node. This is very helpful when you are doing a rolling upgrade without shutting down the cluster. -``` -class MyCustomSerializer : public serialization::Serializer { - public: - void write(serialization::ObjectDataOutput & out, const ExampleBaseClass& object); - void read(serialization::ObjectDataInput & in, ExampleBaseClass& object); - int getHazelcastTypeId() const; - }; -``` +Also note that Portable serialization is totally language independent and is used as the binary protocol between Hazelcast server and clients. -If your classes are not inherited from the same base class, you can use a serializer class with templates as shown in the example snippet below: +A sample Portable implementation of a `PortableSerializableSample` class will look like the following: -``` -template -class MyCustomSerializer : public serialization::Serializer { - public: - void write(serialization::ObjectDataOutput & out, const T& object) { - //..... - } - void read(serialization::ObjectDataInput & in, T& object) { - //..... - } - int getHazelcastTypeId() const { - //.. - } - }; -``` +```C++ +class PortableSerializableSample : public serialization::Portable { +public: + static const int CLASS_ID = 1; -Along with your serializer, you should provide the function `getHazelcastTypeId()` with the same namespace to which `ExampleBaseClass` belongs as shown below: + virtual int getFactoryId() const { + return 1; + } -``` -int getHazelcastTypeId(const MyClass*); -``` + virtual int getClassId() const { + return CLASS_ID; + } -This function should return the same ID with its serializer. This ID is used to determine which serializer needs to be used for your classes. + virtual void writePortable(serialization::PortableWriter &writer) const { + writer.writeInt("id", id); + writer.writeUTF("name", &name); + writer.writeLong("lastOrder", lastOrder); + } -After you implement your serializer, you can register it using `SerializationConfig` as shown below: + virtual void readPortable(serialization::PortableReader &reader) { + id = reader.readInt("id"); + name = *reader.readUTF("name"); + lastOrder = reader.readLong("lastOrder"); + } -``` -clientConfig.getSerializationConfig(). -registerSerializer(boost::shared_ptr(new MyCustomSerializer()); +private: + std::string name; + int32_t id; + int64_t lastOrder; +}; ``` -## Polymorphic Types Serialization -The client library can handle polymorphic objects such that you can use a base class type structure, such as `IMap`, you can put and get derived types. This support exists for `IdentifiedDataSerializable`, `Portable` and custom objects. +Similar to `Portable`, a Portable object must provide `classId` and `factoryId`. The factory object will be used to create the Portable object given the classId. -You can find the polymorphic usage examples at examples/distributed-map/mixed-map folder. +A sample `PortableFactory` could be implemented as following: -For `IdentifiedDataSerializable` objects, you need to register a factory implementation of type `serialization::DataSerializableFactory`. See the below example: -``` -class PolymorphicDataSerializableFactory : public serialization::DataSerializableFactory { +```C++ +class SamplePortableFactory : public serialization::PortableFactory { public: - virtual std::auto_ptr create(int32_t typeId) { - switch (typeId) { - case 10: - return std::auto_ptr(new BaseDataSerializable); - case 11: - return std::auto_ptr(new Derived1DataSerializable); - case 12: - return std::auto_ptr(new Derived2DataSerializable); + static const int FACTORY_ID = 1; + + virtual std::auto_ptr create(int32_t classId) const { + switch (classId) { + case 1: + return std::auto_ptr(new PortableSerializableSample()); default: - return std::auto_ptr(); + return std::auto_ptr(); } } - serializationConfig.addDataSerializableFactory(666, - boost::shared_ptr( - new PolymorphicDataSerializableFactory())); }; - - // Register the factory - serializationConfig.addDataSerializableFactory(666, - boost::shared_ptr( - new PolymorphicDataSerializableFactory())); ``` -Similarly for `Portable` types you need to register a factory of type `serialization::PortableFactory`. -``` - serializationConfig.addPortableFactory(666, boost::shared_ptr( - new PolymorphicPortableFactory)); -``` +The last step is to register the `PortableFactory` to the `SerializationConfig`. -If you want to use custom objects which are polymorphic, you need to register both the derived and base object serializers like this: +**Programmatic Configuration:** +```C++ + ClientConfig clientConfig; + clientConfig.getSerializationConfig().addPortableFactory(SamplePortableFactory::FACTORY_ID, + boost::shared_ptr( + new SamplePortableFactory())); ``` - serializationConfig.registerSerializer( - boost::shared_ptr(new BaseCustomSerializer)); - serializationConfig.registerSerializer( - boost::shared_ptr(new Derived1CustomSerializer)); -``` +Note that the id that is passed to the `SerializationConfig` is same as the `factoryId` that `PortableSerializableSample` object returns. -## Global Serializer -Sometimes you may want to use your own serialization for some objects and do not want to define the Hazelcast serialization for those objects. Or, you may not want to define the getHazelcastTypeId free function for those objects. In these cases, you can use the global serializer configuration for this purpose. You set the global serializer and manage how you serialize and deserialize the objects. Here is how you register a global serializer: -``` - hazelcast::client::ClientConfig config; - hazelcast::client::SerializationConfig serializationConfig; - serializationConfig.setGlobalSerializer(boost::shared_ptr( - new MyGlobalSerializer())); - config.setSerializationConfig(serializationConfig); - hazelcast::client::HazelcastClient hz(config); -``` +## 4.3. Custom Serialization -MyGlobalSerializer is your implementation of StreamSerializer interface. You do not need to write the getHazelcastTypeId function for your objects which are handled by MyGlobalSerializer class. +Hazelcast lets you plug a custom serializer to be used for serialization of objects. -# Invocation And Retry Mechanism -The client works by sending requests to the members in the cluster and processes the reponses. This is called a client invocation to the cluster. If any problem occurs during an invocation, the following rules apply to decide if the invocation should be retried: +Let's say you have an object `Musician` and you would like to customize the serialization. The reason may be you want to use an external serializer for only one object. -- If the problem is an IOException, which means that the request could not even be sent to the cluster member, e.g., due to a network connection problem, then all such requests can be retried. -- If the invocation was sent to the cluster and the member returned an exception to the client, the client will retry the invocation only if the operation is a retryable idempotent operation. For example, the IMap::get is idempotent and can be retried but IMap::put is not idempotent. +```C++ +class C++ { +public: + Musician() {} -When invocation is being retried, the client may wait some time before it retries again. This retry wait time is configurable with this configuration property: + Musician(const std::string &name) : name(name) {} -``` -config.setProperty("hazelcast.client.invocation.retry.pause.millis", "500"); -``` + virtual ~Musician() { + } -The default retry wait time is 1 second. + const std::string &getName() const { + return name; + } -Of course, this retry does not continue indefinitely but finishes when the invocation timeout occurs. This duration is configurable with this configuration property: + void setName(const std::string &value) { + Musician::name = value; + } -``` -config.setProperty("hazelcast.client.invocation.timeout.seconds", "30"); +private: + std::string name; +}; ``` -The default timeout is 120 seconds. +Let's say your custom `MusicianSerializer` will serialize `Musician`. -## Client Backpressure -Sometimes, e.g., when your servers are overloaded, you may want to slow down the client operations to the cluster. Then the client can be configured to wait until number of outstanding invocations whose reponses are not received to become less than a certain number. This is called "Client Backpressure". By default, the backpressure is disabled. There are a few properties which control the backpressure. These client configuration properties are: +```C++ +class MusicianSerializer : public serialization::StreamSerializer { +public: + virtual int32_t getHazelcastTypeId() const { + return 10; + } -- "hazelcast.client.max.concurrent.invocations" : The maximum number of concurrent invocations allowed. To prevent the system from overloading, you can apply a constraint on the number of concurrent invocations. If the maximum number of concurrent invocations has been exceeded and a new invocation comes in, then Hazelcast will throw HazelcastOverloadException. By default this property is configured as INT32_MAX. -- "hazelcast.client.invocation.backoff.timeout.millis": Controls the maximum timeout in millis to wait for an invocation space to be available. If an invocation can't be made because there are too many pending invocations, then an exponential backoff is done to give the system time to deal with the backlog of invocations. This property controls how long an invocation is allowed to wait before getting a HazelcastOverloadException. When set to -1 then HazelcastOverloadException is thrown immediately without any waiting. This is the default value. + virtual void write(serialization::ObjectDataOutput &out, const void *object) { + const Musician *csObject = static_cast(object); + const std::string &value = csObject->getName(); + int length = (int) value.length(); + std::vector bytes; + for (int i = 0; i < length; ++i) { + bytes.push_back((hazelcast::byte) value[i]); + } + out.writeInt((int) length); + out.write(bytes); + } -# Client Connection Strategy -Hazelcast client-cluster connection and reconnection strategy can be configured. Sometimes, you may not want your application to wait for the client to connect to the cluster, you may just want to get the client and let the client connect in the background. This is configured by: + virtual void *read(serialization::ObjectDataInput &in) { + int32_t len = in.readInt(); + std::ostringstream value; + for (int i = 0; i < len; ++i) { + value << (char) in.readByte(); + } -``` -ClientConfig::getConnectionStrategyConfig().setAsyncStart(bool); + return new Musician(value.str()); + } +}; ``` -When this config is set true, the client creation won't wait to connect to cluster. The client instance will throw exception for any request, until it connects to cluster and become ready. -If it is set to false (the default case), HazelcastClient(const ClientConfig) will block until a cluster connection is established and it's ready to use client instance. +Note that the serializer `getHazelcastTypeId` method must must return a unique id as Hazelcast will use it to lookup the `MusicianSerializer` while it deserializes the object. Now the last required step is to register the `MusicianSerializer` to the configuration. -## Configure Client Reconnect Strategy -You can configure how the client should act when the client disconnects from the cluster for any reason. This is configured using the following configuration: +**Programmatic Configuration:** + +```C++ + ClientConfig clientConfig; + clientConfig.getSerializationConfig().registerSerializer( + boost::shared_ptr(new MusicianSerializer())); -``` -ClientConfig::getConnectionStrategyConfig().setReconnectMode(const hazelcast::client::config::ClientConnectionStrategyConfig::ReconnectMode &); ``` -Possible values for the ReconnectMode are: +From now on, Hazelcast will use `MusicianSerializer` to serialize `Musician` objects. -- OFF: Prevents reconnect to cluster after a disconnect. -- ON: Reconnect to cluster by blocking invocations. -- ASYNC: Reconnect to cluster without blocking invocations. Invocations will receive HazelcastClientOfflineException. +## 4.4. Global Serialization -## Shuffle Cluster Connection Member List -When the client is connecting to the cluster, the configured and discovered member addresses are being tried sequentially. The order with which the addresses are tried is by default in a randomly shuffled list. But for some reason, you may want the client try only in the order as provided. To achieve this you need to set this configuration: +The global serializer is identical to custom serializers from the implementation perspective. The global serializer is registered as a fallback serializer to handle all other objects if a serializer cannot be located for them. -``` -config.setProperty("hazelcast.client.shuffle.member.list", "false") -``` +By default, global serializer is used if the object is not `IdentifiedDataSerializable` or `Portable` or there is no custom serializer for it. -Client shuffles the given member list to prevent all clients to connect to the same node when this property is set to true. When it is set to false, the client tries to connect to the nodes in the given order. We force the client to not shuffle and try connect in the provided order the addresses are added. +**Use cases** -# Listener Events -When a listener is added then the events are being delivered to the listener as they are received. The events are delivered to your listener implementation via event threads. The number of these event threads can be configured using this property: +- Third party serialization frameworks can be integrated using the global serializer. +- For your custom objects, you can implement a single serializer to handle all of them. + +A sample global serializer that integrates with a third party serializer is shown below. + +```C++ +class GlobalSerializer : public serialization::StreamSerializer { +public: + virtual int32_t getHazelcastTypeId() const { + return 20; + } + + virtual void write(serialization::ObjectDataOutput &out, const void *object) { + // out.write(MyFavoriteSerializer.serialize(object)) + } + + virtual void *read(serialization::ObjectDataInput &in) { + // return MyFavoriteSerializer.deserialize(in); + return NULL; + } +}; ``` -config.setProperty("hazelcast.client.event.thread.count", "7"); + +You should register the global serializer in the configuration. + +**Programmatic Configuration:** + +```C++ + ClientConfig clientConfig; + clientConfig.getSerializationConfig().setGlobalSerializer( + boost::shared_ptr(new GlobalSerializer())); ``` -By default, the event thread count is 5. You may set the number of event threads for your application needs. For most of the cases, you do not need to change this value. +# 5. Setting Up Client Network -# Logger Configuration -By default, the Hazelcast logger prints out the INFO level and above logs to the standard output. The following log levels exist: -- FINEST (DEBUG) -- INFO -- WARNING -- SEVERE (FATAL) +All network related configuration of Hazelcast C++ client is performed via the `ClientNetworkConfig` when using programmatic configuration. Let’s first give an example. -The order is in increasing order. Hence, for INFO level configuration the INFO, WARNING and SEVERE logs are written. +### Programmatic Client Network Configuration -In some applications you may want to use your custom logging statements, you may want to direct the logs to a file instead of standard output, and enable/disable certain log levels. This can be done using the LoggerConfig in the ClientConfig object which is used to configure the client. Each client may have a separate configuration. The customized options needs to be configured using a configuration file. E.g.: +Here is an example of configuring network for C++ Client programmatically. +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().addAddress(Address("10.1.1.21", 5701)); + clientConfig.getNetworkConfig().addAddress(Address("10.1.1.22", 5703)); + clientConfig.getNetworkConfig().setSmartRouting(true); + clientConfig.setRedoOperation(true); + clientConfig.getNetworkConfig().setConnectionTimeout(6000); + clientConfig.getNetworkConfig().setConnectionAttemptPeriod(5000); + clientConfig.getNetworkConfig().setConnectionAttemptLimit(5); ``` -clientConfig.getLoggerConfig().setConfigurationFileName("logger-config.txt"); -``` -The file name is relative path to the application working directory or should be an absolute path. The configuration file will use the format as supported by the configured logger type. Currently, only the easylogging++ (https://github.com/muflihun/easyloggingpp/tree/v8.91) logger is supported, hence the configuration should be done in accordance with the easylogging++ configuration: https://github.com/muflihun/easyloggingpp/tree/v8.91#configuration-file -If you provide a non-existent or invalid logger configuration file, the library will fail fast by throwing exception::IllegalStateException with the cause of the problem. +## 5.1. Providing the Member Addresses -**Important Note:** If you configured the logger configuration file, then the ClientConfig::setLogLevel will not be effective since the levels will be controlled from the configuration file. +Address list is the initial list of cluster addresses to which the client will connect. The client uses this +list to find an alive member. Although it may be enough to give only one address of a member in the cluster +(since all members communicate with each other), it is recommended that you give the addresses for all the members. -# Raw Pointer API +**Programmatic:** -When using C++ client you can have the ownership of raw pointers for the objects you create and return. This allows you to keep the objects in your library/application without any need for copy. +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().addAddress(Address("10.1.1.21", 5701)); + clientConfig.getNetworkConfig().addAddress(Address("10.1.1.22", 5703)); +``` -For each container you can use the adapter classes, whose names start with `RawPointer`, to access the raw pointers of the created objects. These adapter classes are found in `hazelcast::client::adaptor` namespace and listed below: +The provided list is shuffled and tried in random order. If no address is provided `127.0.0.1:5701` is tried by default. -- `RawPointerList` -- `RawPointerQueue` -- `RawPointerTransactionalMultiMap` -- `RawPointerMap` -- `RawPointerSet` -- `RawPointerTransactionalQueue` -- `RawPointerMultiMap` -- `RawPointerTransactionalMap` +## 5.2. Setting Smart Routing -These are adapter classes and they do not create new structures. You just provide the legacy containers as parameters and then you can work with these raw capability containers freely. An example usage of `RawPointerMap` is shown below: +Smart routing defines whether the client mode is smart or unisocket. See [C++ Client Operation Modes section](#cpp-client-operation-modes) +for the description of smart and unisocket modes. + +The following is example configuration. +**Programmatic:** + +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().setSmartRouting(true); ``` -hazelcast::client::IMap m = hz.getMap("map"); -hazelcast::client::adaptor::RawPointerMap map(m); -map.put("1", "Tokyo"); -map.put("2", "Paris"); -map.put("3", "New York"); -std::cout << "Finished loading map" << std::endl; -std::auto_ptr > vals = map.values(); -std::auto_ptr > entries = map.entrySet(); +Its default value is `true` (smart client mode). -std::cout << "There are " << vals->size() << " values in the map" << std::endl; -std::cout << "There are " << entries->size() << " entries in the map" << std::endl; +## 5.3. Enabling Redo Operation -for (size_t i = 0; i < entries->size(); ++i) { - const std::string * key = entries->getKey(i); - if ((std::string *) NULL == key) { - std::cout << "The key at index " << i << " is NULL" << std::endl; - } else { - std::auto_ptr val = entries->releaseValue(i); - std::cout << "(Key, Value) for index " << i << " is: (" << *key << ", " << - (val.get() == NULL ? "NULL" : *val) << ")" << std::endl; - } - } - std::cout << "Finished" << std::endl; -``` - -Raw pointer API uses the DataArray and EntryArray interfaces which allow late deserialization of objects. The entry in the returned array is deserialized only when it is accessed. Please see the example code below: - -``` -// No deserialization here -std::auto_ptr > vals = map.values(); - -// deserializes the item at index 0 assuming that there are at least 1 items in the array -const std::string *value = vals->get(0); - -// no deserialization here since it was already de-serialized -value = vals->get(0); - -// no deserialization here since it was already de-serialized -value = (*vals)[0]; +It enables/disables redo-able operations. While sending the requests to related members, operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redoOperation` to `true`. -// releases the value so that you can keep this object pointer in your application at some other place -std::auto_ptr releasedValue = vals->release(0); - -// deserialization occurs again since the value was released already -value = vals->get(0); -``` - -Using raw pointer based API may improve performance if you are using the API to return multiple values such as values, keySet, and entrySet. In this case, cost of deserialization is delayed until the item is actually accessed. +**Programmatic:** -# Mixed Object Types Supporting HazelcastClient -Sometimes, you may need to use Hazelcast data structures with objects of different types. For example, you may want to put `int`, `string`, `IdentifiedDataSerializable`, etc. objects into the same Hazelcast `IMap` data structure. You can do this by using the mixed type adopted `HazelcastClient`. You can adopt the client in this way: -``` - ClientConfig config; - HazelcastClient client(config); - mixedtype::HazelcastClient &hazelcastClient = client.toMixedType(); -``` -The `mixedtype::HazelcastClient` interface is designed to provide you the data structures which allows you to work with any object types in a mixed manner. For example, the interface allows you to provide the key and value type differently for each map.put call. An example usage is shown below: +```C++ + ClientConfig clientConfig; + clientConfig.setRedoOperation(true); ``` - mixedtype::IMap map = hazelcastClient.getMap("MyMap"); - map.put(3, 5); - map.put("string key1", "MyStringValue"); - map.put("string key1", myCustomInstance); - TypedData result = map.get(3); -``` - -As you can see in the above code snippet, we are putting `int`, `string` and MyCustomObject to the same map. Both the key and value can be of different type for each map.put call. +Its default value is `false` (disabled). -If you want to use mixed type map with near cache, then you should use the MixedNearCacheConfig class and add config to the ClientConfig using the addMixedNearCacheConfig method. See the below example: -``` - boost::shared_ptr nearCacheConfig(new mixedtype::config::MixedNearCacheConfig("MixedMapTestMap")); - clientConfig.addMixedNearCacheConfig(nearCacheConfig); -``` -Mixed type support for near cache only exists when the in-memory format is BINARY. The OBJECT in-memory format is not supported for MixedNearCacheConfig; +## 5.4. Setting Connection Timeout -The mixed type API uses the TypedData class at the user interface. +Connection timeout is the timeout value in milliseconds for members to accept client connection requests. +If server does not respond within the timeout, the client will retry to connect as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. + +The following is an example configuration. -## TypedData API -The TypedData class is a wrapper class for the serialized binary data. It presents the following user APIs: -``` - /** - * - * @return The type of the underlying object for this binary. - */ - const serialization::pimpl::ObjectType getType() const; +**Programmatic:** - /** - * Deserializes the underlying binary data and produces the object of type T. - * - * CAUTION: The type that you provide should be compatible with what object type is returned with - * the getType API, otherwise you will either get an exception of incorrectly try deserialize the binary data. - * - * @tparam T The type to be used for deserialization - * @return The object instance of type T. - */ - template - std::auto_ptr get() const; +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().setConnectionTimeout(6000); ``` -TypedData does late deserialization of the data only when the get method is called. - -This TypedData allows you to retrieve the data type of the underlying binary to be used when being deserialized. This class represents the type of a Hazelcast serializable object. The fields can take the following values: -1. Primitive types: `factoryId=-1`, `classId=-1`, `typeId` is the type ID for that primitive as listed in -`SerializationConstants` -2. Array of primitives: `factoryId=-1`, `classId=-1`, `typeId` is the type ID for that array as listed in -`SerializationConstants` -3. IdentifiedDataSerializable: `factoryId`, `classId` and `typeId` are non-negative values as registered by -the `DataSerializableFactory`. -4. Portable: `factoryId`, `classId` and `typeId` are non-negative values as registered by the `PortableFactory`. -5. Custom serialized objects: `factoryId=-1`, `classId=-1`, `typeId` is the non-negative type ID as -registered for the custom object. - -# Query API +Its default value is `5000` milliseconds. -C++ client API allows you to query map values, keys and entries using predicates. It also allows you to use Hazelcast Map's `executeOnKey` and `executeOnEntries` methods with predicates. You can run a processor on a subset of entries with these methods. +## 5.5. Setting Connection Attempt Limit -You can add entry listeners with predicates using C++ client API. By this way, only the events for the selected subset of entries matching the query criteria are received by your listener. +While the client is trying to connect initially to one of the members in the `ClientNetworkConfig::getAddresses()` (and the cluster member list addresses if the cluster member list were loaded at least once during a previous connection to the cluster), that member might not be available at that moment. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. This is also the case when the previously established connection between the client and that member goes down. -C++ client API provides a rich set of built-in predicates as supported by the Java client. You can create your own predicates by implementing `Predicate` interfaces both at the C++ client side and server side. Built-in predicates are listed below: +The following is an example configuration. -- `AndPredicate` -- `EqualPredicate` -- `ILikePredicate` -- `LikePredicate` -- `OrPredicate` -- `TruePredicate` -- `BetweenPredicate` -- `FalsePredicate` -- `InPredicate` -- `NotEqualPredicate` -- `PagingPredicate` -- `RegexPredicate` -- `GreaterLessPredicate` -- `InstanceOfPredicate` -- `NotPredicate` -- `SqlPredicate` +**Programmatic:** -An example query is shown in the following snippet: - -``` -IMap intMap = client.getMap("testValuesWithPredicateIntMap"); -adaptor::RawPointerMap rawMap(intMap); -// ... -// BetweenPredicate -// 5 <= key <= 10 -valuesArray = rawMap.values(query::BetweenPredicate(query::QueryConstants::getKeyAttributeName(), 5, 10)); +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().setConnectionAttemptLimit(5); ``` -This example query returns the values between 5 and 10, inclusive. You can find the examples of each built-in predicate in `distributed-map/query` folder of `examples`. +Its default value is `2`. +## 5.6. Setting Connection Attempt Period -*NOTE: API that returns pointers may return null pointers for null values. You need to check for null values.* - -# Ringbuffer +Connection timeout period is the duration in milliseconds between the connection attempts defined by `ClientNetworkConfig::getConnectionAttemptLimit()`. + +The following is an example configuration. -You can benefit from Hazelcast `Ringbuffer` using the C++ client library. You can start by obtaining the `Ringbuffer` using the `HazelcastClient` as usual, as shown below: +**Programmatic:** +```C++ + ClientConfig clientConfig; + clientConfig.getNetworkConfig().setConnectionAttemptPeriod(5000); ``` -boost::shared_ptr > rb = client.getRingbuffer("myringbuffer"); -``` - -`Ringbuffer` interface allows you to add a new item to the `Ringbuffer` or read an entry at a sequence number. -You can query the `Ringbuffer` capacity which is configured at the server side. - -# Reliable Topic - -You can use Reliable Topic if you do not want to miss any messages during failures. Hazelcast Reliable Topic provides a very similar interface to Topic structure but it has several configuration options. - -Reliable Topic implementation depends on the `Ringbuffer` data structure. The data is kept in the Hazelcast cluster's `Ringbuffer` structure. These `Ringbuffer` structures' names start with "\_hz\_rb\_". - -Reliable Topic also supports batch reads from the `Ringbuffer`. You can optimize the internal working of listener using the method `ReliableTopicConfig::setReadBatchSize`. +Its default value is `3000` milliseconds. -# Map Near Cache -Map can be configured to work with near cache enabled. Near cache allows local caching of entries fetched from the cluster at the client side. The local cache is updated when the client does a get request for a map entry. The locally cached entries are updated automatically as any change occurs in the map data in the cluster. Special internal event listeners are utilized for this purpose. +## 5.7. Enabling Client TLS/SSL -If you are doing mostly reads from the map rather than map updates, then enabling near cache allows you to get the entries faster since it will return the locally cached entry which will eliminate any network interaction. But if your application is doing mostly writes (update, remove, etc.) from map, then you may not gain much by enabling the near cache since the updates will cause network traffic. +You can use TLS/SSL to secure the connection between the clients and members. If you want TLS/SSL enabled +for the client-cluster connection, you should set an SSL configuration. Please see [TLS/SSL section](#1-tlsssl). -You can enable the near cache for a map by adding a near cache configuration for its name. For example: +As explained in the [TLS/SSL section](#1-tlsssl), Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast C++ clients have certificate authorities used to define which members they can trust. -``` -boost::shared_ptr > nearCacheConfig( - new config::NearCacheConfig("myMap")); -clientConfig..addNearCacheConfig(nearCacheConfig); -``` +# 6. Securing Client Connection -As for all configuration options, this near cache config should be performed before the client instance is obtained and the client config should not be altered after the client is instantiated. +This chapter describes the security features of Hazelcast C++ client. These include using TLS/SSL for connections between members and between clients and members. These security features require **Hazelcast IMDG Enterprise** edition. -The NearCacheConfig class has a number of options including the EvictionConfig which determines the eviction strategy used. The options work exactly the same way as the Java client options. More information on the options can be found at http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#creating-near-cache-for-map and http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#map-eviction. +### 6.1. TLS/SSL -Examples are also provided for some options at the near cache folder under examples (also can be see at https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples). +One of the offers of Hazelcast is the TLS/SSL protocol which you can use to establish an encrypted communication across your cluster with key stores and trust stores. -# PN Counter -A Conflict-free Replicated Data Type (CRDT) is a distributed data structure that achieves high availability by relaxing consistency constraints. There may be several replicas for the same data and these replicas can be modified concurrently without coordination. This means that you may achieve high throughput and low latency when updating a CRDT data structure. (see http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter for details) +- A Java `keyStore` is a file that includes a private key and a public certificate. -Hazelcast offers a lightweight CRDT PN Counter (Positive-Negative Counter) implementation where each Hazelcast instance can increment and decrement the counter value and these updates are propagated to all replicas. Simple usage example is: -``` - boost::shared_ptr pnCounter = hz.getPNCounter("pncounterexample"); - std::cout << "Counter started with value:" << pnCounter->get() << std::endl; - std::cout << "Counter new value after adding is: " << pnCounter->addAndGet(5) << std::endl; -``` +- A Java `trustStore` is a file that includes a list of certificates trusted by your application which is named certificate authority. -# Flake Id Generator -Hazelcast Flake ID Generator is used to generate cluster-wide unique identifiers. Generated identifiers are int64_t primitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to INT64_MAX. (See http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator for details of the generated ids.) +You should set `keyStore` and `trustStore` before starting the members. See the next section how to set `keyStore` and `trustStore` on the server side. -You can use the id generator as in this example: -``` -hazelcast::client::FlakeIdGenerator generator = hz.getFlakeIdGenerator("flakeIdGenerator"); -int64_t newId = generator.newId(); -``` +#### 6.1.1. TLS/SSL for Hazelcast Members -The IDs contain timestamp component and a node ID component, which is assigned when the member joins the cluster. This allows the IDs to be ordered and unique without any coordination between the members, which makes the generator safe even in split-brain scenarios. -# TLS Feature +Hazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members). -*Note: This is a Hazelcast IMDG Enterprise feature. You need to provide compile flag -DHZ_BUILD_WITH_SSL when compiling since TLS feature depends on openssl library. +#### 6.1.2. TLS/SSL for Hazelcast C++ Clients +You need to provide compile flag -DHZ_BUILD_WITH_SSL when compiling since TLS feature depends on openssl library. You need the openssl development library installed in your development environment. You need to enable the SSL config in client network config. Furthermore, you should specify a correct path to the CA verification file for the trusted server. This path can be relative to the executable working directory. You can set the protocol type. The default protocol is TLSv1.2. The SSL config also lets you set the cipher suite to be used. -You can encrypt all the communication between the client and the cluster using this feature. It requires the openssl development library installed in your development environment. You need to enable the SSL config in client network config. Furthermore, you should specify a correct path to the CA verification file for the trusted server. This path can be relative to the executable working directory. You can set the protocol type. The default protocol is TLSv1.2. The SSL config also lets you set the cipher suite to be used. +The following is an example configuration. -Example usage: -``` +```C++ config.getNetworkConfig().getSSLConfig().setEnabled(true).addVerifyFile(getCAFilePath()); //Cipher suite is set using a string which is defined by OpenSSL standard at this page: https://www.openssl.org/docs/man1.0.2/apps/ciphers.html An example usage: @@ -798,486 +1068,285 @@ sslv2, sslv3, tlsv1, sslv23, tlsv11, tlsv12 The default value for the protocol if not set is tlsv12. The SSLConfig.setEnabled should be called explicitly to enable the SSL. The path of the certificate should be correctly provided. -# AWS Cloud Discovery +# 7. Using C++ Client with Hazelcast IMDG -Note: You need to provide compile flag -DHZ_BUILD_WITH_SSL when compiling since AWS depends on openssl library. +## 7.1. C++ Client API Overview -The C++ client can discover the exiting Hazelcast servers in the Amazon AWS environment. The client queries the Amazon AWS environment using the "describe-instances (http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html)" query of AWS. The client finds only the up and running instances and filters them based on the filter config provided at the ClientAwsConfig configuration. - -An example configuration: -```cpp -clientConfig.getNetworkConfig().getAwsConfig().setEnabled(true). - setAccessKey(getenv("AWS_ACCESS_KEY_ID")).setSecretKey(getenv("AWS_SECRET_ACCESS_KEY")). - setTagKey("aws-test-tag").setTagValue("aws-tag-value-1").setSecurityGroupName("MySecureGroup").setRegion("us-east-1"); -``` -You need to enable the discovery by calling setEnabled(true). You can set your access key and secret in the config as shown in this example. You can filter the instances by setting which tags they have or by the security group setting. You can set the region for which the instances will be retrieved from, the default is to use us-east-1. - -The C++ client works the same way as the Java client as described at https://github.com/hazelcast/hazelcast-aws/blob/master/README.md. - -## IAM Role Usage -You can set the IAM role using the setIamRole method. If the access key is configured and no IAM role is set, the client will use the configured access key and secret. Otherwise, the client should be inside the AWS network and it will try to retrieve the access key for the configured IAM role. +Most of the C++ API are synchronous methods. The failures are communicated via exceptions. All exceptions are derived from hazelcast::client::exception::IException base method. There are also asynchronous versions of some of the API. The asynchronous API uses hazelcast::client::Future future object. It works similar to the std::future. -If IAM role is set in the config the key and secret is obtained using the query http://169.254.169.254/latest/meta-data/iam/security-credentials/. Example usage: -```cpp - clientConfig.getNetworkConfig().getAwsConfig().setEnabled(true).setTagKey("aws-test-tag"). - setTagValue("aws-tag-value-1").setIamRole("MyInstanceRole").setInsideAws(true); -``` -If no IAM role or access key is provided, the client will retrieve the IAM role using the HTTP query http://169.254.169.254/latest/meta-data/iam/security-credentials/ and then it will retrieve the key and secret using the HTTP query http://169.254.169.254/latest/meta-data/iam/security-credentials/. -```cpp - clientConfig.getNetworkConfig().getAwsConfig().setEnabled(true).setTagKey("aws-test-tag"). - setTagValue("aws-tag-value-1").setInsideAws(true); -``` - -The details of IAM role usage for applications are described at http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html. The IAM role access key retrieval requires that the instance has a valid instance profile association for the IAM role and the IAM role should have the AWS DescribeInstances permission. - -### Policy for IAM User +If you are ready to go, let's start to use Hazelcast C++ client! -If you are using IAM role configuration (`iam-role`) for EC2 discovery, you need to give the following policy to your IAM user at the least: +The first step is configuration. You can configure the C++ client programmatically. -`"ec2:DescribeInstances"` +```C++ + ClientConfig clientConfig; + clientConfig.getGroupConfig().setName("dev"); + clientConfig.getNetworkConfig().addAddress(Address("'10.90.0.1", 5701)).addAddress(Address("'10.90.0.2", 5702)); ``` -{ - "Version": "XXXXXXXX", - "Statement": [ - { - "Sid": "XXXXXXXX", - "Action": [ - "ec2:DescribeInstances" - ], - "Effect": "Allow", - "Resource": "*" - } - ] -} -``` -# PartitionAware -Hazelcast client distributes the data that throughout the cluster. The hash code of the serialized key is calculated and the partition ID is found from the hash code. Based on the partition ID - member mapping table, the request is sent to the corresponding member in the cluster. You may sometimes want certain related keys and values to be on the same member at the cluster, so that you can use/change them using local operations on the member. `PartitionAware` interface allows you to put data on a specific partition, hence on a certain member. This allows data affinity for the related data items, e.g., customer and orders. +The second step is initializing the `HazelcastClient` to be connected to the cluster. -A more detailed explanation of the "Data Affinity" can be found at [Hazelcast Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#data-affinity). +```C++ + HazelcastClient client(clientConfig); + // some operation +}); +``` -Please see examples/distributed-map/partitionaware folder for an example usage of `PartitionAware` interface. - -# Code Examples +**This client object is your gateway to access all Hazelcast distributed objects.** -You can try the following C++ client code examples. You need to have a Hazelcast client member running for the code examples to work. +Let’s create a map and populate it with some data. -## Map +```C++ + // Get the Distributed Map from Cluster. + IMap map = client.getMap("my-distributed-map"); + //Standard Put and Get. + map.put("key", "value"); + map.get("key"); + //Concurrent Map methods, optimistic updating + map.putIfAbsent("somekey", "somevalue"); + map.replace("key", "value", "newvalue"); + ``` -```cpp -#include -#include +As a final step, if you are done with your client, you can shut it down as shown below. This will release all the used resources and will close connections to the cluster. -using namespace hazelcast::client; +```C++ + // Shutdown this Hazelcast Client + client.shutdown(); +``` -int main() { - ClientConfig clientConfig; - Address address( "localhost", 5701 ); - clientConfig.addAddress( address ); +The client object destructor also shuts down the client upon destruction if you do not explicitly call the shutdown method. - HazelcastClient hazelcastClient( clientConfig ); +## 7.2. C++ Client Operation Modes - IMap myMap = hazelcastClient.getMap( "myIntMap" ); - myMap.put( 1,3 ); - boost::shared_ptr value = myMap.get( 1 ); - if( value.get() != NULL ) { - //process the item - } +The client has two operation modes because of the distributed nature of the data and cluster. - return 0; -} -``` +### 7.2.1. Smart Client -## Map With Near Cache Enabled +In the smart mode, clients connect to each cluster member. Since each data partition uses the well known and consistent hashing algorithm, each client can send an operation to the relevant cluster member, which increases the overall throughput and efficiency. Smart mode is the default mode. -```cpp -#include -#include -using namespace hazelcast::client; +### 7.2.2. Unisocket Client -int main() { - ClientConfig clientConfig; - Address address( "localhost", 5701 ); - clientConfig.addAddress( address ); - - boost::shared_ptr > nearCacheConfig( - new config::NearCacheConfig(mapName, config::OBJECT)); - nearCacheConfig->setInvalidateOnChange(false); - nearCacheConfig->getEvictionConfig()->setEvictionPolicy(config::NONE) - .setMaximumSizePolicy(config::EvictionConfig::ENTRY_COUNT); - config.addNearCacheConfig(nearCacheConfig); - - HazelcastClient hazelcastClient( clientConfig ); - - IMap myMap = hazelcastClient.getMap( "myIntMap" ); - myMap.put( 1,3 ); - boost::shared_ptr value = myMap.get( 1 ); - if( value.get() != NULL ) { - //process the item - } - - hazelcast::client::monitor::NearCacheStats *stats = map.getLocalMapStats().getNearCacheStats(); - - printf("The Near Cache contains %lld entries.\n", stats->getOwnedEntryCount()); - - return 0; -} -``` +For some cases, the clients can be required to connect to a single member instead of each member in the cluster. Firewalls, security, or some custom networking issues can be the reason for these cases. -## Queue +In the unisocket client mode, the client will only connect to one of the configured addresses. This single member will behave as a gateway to the other members. For any operation requested from the client, it will redirect the request to the relevant member and return the response back to the client returned from this member. -```cpp -#include -#include -#include +## 7.3. Handling Failures -using namespace hazelcast::client; +There are two main failure cases you should be aware of, and configurations you can perform to achieve proper behavior. -int main() { - ClientConfig clientConfig; - Address address( "localhost", 5701 ); - clientConfig.addAddress( address ); - - HazelcastClient hazelcastClient( clientConfig ); - - IQueue queue = hazelcastClient.getQueue( "q" ); - queue.offer( "sample" ); - boost::shared_ptr value = queue.poll(); - if( value.get() != NULL ) { - //process the item - } - return 0; -} -``` +### 7.3.1. Handling Client Connection Failure -## Entry Listener +While the client is trying to connect initially to one of the members, some members might be not available. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. -```cpp -#include "hazelcast/client/ClientConfig.h" -#include "hazelcast/client/EntryEvent.h" -#include "hazelcast/client/IMap.h" -#include "hazelcast/client/Address.h" -#include "hazelcast/client/HazelcastClient.h" -#include -#include +You can configure `connectionAttemptLimit` for the number of times you want the client to retry connecting. Please see [Setting Connection Attempt Limit](#5-setting-connection-attempt-limit). -using namespace hazelcast::client; +The client executes each operation through the already established connection to the cluster. If this connection(s) disconnects or drops, the client will try to reconnect as configured. -class SampleEntryListener { - public: +### 7.3.2. Handling Retry-able Operation Failure - void entryAdded( EntryEvent &event ) { - std::cout << "entry added " << event.getKey() << " " - << event.getValue() << std::endl; - }; +While sending the requests to related members, operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redoOperation` to `true`. Please see [Enabling Redo Operation](#3-enabling-redo-operation). - void entryRemoved( EntryEvent &event ) { - std::cout << "entry added " << event.getKey() << " " - << event.getValue() << std::endl; - } +You can set a timeout for retrying the operations sent to a member. This can be provided by using the property `hazelcast.client.invocation.timeout.seconds` (`ClientProperties::INVOCATION_TIMEOUT_SECONDS`) using `ClientConfig::setProperty()` API using the property as the key. The default value for this property is 120 seconds. The client will retry an operation within this given period, of course, if it is a read-only operation or you enabled the `redoOperation` as stated in the above paragraph. This timeout value is important when there is a failure resulted by either of the following causes: - void entryUpdated( EntryEvent &event ) { - std::cout << "entry added " << event.getKey() << " " - << event.getValue() << std::endl; - } +- Member throws an exception. - void entryEvicted( EntryEvent &event ) { - std::cout << "entry added " << event.getKey() << " " - << event.getValue() << std::endl; - } -}; +- Connection between the client and member is closed. +- Client’s heartbeat requests are timed out. -int main( int argc, char **argv ) { - ClientConfig clientConfig; - Address address( "localhost", 5701 ); - clientConfig.addAddress( address ); - - HazelcastClient hazelcastClient( clientConfig ); - - IMap myMap = hazelcastClient - .getMap( "myIntMap" ); - SampleEntryListener * listener = new SampleEntryListener(); - - std::string id = myMap.addEntryListener( *listener, true ); - // Prints entryAdded - myMap.put( "key1", "value1" ); - // Prints updated - myMap.put( "key1", "value2" ); - // Prints entryRemoved - myMap.remove( "key1" ); - // Prints entryEvicted after 1 second - myMap.put( "key2", "value2", 1000 ); - - // WARNING: deleting listener before removing it from Hazelcast leads to crashes. - myMap.removeEntryListener( id ); - - // listen using predicates - // only listen the events for entries which has the value that matches the - // string "%VALue%1%", i.e. any string containing the text value1 case insensitive - id = myMap.addEntryListener(*listener, query::ILikePredicate( - query::QueryConstants::getValueAttributeName(), "%VALue%1%"), true); - - // this will generate an event - myMap.put("key1", "my__value1_new" ); - - sleep(1); - - myMap.removeEntryListener( id ); - - // Delete listener after removing it from Hazelcast. - delete listener; - return 0; -}; -``` +When a connection problem occurs, an operation is retried if it is certain that it has not run on the member yet or if it is idempotent such as a read-only operation, i.e., retrying does not have a side effect. If it is not certain whether the operation has run on the member, then the non-idempotent operations are not retried. However, as explained in the first paragraph of this section, you can force all client operations to be retried (`redoOperation`) when there is a connection failure between the client and member. But in this case, you should know that some operations may run multiple times causing conflicts. For example, assume that your client sent a `queue.offer` operation to the member, and then the connection is lost. Since there will be no response for this operation, you will not now whether it has run on the member or not. If you enabled `redoOperation`, it means this operation may run again, which may cause two instances of the same object in the queue. -## Serialization +When invocation is being retried, the client may wait some time before it retries again. This retry wait time is configurable with this configuration property:`hazelcast.client.invocation.retry.pause.millis` The default retry wait time is 1 second. -Assume that you have the following two classes in Java and you want to use them with a C++ client. +## 7.3.3 Client Backpressure +Sometimes, e.g., when your servers are overloaded, you may want to slow down the client operations to the cluster. Then the client can be configured to wait until number of outstanding invocations whose reponses are not received to become less than a certain number. This is called "Client Backpressure". By default, the backpressure is disabled. There are a few properties which control the backpressure. These client configuration properties are: -```java -class Foo implements Serializable { - private int age; - private String name; -} +- "hazelcast.client.max.concurrent.invocations" : The maximum number of concurrent invocations allowed. To prevent the system from overloading, you can apply a constraint on the number of concurrent invocations. If the maximum number of concurrent invocations has been exceeded and a new invocation comes in, then Hazelcast will throw HazelcastOverloadException. By default this property is configured as INT32_MAX. +- "hazelcast.client.invocation.backoff.timeout.millis": Controls the maximum timeout in millis to wait for an invocation space to be available. If an invocation can't be made because there are too many pending invocations, then an exponential backoff is done to give the system time to deal with the backlog of invocations. This property controls how long an invocation is allowed to wait before getting a HazelcastOverloadException. When set to -1 then HazelcastOverloadException is thrown immediately without any waiting. This is the default value. + +## 7.3.4 Client Connection Strategy +Hazelcast client-cluster connection and reconnection strategy can be configured. Sometimes, you may not want your application to wait for the client to connect to the cluster, you may just want to get the client and let the client connect in the background. This is configured by: -class Bar implements Serializable { - private float x; - private float y; -} +``` +ClientConfig::getConnectionStrategyConfig().setAsyncStart(bool); ``` -**First**, let them implement `Portable` or `IdentifiedDataSerializable` as shown below. +When this config is set true, the client creation won't wait to connect to cluster. The client instance will throw exception for any request, until it connects to cluster and become ready. +If it is set to false (the default case), HazelcastClient(const ClientConfig) will block until a cluster connection is established and it's ready to use client instance. -```java -class Foo implements Portable { - private int age; - private String name; - - public int getFactoryId() { - // a positive id that you choose - return 123; - } - - public int getClassId() { - // a positive id that you choose - return 2; - } - - public void writePortable( PortableWriter writer ) throws IOException { - writer.writeUTF( "n", name ); - writer.writeInt( "a", age ); - } - - public void readPortable( PortableReader reader ) throws IOException { - name = reader.readUTF( "n" ); - age = reader.readInt( "a" ); - } -} +## 7.3.5 Configure Client Reconnect Strategy +You can configure how the client should act when the client disconnects from the cluster for any reason. This is configured using the following configuration: -class Bar implements IdentifiedDataSerializable { - private float x; - private float y; - - public int getFactoryId() { - // a positive id that you choose - return 4; - } - - public int getId() { - // a positive id that you choose - return 5; - } - - public void writeData( ObjectDataOutput out ) throws IOException { - out.writeFloat( x ); - out.writeFloat( y ); - } - - public void readData( ObjectDataInput in ) throws IOException { - x = in.readFloat(); - y = in.readFloat(); - } -} ``` +ClientConfig::getConnectionStrategyConfig().setReconnectMode(const hazelcast::client::config::ClientConnectionStrategyConfig::ReconnectMode &); +``` + +Possible values for the ReconnectMode are: -**Then**, implement the corresponding classes in C++ with same factory and class ID as shown below. +- OFF: Prevents reconnect to cluster after a disconnect. +- ON: Reconnect to cluster by blocking invocations. +- ASYNC: Reconnect to cluster without blocking invocations. Invocations will receive HazelcastClientOfflineException. -```cpp -class Foo : public Portable { - public: - int getFactoryId() const { - return 123; - }; +## 7.4. Using Distributed Data Structures - int getClassId() const { - return 2; - }; +Most of the Distributed Data Structures are supported by the C++ client. In this chapter, you will learn how to use these distributed data structures. - void writePortable( serialization::PortableWriter &writer ) const { - writer.writeUTF( "n", name ); - writer.writeInt( "a", age ); - }; +### 7.4.1. Using Map - void readPortable( serialization::PortableReader &reader ) { - name = reader.readUTF( "n" ); - age = reader.readInt( "a" ); - }; +Hazelcast Map (`IMap`) is a distributed map. Through C++ client, you can perform operations like reading and writing from/to a Hazelcast Map with the well known get and put methods. For details refer to [Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#map) in the Hazelcast IMDG Reference Manual. - private: - int age; - std::string name; -}; +A Map usage example is shown below. -class Bar : public IdentifiedDataSerializable { - public: - int getFactoryId() const { - return 4; - }; - - int getClassId() const { - return 2; - }; - - void writeData( serialization::ObjectDataOutput& out ) const { - out.writeFloat(x); - out.writeFloat(y); - }; - - void readData( serialization::ObjectDataInput& in ) { - x = in.readFloat(); - y = in.readFloat(); - }; - - private: - float x; - float y; -}; +```C++ + // Get the Distributed Map from Cluster. + IMap map = hz.getMap("my-distributed-map"); + //Standard Put and Get. + map.put("key", "value"); + map.get("key"); + //Concurrent Map methods, optimistic updating + map.putIfAbsent("somekey", "somevalue"); + map.replace("key", "value", "newvalue"); ``` -Now, you can use the classes `Foo` and `Bar` in distributed structures. For example, you can use as Key or Value of `IMap` or as an Item in `IQueue`. +### 7.4.2. Using MultiMap -## Continuous Query +Hazelcast `MultiMap` is a distributed and specialized map where you can store multiple values under a single key. For details refer to [MultiMap section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#multimap) in the Hazelcast IMDG Reference Manual. -You can register an `EntryListener` for a map that will be triggered only when the specific map entries you choose are affected. This allows you to do more optimal queries. +A MultiMap usage example is shown below. -Let’s start with an example. - -``` -1. intMap = client->getMap<int, int>("IntMap"); -2. -3. MyListener listener; -4. -5. // 5 <=key <= 8 -6. std::string listenerId = intMap.addEntryListener(listener, query::BetweenPredicate<int>( -7. query::QueryConstants::getKeyAttributeName(), 5, 8), true); -8. -9. intMap.put(1, 1); -10. intMap.put(8, 17); -11. intMap.put(5, 12, 1000); // evict after 1 second -12. intMap.remove(1); -13. -14. util::sleep(2); -15. -16. intMap.get(5); // trigger eviction +```C++ + // Get the Distributed MultiMap from Cluster. + MultiMap multiMap = hz.getMultiMap("my-distributed-multimap"); + // Put values in the map against the same key + multiMap.put("my-key", "value1"); + multiMap.put("my-key", "value2"); + multiMap.put("my-key", "value3"); + // Print out all the values for associated with key called "my-key" + std::vector values = multiMap.get("my-key"); + for (std::vector::const_iterator it = values.begin();it != values.end(); ++it) { + std::cout << *it << std::endl; // will print value1, value2 and value3 each per line. + } + // remove specific key/value pair + multiMap.remove("my-key", "value2"); // Shutdown this Hazelcast Client ``` -In this example, we register an `EntryListener` that only retrieves map events for entries with keys between 5 and 8 (inclusive). If an entry within this key range is added/updated/removed/evicted, you will be notified through the registered listener. - -**Line 6:** Creates a built-in `BetweenPredicate` (see [Query API](http://docs.hazelcast.org/docs/latest-dev/manual/html-single/index.html#query-api) and [Query Example](https://github.com/hazelcast/hazelcast-cpp-client/blob/master/examples/distributed-map/query/queryExample.cpp) for all built-in predicates) and an `EntryListener` is added for listening to entry events with keys 5 through 8. The method “`query::QueryConstants::getKeyAttributeName()`” is used to tell that the query is being performed on the key of the entry. Similarly, you can use the method “`query::QueryConstants::getValueAttributeName()`” if you want to query on the value of the entry. +### 7.4.3. Using ReplicatedMap -**Line 9:** Puts an entry for key = 1. This does not match your listener predicate, hence no event is received for this line. +Hazelcast `ReplicatedMap` is a distributed key-value data structure where the data is replicated to all members in the cluster. It provides full replication of entries to all members for high speed access. For details refer to [Replicated Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#replicated-map) in the Hazelcast IMDG Reference Manual. -**Line 10:** Puts an entry for key = 8. This key matches our interested key range, so you receive an event for the entry addition, and the `entryAdded` method of our listener will be called. +A Replicated Map usage example is shown below. -**Line 11:** Adds an entry for key = 5 with an expiry timeout of 1,000 milliseconds. This operation triggers an `entryAdded` event for our listener since the key is in our interested range. - -**Line 12:** Removal of entry with key = 1 does not trigger any event and thus you will not receive any callback to your listener. +```C++ + boost::shared_ptr > replicatedMap = hz.getReplicatedMap("myReplicatedMap"); + replicatedMap->put(1, "Furkan"); + replicatedMap->put(2, "Ahmet"); + std::cout << "Replicated map value for key 2 is " << *replicatedMap->get(2) << std::endl; // Replicated map value for key 2 is Ahmet +``` -**Line 14:** Sets sleep for enough time that the entry with key = 5 will be evicted. +### 7.4.4. Using Queue -**Line 16:** Triggers an eviction for entry with key = 5. This line will cause an `entryEvicted` event to be received by your listener. +Hazelcast Queue(`IQueue`) is a distributed queue which enables all cluster members to interact with it. For details refer to [Queue section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#queue) in the Hazelcast IMDG Reference Manual. -As shown in the above example, by using an `EntryListener` with predicates you can minimize the events to only ones in which you are interested. This is also known as the “Continuous Query” feature. You will keep receiving all events that match your query (which is the predicate that you provide on registration of your listener) while entries are being modified in the map in real time. The filtering is performed at the server side, so this method is a lot faster than receiving all the events and filtering at the client side. +A Queue usage example is shown below. -Please examine all different kinds of built in predicates that we provide in the code examples. You can just grab a predicate or combine them using `AndPredicate` or `OrPredicate`. This provides you the capability to write complex queries. You cannot only query on keys or values, as explained above, but you can also use properties of an object for querying. The object can be the key as well as the value. Please refer to the [Query API section](http://docs.hazelcast.org/docs/latest-dev/manual/html-single/index.html#query-api) of the Hazelcast Reference Manual for details on queries. +```C++ + // Get a Blocking Queue called "my-distributed-queue" + IQueue queue = hz.getQueue("my-distributed-queue"); + // Offer a String into the Distributed Queue + queue.offer("item"); + // Poll the Distributed Queue and return the String + boost::shared_ptr item = queue.poll(); // gets first "item" + //Timed blocking Operations + queue.offer("anotheritem", 500); + boost::shared_ptr anotherItem = queue.poll(5 * 1000); + //Indefinitely blocking Operations + queue.put("yetanotheritem"); + std::cout << *queue.take() << std::endl; // Will print yetanotheritem +``` -Please be careful with the runtime behavior of your listener implementations to ensure that the listener callback methods return very quickly. If you need to perform long running tasks when an entry is changed, please off-load those operations to another thread. +## 7.4.5. Using Set -In addition to this rich set of built-in predicates, you can also write your own custom queries simply by implementing your own predicate (you also have to implement the Java server-side predicate as well to make this work). +Hazelcast Set(`ISet`) is a distributed set which does not allow duplicate elements. For details refer to [Set section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#set) in the Hazelcast IMDG Reference Manual. -## Ringbuffer +A Set usage example is shown below. +```C++ + // Get the Distributed Set from Cluster. + ISet set = hz.getSet("my-distributed-set"); + // Add items to the set with duplicates + set.add("item1"); + set.add("item1"); + set.add("item2"); + set.add("item2"); + set.add("item2"); + set.add("item3"); + // Get the items. Note that there are no duplicates. + std::vector values = set.toArray(); + for (std::vector::const_iterator it=values.begin();it != values.end();++it) { + std::cout << (*it) << std::endl; + } ``` - boost::shared_ptr > rb = client.getRingbuffer("myringbuffer"); - std::cout << "Capacity of the ringbuffer is:" << rb->capacity() << std::endl; +## 7.4.6. Using List - int64_t sequenceNumber = rb->add("First Item"); +Hazelcast List(`IList`) is distributed list which allows duplicate elements and preserves the order of elements. For details refer to [List section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#list) in the Hazelcast IMDG Reference Manual. - std::cout << "Added the first item at sequence " << sequenceNumber << std::endl; +A List usage example is shown below. - rb->add("Second item"); - - std::cout << "There are " << rb->size() << " items in the ring buffer " << std::endl; - - std::auto_ptr val = rb->readOne(sequenceNumber); - - if ((std::string *)NULL != val.get()) { - std::cout << "The item at read at sequence " << sequenceNumber << " is " << *val << std::endl; - } +```C++ + // Get the Distributed List from Cluster. + IList list = hz.getList("my-distributed-list"); + // Add elements to the list + list.add("item1"); + list.add("item2"); - std::cout << "Finished" << std::endl; + // Remove the first element + std::cout << "Removed: " << *list.remove(0); + // There is only one element left + std::cout << "Current size is " << list.size() << std::endl; + // Clear the list + list.clear(); ``` -## Reliable Topic - -### Publisher - -``` -void publishWithDefaultConfig() { - hazelcast::client::ClientConfig config; - hazelcast::client::HazelcastClient client(config); +## 7.4.7. Using Ringbuffer - boost::shared_ptr > topic = client.getReliableTopic("MyReliableTopic"); - std::string message("My first message"); - topic->publish(&message); -} +Hazelcast `Ringbuffer` is a replicated but not partitioned data structure that stores its data in a ring-like structure. You can think of it as a circular array with a given capacity. Each Ringbuffer has a tail and a head. The tail is where the items are added and the head is where the items are overwritten or expired. You can reach each element in a Ringbuffer using a sequence ID, which is mapped to the elements between the head and tail (inclusive) of the Ringbuffer. For details refer to [Ringbuffer section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#ringbuffer) in the Hazelcast IMDG Reference Manual. -void publishWithNonDefaultConfig() { - hazelcast::client::ClientConfig clientConfig; - std::string topicName("MyReliableTopic"); - hazelcast::client::config::ReliableTopicConfig reliableTopicConfig(topicName.c_str()); - reliableTopicConfig.setReadBatchSize(5); - clientConfig.addReliableTopicConfig(reliableTopicConfig); - hazelcast::client::HazelcastClient client(clientConfig); +A Ringbuffer usage example is shown below. - boost::shared_ptr > topic = client.getReliableTopic(topicName); +```C++ + boost::shared_ptr > rb = hz.getRingbuffer("rb"); + // add two items into ring buffer + rb->add(100); + rb->add(200); + // we start from the oldest item. + // if you want to start from the next item, call rb.tailSequence()+1 + int64_t sequence = rb->headSequence(); + std::cout << *rb->readOne(sequence) << std::endl; // + sequence++; + std::cout << *rb->readOne(sequence) << std::endl; +``` - std::string message("My first message"); +## 7.4.8. Using Reliable Topic - topic->publish(&message); -} -``` -### Subscriber +Hazelcast `ReliableTopic` is a distributed topic implementation backed up by the `Ringbuffer` data structure. For details refer to [Reliable Topic section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#reliable-topic) in the Hazelcast IMDG Reference Manual. -To write a subscriber, you need to implement the interface `hazelcast::client::topic::ReliableMessageListener`. +A Reliable Topic usage example is shown below. -``` +```C++ class MyListener : public hazelcast::client::topic::ReliableMessageListener { public: - MyListener() : startSequence(-1), numberOfMessagesReceived(0), lastReceivedSequence(-1) { + MyListener() : startSequence(-1), lastReceivedSequence(-1) { } - MyListener(int64_t sequence) : startSequence(sequence), numberOfMessagesReceived(0), lastReceivedSequence(-1) { + MyListener(int64_t sequence) : startSequence(sequence), lastReceivedSequence(-1) { } virtual ~MyListener() { } virtual void onMessage(std::auto_ptr > message) { - ++numberOfMessagesReceived; const std::string *object = message->getMessageObject(); if (NULL != object) { @@ -1304,113 +1373,1050 @@ public: virtual bool isTerminal(const hazelcast::client::exception::IException &failure) const { return false; } - - int getNumberOfMessagesReceived() { - int value = numberOfMessagesReceived; - return value; - } - + private: int64_t startSequence; - hazelcast::util::AtomicInt numberOfMessagesReceived; int64_t lastReceivedSequence; -}; -``` - -Using this listener, you can subscribe for the topic as shown below: +}; -``` -void listenWithDefaultConfig() { - hazelcast::client::ClientConfig config; - hazelcast::client::HazelcastClient client(config); +int main() { + std::string topicName("MyReliableTopic"); boost::shared_ptr > topic = client.getReliableTopic(topicName); - + MyListener listener; const std::string &listenerId = topic->addMessageListener(listener); std::cout << "Registered the listener with listener id:" << listenerId << std::endl; - while (listener.getNumberOfMessagesReceived() < 1) { - hazelcast::util::sleep(1); - } - + std::string message("My first message"); + topic->publish(&message); + if (topic->removeMessageListener(listenerId)) { std::cout << "Successfully removed the listener " << listenerId << " for topic " << topicName << std::endl; } else { std::cerr << "Failed to remove the listener " << listenerId << " for topic " << topicName << std::endl; } + + return 0; } +``` -void listenWithConfig() { - hazelcast::client::ClientConfig clientConfig; - std::string topicName("MyReliableTopic"); - hazelcast::client::config::ReliableTopicConfig reliableTopicConfig(topicName.c_str()); - reliableTopicConfig.setReadBatchSize(5); - clientConfig.addReliableTopicConfig(reliableTopicConfig); - hazelcast::client::HazelcastClient client(clientConfig); - - boost::shared_ptr > topic = client.getReliableTopic(topicName); +## 7.4.9 Using Lock - MyListener listener; - const std::string &listenerId = topic->addMessageListener(listener); +Hazelcast Lock(`ILock`) is a distributed lock implementation. You can synchronize Hazelcast members and clients using a Lock. For details refer to [Lock section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#lock) in the Hazelcast IMDG Reference Manual. - std::cout << "Registered the listener with listener id:" << listenerId << std::endl; +A Lock usage example is shown below. - while (listener.getNumberOfMessagesReceived() < 1) { - hazelcast::util::sleep(1); +```C++ + // Get a distributed lock called "my-distributed-lock" + ILock lock = hz.getILock("my-distributed-lock"); + // Now create a lock and execute some guarded code. + lock.lock(); + try { + //do something here + } catch (...) { + lock.unlock(); + throw; } - if (topic->removeMessageListener(listenerId)) { - std::cout << "Successfully removed the listener " << listenerId << " for topic " << topicName << std::endl; - } else { - std::cerr << "Failed to remove the listener " << listenerId << " for topic " << topicName << std::endl; +``` + +## 7.4.10 Using Atomic Long + +Hazelcast Atomic Long(`IAtomicLong`) is the distributed long which offers most of the operations such as `get`, `set`, `getAndSet`, `compareAndSet` and `incrementAndGet`. For details refer to [Atomic Long section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#iatomiclong) in the Hazelcast IMDG Reference Manual. + +An Atomic Long usage example is shown below. + +```C++ + hazelcast::client::IAtomicLong counter = hz.getIAtomicLong("counter"); + + for (int i = 0; i < 1000 * 1000; ++i) { + counter.incrementAndGet(); } -} + + std::cout << "Count is:" << counter.get() << std::endl; // Counter is 1000000 ``` -## TLS +## 7.4.11 Using Semaphore + +Hazelcast Semaphore(`ISemaphore`) is a distributed semaphore implementation. For details refer to [Semaphore section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#isemaphore) in the Hazelcast IMDG Reference Manual. + +A Semaphore usage example is shown below. + +```C++ + hazelcast::client::ISemaphore semaphore = hz.getISemaphore("semaphore"); + semaphore.init(10); + semaphore.acquire(5); + std::cout << "Number of available permits: " << semaphore.availablePermits() << std::endl; // Number of available permits: 5 ``` - hazelcast::client::ClientConfig config; - hazelcast::client::Address serverAddress("127.0.0.1", 5701); - config.addAddress(serverAddress); - - config.getNetworkConfig().getSSLConfig(). - setEnabled(true). // Mandatory setting - addVerifyFile("MyCAFile"). // Mandatory setting - setCipherList("HIGH"); // optional setting (values for string are described at - // https://www.openssl.org/docs/man1.0.2/apps/ciphers.html) - - hazelcast::client::HazelcastClient hz(config); - hazelcast::client::IMap map = hz.getMap("MyMap"); - - map.put(1, 100); - map.put(2, 200); +## 7.4.12 Using PN Counter - boost::shared_ptr value = map.get(1); +Hazelcast `PNCounter` (Positive-Negative Counter) is a CRDT positive-negative counter implementation. It is an eventually consistent counter given there is no member failure. For details refer to [PN Counter section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter) in the Hazelcast IMDG Reference Manual. - if (value.get()) { - std::cout << "Value for key 1 is " << *value << std::endl; - } +A PN Counter usage example is shown below. - std::cout << "Finished" << std::endl; +```C++ + boost::shared_ptr pnCounter = hz.getPNCounter("pncounterexample"); - return 0; + std::cout << "Counter started with value:" << pnCounter->get() << std::endl; // Counter started with value:0 + + std::cout << "Counter new value after adding is: " << pnCounter->addAndGet(5) << std::endl; // Counter new value after adding is: 5 + + std::cout << "Counter new value before adding is: " << pnCounter->getAndAdd(2) << std::endl; // Counter new value before adding is: 5 + + std::cout << "Counter new value is: " << pnCounter->get() << std::endl; // Counter new value is: 7 + + std::cout << "Decremented counter by one to: " << pnCounter->decrementAndGet() << std::endl; // Decremented counter by one to: 6 +``` + +## 7.4.13 Using Flake ID Generator + +Hazelcast `FlakeIdGenerator` is used to generate cluster-wide unique identifiers. Generated identifiers are long primitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to `2^63-1 (maximum signed long value)`. For details refer to [FlakeIdGenerator section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator) in the Hazelcast IMDG Reference Manual. + +A Flake ID Generator usage example is shown below. + +```C++ + hazelcast::client::FlakeIdGenerator generator = hz.getFlakeIdGenerator("flakeIdGenerator"); + std::cout << "Id : " << generator.newId() << std::endl; // Id : ``` -# Mail Group -Please join the mail group if you are interested in using or developing Hazelcast. +## 7.5. Distributed Events + + +This chapter explains when various events are fired and describes how you can add event listeners on a Hazelcast C++ client. These events can be categorized as cluster and distributed data structure events. + +### 7.5.1. Cluster Events + +You can add event listeners to a Hazelcast C++ client. You can configure the following listeners to listen to the events on the client side. + +`Membership Listener`: Notifies when a member joins to/leaves the cluster, or when an attribute is changed in a member. + +`Lifecycle Listener`: Notifies when the client is starting, started, shutting down, and shutdown. + +#### 7.5.1.1. Listening for Member Events + +You can add the following types of member events to the `ClusterService`. + +- `memberAdded`: A new member is added to the cluster. +- `memberRemoved`: An existing member leaves the cluster. +- `memberAttributeChanged`: An attribute of a member is changed. Please refer to [Defining Member Attributes](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#defining-member-attributes) section in the Hazelcast IMDG Reference Manual to learn about member attributes. + +You use HazelcastClient::getCluster() object to register for the membership listeners. There two type os listeners, InitialMembershipListener and MembershipListener. The difference is that InitialMembershipListener also gets notified when the client connects to the cluster and retrieves the whole membership list. You need to implement one of these two interfaces and register an instance of the listener to the cluster. + +The following demonstrates both initial and regular membership listener registration. + +```C++ + class MyInitialMemberListener : public hazelcast::client::InitialMembershipListener { + public: + void init(const hazelcast::client::InitialMembershipEvent &event) { + std::vector members = event.getMembers(); + std::cout << "The following are the initial members in the cluster:" << std::endl; + for (std::vector::const_iterator it = members.begin(); it != members.end(); ++it) { + std::cout << it->getAddress() << std::endl; + } + } + + void memberAdded(const hazelcast::client::MembershipEvent &membershipEvent) { + std::cout << "[MyInitialMemberListener::memberAdded] New member joined:" << + membershipEvent.getMember().getAddress() << + std::endl; + } + + void memberRemoved(const hazelcast::client::MembershipEvent &membershipEvent) { + std::cout << "[MyInitialMemberListener::memberRemoved] Member left:" << + membershipEvent.getMember().getAddress() << std::endl; + } + + void memberAttributeChanged(const hazelcast::client::MemberAttributeEvent &memberAttributeEvent) { + std::cout << "[MyInitialMemberListener::memberAttributeChanged] Member attribute:" << + memberAttributeEvent.getKey() + << " changed. Value:" << memberAttributeEvent.getValue() << " for member:" << + memberAttributeEvent.getMember().getAddress() << std::endl; + } + }; + + class MyMemberListener : public hazelcast::client::MembershipListener { + public: + void memberAdded(const hazelcast::client::MembershipEvent &membershipEvent) { + std::cout << "[MyMemberListener::memberAdded] New member joined:" << membershipEvent.getMember().getAddress() << + std::endl; + } + + void memberRemoved(const hazelcast::client::MembershipEvent &membershipEvent) { + std::cout << "[MyMemberListener::memberRemoved] Member left:" << + membershipEvent.getMember().getAddress() << std::endl; + } + + void memberAttributeChanged(const hazelcast::client::MemberAttributeEvent &memberAttributeEvent) { + std::cout << "[MyMemberListener::memberAttributeChanged] Member attribute:" << memberAttributeEvent.getKey() + << " changed. Value:" << memberAttributeEvent.getValue() << " for member:" << + memberAttributeEvent.getMember().getAddress() << std::endl; + } + }; + + int main() { + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hz(config); + + hz.getCluster().addMembershipListener(boost::shared_ptr(new MyMemberListener())); + hz.getCluster().addMembershipListener(boost::shared_ptr(new MyInitialMemberListener())); + ... +``` + +The `memberAttributeChanged` has its own type of event named `MemberAttributeEvent`. When there is an attribute change on the member, this event is fired. + +#### 7.5.1.3. Listening for Lifecycle Events + +The Lifecycle Listener notifies for the following events: +- `starting`: A client is starting. +- `started`: A client has started. +- `shuttingDown`: A client is shutting down. +- `shutdown`: A client’s shutdown has completed. +- `clientConnected`: The client is connected to the cluster. +- `clientConnected`: The client is disconnected from the cluster. + +The following is an example of Lifecycle Listener. + +```C++ +class ConnectedListener : public hazelcast::client::LifecycleListener { +public: + virtual void stateChanged(const hazelcast::client::LifecycleEvent &lifecycleEvent) { + if (lifecycleEvent.getState() == hazelcast::client::LifecycleEvent::CLIENT_CONNECTED) { + std::cout << "Client connected to the cluster" << std::endl; + } else if (lifecycleEvent.getState() == hazelcast::client::LifecycleEvent::CLIENT_DISCONNECTED) { + std::cout << "Client is disconnected from the cluster" << std::endl; + } + } +}; + +int main() { + ConnectedListener listener; + + hazelcast::client::ClientConfig config; + + // Add a lifecycle listener so that we can track when the client is connected/disconnected + config.addListener(&listener); + + hazelcast::client::HazelcastClient hz(config); + + hz.shutdown(); + return 0; +} +``` + +**Output:** + +``` +24/10/2018 13:54:06.392 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] Trying to connect to Address[127.0.0.1:5701] as owner member +24/10/2018 13:54:06.395 INFO: [0x7000069c1000] hz.client_1[dev] [3.10.2-SNAPSHOT] Setting ClientConnection{alive=1, connectionId=1, remoteEndpoint=Address[localhost:5701], lastReadTime=2018-10-24 10:54:06.394000, closedTime=never, connected server version=3.11-SNAPSHOT} as owner with principal uuid: 160e2baf-566b-4cc3-ab25-a1b3e34de320 ownerUuid: 7625eae9-4f8e-4285-82c0-be2f2eee2a50 +24/10/2018 13:54:06.395 INFO: [0x7000069c1000] hz.client_1[dev] [3.10.2-SNAPSHOT] Authenticated with server Address[localhost:5701], server version:3.11-SNAPSHOT Local address: Address[127.0.0.1:56007] +24/10/2018 13:54:06.495 INFO: [0x700006cd3000] hz.client_1[dev] [3.10.2-SNAPSHOT] + +Members [2] { + Member[localhost]:5702 - 44cf4017-5355-4859-9bf5-fd374d7fc69c + Member[localhost]:5701 - 7625eae9-4f8e-4285-82c0-be2f2eee2a50 +} + +24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED +Client connected to the cluster +24/10/2018 13:54:12.269 INFO: [0x7fff97559340] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent SHUTTING_DOWN +24/10/2018 13:54:12.270 INFO: [0x7fff97559340] hz.client_1[dev] [3.10.2-SNAPSHOT] Removed connection to endpoint: Address[localhost:5701], connection: ClientConnection{alive=0, connectionId=1, remoteEndpoint=Address[localhost:5701], lastReadTime=2018-10-24 10:54:06.711000, closedTime=2018-10-24 10:54:12.269000, connected server version=3.11-SNAPSHOT} +Client is disconnected from the cluster +24/10/2018 13:54:12.268 INFO: [0x7fff97559340] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent SHUTDOWN +``` + +### 7.5.2. Distributed Data Structure Events + +You can add event listeners to the Distributed Data Structures. + +#### 7.5.2.1. Listening for Map Events + +You can listen to map-wide or entry-based events by using the functions in the `EntryListener` interface. To listen to these events, you need to implement the relevant `EntryListener` interface. + +- An entry-based event is fired after the operations that affect a specific entry. For example, `IMap::put()`, `IMap::remove()` or `IMap::evict()`. You should use the `EntryListener` type to listen these events. An `EntryEvent` object is passed to the listener function. + +Let’s take a look at the following example. + +```C++ +class MyEntryListener : public hazelcast::client::EntryListener { +public: + virtual void entryAdded(const hazelcast::client::EntryEvent &event) { + std::cout << "Entry added:" << event.getKey() << " --> " << event.getValue() << std::endl; + } + + virtual void entryRemoved(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryUpdated(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryEvicted(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryExpired(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryMerged(const hazelcast::client::EntryEvent &event) { + } + + virtual void mapEvicted(const hazelcast::client::MapEvent &event) { + } + + virtual void mapCleared(const hazelcast::client::MapEvent &event) { + } + +}; + +... +int main() { + hazelcast::client::ClientConfig config; + + hazelcast::client::HazelcastClient hz(config); + + hazelcast::client::IMap map = hz.getMap("EntryListenerExampleMap"); + MyEntryListener entryListener; + map.addEntryListener(entryListener, false); + + map.put(1, "My new entry"); + + ... + + return 0; +} +``` + + +- A map-wide event is fired as a result of a map-wide operation. For example, `IMap::clear()` or `IMap::evictAll()`. You should use the `EntryListener` type to listen these events. A `MapEvent` object is passed to the listener function. + +Let’s take a look at the following example. + +```C++ +class MyEntryListener : public hazelcast::client::EntryListener { +public: + virtual void entryAdded(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryRemoved(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryUpdated(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryEvicted(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryExpired(const hazelcast::client::EntryEvent &event) { + } + + virtual void entryMerged(const hazelcast::client::EntryEvent &event) { + } + + virtual void mapEvicted(const hazelcast::client::MapEvent &event) { + } + + virtual void mapCleared(const hazelcast::client::MapEvent &event) { + std::cout << "Map cleared:" << event.getNumberOfEntriesAffected() << std::endl; // Map Cleared: 3 + } + +}; + +int main() { + hazelcast::client::ClientConfig config; + + hazelcast::client::HazelcastClient hz(config); + + hazelcast::client::IMap map = hz.getMap("EntryListenerExampleMap"); + MyEntryListener entryListener; + map.addEntryListener(entryListener, false); + + map.put(1, "Mali"); + map.put(2, "Ahmet"); + map.put(3, "Furkan"); + map.clear(); + + ... + + return 0; +} +``` + +## 7.6. Distributed Computing + +This chapter explains Hazelcast’s entry processor implementation. + +### 7.6.1. Using EntryProcessor + +Hazelcast supports entry processing. An entry processor is a function that executes your code on a map entry in an atomic way. + +An entry processor is a good option if you perform bulk processing on an `IMap`. Usually you perform a loop of keys-- executing `IMap.get(key)`, mutating the value, and finally putting the entry back in the map using `IMap.put(key,value)`. If you perform this process from a client or from a member where the keys do not exist, you effectively perform two network hops for each update: the first to retrieve the data and the second to update the mutated value. + +If you are doing the process described above, you should consider using entry processors. An entry processor executes a read and updates upon the member where the data resides. This eliminates the costly network hops described above. + +> **NOTE: Entry processor is meant to process a single entry per call. Processing multiple entries and data structures in an entry processor is not supported as it may result in deadlocks on the server side.** + +Hazelcast sends the entry processor to each cluster member and these members apply it to the map entries. Therefore, if you add more members, your processing completes faster. + +#### Processing Entries + +The `IMap` interface provides the following functions for entry processing: + +- `executeOnKey` processes an entry mapped by a key. + +- `executeOnKeys` processes entries mapped by a list of keys. + +- `executeOnEntries` can process all entries in a map with a defined predicate. Predicate is optional. + +In the C++ client, an `EntryProcessor` should be `IdentifiedDataSerializable`, `Portable` or custom serializable, because the server should be able to deserialize it to process. + +The following is an example for `EntryProcessor` which is `IdentifiedDataSerializable`. + +```C++ +class ExampleEntryProcessor : public hazelcast::client::serialization::IdentifiedDataSerializable { +public: + ExampleEntryProcessor() {} + + ExampleEntryProcessor(const std::string &value) : value(value) {} + + int getFactoryId() const { + return 5; + } + + int getClassId() const { + return 1; + } + + void writeData(hazelcast::client::serialization::ObjectDataOutput &writer) const { + writer.writeUTF(&value); + } + + void readData(hazelcast::client::serialization::ObjectDataInput &reader) { + value = *reader.readUTF(); + } + +private: + std::string value; +}; +``` + +Now, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, please see [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). + +The following is an example code which can be the Java equivalent of entry processor in C++ client: + +```java +import com.hazelcast.map.AbstractEntryProcessor; +import com.hazelcast.nio.ObjectDataInput; +import com.hazelcast.nio.ObjectDataOutput; +import com.hazelcast.nio.serialization.IdentifiedDataSerializable; +import java.io.IOException; +import java.util.Map; + +public class IdentifiedEntryProcessor extends AbstractEntryProcessor implements IdentifiedDataSerializable { + static final int CLASS_ID = 1; + private String value; + + public IdentifiedEntryProcessor() { + } + + @Override + public int getFactoryId() { + return IdentifiedFactory.FACTORY_ID; + } + + @Override + public int getId() { + return CLASS_ID; + } + + @Override + public void writeData(ObjectDataOutput out) throws IOException { + out.writeUTF(value); + } + + @Override + public void readData(ObjectDataInput in) throws IOException { + value = in.readUTF(); + } + + @Override + public Object process(Map.Entry entry) { + entry.setValue(value); + return value; + } +} +``` + +You can implement the above processor’s factory as follows: + +```java +import com.hazelcast.nio.serialization.DataSerializableFactory; +import com.hazelcast.nio.serialization.IdentifiedDataSerializable; + +public class IdentifiedFactory implements DataSerializableFactory { + public static final int FACTORY_ID = 5; + + @Override + public IdentifiedDataSerializable create(int typeId) { + if (typeId == IdentifiedEntryProcessor.CLASS_ID) { + return new IdentifiedEntryProcessor(); + } + return null; + } +} +``` + +Note that you need to configure the `hazelcast.xml` to add your factory. And the following is the configuration for the above factory: + +```xml + + + + + IdentifiedFactory + + + + +``` + +The code that runs on the entries is implemented in Java on the server side. Client side entry processor is used to specify which entry processor should be called. For more details about the Java implementation of the entry processor, please see [Entry Processor section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#entry-processor) in the Hazelcast IMDG Reference Manual. + +After all implementation and starting the server where your library is added to its `CLASSPATH`, you can use the entry processor in the `IMap` functions. Let's take a look at the following example. + +```C++ + hazelcast::client::IMap map = hz.getMap("my-distributed-map"); + map.executeOnKey("key", ExampleEntryProcessor("my new name")); + std::cout << "Value for 'key' is : '" << *map.get("key") << "'" << std::endl; // value for 'key' is 'my new name' +``` + +## 7.7. Distributed Query + +Hazelcast partitions your data and spreads it across cluster of members. You can iterate over the map entries and look for certain entries (specified by predicates) you are interested in. However, this is not very efficient because you will have to bring the entire entry set and iterate locally. Instead, Hazelcast allows you to run distributed queries on your distributed map. + +### 7.7.1. How Distributed Query Works + +1. The requested predicate is sent to each member in the cluster. +2. Each member looks at its own local entries and filters them according to the predicate. At this stage, key/value pairs of the entries are deserialized and then passed to the predicate. +3. The predicate requester merges all the results coming from each member into a single set. + +Distributed query is highly scalable. If you add new members to the cluster, the partition count for each member is reduced and thus the time spent by each member on iterating its entries is reduced. In addition, the pool of partition threads evaluates the entries concurrently in each member, and the network traffic is also reduced since only filtered data is sent to the requester. + +**Built-in Predicates For Query** + +There for many built-in `Predicate` implementations for your query requirements. Some of them are explained below. + +- `TruePredicate`: This predicate returns true and hence includes all the entries on the response. + +- `FalsePredicate`: This predicate returns false and hence filters out all the entries in the response. + +- `EqualPredicate`: Checks if the result of an expression is equal to a given value. + +- `NotEqualPredicate`: Checks if the result of an expression is not equal to a given value. + +- `InstanceOfPredicate`: Checks if the result of an expression has a certain type. + +- `LikePredicate`: Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. + +- `ILikePredicate`: Same as LikePredicate. Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. + +- `GreaterLessPredicate`: Checks if the result of an expression is greater equal or less equal than a certain value. + +- `BetweenPredicate`: Checks if the result of an expression is between two values (start and end values are inclusive). + +- `InPredicate`: Checks if the result of an expression is an element of a certain list. + +- `NotPredicate`: Negates a provided predicate result. + +- `RegexPredicate`: Checks if the result of an expression matches some regular expression. + +- `SqlPredicate`: Query using SQL syntax. + +- `PagingPredicate`: Returns matching entries page by page. + +Hazelcast offers the following ways for distributed query purposes: + +- Combining Predicates: AndPredicate, OrPredicate, NotPredicate. + +- Distributed SQL Query + +#### 7.7.1.1. Employee Map Query Example + +Assume that you have an `employee` map containing values of `Employee` objects, as coded below. + +```C++ +class Employee : public serialization::IdentifiedDataSerializable { +public: + static const int TYPE_ID = 100; + + Employee() : active(false), salary(0.0) {} + + Employee(int id, const std::string &name, bool active, double salary) : id(id), name(name), active(active), + salary(salary) {} + + virtual int getFactoryId() const { + return 1000; + } + + virtual int getClassId() const { + return TYPE_ID; + } + + virtual void writeData(serialization::ObjectDataOutput &writer) const { + writer.writeInt(id); + writer.writeUTF(&name); + writer.writeBoolean(active); + writer.writeDouble(salary); + } + + virtual void readData(serialization::ObjectDataInput &reader) { + id = reader.readInt(); + name = *reader.readUTF(); + active = reader.readBoolean(); + salary = reader.readDouble(); + } + +private: + int id; + std::string name; + bool active; + double salary; +}; +``` + +Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want to save the `Employee` objects as byte arrays on the map, you don't need to implement its equivalent on the server-side. However, if you want to query on the `employee` map, server needs the `Employee` objects rather than byte array formats. Therefore, you need to implement its Java equivalent and its data serializable factory on server side for server to reconstitute the objects from binary formats. After implementing the Java class and its factory, you need to add the factory to the data serializable factories or the portable factories by giving a factory `id`. Here is the example XML configuration of the server. + +```xml + + ... + + + + mypackage.MyIdentifiedFactory + + + + ... + +``` + +Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` folder in the extracted `hazelcast-.zip`. See [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). + +> **NOTE: You can also make this object `Portable` and implement its Java equivalent and its portable factory on the server side. Note that querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** + +#### 7.7.1.2. Querying by Combining Predicates with AND, OR, NOT + +You can combine predicates by using the `and`, `or`, and `not` operators, as shown in the below example. + +```C++ + query::AndPredicate andPredicate; + andPredicate.add(std::auto_ptr(new query::EqualPredicate("active", true))).add(std::auto_ptr(new query::GreaterLessPredicate("age", 30, false, true))); + std::vector activeEmployeesLessThan30 = employees.values(andPredicate); +``` + +In the above example code, `predicate` verifies whether the entry is active and its `age` value is less than 30. This `predicate` is applied to the `employee` map using the IMap::values(const query::Predicate &predicate)` method. This method sends the predicate to all cluster members and merges the results coming from them. + +> **NOTE: Predicates can also be applied to `keySet` and `entrySet` of the Hazelcast distributed map.** + +#### 7.7.1.3. Querying with SQL + +`SqlPredicate` takes the regular SQL `where` clause. Here is an example: + +```C++ + query::SqlPredicate sqlPredicate("active AND age < 30"); + std::vector activeEmployeesLessThan30 = employees.values(sqlPredicate); +``` + +##### Supported SQL Syntax + +**AND/OR:** ` AND AND …` + +- `active AND age > 30` +- `active = false OR age = 45 OR name = 'Joe'` +- `active AND ( age > 20 OR salary < 60000 )` + +**Equality:** `=, !=, <, ⇐, >, >=` + +- ` = value` +- `age <= 30` +- `name = 'Joe'` +- `salary != 50000` + +**BETWEEN:** ` [NOT] BETWEEN AND ` + +- `age BETWEEN 20 AND 33 ( same as age >= 20 AND age ⇐ 33 )` +- `age NOT BETWEEN 30 AND 40 ( same as age < 30 OR age > 40 )` + +**IN:** ` [NOT] IN (val1, val2,…)` + +- `age IN ( 20, 30, 40 )` +- `age NOT IN ( 60, 70 )` +- `active AND ( salary >= 50000 OR ( age NOT BETWEEN 20 AND 30 ) )` +- `age IN ( 20, 30, 40 ) AND salary BETWEEN ( 50000, 80000 )` + +**LIKE:** ` [NOT] LIKE 'expression'` + +The `%` (percentage sign) is placeholder for multiple characters, an `_` (underscore) is placeholder for only one character. + +- `name LIKE 'Jo%'` (true for 'Joe', 'Josh', 'Joseph' etc.) +- `name LIKE 'Jo_'` (true for 'Joe'; false for 'Josh') +- `name NOT LIKE 'Jo_'` (true for 'Josh'; false for 'Joe') +- `name LIKE 'J_s%'` (true for 'Josh', 'Joseph'; false 'John', 'Joe') + +**ILIKE:** ` [NOT] ILIKE 'expression'` + +Similar to LIKE predicate but in a case-insensitive manner. + +- `name ILIKE 'Jo%'` (true for 'Joe', 'joe', 'jOe','Josh','joSH', etc.) +- `name ILIKE 'Jo_'` (true for 'Joe' or 'jOE'; false for 'Josh') + +**REGEX:** ` [NOT] REGEX 'expression'` + +- `name REGEX 'abc-.*'` (true for 'abc-123'; false for 'abx-123') + +##### Querying Examples with Predicates + +You can use `query::QueryConstants::getKeyAttributeName()` which stands for string `__key` attribute to perform a predicated search for entry keys. Please see the following example: + +```C++ + hazelcast::client::IMap map = hz.getMap("personMap;"); + map.put("Mali", 28); + map.put("Ahmet", 30); + map.put("Furkan", 23); + query::SqlPredicate sqlPredicate("__key like F%"); + std::vector > startingWithF = map.entrySet(sqlPredicate); + std::cout << "First person:" << startingWithF[0].first << ", age:" << startingWithF[0].second << std::endl; +``` + +In this example, the code creates a list with the values whose keys start with the letter "F”. + +You can use `query::QueryConstants::getValueAttributeName()` which stands for `this` attribute to perform a predicated search for entry values. Please see the following example: + +```C++ + hazelcast::client::IMap map = hz.getMap("personMap;"); + map.put("Mali", 28); + map.put("Ahmet", 30); + map.put("Furkan", 23); + query::GreaterLessPredicate greaterThan27Predicate(query::QueryConstants::getValueAttributeName(), 27, false, false); + std::vector olderThan27 = map.keySet(greaterThan27Predicate); + std::cout << "First person:" << olderThan27[0] << ", second person:" << olderThan27[1] << std::endl; +``` + +In this example, the code creates a list with the values greater than or equal to "27". + +#### 7.7.1.4. Filtering with Paging Predicates + +The C++ client provides paging for defined predicates. With its `PagingPredicate` object, you can get a list of keys, values, or entries page by page by filtering them with predicates and giving the size of the pages. Also, you can sort the entries by specifying comparators. + +```C++ + hazelcast::client::IMap map = hz.getMap("personMap;"); + query::PagingPredicate pagingPredicate(std::auto_ptr(new query::GreaterLessPredicate(query::QueryConstants::getValueAttributeName(), 18, false, false)), 5); + // Set page to retrieve third page + pagingPredicate.setPage(3); + std::vector values = map.values(pagingPredicate); + //... + + // Set up next page + pagingPredicate.nextPage(); + + // Retrieve next page + values = map.values(pagingPredicate); + + //... +``` + +If you want to sort the result before paging, you need to specify a comparator object that implements the `query::EntryComparator` interface. Also, this comparator object should be Hazelcast serializable since it will be sent to the server side. After implementing the C++ version, you need to implement the Java equivalent of the comparator and its factory. The Java equivalent of the comparator should implement `java.util.Comparator`. Note that `compare` function of the `Comparator` on the Java side is the equivalent of the `compare` function of `EntryComparator` on the C++ side. When you implement the `Comparator` and its factory, you can add them to the `CLASSPATH` of the server side. See [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). + +Also, You can access a specific page more easily with the help of the `setPage` function. This way, if you make a query for the hundredth page, for example, it will get all 100 pages at once instead of reaching the hundredth page one by one using the `nextPage` function. + +## 7.8. Raw Pointer API + +When using C++ client you can have the ownership of raw pointers for the objects you create and return. This allows you to keep the objects in your library/application without any need for copy. + +For each container you can use the adapter classes, whose names start with `RawPointer`, to access the raw pointers of the created objects. These adapter classes are found in `hazelcast::client::adaptor` namespace and listed below: + +- `RawPointerList` +- `RawPointerQueue` +- `RawPointerTransactionalMultiMap` +- `RawPointerMap` +- `RawPointerSet` +- `RawPointerTransactionalQueue` +- `RawPointerMultiMap` +- `RawPointerTransactionalMap` + +These are adapter classes and they do not create new structures. You just provide the legacy containers as parameters and then you can work with these raw capability containers freely. An example usage of `RawPointerMap` is shown below: + +``` +hazelcast::client::IMap m = hz.getMap("map"); +hazelcast::client::adaptor::RawPointerMap map(m); +map.put("1", "Tokyo"); +map.put("2", "Paris"); +map.put("3", "New York"); +std::cout << "Finished loading map" << std::endl; + +std::auto_ptr > vals = map.values(); +std::auto_ptr > entries = map.entrySet(); + +std::cout << "There are " << vals->size() << " values in the map" << std::endl; +std::cout << "There are " << entries->size() << " entries in the map" << std::endl; + +for (size_t i = 0; i < entries->size(); ++i) { + const std::string * key = entries->getKey(i); + if ((std::string *) NULL == key) { + std::cout << "The key at index " << i << " is NULL" << std::endl; + } else { + std::auto_ptr val = entries->releaseValue(i); + std::cout << "(Key, Value) for index " << i << " is: (" << *key << ", " << + (val.get() == NULL ? "NULL" : *val) << ")" << std::endl; + } + } + std::cout << "Finished" << std::endl; +``` + +Raw pointer API uses the DataArray and EntryArray interfaces which allow late deserialization of objects. The entry in the returned array is deserialized only when it is accessed. Please see the example code below: + +``` +// No deserialization here +std::auto_ptr > vals = map.values(); + +// deserializes the item at index 0 assuming that there are at least 1 items in the array +const std::string *value = vals->get(0); + +// no deserialization here since it was already de-serialized +value = vals->get(0); + +// no deserialization here since it was already de-serialized +value = (*vals)[0]; + +// releases the value so that you can keep this object pointer in your application at some other place +std::auto_ptr releasedValue = vals->release(0); + +// deserialization occurs again since the value was released already +value = vals->get(0); +``` + +Using raw pointer based API may improve performance if you are using the API to return multiple values such as values, keySet, and entrySet. In this case, cost of deserialization is delayed until the item is actually accessed. + +## 7.9. Mixed Object Types Supporting HazelcastClient +Sometimes, you may need to use Hazelcast data structures with objects of different types. For example, you may want to put `int`, `string`, `IdentifiedDataSerializable`, etc. objects into the same Hazelcast `IMap` data structure. You can do this by using the mixed type adopted `HazelcastClient`. You can adopt the client in this way: +``` + ClientConfig config; + HazelcastClient client(config); + mixedtype::HazelcastClient &hazelcastClient = client.toMixedType(); +``` +The `mixedtype::HazelcastClient` interface is designed to provide you the data structures which allows you to work with any object types in a mixed manner. For example, the interface allows you to provide the key and value type differently for each map.put call. An example usage is shown below: +``` + mixedtype::IMap map = hazelcastClient.getMap("MyMap"); + + map.put(3, 5); + map.put("string key1", "MyStringValue"); + map.put("string key1", myCustomInstance); + TypedData result = map.get(3); +``` + +As you can see in the above code snippet, we are putting `int`, `string` and MyCustomObject to the same map. Both the key and value can be of different type for each map.put call. + +If you want to use mixed type map with near cache, then you should use the MixedNearCacheConfig class and add config to the ClientConfig using the addMixedNearCacheConfig method. See the below example: +``` + boost::shared_ptr nearCacheConfig(new mixedtype::config::MixedNearCacheConfig("MixedMapTestMap")); + clientConfig.addMixedNearCacheConfig(nearCacheConfig); +``` +Mixed type support for near cache only exists when the in-memory format is BINARY. The OBJECT in-memory format is not supported for MixedNearCacheConfig; + +The mixed type API uses the TypedData class at the user interface. + +### 7.9.1. TypedData API +The TypedData class is a wrapper class for the serialized binary data. It presents the following user APIs: +``` + /** + * + * @return The type of the underlying object for this binary. + */ + const serialization::pimpl::ObjectType getType() const; + + /** + * Deserializes the underlying binary data and produces the object of type T. + * + * CAUTION: The type that you provide should be compatible with what object type is returned with + * the getType API, otherwise you will either get an exception of incorrectly try deserialize the binary data. + * + * @tparam T The type to be used for deserialization + * @return The object instance of type T. + */ + template + std::auto_ptr get() const; +``` + +TypedData does late deserialization of the data only when the get method is called. + +This TypedData allows you to retrieve the data type of the underlying binary to be used when being deserialized. This class represents the type of a Hazelcast serializable object. The fields can take the following values: +1. Primitive types: `factoryId=-1`, `classId=-1`, `typeId` is the type ID for that primitive as listed in +`SerializationConstants` +2. Array of primitives: `factoryId=-1`, `classId=-1`, `typeId` is the type ID for that array as listed in +`SerializationConstants` +3. IdentifiedDataSerializable: `factoryId`, `classId` and `typeId` are non-negative values as registered by +the `DataSerializableFactory`. +4. Portable: `factoryId`, `classId` and `typeId` are non-negative values as registered by the `PortableFactory`. +5. Custom serialized objects: `factoryId=-1`, `classId=-1`, `typeId` is the non-negative type ID as +registered for the custom object. + + +# 8. Development and Testing + +Hazelcast C++ client is developed using C++. If you want to help with bug fixes, develop new features or +tweak the implementation to your application's needs, you can follow the steps in this section. + +## 8.1. Building and Using Client From Sources + +For compiling with SSL support: + +- You need to have the openssl (version 1.0.2) installed in your development environment: (i) Add openssl `include` directory to include directories, (ii) Add openssl `library` directory to the link directories list (This is the directory named `tls`. e.g. `cpp/Linux_64/hazelcast/lib/tls`), (iii) Set the openssl libraries to link. + +- You can provide your openssl installation directory to cmake using the following flags: + - -DHZ_OPENSSL_INCLUDE_DIR="Path to open installation include directory" + - -DHZ_OPENSSL_LIB_DIR="Path to openssl lib directory" + +- Use -DHZ_COMPILE_WITH_SSL=ON + +- Check the top level CMakeLists.txt file to see which libraries we link for openssl in which environment. + - For Mac OS and Linux, we link with "ssl" and "crypto" + - For Windows, install also the 1.1.x version of the openssl library. An example linkage for 64 bit library and release build: "libeay32MD ssleay32MD libcrypto64MD libSSL64MD". + + +Follow the below steps to build and install Hazelcast C++ client from its source: + +1. Clone the project from github: `git clone --recursive git@github.com:hazelcast/hazelcast-cpp-client.git` We use --recursive flag for our dependency on googletest framework. +2. Create a build directory (e.g. build) from the root of the project. In the build directory, run the cmake command depending on your environment as depicted in the next sections. + +### 8.1.1 Mac + +**Release:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + +**Code Coverage:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_CODE_COVERAGE=ON + gcovr -u -e .*external.* -e SimpleMapTest.h --html --html-details -o coverage.html + +**Xcode Development:** + + cmake .. -G Xcode -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug + + cmake .. -G Xcode -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=archive -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=library + +**Valgrind sample run with suppressions:** + + valgrind --leak-check=yes --gen-suppressions=all --suppressions=../test.sup ./hazelcast/test/clientTest_STATIC_64.exe > log.t + +### 8.1.2 Linux + +**Release:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release + cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + +**Building the tests:** + + Add the -DHZ_BUILD_TESTS=ON flag to the cmake flags. e.g.: + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_BUILD_TESTS=ON + +**Building the examples:** + + Add the -DHZ_BUILD_EXAMPLES=ON flag to the cmake flags. e.g.: + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_BUILD_EXAMPLES=ON + +**Valgrind:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug -DHZ_VALGRIND=ON + +### 8.1.3 Windows + +**Release:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release + cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Release + cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Release + + MSBuild.exe HazelcastClient.sln /property:Configuration=Release + +**Debug:** + + cmake .. -DHZ_LIB_TYPE=STATIC -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Debug + cmake .. -DHZ_LIB_TYPE=SHARED -DHZ_BIT=32 -DCMAKE_BUILD_TYPE=Debug + cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=STATIC -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug + cmake .. -G "Visual Studio 12 Win64" -DHZ_LIB_TYPE=SHARED -DHZ_BIT=64 -DCMAKE_BUILD_TYPE=Debug + + MSBuild.exe HazelcastClient.sln /property:TreatWarningsAsErrors=true /property:Configuration=Debug + +## 8.2. Testing +In order to test Hazelcast C++ client locally, you will need the following: +* Java 6 or newer +* Maven +* cmake +* openssl +* Python 2 and pip + +You can also pull our test docker images from docker hub using the command: `docker pull ihsan/gcc_346_ssl` This images has all the tools for building the project. + +Following command builds and runs the tests: + +- Linux: `./testLinuxSingleCase.sh 64 SHARED Debug` +- MacOS: `./testLinuxSingleCase.sh 64 SHARED Debug` +- Windows: `./testWindowsSingleCase.bat 64 SHARED Debug` + +### 8.2.1 Tested Platforms +Our CI tests are run continuously on the following platforms and compilers: +- Linux: CentOs5 gcc 3.4.6, CentOs5.11 gcc 4.1.2, centos 7 gcc 4.8.2 +- Windows: Visual Studio 12 +- Mac OS: Apple LLVM version 7.3.0 (clang-703.0.31) + +## 8.3 Reproducing Released Libraries + +Sometimes you may want to reproduce the released library for your own compiler environment. You need to run the release script and it will produce a release folder named "cpp". + +Note: +- The default release scripts require that you have openssl (version 1.0.2) installed in your development environment. + +## Mac + +Run releaseOSX.sh. + +## Linux + +Run releaseLinux.sh + +## Windows + +Run releaseWindows.bat. + + +# 9. Getting Help + +You can use the following channels for your questions and development/usage issues: + +* This repository by opening an issue. +* Hazelcast Hazelcast channel on Gitter: +[![Join the chat at https://gitter.im/hazelcast/hazelcast](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hazelcast/hazelcast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +* Our Google Groups directory: https://groups.google.com/forum/#!forum/hazelcast +* Stack Overflow: https://stackoverflow.com/questions/tagged/hazelcast + +# 10. Contributing -[http://groups.google.com/group/hazelcast](http://groups.google.com/group/hazelcast) +Besides your development contributions as explained in the [Development and Testing chapter](#8-development-and-testing) above, you can always open a pull request on this repository for your other requests such as documentation changes. -# License +# 11. License -Hazelcast C++ Client is available under the Apache 2 License. Please see the [Licensing appendix](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#license-questions) for more information. +[Apache 2 License](https://github.com/hazelcast/hazelcast-nodejs-client/blob/master/LICENSE). -# Copyright +# 12. Copyright Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved. -Visit [www.hazelcast.com](http://www.hazelcast.com/) for more information. +Visit [www.hazelcast.com](http://www.hazelcast.com) for more information. From 855f8f83cea77b66c871b2beee02da9a4155e223 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Tue, 6 Nov 2018 13:48:48 +0300 Subject: [PATCH 02/11] Rebased with changes for Node.js client up and including to Node.js client README update commit number 2e024a8e20c8e26f3f09542aed1915e14c60f3b6 . --- README.md | 482 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 249 insertions(+), 233 deletions(-) diff --git a/README.md b/README.md index 502240e18f..5f94764ab5 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,15 @@ * [Introduction](#introduction) * [1. Getting Started](#1-getting-started) * [1.1. Requirements](#11-requirements) - * [1.2. Working with Hazelcast Clusters](#12-working-with-hazelcast-clusters) + * [1.2. Working with Hazelcast IMDG Clusters](#12-working-with-hazelcast-imdg-clusters) + * [1.2.1. Setting Up a Hazelcast IMDG Cluster](#121-setting-up-a-hazelcast-imdg-cluster) + * [1.2.2. Running Standalone Jars](#122-running-standalone-jars) + * [1.2.3. Adding User Library to CLASSPATH](#123-adding-user-library-to-classpath) + * [1.2.4. Using hazelcast-member Tool](#124-using-hazelcast-member-tool) * [1.3. Downloading and Installing](#13-downloading-and-installing) * [1.4. Basic Configuration](#14-basic-configuration) - * [1.4.1. IMDG Configuration](#141-imdg-configuration) - * [1.4.2. Hazelcast Client Configuration](#142-hazelcast-client-configuration) + * [1.4.1. Configuring Hazelcast IMDG](#141-configuring-hazelcast-imdg) + * [1.4.2. Configuring Hazelcast C++ Client](#142-configuring-hazelcast-cpp-client) * [1.5. Basic Usage](#15-basic-usage) * [1.6. Code Samples](#16-code-samples) * [2. Features](#2-features) @@ -20,7 +24,7 @@ * [4.3. Custom Serialization](#43-custom-serialization) * [4.4. Global Serialization](#44-global-serialization) * [5. Setting Up Client Network](#5-setting-up-client-network) - * [5.1. Providing the Member Addresses](#51-providing-the-member-addresses) + * [5.1. Providing Member Addresses](#51-providing-member-addresses) * [5.2. Setting Smart Routing](#52-setting-smart-routing) * [5.3. Enabling Redo Operation](#53-enabling-redo-operation) * [5.4. Setting Connection Timeout](#54-setting-connection-timeout) @@ -40,6 +44,7 @@ * [7.2.2. Unisocket Client](#722-unisocket-client) * [7.3. Handling Failures](#73-handling-failures) * [7.3.1. Handling Client Connection Failure](#731-handling-client-connection-failure) + * [7.3.2. Handling Retry-able Operation Failure](#732-handling-retry-able-operation-failure) * [7.3.3. Backpressure](#732-backpressure) * [7.3.4. Client Connection Strategy](#732-client-connection-strategy) * [7.3.4. Client Reconnect Strategy](#732-client-reconnection-strategy) @@ -96,20 +101,19 @@ This document provides information about the C++ client for [Hazelcast](https:// ### Resources -See the following for more information on C++ and Hazelcast IMDG: +See the following for more information on Hazelcast IMDG: * Hazelcast IMDG [website](https://hazelcast.org/) * Hazelcast IMDG [Reference Manual](https://hazelcast.org/documentation/#imdg) ### Release Notes -You can see the release notes for each C++ client release on the [Releases](https://github.com/hazelcast/hazelcast-cpp-client/releases) page of this repository. +See the [Releases](https://github.com/hazelcast/hazelcast-cpp-client/releases) page of this repository. # 1. Getting Started -This chapter explains all the necessary things to start using Hazelcast C++ Client including basic Hazelcast IMDG and client -configuration and how to use distributed maps with Hazelcast. +This chapter provides information on how to get started with your Hazelcast C++ client. It outlines the requirements, installation and configuration of the client, setting up a cluster, and provides a simple application that uses a distributed map in C++ client. ## 1.1. Requirements @@ -118,32 +122,41 @@ configuration and how to use distributed maps with Hazelcast. - Hazelcast IMDG 3.6 or newer - Latest Hazelcast C++ Client -## 1.2. Working with Hazelcast Clusters +## 1.2. Working with Hazelcast IMDG Clusters -Hazelcast C++ Client requires a working Hazelcast IMDG cluster to run. IMDG cluster handles storage and manipulation of the user data. -Clients are a way to connect to IMDG cluster and access such data. +Hazelcast C++ client requires a working Hazelcast IMDG cluster to run. This cluster handles storage and manipulation of the user data. +Clients are a way to connect to the Hazelcast IMDG cluster and access such data. -IMDG cluster consists of one or more Hazelcast IMDG members. These members generally run on multiple virtual or physical machines +Hazelcast IMDG cluster consists of one or more cluster members. These members generally run on multiple virtual or physical machines and are connected to each other via network. Any data put on the cluster is partitioned to multiple members transparent to the user. -It is therefore very easy to scale the system by adding new members as the data grows. IMDG cluster also offers resilience. Should +It is therefore very easy to scale the system by adding new members as the data grows. Hazelcast IMDG cluster also offers resilience. Should any hardware or software problem causes a crash to any member, the data on that member is recovered from backups and the cluster -continues to operate without any downtime. Hazelcast clients are an easy way to connect to an IMDG cluster and perform tasks on +continues to operate without any downtime. Hazelcast clients are an easy way to connect to a Hazelcast IMDG cluster and perform tasks on distributed data structures that live on the cluster. -In order to use Hazelcast C++ Client, we first need to setup an IMDG cluster. +In order to use Hazelcast C++ client, we first need to setup a Hazelcast IMDG cluster. -### Setting Up an IMDG Cluster +### 1.2.1. Setting Up a Hazelcast IMDG Cluster -There are multiple ways of starting an IMDG cluster easily. You can run standalone IMDG members by downloading and running jar files -from the website. You can embed IMDG members to your Java projects. The easiest way is to use [hazelcast-member tool](https://github.com/hazelcast/hazelcast-member-tool) -if you have brew installed in your computer. We are going to download jars from the website and run a standalone member for this guide. +There are following options to start a Hazelcast IMDG cluster easily: -#### Running Standalone Jars +* You can run standalone members by downloading and running JAR files from the website. +* You can embed members to your Java projects. The easiest way is to use [hazelcast-member tool](https://github.com/hazelcast/hazelcast-member-tool) if you have brew installed in your computer. + +We are going to download JARs from the website and run a standalone member for this guide. + +#### 1.2.2. Running Standalone JARs + +Follow the instructions below to create a Hazelcast IMDG cluster: + +1. Go to Hazelcast's download [page](https://hazelcast.org/download/) and download either the `.zip` or `.tar` distribution of Hazelcast IMDG. +2. Decompress the contents into any directory that you +want to run members from. +3. Change into the directory that you decompressed the Hazelcast content and then into the `bin` directory. +4. Use either `start.sh` or `start.bat` depending on your operating system. Once you run the start script, you should see the Hazelcast IMDG logs in the terminal. + +You should see a log similar to the following, which means that your 1-member cluster is ready to be used: -Go to https://hazelcast.org/download/ and download `.zip` or `.tar` distribution of Hazelcast IMDG. Decompress the contents into any directory that you -want to run IMDG members from. Change into the directory that you decompressed the Hazelcast content. Go into `bin` directory. Use either -`start.sh` or `start.bat` depending on your operating system. Once you run the start script, you should see IMDG logs on the terminal. -Once you see some log similar to the following, your 1-member cluster is ready to use: ``` INFO: [192.168.0.3]:5701 [dev] [3.10.4] @@ -155,11 +168,15 @@ Sep 06, 2018 10:50:23 AM com.hazelcast.core.LifecycleService INFO: [192.168.0.3]:5701 [dev] [3.10.4] [192.168.0.3]:5701 is STARTED ``` -#### Adding User Library to CLASSPATH +#### 1.2.3. Adding User Library to CLASSPATH - When you want to use features such as querying and language interoperability, you might need to add your own Java classes to Hazelcast member in order to use them from your C++ client. This can be done by adding your own compiled code to the `CLASSPATH`. To do this, compile your code with the `CLASSPATH` and add the compiled files to `user-lib` folder in the extracted `hazelcast-.zip`. Then, you can start your Hazelcast member by using the start scripts in the `bin` folder. The start scripts will automatically add your compiled classes to the `CLASSPATH`. - Note that if you are adding an `IdentifiedDataSerializable` or a `Portable` class, you need to add its factory too. Then, you should configure the factory in the `hazelcast.xml` in the `bin` folder like the following: - ```xml +When you want to use features such as querying and language interoperability, you might need to add your own Java classes to the Hazelcast member in order to use them from your C++ client. This can be done by adding your own compiled code to the `CLASSPATH`. To do this, compile your code with the `CLASSPATH` and add the compiled files to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). Then, you can start your Hazelcast member by using the start scripts in the `bin` directory. The start scripts will automatically add your compiled classes to the `CLASSPATH`. + +Note that if you are adding an `IdentifiedDataSerializable` or a `Portable` class, you need to add its factory too. Then, you should configure the factory in the `hazelcast.xml` configuration file. This file resides in the `bin` directory where you extracted the `hazelcast-.zip` (or `tar`). + +The following is an example configuration when you are adding an `IdentifiedDataSerializable` class: + +```xml ... @@ -172,23 +189,32 @@ INFO: [192.168.0.3]:5701 [dev] [3.10.4] [192.168.0.3]:5701 is STARTED ... ``` -Similarly, use `` instead of `` if you are using portables. +If you want to add a `Portable` class, you should use `` instead of `` in the above configuration. -#### Using hazelcast-member Tool +#### 1.2.4. Using hazelcast-member Tool + +`hazelcast-member` is a tool to download and run Hazelcast IMDG members easily. If you have brew installed, run the following commands to instal this tool: -`hazelcast-member` is a tool to make downloading and running IMDG members as easy as it could be. If you have brew installed, run the following commands: ``` brew tap hazelcast/homebrew-hazelcast brew install hazelcast-member +``` + +Now, you can start a member by running the following command: + +``` hazelcast-member start ``` -In order to stop the member, run the following command: + +To stop a member, run the following command: + ``` hazelcast-member stop ``` -Find more information about `hazelcast-member` tool at https://github.com/hazelcast/hazelcast-member-tool -Refer to the official [Hazelcast IMDG Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#getting-started) for more information regarding starting clusters. +You can find more information about the `hazelcast-member` tool at its GitHub [repo](https://github.com/hazelcast/hazelcast-member-tool). + +See the [Hazelcast IMDG Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#getting-started) for more information on setting up the clusters. ## 1.3. Downloading and Installing Download the latest C++ client library from [Hazelcast C++ Client Website](https://hazelcast.org/clients/cplusplus/). You need to download the zip file for your platform. For Linux and Windows, 32 and 64-bit libraries exist while for MacOS only has the 64-bit version. @@ -250,26 +276,33 @@ When compiling for Windows environment the user should specify one of the follow HAZELCAST_USE_SHARED: You want the application to use the shared Hazelcast library. ## 1.4. Basic Configuration -If you are using Hazelcast IMDG and C++ Client on the same computer, generally default configuration just works. This is great for + +If you are using Hazelcast IMDG and C++ Client on the same computer, generally the default configuration should be fine. This is great for trying out the client. However, if you run the client on a different computer than any of the cluster members, you may -need to do some simple configuration such as specifying the member addresses. +need to do some simple configurations such as specifying the member addresses. + +The Hazelcast IMDG members and clients have their own configuration options. You may need to reflect some of the member side configurations on the client side to properly connect to the cluster. -The IMDG members and clients have their own configuration options. You may need to reflect some of the member side configurations on the client side to properly connect to the cluster. This section describes the most common configuration elements to get you started in no time. -It discusses some member side configuration options to ease understanding Hazelcast's ecosystem. Then, client side configuration options -regarding cluster connection are discussed. Configuration material regarding data structures are discussed in the following sections. -You can refer to [IMDG Documentation](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview](#configuration-overview) for more information. +It discusses some member side configuration options to ease the understanding of Hazelcast's ecosystem. Then, the client side configuration options +regarding the cluster connection are discussed. The configurations for the Hazelcast IMDG data structures that can be used in the C++ client are discussed in the following sections. + +See the [Hazelcast IMDG Reference Manual](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview section](#configuration-overview) for more information. + +### 1.4.1. Configuring Hazelcast IMDG + +Hazelcast IMDG aims to run out-of-the-box for most common scenarios. However if you have limitations on your network such as multicast being disabled, +you may have to configure your Hazelcast IMDG members so that they can find each other on the network. Also, since most of the distributed data structures are configurable, you may want to configure them according to your needs. We will show you the basics about network configuration here. + +There are two ways to configure Hazelcast IMDG: -### 1.4.1. IMDG Configuration -Hazelcast IMDG aims to run out of the box for most common scenarios. However if you have limitations on your network such as multicast being disabled, -you may have to configure your Hazelcast IMDG instances so that they can find each other on the network. Also most data structures are configurable. -Therefore, you may want to configure your Hazelcast IMDG. We will show you the basics about network configuration here. +* Using the `hazelcast.xml` configuration file. +* Programmatically configuring the member before starting it from the Java code. -There are two ways to configure Hazelcast IMDG. One is to use a `hazelcast.xml` file and the other is to programmatically configure the -instance before starting it from Java code. Since we use standalone servers, we will use `hazelcast.xml` to configure our cluster members. +Since we use standalone servers, we will use the `hazelcast.xml` file to configure our cluster members. + +When you download and unzip `hazelcast-.zip` (or `tar`), you see the `hazelcast.xml` in the `bin` directory. When a Hazelcast member starts, it looks for the `hazelcast.xml` file to load the configuration from. A sample `hazelcast.xml` is shown below. -When you download and unzip `hazelcast-.zip`, you see the `hazelcast.xml` in `bin` folder. When a Hazelcast member starts, it looks for -`hazelcast.xml` file to load configuration from. A sample `hazelcast.xml` is below. We will go over some important elements in the rest of this section. ```xml @@ -299,8 +332,10 @@ When you download and unzip `hazelcast-.zip`, you see the `hazelcast.xm ``` -- ``: Specifies which cluster this member belongs to. A member connects only to other members that are in the same group as -itself. You will see `` and `` tags with some pre-configured values. You may give your clusters different names so that they can +We will go over some important configuration elements in the rest of this section. + +- ``: Specifies which cluster this member belongs to. A member connects only to the other members that are in the same group as +itself. As shown in the above configuration sample, there are `` and `` tags under the `` element with some pre-configured values. You may give your clusters different names so that they can live in the same network without disturbing each other. Note that the cluster name should be the same across all members and clients that belong to the same cluster. `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility purposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later. @@ -316,14 +351,14 @@ purposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later. putting multiple known member addresses there to avoid disconnectivity should one of the members in the list is unavailable at the time of connection. -These configuration elements are enough for most connection scenarios. Now we will move onto configuration of the C++ client. +These configuration elements are enough for most connection scenarios. Now we will move onto the configuration of the C++ client. + +### 1.4.2. Configuring Hazelcast C++ Client -### 1.4.2. Hazelcast Client Configuration You can configure Hazelcast C++ Client programatically. -This section describes some network configuration settings to cover common use cases in connecting the client to a cluster. Refer to [Configuration Overview](#configuration-overview) -and the following sections for information about detailed network configuration and/or additional features of Hazelcast C++ client -configuration. +This section describes some network configuration settings to cover common use cases in connecting the client to a cluster. See the [Configuration Overview section](#configuration-overview) +and the following sections for information about detailed network configurations and/or additional features of Hazelcast C++ client configuration. An easy way to configure your Hazelcast C++ Client is to create a `ClientConfig` object and set the appropriate options. Then you can supply this object to your client at the startup. @@ -337,7 +372,7 @@ You need to create a `ClientConfig` object and adjust its properties. Then you c hazelcast::client::HazelcastClient hz(config); // Connects to the cluster ``` -If you run Hazelcast IMDG members in a different server than the client, you most probably have configured the members' ports and cluster +If you run the Hazelcast IMDG members in a different server than the client, you most probably have configured the members' ports and cluster names as explained in the previous section. If you did, then you need to make certain changes to the network settings of your client. ### Group Settings @@ -349,7 +384,8 @@ names as explained in the previous section. If you did, then you need to make ce ``` ### Network Settings -You need to provide the ip address and port of at least one member in your cluster so the client finds it. + +You need to provide the IP address and port of at least one member in your cluster so the client can find it. **Programmatic:** ```C++ @@ -390,11 +426,11 @@ Members [2] { 24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED Started the Hazelcast C++ client instance hz.client_1 ``` -Congratulations, you just started a Hazelcast C++ Client. +Congratulations! You just started a Hazelcast C++ Client. **Using a Map** -Let us manipulate a distributed map on a cluster using the client. +Let's manipulate a distributed map on a cluster using the client. Save the following file as `IT.cpp`, compile it using a command similar to (Linux g++ compilation is used for demonstration): ```C++ @@ -426,7 +462,9 @@ int main() { return 0; } ``` + **Output** + ``` 24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED Added IT personnel. Logging all known personnel @@ -435,19 +473,23 @@ Clark is in IT department Bob is in IT department ``` -You see this example puts all IT personnel into a cluster-wide `personnelMap` and then prints all known personnel. +You see this example puts all the IT personnel into a cluster-wide `personnelMap` and then prints all the known personnel. + +Now create a`Sales.cpp` and compile and run it. + +**Compile:** -Now create `Sales.cpp` and compile and run it. -Compile: ```C++ g++ Sales.cpp -o Sales -lpthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64 ``` -Then, you can run the application as +**Run** +You can run the application using the following command: ``` ./Sales ``` **Sales.cpp** + ```C++ #include int main() { @@ -470,7 +512,9 @@ int main() { return 0; } ``` + **Output** + ``` 24/10/2018 13:54:06.600 INFO: [0x700006a44000] hz.client_1[dev] [3.10.2-SNAPSHOT] LifecycleService::LifecycleEvent CLIENT_CONNECTED Added Sales personnel. Logging all known personnel @@ -487,9 +531,9 @@ That is because our map lives in the cluster and no matter which client we use, ## 1.6. Code Samples -Please see Hazelcast C++ [code samples](https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples) for more examples. +See Hazelcast C++ [code samples](https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples) for more examples. -You can also refer to Hazelcast C++ [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/). +You can also see the Hazelcast C++ [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/). # 2. Features @@ -546,11 +590,12 @@ desired aspects. An example is shown below. hazelcast::client::HazelcastClient hz(config); // Connects to the cluster ``` -Refer to `ClientConfig` class documentation at [Hazelcast C++ Client API Docs](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/) for details. +See the `ClientConfig` class documentation at [Hazelcast C++ Client API Docs](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/) for details. # 4. Serialization -Serialization is the process of converting an object into a stream of bytes to store the object in memory, a file or database, or transmit it through network. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization. Hazelcast offers you its own native serialization methods. You will see these methods throughout the chapter. +Serialization is the process of converting an object into a stream of bytes to store the object in the memory, a file or database, or transmit it through the network. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization. Hazelcast offers you its own native serialization methods. You will see these methods throughout this chapter. + Hazelcast serializes all your objects before sending them to the server. The `unsigned char` (`byte`), `bool`, `char`, `short`, `int32_t`, `int64_t`, `float`, `double`, `std::string` types are serialized natively and you cannot override this behavior. The following table is the conversion of types for Java server side. @@ -566,9 +611,9 @@ Hazelcast serializes all your objects before sending them to the server. The `un | double | Double | | std::string | String | -Vector of the above types can be serialized by default as `boolean[]`, `byte[]`, `short[]`, `int[]`, `float[]`, `double[]`, `long[]` and `string[]` for Java server side respectively. +Vector of the above types can be serialized as `boolean[]`, `byte[]`, `short[]`, `int[]`, `float[]`, `double[]`, `long[]` and `string[]` for the Java server side, respectively. -If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [IdentifiedDataSerializable Serialization](#1-identifieddataserializable-serialization) and [Portable Serialization](#2-portable-serialization). +If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [`IdentifiedDataSerializable` Serialization](#1-identifieddataserializable-serialization) and [`Portable` Serialization](#2-portable-serialization). On top of all, if you want to use your own serialization type, you can use a [Custom Serialization](#3-custom-serialization). @@ -584,9 +629,7 @@ If all the above fails, then `exception::HazelcastSerializationException` except ## 4.1. IdentifiedDataSerializable Serialization -For a faster serialization of objects, Hazelcast recommends to implement IdentifiedDataSerializable interface. - -Here is an example of an object implementing IdentifiedDataSerializable interface: +For a faster serialization of objects, Hazelcast recommends to implement the `IdentifiedDataSerializable` interface. The following is an example of an object implementing this interface: ```C++ class Employee : public serialization::IdentifiedDataSerializable { @@ -616,10 +659,9 @@ private: std::string name; }; ``` +The `IdentifiedDataSerializable` interface uses `getClassId()` and `getFactoryId()` to reconstitute the object. To complete the implementation, `DataSerializableFactory` should also be implemented and registered into `SerializationConfig` which can be accessed from `clientConfig.getSerializationConfig().addDataSerializableFactory`. The factory's responsibility is to return an instance of the right `IdentifiedDataSerializable` object, given the `classId`. -IdentifiedDataSerializable uses `getClassId()` and `getFactoryId()` to reconstitute the object. To complete the implementation `DataSerializableFactory` should also be implemented and registered into `SerializationConfig` which can be accessed from `clientConfig.getSerializationConfig().addDataSerializableFactory`. The factory's responsibility is to return an instance of the right `IdentifiedDataSerializable` object, given the class id. - -A sample `IdentifiedDataSerializableFactory` could be implemented as following: +A sample `DataSerializableFactory` could be implemented as follows: ```C++ class SampleDataSerializableFactory : public serialization::DataSerializableFactory { @@ -648,23 +690,23 @@ The last step is to register the `IdentifiedDataSerializableFactory` to the `Ser new SampleDataSerializableFactory())); ``` -Note that the id that is passed to the `SerializationConfig` is same as the `factoryId` that `Employee` object returns. +Note that the ID that is passed to the `SerializationConfig` is same as the `factoryId` that the `Employee` object returns. ## 4.2. Portable Serialization -As an alternative to the existing serialization methods, Hazelcast offers Portable serialization. To use it, you need to implement `Portable` interface. Portable serialization has the following advantages: +As an alternative to the existing serialization methods, Hazelcast offers portable serialization. To use it, you need to implement the `Portable` interface. Portable serialization has the following advantages: -- Supporting multiversion of the same object type -- Fetching individual fields without having to rely on reflection -- Querying and indexing support without de-serialization and/or reflection +- Supporting multiversion of the same object type. +- Fetching individual fields without having to rely on the reflection. +- Querying and indexing support without deserialization and/or reflection. -In order to support these features, a serialized Portable object contains meta information like the version and the concrete location of the each field in the binary data. This way Hazelcast is able to navigate in the binary data and de-serialize only the required field without actually de-serializing the whole object which improves the Query performance. +In order to support these features, a serialized `Portable` object contains meta information like the version and concrete location of the each field in the binary data. This way Hazelcast is able to navigate in the binary data and deserialize only the required field without actually deserializing the whole object which improves the query performance. -With multiversion support, you can have two nodes where each of them having different versions of the same object and Hazelcast will store both meta information and use the correct one to serialize and de-serialize Portable objects depending on the node. This is very helpful when you are doing a rolling upgrade without shutting down the cluster. +With multiversion support, you can have two members where each of them having different versions of the same object, and Hazelcast will store both meta information and use the correct one to serialize and deserialize portable objects depending on the member. This is very helpful when you are doing a rolling upgrade without shutting down the cluster. -Also note that Portable serialization is totally language independent and is used as the binary protocol between Hazelcast server and clients. +Also note that portable serialization is totally language independent and is used as the binary protocol between Hazelcast server and clients. -A sample Portable implementation of a `PortableSerializableSample` class will look like the following: +A sample portable implementation of a `PortableSerializableSample` class looks like the following: ```C++ class PortableSerializableSample : public serialization::Portable { @@ -698,9 +740,9 @@ private: }; ``` -Similar to `Portable`, a Portable object must provide `classId` and `factoryId`. The factory object will be used to create the Portable object given the classId. +Similar to `IdentifiedDataSerializable`, a `Portable` object must provide `classId` and `factoryId`. The factory object will be used to create the `Portable` object given the `classId`. -A sample `PortableFactory` could be implemented as following: +A sample `PortableFactory` could be implemented as follows: ```C++ class SamplePortableFactory : public serialization::PortableFactory { @@ -728,13 +770,13 @@ The last step is to register the `PortableFactory` to the `SerializationConfig`. new SamplePortableFactory())); ``` -Note that the id that is passed to the `SerializationConfig` is same as the `factoryId` that `PortableSerializableSample` object returns. +Note that the ID that is passed to the `SerializationConfig` is same as the `factoryId` that `PortableSerializableSample` object returns. ## 4.3. Custom Serialization Hazelcast lets you plug a custom serializer to be used for serialization of objects. -Let's say you have an object `Musician` and you would like to customize the serialization. The reason may be you want to use an external serializer for only one object. +Let's say you have an object `Musician` and you would like to customize the serialization. The reason might be that you want to use an external serializer for only one object. ```C++ class C++ { @@ -809,13 +851,12 @@ From now on, Hazelcast will use `MusicianSerializer` to serialize `Musician` obj The global serializer is identical to custom serializers from the implementation perspective. The global serializer is registered as a fallback serializer to handle all other objects if a serializer cannot be located for them. -By default, global serializer is used if the object is not `IdentifiedDataSerializable` or `Portable` or there is no custom serializer for it. +By default, global serializer is used if the object is not `IdentifiedDataSerializable` or `Portable` or there is no custom serializer for it. -**Use cases** +You can use the global serialization for the following cases: -- Third party serialization frameworks can be integrated using the global serializer. - -- For your custom objects, you can implement a single serializer to handle all of them. +* Third party serialization frameworks can be integrated using the global serializer. +* For your custom objects, you can implement a single serializer to handle all of them. A sample global serializer that integrates with a third party serializer is shown below. @@ -853,7 +894,7 @@ All network related configuration of Hazelcast C++ client is performed via the ` ### Programmatic Client Network Configuration -Here is an example of configuring network for C++ Client programmatically. +Here is an example of configuring the network for C++ Client programmatically. ```C++ ClientConfig clientConfig; @@ -866,9 +907,9 @@ Here is an example of configuring network for C++ Client programmatically. clientConfig.getNetworkConfig().setConnectionAttemptLimit(5); ``` -## 5.1. Providing the Member Addresses +## 5.1. Providing Member Addresses -Address list is the initial list of cluster addresses to which the client will connect. The client uses this +Address list is the initial list of cluster addresses which the client will connect to. The client uses this list to find an alive member. Although it may be enough to give only one address of a member in the cluster (since all members communicate with each other), it is recommended that you give the addresses for all the members. @@ -880,7 +921,7 @@ list to find an alive member. Although it may be enough to give only one address clientConfig.getNetworkConfig().addAddress(Address("10.1.1.22", 5703)); ``` -The provided list is shuffled and tried in random order. If no address is provided `127.0.0.1:5701` is tried by default. +The provided list is shuffled and tried in a random order. If no address is added to the `ClientNetworConfig`, then `127.0.0.1:5701` is tried by default. ## 5.2. Setting Smart Routing @@ -900,7 +941,7 @@ Its default value is `true` (smart client mode). ## 5.3. Enabling Redo Operation -It enables/disables redo-able operations. While sending the requests to related members, operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redoOperation` to `true`. +It enables/disables redo-able operations. While sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redoOperation` to `true`. **Programmatic:** @@ -913,8 +954,8 @@ Its default value is `false` (disabled). ## 5.4. Setting Connection Timeout -Connection timeout is the timeout value in milliseconds for members to accept client connection requests. -If server does not respond within the timeout, the client will retry to connect as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. +Connection timeout is the timeout value in milliseconds for the members to accept the client connection requests. +If the member does not respond within the timeout, the client will retry to connect as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. The following is an example configuration. @@ -959,7 +1000,7 @@ Its default value is `3000` milliseconds. ## 5.7. Enabling Client TLS/SSL -You can use TLS/SSL to secure the connection between the clients and members. If you want TLS/SSL enabled +You can use TLS/SSL to secure the connection between the clients and members. If you want to enable TLS/SSL for the client-cluster connection, you should set an SSL configuration. Please see [TLS/SSL section](#1-tlsssl). As explained in the [TLS/SSL section](#1-tlsssl), Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast C++ clients have certificate authorities used to define which members they can trust. @@ -972,15 +1013,14 @@ This chapter describes the security features of Hazelcast C++ client. These incl One of the offers of Hazelcast is the TLS/SSL protocol which you can use to establish an encrypted communication across your cluster with key stores and trust stores. -- A Java `keyStore` is a file that includes a private key and a public certificate. - -- A Java `trustStore` is a file that includes a list of certificates trusted by your application which is named certificate authority. +* A Java `keyStore` is a file that includes a private key and a public certificate. +* A Java `trustStore` is a file that includes a list of certificates trusted by your application which is named as "certificate authority". -You should set `keyStore` and `trustStore` before starting the members. See the next section how to set `keyStore` and `trustStore` on the server side. +You should set `keyStore` and `trustStore` before starting the members. See the next section on setting `keyStore` and `trustStore` on the server side. #### 6.1.1. TLS/SSL for Hazelcast Members -Hazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members). +Hazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see the [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members). #### 6.1.2. TLS/SSL for Hazelcast C++ Clients @@ -1072,11 +1112,15 @@ The path of the certificate should be correctly provided. ## 7.1. C++ Client API Overview +This chapter provides information on how you can use Hazelcast IMDG's data structures in the C++ client, after giving some basic information including an overview to the client API, operation modes of the client and how it handles the failures. + Most of the C++ API are synchronous methods. The failures are communicated via exceptions. All exceptions are derived from hazelcast::client::exception::IException base method. There are also asynchronous versions of some of the API. The asynchronous API uses hazelcast::client::Future future object. It works similar to the std::future. If you are ready to go, let's start to use Hazelcast C++ client! -The first step is configuration. You can configure the C++ client programmatically. +The first step is the configuration. You can configure the C++ client programmatically. See the [Programmatic Configuration section](#programmatic-configuration) for details. + +The following is an example on how to create a `ClientConfig` object and configure it programmatically: ```C++ ClientConfig clientConfig; @@ -1084,7 +1128,7 @@ The first step is configuration. You can configure the C++ client programmatical clientConfig.getNetworkConfig().addAddress(Address("'10.90.0.1", 5701)).addAddress(Address("'10.90.0.2", 5702)); ``` -The second step is initializing the `HazelcastClient` to be connected to the cluster. +The second step is initializing the `HazelcastClient` to be connected to the cluster: ```C++ HazelcastClient client(clientConfig); @@ -1092,9 +1136,9 @@ The second step is initializing the `HazelcastClient` to be connected to the clu }); ``` -**This client object is your gateway to access all Hazelcast distributed objects.** +**This client object is your gateway to access all the Hazelcast distributed objects.** -Let’s create a map and populate it with some data. +Let's create a map and populate it with some data, as shown below. ```C++ // Get the Distributed Map from Cluster. @@ -1107,7 +1151,7 @@ Let’s create a map and populate it with some data. map.replace("key", "value", "newvalue"); ``` -As a final step, if you are done with your client, you can shut it down as shown below. This will release all the used resources and will close connections to the cluster. +As the final step, if you are done with your client, you can shut it down as shown below. This will release all the used resources and close connections to the cluster. ```C++ // Shutdown this Hazelcast Client @@ -1118,52 +1162,41 @@ The client object destructor also shuts down the client upon destruction if you ## 7.2. C++ Client Operation Modes -The client has two operation modes because of the distributed nature of the data and cluster. +The client has two operation modes because of the distributed nature of the data and cluster: smart and unisocket. ### 7.2.1. Smart Client -In the smart mode, clients connect to each cluster member. Since each data partition uses the well known and consistent hashing algorithm, each client can send an operation to the relevant cluster member, which increases the overall throughput and efficiency. Smart mode is the default mode. - +In the smart mode, the clients connect to each cluster member. Since each data partition uses the well known and consistent hashing algorithm, each client can send an operation to the relevant cluster member, which increases the overall throughput and efficiency. Smart mode is the default mode. ### 7.2.2. Unisocket Client -For some cases, the clients can be required to connect to a single member instead of each member in the cluster. Firewalls, security, or some custom networking issues can be the reason for these cases. +For some cases, the clients can be required to connect to a single member instead of each member in the cluster. Firewalls, security or some custom networking issues can be the reason for these cases. In the unisocket client mode, the client will only connect to one of the configured addresses. This single member will behave as a gateway to the other members. For any operation requested from the client, it will redirect the request to the relevant member and return the response back to the client returned from this member. ## 7.3. Handling Failures -There are two main failure cases you should be aware of, and configurations you can perform to achieve proper behavior. +There are two main failure cases you should be aware of. Below sections explain these and the configurations you can perform to achieve proper behavior. ### 7.3.1. Handling Client Connection Failure -While the client is trying to connect initially to one of the members, some members might be not available. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `ClientNetworkConfig::getConnectionAttemptLimit()` times. +While the client is trying to connect initially to one of the members in the `ClientNetworkConfig::getAddresses()`, all the members might not be available. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `connectionAttemptLimit` times. -You can configure `connectionAttemptLimit` for the number of times you want the client to retry connecting. Please see [Setting Connection Attempt Limit](#5-setting-connection-attempt-limit). +You can configure `connectionAttemptLimit` for the number of times you want the client to retry connecting. See the [Setting Connection Attempt Limit section](#5-setting-connection-attempt-limit). The client executes each operation through the already established connection to the cluster. If this connection(s) disconnects or drops, the client will try to reconnect as configured. ### 7.3.2. Handling Retry-able Operation Failure -While sending the requests to related members, operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redoOperation` to `true`. Please see [Enabling Redo Operation](#3-enabling-redo-operation). - -You can set a timeout for retrying the operations sent to a member. This can be provided by using the property `hazelcast.client.invocation.timeout.seconds` (`ClientProperties::INVOCATION_TIMEOUT_SECONDS`) using `ClientConfig::setProperty()` API using the property as the key. The default value for this property is 120 seconds. The client will retry an operation within this given period, of course, if it is a read-only operation or you enabled the `redoOperation` as stated in the above paragraph. This timeout value is important when there is a failure resulted by either of the following causes: +While sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retrying for the other operations, you can set the `redoOperation` to `true`. See the [Enabling Redo Operation section](#3-enabling-redo-operation). -- Member throws an exception. +You can set a timeout for retrying the operations sent to a member. This can be provided by using the property `hazelcast.client.invocation.timeout.seconds` in `ClientConfig.properties`. The client will retry an operation within this given period, of course, if it is a read-only operation or you enabled the `redoOperation` as stated in the above paragraph. This timeout value is important when there is a failure resulted by either of the following causes: -- Connection between the client and member is closed. +* Member throws an exception. +* Connection between the client and member is closed. +* Client’s heartbeat requests are timed out. -- Client’s heartbeat requests are timed out. - -When a connection problem occurs, an operation is retried if it is certain that it has not run on the member yet or if it is idempotent such as a read-only operation, i.e., retrying does not have a side effect. If it is not certain whether the operation has run on the member, then the non-idempotent operations are not retried. However, as explained in the first paragraph of this section, you can force all client operations to be retried (`redoOperation`) when there is a connection failure between the client and member. But in this case, you should know that some operations may run multiple times causing conflicts. For example, assume that your client sent a `queue.offer` operation to the member, and then the connection is lost. Since there will be no response for this operation, you will not now whether it has run on the member or not. If you enabled `redoOperation`, it means this operation may run again, which may cause two instances of the same object in the queue. - -When invocation is being retried, the client may wait some time before it retries again. This retry wait time is configurable with this configuration property:`hazelcast.client.invocation.retry.pause.millis` The default retry wait time is 1 second. - -## 7.3.3 Client Backpressure -Sometimes, e.g., when your servers are overloaded, you may want to slow down the client operations to the cluster. Then the client can be configured to wait until number of outstanding invocations whose reponses are not received to become less than a certain number. This is called "Client Backpressure". By default, the backpressure is disabled. There are a few properties which control the backpressure. These client configuration properties are: - -- "hazelcast.client.max.concurrent.invocations" : The maximum number of concurrent invocations allowed. To prevent the system from overloading, you can apply a constraint on the number of concurrent invocations. If the maximum number of concurrent invocations has been exceeded and a new invocation comes in, then Hazelcast will throw HazelcastOverloadException. By default this property is configured as INT32_MAX. -- "hazelcast.client.invocation.backoff.timeout.millis": Controls the maximum timeout in millis to wait for an invocation space to be available. If an invocation can't be made because there are too many pending invocations, then an exponential backoff is done to give the system time to deal with the backlog of invocations. This property controls how long an invocation is allowed to wait before getting a HazelcastOverloadException. When set to -1 then HazelcastOverloadException is thrown immediately without any waiting. This is the default value. +When a connection problem occurs, an operation is retried if it is certain that it has not run on the member yet or if it is idempotent such as a read-only operation, i.e., retrying does not have a side effect. If it is not certain whether the operation has run on the member, then the non-idempotent operations are not retried. However, as explained in the first paragraph of this section, you can force all the client operations to be retried (`redoOperation`) when there is a connection failure between the client and member. But in this case, you should know that some operations may run multiple times causing conflicts. For example, assume that your client sent a `queue.offer` operation to the member and then the connection is lost. Since there will be no response for this operation, you will not know whether it has run on the member or not. If you enabled `redoOperation`, it means this operation may run again, which may cause two instances of the same object in the queue. ## 7.3.4 Client Connection Strategy Hazelcast client-cluster connection and reconnection strategy can be configured. Sometimes, you may not want your application to wait for the client to connect to the cluster, you may just want to get the client and let the client connect in the background. This is configured by: @@ -1190,11 +1223,11 @@ Possible values for the ReconnectMode are: ## 7.4. Using Distributed Data Structures -Most of the Distributed Data Structures are supported by the C++ client. In this chapter, you will learn how to use these distributed data structures. +Most of the distributed data structures are supported by the C++ client. In this chapter, you will learn how to use these distributed data structures. ### 7.4.1. Using Map -Hazelcast Map (`IMap`) is a distributed map. Through C++ client, you can perform operations like reading and writing from/to a Hazelcast Map with the well known get and put methods. For details refer to [Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#map) in the Hazelcast IMDG Reference Manual. +Hazelcast Map (`IMap`) is a distributed map. Through the C++ client, you can perform operations like reading and writing from/to a Hazelcast Map with the well known get and put methods. For details, see the [Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#map) in the Hazelcast IMDG Reference Manual. A Map usage example is shown below. @@ -1211,7 +1244,7 @@ A Map usage example is shown below. ### 7.4.2. Using MultiMap -Hazelcast `MultiMap` is a distributed and specialized map where you can store multiple values under a single key. For details refer to [MultiMap section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#multimap) in the Hazelcast IMDG Reference Manual. +Hazelcast `MultiMap` is a distributed and specialized map where you can store multiple values under a single key. For details, see the [MultiMap section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#multimap) in the Hazelcast IMDG Reference Manual. A MultiMap usage example is shown below. @@ -1231,9 +1264,9 @@ A MultiMap usage example is shown below. multiMap.remove("my-key", "value2"); // Shutdown this Hazelcast Client ``` -### 7.4.3. Using ReplicatedMap +### 7.4.3. Using Replicated Map -Hazelcast `ReplicatedMap` is a distributed key-value data structure where the data is replicated to all members in the cluster. It provides full replication of entries to all members for high speed access. For details refer to [Replicated Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#replicated-map) in the Hazelcast IMDG Reference Manual. +Hazelcast `ReplicatedMap` is a distributed key-value data structure where the data is replicated to all members in the cluster. It provides full replication of entries to all members for high speed access. For details, see the [Replicated Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#replicated-map) in the Hazelcast IMDG Reference Manual. A Replicated Map usage example is shown below. @@ -1246,7 +1279,7 @@ A Replicated Map usage example is shown below. ### 7.4.4. Using Queue -Hazelcast Queue(`IQueue`) is a distributed queue which enables all cluster members to interact with it. For details refer to [Queue section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#queue) in the Hazelcast IMDG Reference Manual. +Hazelcast Queue (`IQueue`) is a distributed queue which enables all cluster members to interact with it. For details, see the [Queue section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#queue) in the Hazelcast IMDG Reference Manual. A Queue usage example is shown below. @@ -1267,7 +1300,7 @@ A Queue usage example is shown below. ## 7.4.5. Using Set -Hazelcast Set(`ISet`) is a distributed set which does not allow duplicate elements. For details refer to [Set section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#set) in the Hazelcast IMDG Reference Manual. +Hazelcast Set (`ISet`) is a distributed set which does not allow duplicate elements. For details, see the [Set section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#set) in the Hazelcast IMDG Reference Manual. A Set usage example is shown below. @@ -1290,7 +1323,7 @@ A Set usage example is shown below. ## 7.4.6. Using List -Hazelcast List(`IList`) is distributed list which allows duplicate elements and preserves the order of elements. For details refer to [List section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#list) in the Hazelcast IMDG Reference Manual. +Hazelcast List (`IList`) is a distributed list which allows duplicate elements and preserves the order of elements. For details, see the [List section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#list) in the Hazelcast IMDG Reference Manual. A List usage example is shown below. @@ -1311,7 +1344,7 @@ A List usage example is shown below. ## 7.4.7. Using Ringbuffer -Hazelcast `Ringbuffer` is a replicated but not partitioned data structure that stores its data in a ring-like structure. You can think of it as a circular array with a given capacity. Each Ringbuffer has a tail and a head. The tail is where the items are added and the head is where the items are overwritten or expired. You can reach each element in a Ringbuffer using a sequence ID, which is mapped to the elements between the head and tail (inclusive) of the Ringbuffer. For details refer to [Ringbuffer section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#ringbuffer) in the Hazelcast IMDG Reference Manual. +Hazelcast `Ringbuffer` is a replicated but not partitioned data structure that stores its data in a ring-like structure. You can think of it as a circular array with a given capacity. Each Ringbuffer has a tail and a head. The tail is where the items are added and the head is where the items are overwritten or expired. You can reach each element in a Ringbuffer using a sequence ID, which is mapped to the elements between the head and tail (inclusive) of the Ringbuffer. For details, see the [Ringbuffer section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#ringbuffer) in the Hazelcast IMDG Reference Manual. A Ringbuffer usage example is shown below. @@ -1330,7 +1363,7 @@ A Ringbuffer usage example is shown below. ## 7.4.8. Using Reliable Topic -Hazelcast `ReliableTopic` is a distributed topic implementation backed up by the `Ringbuffer` data structure. For details refer to [Reliable Topic section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#reliable-topic) in the Hazelcast IMDG Reference Manual. +Hazelcast `ReliableTopic` is a distributed topic implementation backed up by the `Ringbuffer` data structure. For details, see the [Reliable Topic section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#reliable-topic) in the Hazelcast IMDG Reference Manual. A Reliable Topic usage example is shown below. @@ -1405,7 +1438,7 @@ int main() { ## 7.4.9 Using Lock -Hazelcast Lock(`ILock`) is a distributed lock implementation. You can synchronize Hazelcast members and clients using a Lock. For details refer to [Lock section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#lock) in the Hazelcast IMDG Reference Manual. +Hazelcast Lock (`ILock`) is a distributed lock implementation. You can synchronize Hazelcast members and clients using a Lock. For details, see the [Lock section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#lock) in the Hazelcast IMDG Reference Manual. A Lock usage example is shown below. @@ -1424,7 +1457,7 @@ A Lock usage example is shown below. ## 7.4.10 Using Atomic Long -Hazelcast Atomic Long(`IAtomicLong`) is the distributed long which offers most of the operations such as `get`, `set`, `getAndSet`, `compareAndSet` and `incrementAndGet`. For details refer to [Atomic Long section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#iatomiclong) in the Hazelcast IMDG Reference Manual. +Hazelcast Atomic Long (`IAtomicLong`) is the distributed long which offers most of the operations such as `get`, `set`, `getAndSet`, `compareAndSet` and `incrementAndGet`. For details, see the [Atomic Long section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#iatomiclong) in the Hazelcast IMDG Reference Manual. An Atomic Long usage example is shown below. @@ -1440,7 +1473,7 @@ An Atomic Long usage example is shown below. ## 7.4.11 Using Semaphore -Hazelcast Semaphore(`ISemaphore`) is a distributed semaphore implementation. For details refer to [Semaphore section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#isemaphore) in the Hazelcast IMDG Reference Manual. +Hazelcast Semaphore (`ISemaphore`) is a distributed semaphore implementation. For details, see the [Semaphore section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#isemaphore) in the Hazelcast IMDG Reference Manual. A Semaphore usage example is shown below. @@ -1453,7 +1486,7 @@ A Semaphore usage example is shown below. ## 7.4.12 Using PN Counter -Hazelcast `PNCounter` (Positive-Negative Counter) is a CRDT positive-negative counter implementation. It is an eventually consistent counter given there is no member failure. For details refer to [PN Counter section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter) in the Hazelcast IMDG Reference Manual. +Hazelcast `PNCounter` (Positive-Negative Counter) is a CRDT positive-negative counter implementation. It is an eventually consistent counter given there is no member failure. For details, see the [PN Counter section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter) in the Hazelcast IMDG Reference Manual. A PN Counter usage example is shown below. @@ -1473,7 +1506,7 @@ A PN Counter usage example is shown below. ## 7.4.13 Using Flake ID Generator -Hazelcast `FlakeIdGenerator` is used to generate cluster-wide unique identifiers. Generated identifiers are long primitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to `2^63-1 (maximum signed long value)`. For details refer to [FlakeIdGenerator section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator) in the Hazelcast IMDG Reference Manual. +Hazelcast `FlakeIdGenerator` is used to generate cluster-wide unique identifiers. Generated identifiers are long primitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to `2^63-1` (maximum signed long value). For details, see the [FlakeIdGenerator section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator) in the Hazelcast IMDG Reference Manual. A Flake ID Generator usage example is shown below. @@ -1482,31 +1515,30 @@ A Flake ID Generator usage example is shown below. std::cout << "Id : " << generator.newId() << std::endl; // Id : ``` - ## 7.5. Distributed Events - This chapter explains when various events are fired and describes how you can add event listeners on a Hazelcast C++ client. These events can be categorized as cluster and distributed data structure events. ### 7.5.1. Cluster Events -You can add event listeners to a Hazelcast C++ client. You can configure the following listeners to listen to the events on the client side. - -`Membership Listener`: Notifies when a member joins to/leaves the cluster, or when an attribute is changed in a member. +You can add event listeners to a Hazelcast C++ client. You can configure the following listeners to listen to the events on the client side: -`Lifecycle Listener`: Notifies when the client is starting, started, shutting down, and shutdown. +* Membership Listener: Notifies when a member joins to/leaves the cluster, or when an attribute is changed in a member. +* Lifecycle Listener: Notifies when the client is starting, started, shutting down and shutdown. #### 7.5.1.1. Listening for Member Events You can add the following types of member events to the `ClusterService`. -- `memberAdded`: A new member is added to the cluster. -- `memberRemoved`: An existing member leaves the cluster. -- `memberAttributeChanged`: An attribute of a member is changed. Please refer to [Defining Member Attributes](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#defining-member-attributes) section in the Hazelcast IMDG Reference Manual to learn about member attributes. +* `memberAdded`: A new member is added to the cluster. +* `memberRemoved`: An existing member leaves the cluster. +* `memberAttributeChanged`: An attribute of a member is changed. See the [Defining Member Attributes section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#defining-member-attributes) in the Hazelcast IMDG Reference Manual to learn about member attributes. -You use HazelcastClient::getCluster() object to register for the membership listeners. There two type os listeners, InitialMembershipListener and MembershipListener. The difference is that InitialMembershipListener also gets notified when the client connects to the cluster and retrieves the whole membership list. You need to implement one of these two interfaces and register an instance of the listener to the cluster. +The `ClusterService` object exposes an `ClusterService.on()` function that allows one or more functions to be attached to the member events emitted by the object. -The following demonstrates both initial and regular membership listener registration. +You can use `Cluster` (`HazelcastClient::getCluster()`) object to register for the membership listeners. There two type of listeners, InitialMembershipListener and MembershipListener. The difference is that InitialMembershipListener also gets notified when the client connects to the cluster and retrieves the whole membership list. You need to implement one of these two interfaces and register an instance of the listener to the cluster. + +The following example demonstrates both initial and regular membership listener registration. ```C++ class MyInitialMemberListener : public hazelcast::client::InitialMembershipListener { @@ -1566,19 +1598,20 @@ The following demonstrates both initial and regular membership listener registra ... ``` -The `memberAttributeChanged` has its own type of event named `MemberAttributeEvent`. When there is an attribute change on the member, this event is fired. +The `memberAttributeChanged` has its own type of event named as `MemberAttributeEvent`. When there is an attribute change on the member, this event is fired. #### 7.5.1.3. Listening for Lifecycle Events -The Lifecycle Listener notifies for the following events: -- `starting`: A client is starting. -- `started`: A client has started. -- `shuttingDown`: A client is shutting down. -- `shutdown`: A client’s shutdown has completed. -- `clientConnected`: The client is connected to the cluster. -- `clientConnected`: The client is disconnected from the cluster. +The `LifecycleListener` interface notifies for the following events: + +* `starting`: A client is starting. +* `started`: A client has started. +* `shuttingDown`: A client is shutting down. +* `shutdown`: A client’s shutdown has completed. +* `clientConnected`: The client is connected to the cluster. +* `clientConnected`: The client is disconnected from the cluster. -The following is an example of Lifecycle Listener. +The following is an example of the `LifecycleListener` that is added to the `ClientConfig` object and its output. ```C++ class ConnectedListener : public hazelcast::client::LifecycleListener { @@ -1630,15 +1663,15 @@ Client is disconnected from the cluster ### 7.5.2. Distributed Data Structure Events -You can add event listeners to the Distributed Data Structures. +You can add event listeners to the distributed data structures. #### 7.5.2.1. Listening for Map Events You can listen to map-wide or entry-based events by using the functions in the `EntryListener` interface. To listen to these events, you need to implement the relevant `EntryListener` interface. -- An entry-based event is fired after the operations that affect a specific entry. For example, `IMap::put()`, `IMap::remove()` or `IMap::evict()`. You should use the `EntryListener` type to listen these events. An `EntryEvent` object is passed to the listener function. +An entry-based event is fired after the operations that affect a specific entry. For example, `IMap::put()`, `IMap::remove()` or `IMap::evict()`. You should use the `EntryListener` type to listen these events. An `EntryEvent` object is passed to the listener function. -Let’s take a look at the following example. +See the following example. ```C++ class MyEntryListener : public hazelcast::client::EntryListener { @@ -1688,10 +1721,9 @@ int main() { } ``` +A map-wide event is fired as a result of a map-wide operation. For example, `IMap::clear()` or `IMap::evictAll()`. You should use the `EntryListener` type to listen to these events. A `MapEvent` object is passed to the listener function. -- A map-wide event is fired as a result of a map-wide operation. For example, `IMap::clear()` or `IMap::evictAll()`. You should use the `EntryListener` type to listen these events. A `MapEvent` object is passed to the listener function. - -Let’s take a look at the following example. +See the following example. ```C++ class MyEntryListener : public hazelcast::client::EntryListener { @@ -1745,13 +1777,13 @@ int main() { ## 7.6. Distributed Computing -This chapter explains Hazelcast’s entry processor implementation. +This chapter explains how you can use Hazelcast IMDG's entry processor implementation in the C++ client. ### 7.6.1. Using EntryProcessor Hazelcast supports entry processing. An entry processor is a function that executes your code on a map entry in an atomic way. -An entry processor is a good option if you perform bulk processing on an `IMap`. Usually you perform a loop of keys-- executing `IMap.get(key)`, mutating the value, and finally putting the entry back in the map using `IMap.put(key,value)`. If you perform this process from a client or from a member where the keys do not exist, you effectively perform two network hops for each update: the first to retrieve the data and the second to update the mutated value. +An entry processor is a good option if you perform bulk processing on an `IMap`. Usually you perform a loop of keys -- executing `IMap.get(key)`, mutating the value and finally putting the entry back in the map using `IMap.put(key,value)`. If you perform this process from a client or from a member where the keys do not exist, you effectively perform two network hops for each update: the first to retrieve the data and the second to update the mutated value. If you are doing the process described above, you should consider using entry processors. An entry processor executes a read and updates upon the member where the data resides. This eliminates the costly network hops described above. @@ -1763,11 +1795,9 @@ Hazelcast sends the entry processor to each cluster member and these members app The `IMap` interface provides the following functions for entry processing: -- `executeOnKey` processes an entry mapped by a key. - -- `executeOnKeys` processes entries mapped by a list of keys. - -- `executeOnEntries` can process all entries in a map with a defined predicate. Predicate is optional. +* `executeOnKey` processes an entry mapped by a key. +* `executeOnKeys` processes entries mapped by a list of keys. +* `executeOnEntries` can process all entries in a map with a defined predicate. Predicate is optional. In the C++ client, an `EntryProcessor` should be `IdentifiedDataSerializable`, `Portable` or custom serializable, because the server should be able to deserialize it to process. @@ -1801,9 +1831,9 @@ private: }; ``` -Now, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, please see [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). +Now, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory, and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, see the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). -The following is an example code which can be the Java equivalent of entry processor in C++ client: +The following is the Java equivalent of the entry processor in C++ client given above: ```java import com.hazelcast.map.AbstractEntryProcessor; @@ -1867,7 +1897,7 @@ public class IdentifiedFactory implements DataSerializableFactory { } ``` -Note that you need to configure the `hazelcast.xml` to add your factory. And the following is the configuration for the above factory: +Now you need to configure the `hazelcast.xml` to add your factory as shown below. ```xml @@ -1881,9 +1911,9 @@ Note that you need to configure the `hazelcast.xml` to add your factory. And the ``` -The code that runs on the entries is implemented in Java on the server side. Client side entry processor is used to specify which entry processor should be called. For more details about the Java implementation of the entry processor, please see [Entry Processor section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#entry-processor) in the Hazelcast IMDG Reference Manual. +The code that runs on the entries is implemented in Java on the server side. The client side entry processor is used to specify which entry processor should be called. For more details about the Java implementation of the entry processor, see the [Entry Processor section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#entry-processor) in the Hazelcast IMDG Reference Manual. -After all implementation and starting the server where your library is added to its `CLASSPATH`, you can use the entry processor in the `IMap` functions. Let's take a look at the following example. +After the above implementations and configuration are done and you start the server where your library is added to its `CLASSPATH`, you can use the entry processor in the `IMap` functions. See the following example. ```C++ hazelcast::client::IMap map = hz.getMap("my-distributed-map"); @@ -1898,7 +1928,7 @@ Hazelcast partitions your data and spreads it across cluster of members. You can ### 7.7.1. How Distributed Query Works 1. The requested predicate is sent to each member in the cluster. -2. Each member looks at its own local entries and filters them according to the predicate. At this stage, key/value pairs of the entries are deserialized and then passed to the predicate. +2. Each member looks at its own local entries and filters them according to the predicate. At this stage, key-value pairs of the entries are deserialized and then passed to the predicate. 3. The predicate requester merges all the results coming from each member into a single set. Distributed query is highly scalable. If you add new members to the cluster, the partition count for each member is reduced and thus the time spent by each member on iterating its entries is reduced. In addition, the pool of partition threads evaluates the entries concurrently in each member, and the network traffic is also reduced since only filtered data is sent to the requester. @@ -1907,43 +1937,29 @@ Distributed query is highly scalable. If you add new members to the cluster, the There for many built-in `Predicate` implementations for your query requirements. Some of them are explained below. -- `TruePredicate`: This predicate returns true and hence includes all the entries on the response. - -- `FalsePredicate`: This predicate returns false and hence filters out all the entries in the response. - -- `EqualPredicate`: Checks if the result of an expression is equal to a given value. - -- `NotEqualPredicate`: Checks if the result of an expression is not equal to a given value. - -- `InstanceOfPredicate`: Checks if the result of an expression has a certain type. - -- `LikePredicate`: Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. - -- `ILikePredicate`: Same as LikePredicate. Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. - -- `GreaterLessPredicate`: Checks if the result of an expression is greater equal or less equal than a certain value. - -- `BetweenPredicate`: Checks if the result of an expression is between two values (start and end values are inclusive). - -- `InPredicate`: Checks if the result of an expression is an element of a certain list. - -- `NotPredicate`: Negates a provided predicate result. - -- `RegexPredicate`: Checks if the result of an expression matches some regular expression. - -- `SqlPredicate`: Query using SQL syntax. - -- `PagingPredicate`: Returns matching entries page by page. +* `TruePredicate`: This predicate returns true and hence includes all the entries on the response. +* `FalsePredicate`: This predicate returns false and hence filters out all the entries in the response. +* `EqualPredicate`: Checks if the result of an expression is equal to a given value. +* `NotEqualPredicate`: Checks if the result of an expression is not equal to a given value. +* `InstanceOfPredicate`: Checks if the result of an expression has a certain type. +* `LikePredicate`: Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. +* `ILikePredicate`: Same as LikePredicate. Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character. +* `GreaterLessPredicate`: Checks if the result of an expression is greater equal or less equal than a certain value. +* `BetweenPredicate`: Checks if the result of an expression is between two values (start and end values are inclusive). +* `InPredicate`: Checks if the result of an expression is an element of a certain list. +* `NotPredicate`: Negates a provided predicate result. +* `RegexPredicate`: Checks if the result of an expression matches some regular expression. +* `SqlPredicate`: Query using SQL syntax. +* `PagingPredicate`: Returns matching entries page by page. Hazelcast offers the following ways for distributed query purposes: -- Combining Predicates: AndPredicate, OrPredicate, NotPredicate. - -- Distributed SQL Query +* Combining Predicates: AndPredicate, OrPredicate, NotPredicate. +* Distributed SQL Query #### 7.7.1.1. Employee Map Query Example -Assume that you have an `employee` map containing values of `Employee` objects, as coded below. +Assume that you have an `employee` map containing the values of `Employee` objects, as coded below. ```C++ class Employee : public serialization::IdentifiedDataSerializable { @@ -1985,7 +2001,7 @@ private: }; ``` -Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want to save the `Employee` objects as byte arrays on the map, you don't need to implement its equivalent on the server-side. However, if you want to query on the `employee` map, server needs the `Employee` objects rather than byte array formats. Therefore, you need to implement its Java equivalent and its data serializable factory on server side for server to reconstitute the objects from binary formats. After implementing the Java class and its factory, you need to add the factory to the data serializable factories or the portable factories by giving a factory `id`. Here is the example XML configuration of the server. +Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want to save the `Employee` objects as byte arrays on the map, you don't need to implement its equivalent on the server-side. However, if you want to query on the `employee` map, the server needs the `Employee` objects rather than byte array formats. Therefore, you need to implement its Java equivalent and its data serializable factory on the server side for server to reconstitute the objects from binary formats. After implementing the Java class and its factory, you need to add the factory to the data serializable factories or the portable factories by giving a factory `id`. The following is an example declarative configuration on the server. ```xml @@ -2001,13 +2017,13 @@ Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want ``` -Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` folder in the extracted `hazelcast-.zip`. See [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). +Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). -> **NOTE: You can also make this object `Portable` and implement its Java equivalent and its portable factory on the server side. Note that querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** +> **NOTE: You can also make this object `Portable` and implement its Java equivalent and portable factory on the server side. Note that querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** #### 7.7.1.2. Querying by Combining Predicates with AND, OR, NOT -You can combine predicates by using the `and`, `or`, and `not` operators, as shown in the below example. +You can combine predicates by using the `AndPredicate`, `OrPredicate` and `NotPredicate` operators, as shown in the below example. ```C++ query::AndPredicate andPredicate; @@ -2017,11 +2033,11 @@ You can combine predicates by using the `and`, `or`, and `not` operators, as sho In the above example code, `predicate` verifies whether the entry is active and its `age` value is less than 30. This `predicate` is applied to the `employee` map using the IMap::values(const query::Predicate &predicate)` method. This method sends the predicate to all cluster members and merges the results coming from them. -> **NOTE: Predicates can also be applied to `keySet` and `entrySet` of the Hazelcast distributed map.** +> **NOTE: Predicates can also be applied to `keySet` and `entrySet` of the Hazelcast IMDG's distributed map.** #### 7.7.1.3. Querying with SQL -`SqlPredicate` takes the regular SQL `where` clause. Here is an example: +`SqlPredicate` takes the regular SQL `where` clause. See the following example: ```C++ query::SqlPredicate sqlPredicate("active AND age < 30"); @@ -2057,7 +2073,7 @@ In the above example code, `predicate` verifies whether the entry is active and **LIKE:** ` [NOT] LIKE 'expression'` -The `%` (percentage sign) is placeholder for multiple characters, an `_` (underscore) is placeholder for only one character. +The `%` (percentage sign) is the placeholder for multiple characters, an `_` (underscore) is the placeholder for only one character. - `name LIKE 'Jo%'` (true for 'Joe', 'Josh', 'Joseph' etc.) - `name LIKE 'Jo_'` (true for 'Joe'; false for 'Josh') @@ -2066,7 +2082,7 @@ The `%` (percentage sign) is placeholder for multiple characters, an `_` (unders **ILIKE:** ` [NOT] ILIKE 'expression'` -Similar to LIKE predicate but in a case-insensitive manner. +ILIKE is similar to the LIKE predicate but in a case-insensitive manner. - `name ILIKE 'Jo%'` (true for 'Joe', 'joe', 'jOe','Josh','joSH', etc.) - `name ILIKE 'Jo_'` (true for 'Joe' or 'jOE'; false for 'Josh') @@ -2077,7 +2093,7 @@ Similar to LIKE predicate but in a case-insensitive manner. ##### Querying Examples with Predicates -You can use `query::QueryConstants::getKeyAttributeName()` which stands for string `__key` attribute to perform a predicated search for entry keys. Please see the following example: +You can use the `query::QueryConstants::getKeyAttributeName()` (`__key`) attribute to perform a predicated search for entry keys. See the following example: ```C++ hazelcast::client::IMap map = hz.getMap("personMap;"); @@ -2091,7 +2107,7 @@ You can use `query::QueryConstants::getKeyAttributeName()` which stands for stri In this example, the code creates a list with the values whose keys start with the letter "F”. -You can use `query::QueryConstants::getValueAttributeName()` which stands for `this` attribute to perform a predicated search for entry values. Please see the following example: +You can use `query::QueryConstants::getValueAttributeName()` (`this`) attribute to perform a predicated search for entry values. See the following example: ```C++ hazelcast::client::IMap map = hz.getMap("personMap;"); @@ -2126,9 +2142,9 @@ The C++ client provides paging for defined predicates. With its `PagingPredicate //... ``` -If you want to sort the result before paging, you need to specify a comparator object that implements the `query::EntryComparator` interface. Also, this comparator object should be Hazelcast serializable since it will be sent to the server side. After implementing the C++ version, you need to implement the Java equivalent of the comparator and its factory. The Java equivalent of the comparator should implement `java.util.Comparator`. Note that `compare` function of the `Comparator` on the Java side is the equivalent of the `compare` function of `EntryComparator` on the C++ side. When you implement the `Comparator` and its factory, you can add them to the `CLASSPATH` of the server side. See [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). +If you want to sort the result before paging, you need to specify a comparator object that implements the `query::EntryComparator` interface. Also, this comparator object should be one of `IdentifiedDataSerializable` or `Portable` or `Custom serializable`. After implementing After implementing this object in C++, you need to implement the Java equivalent of it and its factory. The Java equivalent of the comparator should implement `java.util.Comparator`. Note that the `compare` function of `Comparator` on the Java side is the equivalent of the `sort` function of `Comparator` on the C++ side. When you implement the `Comparator` and its factory, you can add them to the `CLASSPATH` of the server side. See the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). -Also, You can access a specific page more easily with the help of the `setPage` function. This way, if you make a query for the hundredth page, for example, it will get all 100 pages at once instead of reaching the hundredth page one by one using the `nextPage` function. +Also, you can access a specific page more easily with the help of the `setPage` function. This way, if you make a query for the 100th page, for example, it will get all 100 pages at once instead of reaching the 100th page one by one using the `nextPage` function. ## 7.8. Raw Pointer API @@ -2361,7 +2377,7 @@ In order to test Hazelcast C++ client locally, you will need the following: * Maven * cmake * openssl -* Python 2 and pip +* Python 2 and pip (Needed for RemoteController used for controlling server instances) You can also pull our test docker images from docker hub using the command: `docker pull ihsan/gcc_346_ssl` This images has all the tools for building the project. @@ -2413,7 +2429,7 @@ Besides your development contributions as explained in the [Development and Test # 11. License -[Apache 2 License](https://github.com/hazelcast/hazelcast-nodejs-client/blob/master/LICENSE). +[Apache 2 License](https://github.com/hazelcast/hazelcast-cpp-client/blob/master/LICENSE). # 12. Copyright From 047a8eab3bd4998ede5725771d6789a88e8e58e5 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Wed, 7 Nov 2018 14:07:31 +0300 Subject: [PATCH 03/11] Review comment fixes. --- README.md | 204 +++++++++++++++++++++++++++++------------------------- 1 file changed, 109 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 5f94764ab5..3be95297fe 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,9 @@ * [7.3. Handling Failures](#73-handling-failures) * [7.3.1. Handling Client Connection Failure](#731-handling-client-connection-failure) * [7.3.2. Handling Retry-able Operation Failure](#732-handling-retry-able-operation-failure) - * [7.3.3. Backpressure](#732-backpressure) - * [7.3.4. Client Connection Strategy](#732-client-connection-strategy) - * [7.3.4. Client Reconnect Strategy](#732-client-reconnection-strategy) - * [7.4. Using Distributed Data Structures](#74-using-distributed-data-structures) + * [7.3.3. Backpressure](#733-backpressure) + * [7.3.4. Client Connection Strategy](#734-client-connection-strategy) + * [7.3.4.1. Configure Client Reconnect Strategy](#7341-configure-client-reconnect-strategy) * [7.4. Using Distributed Data Structures](#74-using-distributed-data-structures) * [7.4.1. Using Map](#741-using-map) * [7.4.2. Using MultiMap](#742-using-multimap) @@ -78,16 +77,16 @@ * [7.7.1.2. Querying by Combining Predicates with AND, OR, NOT](#7712-querying-by-combining-predicates-with-and-or-not) * [7.7.1.3. Querying with SQL](#7713-querying-with-sql) * [7.7.1.4. Filtering with Paging Predicates](#7714-filtering-with-paging-predicates) - * [7.8 Raw Pointer API](#raw-pointer-api) - * [7.9 Mixed Object Types Supporting HazelcastClient](#mixed-object-types-supporting-hazelcastclient) - * [7.9.1 TypedData API](#typeddata-api) + * [7.8 Raw Pointer API](#78-raw-pointer-api) + * [7.9 Mixed Object Types Supporting HazelcastClient](#79-mixed-object-types-supporting-hazelcastclient) + * [7.9.1 TypedData API](#791-typeddata-api) * [8. Development and Testing](#8-development-and-testing) * [8.1. Building and Using Client From Sources](#81-building-and-using-client-from-sources) - * [8.1.1 Mac](#811-mac) - * [8.1.2 Linux](#811-linux) - * [8.1.3 Windows](#811-windows) + * [8.1.1 Mac](#811-mac) + * [8.1.2 Linux](#812-linux) + * [8.1.3 Windows](#813-windows) * [8.2. Testing](#82-testing) - * [8.2.1 Tested Platforms](#811-tested-platforms) + * [8.2.1 Tested Platforms](#821-tested-platforms) * [8.3. Reproducing Released Libraries](#83-reproducing-released-libraries) * [9. Getting Help](#9-getting-help) * [10. Contributing](#10-contributing) @@ -217,63 +216,63 @@ You can find more information about the `hazelcast-member` tool at its GitHub [r See the [Hazelcast IMDG Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#getting-started) for more information on setting up the clusters. ## 1.3. Downloading and Installing -Download the latest C++ client library from [Hazelcast C++ Client Website](https://hazelcast.org/clients/cplusplus/). You need to download the zip file for your platform. For Linux and Windows, 32 and 64-bit libraries exist while for MacOS only has the 64-bit version. - -Unzip the file. Below is the directory structure for "Linux 64-bit ZIP" and the others are similar: - -- cpp/Linux_64/ : - - hazelcast : - - lib : The shared and static library folder. - - tls : Contains the library with TLS (SSL) support enabled. - - include : The include folder you need to include when compiling your project. - - external/include : External folder that you need to include when compiling your project. Currently the only dependency is `boost/shared_ptr.hpp`. - - boost : The external boost files for boost/shared_ptr. - - - examples : There are a number of examples in this folder for each feature. Each example produces an executable which you can run in a cluster. You may need to set the server IP addresses for the examples to run. - +Download the latest C++ client library from [Hazelcast C++ Client Website](https://hazelcast.org/clients/cplusplus/). You need to download the zip file for your platform. For Linux and Windows, 32- and 64-bit libraries exist. For MacOS, there is only 64-bit version. + +Unzip the file. Following is the directory structure for Linux 64-bit zip. The structure is similar for the other C++ client distributions. + +- `cpp/Linux_64` + - `hazelcast`: + - `lib`: Shared and static library directory. + - `tls`: Contains the library with TLS (SSL) support enabled. + - `include`: Directory you need to include when compiling your project. + - `external/include`: External directory that you need to include when compiling your project. Currently the only dependency is `boost/shared_ptr.hpp`. + - `boost`: External boost files for `boost/shared_ptr`. + - `examples`: Contains various examples for each C++ client feature. Each example produces an executable which you can run in a cluster. You may need to set the server IP addresses for the examples to run. + ### 1.3.1 Compiling Your Project -For compilation, you need to include the `hazelcast/include` and `external/include` folders in your distribution. You also need to link your application to the appropriate static or shared library. +For compilation, you need to include the `hazelcast/include` and `external/include` directories in your in your distribution. You also need to link your application to the appropriate static or shared library. -If you want to use tls feature, use the lib directory similar to: cpp/Linux_64/hazelcast/lib/tls +If you want to use the TLS feature, use the `lib` directory with TLS support enabled, e.g., `cpp/Linux_64/hazelcast/lib/tls`. #### 1.3.1.1 Mac Client -For Mac, there is one distribution: 64 bit. +For Mac, there is only 64-bit distribution. -Here is an example script to build with static library: +Here is an example script to build with the static library: `g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include cpp/Mac_64/hazelcast/lib/libHazelcastClientStatic_64.a` -Here is an example script to build with shared library: +Here is an example script to build with the shared library: `g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include -Lcpp/Mac_64/hazelcast/lib -lHazelcastClientShared_64` #### 1.3.1.2 Linux Client -For Linux, there are two distributions: 32 bit and 64 bit. +For Linux, there are 32- and 64-bit distributions. -Here is an example script to build with static library: +Here is an example script to build with the static library: `g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` -Here is an example script to build with shared library: +Here is an example script to build with the shared library: `g++ main.cpp -lpthread -Wl,–no-as-needed -lrt -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClientShared_64` -Please add the **__STDC_LIMIT_MACROS** and **__STDC_CONSTANT_MACROS** compilation flags for environments for which the compilation fails with error "INT32_MAX" could not be determined. For example: +Please add the `__STDC_LIMIT_MACROS` and `__STDC_CONSTANT_MACROS` compilation flags for the environments for which the compilation fails with the error `INT32_MAX could not be determined`. The following is an example command to add these flags: `g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` #### 1.3.1.3 Windows Client -For Windows, there are two distributions; 32 bit and 64 bit. The static library is located in a folder named "static" while the dynamic library(dll) is in the folder named as "shared". +For Windows, there are 32- and 64-bit distributions. The static library is in the `static` directory and the dynamic library (`dll`) is in the `shared` directory. -When compiling for Windows environment the user should specify one of the following flags: - HAZELCAST_USE_STATIC: You want the application to use the static Hazelcast library. - HAZELCAST_USE_SHARED: You want the application to use the shared Hazelcast library. +When compiling for Windows environment, you should specify one of the following flags: + +- `HAZELCAST_USE_STATIC`: You want the application to use the static Hazelcast library. +- `HAZELCAST_USE_SHARED`: You want the application to use the shared Hazelcast library. ## 1.4. Basic Configuration @@ -432,11 +431,13 @@ Congratulations! You just started a Hazelcast C++ Client. Let's manipulate a distributed map on a cluster using the client. -Save the following file as `IT.cpp`, compile it using a command similar to (Linux g++ compilation is used for demonstration): +Save the following file as `IT.cpp` and compile it using a command similar to the following (Linux g++ compilation is used for demonstration): + ```C++ g++ IT.cpp -o IT -lpthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64 ``` -Then, you can run the application as +Then, you can run the application using the following command: + ``` ./IT ``` @@ -475,7 +476,7 @@ Bob is in IT department You see this example puts all the IT personnel into a cluster-wide `personnelMap` and then prints all the known personnel. -Now create a`Sales.cpp` and compile and run it. +Now create a `Sales.cpp` file, compile and run it as shown below. **Compile:** @@ -483,7 +484,9 @@ Now create a`Sales.cpp` and compile and run it. g++ Sales.cpp -o Sales -lpthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64 ``` **Run** -You can run the application using the following command: + +Then, you can run the application using the following command: + ``` ./Sales ``` @@ -531,9 +534,9 @@ That is because our map lives in the cluster and no matter which client we use, ## 1.6. Code Samples -See Hazelcast C++ [code samples](https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples) for more examples. +See the Hazelcast C++ [code samples](https://github.com/hazelcast/hazelcast-cpp-client/tree/master/examples) for more examples. -You can also see the Hazelcast C++ [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/). +You can also see the Hazelcast C++ client [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/). # 2. Features @@ -590,7 +593,7 @@ desired aspects. An example is shown below. hazelcast::client::HazelcastClient hz(config); // Connects to the cluster ``` -See the `ClientConfig` class documentation at [Hazelcast C++ Client API Docs](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/) for details. +See the `ClientConfig` class reference at the Hazelcast C++ client [API Documentation](https://docs.hazelcast.org/docs/clients/cpp/3.10.1/html/classhazelcast_1_1client_1_1_client_config.html) for details. # 4. Serialization @@ -613,19 +616,19 @@ Hazelcast serializes all your objects before sending them to the server. The `un Vector of the above types can be serialized as `boolean[]`, `byte[]`, `short[]`, `int[]`, `float[]`, `double[]`, `long[]` and `string[]` for the Java server side, respectively. -If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [`IdentifiedDataSerializable` Serialization](#1-identifieddataserializable-serialization) and [`Portable` Serialization](#2-portable-serialization). +If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [`IdentifiedDataSerializable` Serialization](#41-identifieddataserializable-serialization) and [`Portable` Serialization](#42-portable-serialization). -On top of all, if you want to use your own serialization type, you can use a [Custom Serialization](#3-custom-serialization). +On top of all, if you want to use your own serialization type, you can use a [Custom Serialization](#43-custom-serialization). When Hazelcast serializes an object into Data (byte array): 1. It first checks whether the object pointer is NULL. -2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type id for the built-in types such as int32_t, bool, short, byte, etc. If it does not match the buil-in types, it may match the Hazelcast serialization types IdentifiedDataSerializable, Portable or custom types. The matching is done based on method parameter matching. +2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type ID for the built-in types such as int32_t, bool, short and byte. If it does not match any of the built-in types, it may match the types offered by Hazelcast, i.e., IdentifiedDataSerializable, Portable or custom. The matching is done based on method parameter matching. 3. If the above check fails, Hazelcast will use the registered Global Serializer if one exists. -If all the above fails, then `exception::HazelcastSerializationException` exception is thrown. +If all the above fails, then `exception::HazelcastSerializationException` is thrown. ## 4.1. IdentifiedDataSerializable Serialization @@ -834,7 +837,9 @@ public: }; ``` -Note that the serializer `getHazelcastTypeId` method must must return a unique id as Hazelcast will use it to lookup the `MusicianSerializer` while it deserializes the object. Now the last required step is to register the `MusicianSerializer` to the configuration. +Note that the serializer `getHazelcastTypeId` method must return a unique `id` as Hazelcast will use it to lookup the `MusicianSerializer` while it deserializes the object. + +Now the last required step is to register the `MusicianSerializer` to the configuration. **Programmatic Configuration:** @@ -890,7 +895,7 @@ You should register the global serializer in the configuration. # 5. Setting Up Client Network -All network related configuration of Hazelcast C++ client is performed via the `ClientNetworkConfig` when using programmatic configuration. Let’s first give an example. +All network related configuration of Hazelcast C++ client is performed programmatically via the `ClientNetworkConfig` object. The following is an example configuration. ### Programmatic Client Network Configuration @@ -1001,9 +1006,9 @@ Its default value is `3000` milliseconds. ## 5.7. Enabling Client TLS/SSL You can use TLS/SSL to secure the connection between the clients and members. If you want to enable TLS/SSL -for the client-cluster connection, you should set an SSL configuration. Please see [TLS/SSL section](#1-tlsssl). +for the client-cluster connection, you should set an SSL configuration. Please see [TLS/SSL section](#61-tlsssl). -As explained in the [TLS/SSL section](#1-tlsssl), Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast C++ clients have certificate authorities used to define which members they can trust. +As explained in the [TLS/SSL section](#61-tlsssl), Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast C++ clients have certificate authorities used to define which members they can trust. # 6. Securing Client Connection @@ -1020,11 +1025,19 @@ You should set `keyStore` and `trustStore` before starting the members. See the #### 6.1.1. TLS/SSL for Hazelcast Members -Hazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see the [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members). +Hazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see the [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members) in the Hazelcast IMDG Reference Manual. #### 6.1.2. TLS/SSL for Hazelcast C++ Clients -You need to provide compile flag -DHZ_BUILD_WITH_SSL when compiling since TLS feature depends on openssl library. You need the openssl development library installed in your development environment. You need to enable the SSL config in client network config. Furthermore, you should specify a correct path to the CA verification file for the trusted server. This path can be relative to the executable working directory. You can set the protocol type. The default protocol is TLSv1.2. The SSL config also lets you set the cipher suite to be used. +To use TLS/SSL with your Hazelcast C++ client, you should perform the following: + +* Provide the compile flag `-DHZ_BUILD_WITH_SSL` when compiling, since the TLS feature depends on OpenSSL library. +* Install the OpenSSL library to your development environment. +* Enable the SSL in the client network configuration. +* Specify the correct path to the CA verification file for the trusted server. This path can be relative to the executable working directory. +* If needed, set the cipher suite to be used via the SSL configuration. + +You can set the protocol type. If not set, the configuration uses `tlsv12` (TLSv1.2) as the default protocol type and version.. The following is an example configuration. @@ -1105,8 +1118,7 @@ You can set the protocol as one of the following values: ``` sslv2, sslv3, tlsv1, sslv23, tlsv11, tlsv12 ``` -The default value for the protocol if not set is tlsv12. The SSLConfig.setEnabled should be called explicitly to enable the SSL. -The path of the certificate should be correctly provided. +The SSLConfig.setEnabled should be called explicitly to enable the SSL. The path of the certificate should be correctly provided. # 7. Using C++ Client with Hazelcast IMDG @@ -1114,11 +1126,11 @@ The path of the certificate should be correctly provided. This chapter provides information on how you can use Hazelcast IMDG's data structures in the C++ client, after giving some basic information including an overview to the client API, operation modes of the client and how it handles the failures. -Most of the C++ API are synchronous methods. The failures are communicated via exceptions. All exceptions are derived from hazelcast::client::exception::IException base method. There are also asynchronous versions of some of the API. The asynchronous API uses hazelcast::client::Future future object. It works similar to the std::future. +Most of the methods in C++ API are synchronous. The failures are communicated via exceptions. All exceptions are derived from the `hazelcast::client::exception::IException` base method. There are also asynchronous versions of some methods in the API. The asynchronous ones use the `hazelcast::client::Future` future object. It works similar to the `std::future`. If you are ready to go, let's start to use Hazelcast C++ client! -The first step is the configuration. You can configure the C++ client programmatically. See the [Programmatic Configuration section](#programmatic-configuration) for details. +The first step is the configuration. You can configure the C++ client programmatically. See the [Programmatic Configuration section](#311-programmatic-configuration) for details. The following is an example on how to create a `ClientConfig` object and configure it programmatically: @@ -1182,13 +1194,13 @@ There are two main failure cases you should be aware of. Below sections explain While the client is trying to connect initially to one of the members in the `ClientNetworkConfig::getAddresses()`, all the members might not be available. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `connectionAttemptLimit` times. -You can configure `connectionAttemptLimit` for the number of times you want the client to retry connecting. See the [Setting Connection Attempt Limit section](#5-setting-connection-attempt-limit). +You can configure `connectionAttemptLimit` for the number of times you want the client to retry connecting. See the [Setting Connection Attempt Limit section](#55-setting-connection-attempt-limit). The client executes each operation through the already established connection to the cluster. If this connection(s) disconnects or drops, the client will try to reconnect as configured. ### 7.3.2. Handling Retry-able Operation Failure -While sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retrying for the other operations, you can set the `redoOperation` to `true`. See the [Enabling Redo Operation section](#3-enabling-redo-operation). +While sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retrying for the other operations, you can set the `redoOperation` to `true`. See the [Enabling Redo Operation section](#53-enabling-redo-operation). You can set a timeout for retrying the operations sent to a member. This can be provided by using the property `hazelcast.client.invocation.timeout.seconds` in `ClientConfig.properties`. The client will retry an operation within this given period, of course, if it is a read-only operation or you enabled the `redoOperation` as stated in the above paragraph. This timeout value is important when there is a failure resulted by either of the following causes: @@ -1199,27 +1211,28 @@ You can set a timeout for retrying the operations sent to a member. This can be When a connection problem occurs, an operation is retried if it is certain that it has not run on the member yet or if it is idempotent such as a read-only operation, i.e., retrying does not have a side effect. If it is not certain whether the operation has run on the member, then the non-idempotent operations are not retried. However, as explained in the first paragraph of this section, you can force all the client operations to be retried (`redoOperation`) when there is a connection failure between the client and member. But in this case, you should know that some operations may run multiple times causing conflicts. For example, assume that your client sent a `queue.offer` operation to the member and then the connection is lost. Since there will be no response for this operation, you will not know whether it has run on the member or not. If you enabled `redoOperation`, it means this operation may run again, which may cause two instances of the same object in the queue. ## 7.3.4 Client Connection Strategy -Hazelcast client-cluster connection and reconnection strategy can be configured. Sometimes, you may not want your application to wait for the client to connect to the cluster, you may just want to get the client and let the client connect in the background. This is configured by: +Hazelcast client-cluster connection and reconnection strategy can be configured. Sometimes, you may not want your application to wait for the client to connect to the cluster, you may just want to get the client and let the client connect in the background. This is configured as follows: ``` ClientConfig::getConnectionStrategyConfig().setAsyncStart(bool); ``` -When this config is set true, the client creation won't wait to connect to cluster. The client instance will throw exception for any request, until it connects to cluster and become ready. -If it is set to false (the default case), HazelcastClient(const ClientConfig) will block until a cluster connection is established and it's ready to use client instance. +When this configuration is set to true, the client creation won't wait to connect to cluster. The client instance will throw an exception for any request, until it connects to the cluster and become ready. -## 7.3.5 Configure Client Reconnect Strategy -You can configure how the client should act when the client disconnects from the cluster for any reason. This is configured using the following configuration: +If it is set to false (the default case), `HazelcastClient(const ClientConfig)` will block until a cluster connection is established and it's ready to use the client instance. + +### 7.3.4.1 Configure Client Reconnect Strategy +You can configure how the client should act when the client disconnects from the cluster for any reason. This is configured as follows: ``` ClientConfig::getConnectionStrategyConfig().setReconnectMode(const hazelcast::client::config::ClientConnectionStrategyConfig::ReconnectMode &); ``` -Possible values for the ReconnectMode are: +Possible values for `ReconnectMode` are: -- OFF: Prevents reconnect to cluster after a disconnect. -- ON: Reconnect to cluster by blocking invocations. -- ASYNC: Reconnect to cluster without blocking invocations. Invocations will receive HazelcastClientOfflineException. +- `OFF`: Prevents reconnection to the cluster after a disconnect. +- `ON`: Reconnects to the cluster by blocking invocations. +- `ASYNC`: Reconnects to the cluster without blocking invocations. Invocations will receive `HazelcastClientOfflineException`. ## 7.4. Using Distributed Data Structures @@ -1831,7 +1844,7 @@ private: }; ``` -Now, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory, and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, see the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). +Now, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory, and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, see the [Adding User Library to CLASSPATH section](#123-adding-user-library-to-classpath). The following is the Java equivalent of the entry processor in C++ client given above: @@ -1935,7 +1948,7 @@ Distributed query is highly scalable. If you add new members to the cluster, the **Built-in Predicates For Query** -There for many built-in `Predicate` implementations for your query requirements. Some of them are explained below. +There are many built-in `Predicate` implementations for your query requirements. Some of them are explained below. * `TruePredicate`: This predicate returns true and hence includes all the entries on the response. * `FalsePredicate`: This predicate returns false and hence filters out all the entries in the response. @@ -1954,7 +1967,7 @@ There for many built-in `Predicate` implementations for your query requirements. Hazelcast offers the following ways for distributed query purposes: -* Combining Predicates: AndPredicate, OrPredicate, NotPredicate. +* Combining predicates with `AndPredicate`, `OrPredicate` and `NotPredicate` * Distributed SQL Query #### 7.7.1.1. Employee Map Query Example @@ -2017,7 +2030,7 @@ Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want ``` -Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath). +Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#123-adding-user-library-to-classpath). > **NOTE: You can also make this object `Portable` and implement its Java equivalent and portable factory on the server side. Note that querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** @@ -2031,7 +2044,7 @@ You can combine predicates by using the `AndPredicate`, `OrPredicate` and `NotPr std::vector activeEmployeesLessThan30 = employees.values(andPredicate); ``` -In the above example code, `predicate` verifies whether the entry is active and its `age` value is less than 30. This `predicate` is applied to the `employee` map using the IMap::values(const query::Predicate &predicate)` method. This method sends the predicate to all cluster members and merges the results coming from them. +In the above example code, `predicate` verifies whether the entry is active and its `age` value is less than 30. This `predicate` is applied to the `employee` map using the `IMap::values(const query::Predicate &predicate)` method. This method sends the predicate to all cluster members and merges the results coming from them. > **NOTE: Predicates can also be applied to `keySet` and `entrySet` of the Hazelcast IMDG's distributed map.** @@ -2150,7 +2163,7 @@ Also, you can access a specific page more easily with the help of the `setPage` When using C++ client you can have the ownership of raw pointers for the objects you create and return. This allows you to keep the objects in your library/application without any need for copy. -For each container you can use the adapter classes, whose names start with `RawPointer`, to access the raw pointers of the created objects. These adapter classes are found in `hazelcast::client::adaptor` namespace and listed below: +For each container, you can use the adapter classes, whose names start with `RawPointer`, to access the raw pointers of the created objects. These adapter classes are found in the `hazelcast::client::adaptor` namespace and listed below: - `RawPointerList` - `RawPointerQueue` @@ -2190,7 +2203,7 @@ for (size_t i = 0; i < entries->size(); ++i) { std::cout << "Finished" << std::endl; ``` -Raw pointer API uses the DataArray and EntryArray interfaces which allow late deserialization of objects. The entry in the returned array is deserialized only when it is accessed. Please see the example code below: +Raw pointer API uses the `DataArray` and `EntryArray` interfaces which allow late deserialization of objects. The entry in the returned array is deserialized only when it is accessed. Please see the example code below: ``` // No deserialization here @@ -2212,7 +2225,7 @@ std::auto_ptr releasedValue = vals->release(0); value = vals->get(0); ``` -Using raw pointer based API may improve performance if you are using the API to return multiple values such as values, keySet, and entrySet. In this case, cost of deserialization is delayed until the item is actually accessed. +Using the raw pointer based API may improve the performance if you are using the API to return multiple values such as `values`, `keySet` and `entrySet`. In this case, the cost of deserialization is delayed until the item is actually accessed. ## 7.9. Mixed Object Types Supporting HazelcastClient Sometimes, you may need to use Hazelcast data structures with objects of different types. For example, you may want to put `int`, `string`, `IdentifiedDataSerializable`, etc. objects into the same Hazelcast `IMap` data structure. You can do this by using the mixed type adopted `HazelcastClient`. You can adopt the client in this way: @@ -2221,7 +2234,7 @@ Sometimes, you may need to use Hazelcast data structures with objects of differe HazelcastClient client(config); mixedtype::HazelcastClient &hazelcastClient = client.toMixedType(); ``` -The `mixedtype::HazelcastClient` interface is designed to provide you the data structures which allows you to work with any object types in a mixed manner. For example, the interface allows you to provide the key and value type differently for each map.put call. An example usage is shown below: +The `mixedtype::HazelcastClient` interface is designed to provide you the data structures which allows you to work with any object types in a mixed manner. For example, the interface allows you to provide the key and value type differently for each `map.put` call. An example usage is shown below: ``` mixedtype::IMap map = hazelcastClient.getMap("MyMap"); @@ -2231,14 +2244,15 @@ The `mixedtype::HazelcastClient` interface is designed to provide you the data s TypedData result = map.get(3); ``` -As you can see in the above code snippet, we are putting `int`, `string` and MyCustomObject to the same map. Both the key and value can be of different type for each map.put call. +As you can see in the above code snippet, we are putting `int`, `string` and `MyCustomObject` to the same map. Both the key and value can be of different type for each `map.put` call. + +If you want to use a mixed type map with near cache, then you should use the `MixedNearCacheConfig` class and add this configuration to the `ClientConfig` using the `addMixedNearCacheConfig` method. See the below example: -If you want to use mixed type map with near cache, then you should use the MixedNearCacheConfig class and add config to the ClientConfig using the addMixedNearCacheConfig method. See the below example: ``` boost::shared_ptr nearCacheConfig(new mixedtype::config::MixedNearCacheConfig("MixedMapTestMap")); clientConfig.addMixedNearCacheConfig(nearCacheConfig); ``` -Mixed type support for near cache only exists when the in-memory format is BINARY. The OBJECT in-memory format is not supported for MixedNearCacheConfig; +Mixed type support for near cache only exists when the in-memory format is BINARY. The OBJECT in-memory format is not supported for `MixedNearCacheConfig`. The mixed type API uses the TypedData class at the user interface. @@ -2287,23 +2301,23 @@ tweak the implementation to your application's needs, you can follow the steps i For compiling with SSL support: -- You need to have the openssl (version 1.0.2) installed in your development environment: (i) Add openssl `include` directory to include directories, (ii) Add openssl `library` directory to the link directories list (This is the directory named `tls`. e.g. `cpp/Linux_64/hazelcast/lib/tls`), (iii) Set the openssl libraries to link. +- You need to have the OpenSSL (version 1.0.2) installed in your development environment: (i) add OpenSSL `include` directory to include directories, (ii) add OpenSSL `library` directory to the link directories list (this is the directory named `tls`, e.g., `cpp/Linux_64/hazelcast/lib/tls`), and (iii) set the OpenSSL libraries to link. -- You can provide your openssl installation directory to cmake using the following flags: - - -DHZ_OPENSSL_INCLUDE_DIR="Path to open installation include directory" - - -DHZ_OPENSSL_LIB_DIR="Path to openssl lib directory" +- You can provide your OpenSSL installation directory to cmake using the following flags: + - -DHZ_OPENSSL_INCLUDE_DIR="Path to OpenSSL installation include directory" + - -DHZ_OPENSSL_LIB_DIR="Path to OpenSSL lib directory" - Use -DHZ_COMPILE_WITH_SSL=ON -- Check the top level CMakeLists.txt file to see which libraries we link for openssl in which environment. - - For Mac OS and Linux, we link with "ssl" and "crypto" - - For Windows, install also the 1.1.x version of the openssl library. An example linkage for 64 bit library and release build: "libeay32MD ssleay32MD libcrypto64MD libSSL64MD". +- Check the top level CMakeLists.txt file to see which libraries we link for OpenSSL in which environment. + - For Mac OS and Linux, we link with "ssl" and "crypto". + - For Windows, install also the 1.1.x version of the OpenSSL library. An example linkage for 64-bit library and release build: "libeay32MD ssleay32MD libcrypto64MD libSSL64MD". Follow the below steps to build and install Hazelcast C++ client from its source: -1. Clone the project from github: `git clone --recursive git@github.com:hazelcast/hazelcast-cpp-client.git` We use --recursive flag for our dependency on googletest framework. -2. Create a build directory (e.g. build) from the root of the project. In the build directory, run the cmake command depending on your environment as depicted in the next sections. +1. Clone the project from GitHub using the following command: `git clone --recursive git@github.com:hazelcast/hazelcast-cpp-client.git`. We use the `--recursive` flag for our dependency on the googletest framework. +2. Create a build directory, e.g., `build`, from the root of the project. In the build directory, run the cmake command depending on your environment as depicted in the next sections. ### 8.1.1 Mac @@ -2376,10 +2390,10 @@ In order to test Hazelcast C++ client locally, you will need the following: * Java 6 or newer * Maven * cmake -* openssl +* OpenSSL * Python 2 and pip (Needed for RemoteController used for controlling server instances) -You can also pull our test docker images from docker hub using the command: `docker pull ihsan/gcc_346_ssl` This images has all the tools for building the project. +You can also pull our test docker images from the docker hub using the following command: `docker pull ihsan/gcc_346_ssl`. These images have all the tools for building the project. Following command builds and runs the tests: @@ -2388,7 +2402,7 @@ Following command builds and runs the tests: - Windows: `./testWindowsSingleCase.bat 64 SHARED Debug` ### 8.2.1 Tested Platforms -Our CI tests are run continuously on the following platforms and compilers: +Our CI tests run continuously on the following platforms and compilers: - Linux: CentOs5 gcc 3.4.6, CentOs5.11 gcc 4.1.2, centos 7 gcc 4.8.2 - Windows: Visual Studio 12 - Mac OS: Apple LLVM version 7.3.0 (clang-703.0.31) @@ -2398,7 +2412,7 @@ Our CI tests are run continuously on the following platforms and compilers: Sometimes you may want to reproduce the released library for your own compiler environment. You need to run the release script and it will produce a release folder named "cpp". Note: -- The default release scripts require that you have openssl (version 1.0.2) installed in your development environment. +- The default release scripts require that you have OpenSSL (version 1.0.2) installed in your development environment. ## Mac From 07db7d86083c4a736b311e8d4f825e1942719431 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Wed, 7 Nov 2018 15:39:10 +0300 Subject: [PATCH 04/11] More review comment fixes. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3be95297fe..19eae908a7 100644 --- a/README.md +++ b/README.md @@ -933,7 +933,7 @@ The provided list is shuffled and tried in a random order. If no address is adde Smart routing defines whether the client mode is smart or unisocket. See [C++ Client Operation Modes section](#cpp-client-operation-modes) for the description of smart and unisocket modes. -The following is example configuration. +The following is an example configuration. **Programmatic:** @@ -1118,7 +1118,7 @@ You can set the protocol as one of the following values: ``` sslv2, sslv3, tlsv1, sslv23, tlsv11, tlsv12 ``` -The SSLConfig.setEnabled should be called explicitly to enable the SSL. The path of the certificate should be correctly provided. +The `SSLConfig.setEnabled` method should be called explicitly to enable the SSL. The path of the certificate should be correctly provided. # 7. Using C++ Client with Hazelcast IMDG From 5f14273166f29a367929a0607b26640a2d1c1705 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Wed, 7 Nov 2018 16:14:29 +0300 Subject: [PATCH 05/11] Minor correction with feedback from the team. --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 19eae908a7..5e170f3b81 100644 --- a/README.md +++ b/README.md @@ -990,7 +990,7 @@ Its default value is `2`. ## 5.6. Setting Connection Attempt Period -Connection timeout period is the duration in milliseconds between the connection attempts defined by `ClientNetworkConfig::getConnectionAttemptLimit()`. +Connection attempt period is the duration in milliseconds between the connection attempts defined by `ClientNetworkConfig::getConnectionAttemptLimit()`. The following is an example configuration. @@ -1617,10 +1617,10 @@ The `memberAttributeChanged` has its own type of event named as `MemberAttribute The `LifecycleListener` interface notifies for the following events: -* `starting`: A client is starting. -* `started`: A client has started. -* `shuttingDown`: A client is shutting down. -* `shutdown`: A client’s shutdown has completed. +* `starting`: The client is starting. +* `started`: The client has started. +* `shuttingDown`: The client is shutting down. +* `shutdown`: The client’s shutdown has completed. * `clientConnected`: The client is connected to the cluster. * `clientConnected`: The client is disconnected from the cluster. @@ -1946,6 +1946,8 @@ Hazelcast partitions your data and spreads it across cluster of members. You can Distributed query is highly scalable. If you add new members to the cluster, the partition count for each member is reduced and thus the time spent by each member on iterating its entries is reduced. In addition, the pool of partition threads evaluates the entries concurrently in each member, and the network traffic is also reduced since only filtered data is sent to the requester. +If queried item is Portable, it can be queried for the fields without deserializing the data at the server side and hence no server side implementation of the queried object class will be needed. + **Built-in Predicates For Query** There are many built-in `Predicate` implementations for your query requirements. Some of them are explained below. From 9dc6134dc3d4dd3dab584296be6d750fd9805089 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Thu, 8 Nov 2018 15:03:44 +0300 Subject: [PATCH 06/11] Changed query example to use Portable and more explanation to why Portable is recommended. Added the section headings for "to be added sections" for Performance and Logging. --- README.md | 110 +++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 5e170f3b81..34dce30037 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,12 @@ * [7.7.1.2. Querying by Combining Predicates with AND, OR, NOT](#7712-querying-by-combining-predicates-with-and-or-not) * [7.7.1.3. Querying with SQL](#7713-querying-with-sql) * [7.7.1.4. Filtering with Paging Predicates](#7714-filtering-with-paging-predicates) - * [7.8 Raw Pointer API](#78-raw-pointer-api) - * [7.9 Mixed Object Types Supporting HazelcastClient](#79-mixed-object-types-supporting-hazelcastclient) - * [7.9.1 TypedData API](#791-typeddata-api) + * [7.8. Performance](#78-performance) + * [7.8.1. Partition Aware](#781-partition-aware) + * [7.9. Monitoring and Logging](#79-monitoring-and-logging) + * [7.10 Raw Pointer API](#710-raw-pointer-api) + * [7.11 Mixed Object Types Supporting HazelcastClient](#711-mixed-object-types-supporting-hazelcastclient) + * [7.11.1 TypedData API](#7111-typeddata-api) * [8. Development and Testing](#8-development-and-testing) * [8.1. Building and Using Client From Sources](#81-building-and-using-client-from-sources) * [8.1.1 Mac](#811-mac) @@ -140,7 +143,8 @@ In order to use Hazelcast C++ client, we first need to setup a Hazelcast IMDG cl There are following options to start a Hazelcast IMDG cluster easily: * You can run standalone members by downloading and running JAR files from the website. -* You can embed members to your Java projects. The easiest way is to use [hazelcast-member tool](https://github.com/hazelcast/hazelcast-member-tool) if you have brew installed in your computer. +* You can embed members to your Java projects. +* The easiest way is to use [hazelcast-member tool](https://github.com/hazelcast/hazelcast-member-tool) if you have brew installed in your computer. We are going to download JARs from the website and run a standalone member for this guide. @@ -241,28 +245,28 @@ For Mac, there is only 64-bit distribution. Here is an example script to build with the static library: -`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include cpp/Mac_64/hazelcast/lib/libHazelcastClientStatic_64.a` +`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include cpp/Mac_64/hazelcast/lib/libHazelcast3.10.1_64.a` Here is an example script to build with the shared library: -`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include -Lcpp/Mac_64/hazelcast/lib -lHazelcastClientShared_64` +`g++ main.cpp -Icpp/Mac_64/hazelcast/external/include -Icpp/Mac_64/hazelcast/include -Lcpp/Mac_64/hazelcast/lib -lHazelcastClient3.10.1_64` #### 1.3.1.2 Linux Client -For Linux, there are 32- and 64-bit distributions. +For Linux, there are 32- and 64-bit distributions. You need to link with pthread library when compiling in Linux. Here is an example script to build with the static library: `g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include - cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` + cpp/Linux_64/hazelcast/lib/libHazelcastClient3.10.1_64.a` Here is an example script to build with the shared library: -`g++ main.cpp -lpthread -Wl,–no-as-needed -lrt -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClientShared_64` +`g++ main.cpp -lpthread -Wl,–no-as-needed -lrt -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -Lcpp/Linux_64/hazelcast/lib -lHazelcastClient3.10.1_64` Please add the `__STDC_LIMIT_MACROS` and `__STDC_CONSTANT_MACROS` compilation flags for the environments for which the compilation fails with the error `INT32_MAX could not be determined`. The following is an example command to add these flags: -`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS cpp/Linux_64/hazelcast/lib/libHazelcastClientStatic_64.a` +`g++ main.cpp -pthread -Icpp/Linux_64/hazelcast/external/include -Icpp/Linux_64/hazelcast/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS cpp/Linux_64/hazelcast/lib/libHazelcastClient3.10.1_64.a` #### 1.3.1.3 Windows Client @@ -286,7 +290,7 @@ This section describes the most common configuration elements to get you started It discusses some member side configuration options to ease the understanding of Hazelcast's ecosystem. Then, the client side configuration options regarding the cluster connection are discussed. The configurations for the Hazelcast IMDG data structures that can be used in the C++ client are discussed in the following sections. -See the [Hazelcast IMDG Reference Manual](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview section](#configuration-overview) for more information. +See the [Hazelcast IMDG Reference Manual](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview section](#3-configuration-overview) for more information. ### 1.4.1. Configuring Hazelcast IMDG @@ -339,7 +343,7 @@ live in the same network without disturbing each other. Note that the cluster na to the same cluster. `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility purposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later. - `` - - ``: Specifies the port number to be used by the member when it starts. Its default value is 5701 You can specify another port number, and if + - ``: Specifies the port number to be used by the member when it starts. Its default value is 5701. You can specify another port number, and if you set `auto-increment` to `true`, then Hazelcast will try subsequent ports until it finds an available port or `port-count` is reached. - ``: Specifies the strategies to be used by the member to find other cluster members. Choose which strategy you want to use by setting its `enabled` attribute to `true` and the others to `false`. @@ -356,15 +360,13 @@ These configuration elements are enough for most connection scenarios. Now we wi You can configure Hazelcast C++ Client programatically. -This section describes some network configuration settings to cover common use cases in connecting the client to a cluster. See the [Configuration Overview section](#configuration-overview) +This section describes some network configuration settings to cover common use cases in connecting the client to a cluster. See the [Configuration Overview section](#3-configuration-overview) and the following sections for information about detailed network configurations and/or additional features of Hazelcast C++ client configuration. -An easy way to configure your Hazelcast C++ Client is to create a `ClientConfig` object and set the appropriate options. Then you can -supply this object to your client at the startup. - **Programmatic configuration** -You need to create a `ClientConfig` object and adjust its properties. Then you can pass this object to the client when starting it. +An easy way to configure your Hazelcast C++ Client is to create a `ClientConfig` object and set the appropriate options. Then you need to +supply this object to your client at the startup. ```C++ hazelcast::client::ClientConfig config; @@ -624,7 +626,7 @@ When Hazelcast serializes an object into Data (byte array): 1. It first checks whether the object pointer is NULL. -2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type ID for the built-in types such as int32_t, bool, short and byte. If it does not match any of the built-in types, it may match the types offered by Hazelcast, i.e., IdentifiedDataSerializable, Portable or custom. The matching is done based on method parameter matching. +2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type ID for the built-in types such as `byte`, `char`, `int32_t`, `bool`, `int64_t`, etc. If it does not match any of the built-in types, it may match the types offered by Hazelcast, i.e., IdentifiedDataSerializable, Portable or custom. The matching is done based on method parameter matching. 3. If the above check fails, Hazelcast will use the registered Global Serializer if one exists. @@ -777,9 +779,9 @@ Note that the ID that is passed to the `SerializationConfig` is same as the `fac ## 4.3. Custom Serialization -Hazelcast lets you plug a custom serializer to be used for serialization of objects. +Hazelcast lets you plug a custom serializer to be used for serialization of objects. Custom serialization lets you use your existing classes without any modification in code for serialization purposes. They will be serialized/deserialized without any code change to already existing classes. -Let's say you have an object `Musician` and you would like to customize the serialization. The reason might be that you want to use an external serializer for only one object. +Let's say you have an object `Musician` and you would like to customize the serialization. The reason might be that you want to use an external serializer for your object. ```C++ class C++ { @@ -839,6 +841,8 @@ public: Note that the serializer `getHazelcastTypeId` method must return a unique `id` as Hazelcast will use it to lookup the `MusicianSerializer` while it deserializes the object. +You should provide the free function `int getHazelcastTypeId(const MusicianSerializer *);` in the same namespace to which `MusicianSerializer` class belongs. This function should return the same id with its serializer. This id is used to determine which serializer needs to be used for your classes. + Now the last required step is to register the `MusicianSerializer` to the configuration. **Programmatic Configuration:** @@ -926,11 +930,11 @@ list to find an alive member. Although it may be enough to give only one address clientConfig.getNetworkConfig().addAddress(Address("10.1.1.22", 5703)); ``` -The provided list is shuffled and tried in a random order. If no address is added to the `ClientNetworConfig`, then `127.0.0.1:5701` is tried by default. +The provided list is shuffled and tried in a random order. If no address is added to the `ClientNetworkConfig`, then `127.0.0.1:5701` is tried by default. ## 5.2. Setting Smart Routing -Smart routing defines whether the client mode is smart or unisocket. See [C++ Client Operation Modes section](#cpp-client-operation-modes) +Smart routing defines whether the client mode is smart or unisocket. See [C++ Client Operation Modes section](#72-cpp-client-operation-modes) for the description of smart and unisocket modes. The following is an example configuration. @@ -990,7 +994,7 @@ Its default value is `2`. ## 5.6. Setting Connection Attempt Period -Connection attempt period is the duration in milliseconds between the connection attempts defined by `ClientNetworkConfig::getConnectionAttemptLimit()`. +Connection attempt period is the duration in milliseconds between the connection attempts defined by `ClientNetworkConfig::getConnectionAttemptPeriod()`. The following is an example configuration. @@ -1145,7 +1149,6 @@ The second step is initializing the `HazelcastClient` to be connected to the clu ```C++ HazelcastClient client(clientConfig); // some operation -}); ``` **This client object is your gateway to access all the Hazelcast distributed objects.** @@ -1186,6 +1189,12 @@ For some cases, the clients can be required to connect to a single member instea In the unisocket client mode, the client will only connect to one of the configured addresses. This single member will behave as a gateway to the other members. For any operation requested from the client, it will redirect the request to the relevant member and return the response back to the client returned from this member. +You can set the unisocket client mode in the `ClientConfig` as shown below. + +```C++ + clientConfig.getNetworkConfig().setSmartRouting(false); +``` + ## 7.3. Handling Failures There are two main failure cases you should be aware of. Below sections explain these and the configurations you can perform to achieve proper behavior. @@ -1541,14 +1550,12 @@ You can add event listeners to a Hazelcast C++ client. You can configure the fol #### 7.5.1.1. Listening for Member Events -You can add the following types of member events to the `ClusterService`. +You can listen the following types of member events in the `Cluster`. * `memberAdded`: A new member is added to the cluster. * `memberRemoved`: An existing member leaves the cluster. * `memberAttributeChanged`: An attribute of a member is changed. See the [Defining Member Attributes section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#defining-member-attributes) in the Hazelcast IMDG Reference Manual to learn about member attributes. -The `ClusterService` object exposes an `ClusterService.on()` function that allows one or more functions to be attached to the member events emitted by the object. - You can use `Cluster` (`HazelcastClient::getCluster()`) object to register for the membership listeners. There two type of listeners, InitialMembershipListener and MembershipListener. The difference is that InitialMembershipListener also gets notified when the client connects to the cluster and retrieves the whole membership list. You need to implement one of these two interfaces and register an instance of the listener to the cluster. The following example demonstrates both initial and regular membership listener registration. @@ -1622,7 +1629,7 @@ The `LifecycleListener` interface notifies for the following events: * `shuttingDown`: The client is shutting down. * `shutdown`: The client’s shutdown has completed. * `clientConnected`: The client is connected to the cluster. -* `clientConnected`: The client is disconnected from the cluster. +* `clientDisconnected`: The client is disconnected from the cluster. The following is an example of the `LifecycleListener` that is added to the `ClientConfig` object and its output. @@ -1682,7 +1689,7 @@ You can add event listeners to the distributed data structures. You can listen to map-wide or entry-based events by using the functions in the `EntryListener` interface. To listen to these events, you need to implement the relevant `EntryListener` interface. -An entry-based event is fired after the operations that affect a specific entry. For example, `IMap::put()`, `IMap::remove()` or `IMap::evict()`. You should use the `EntryListener` type to listen these events. An `EntryEvent` object is passed to the listener function. +An entry-based event is fired after the operations that affect a specific entry. For example, `IMap::put()`, `IMap::remove()` or `IMap::evict()`. You should use the `EntryListener` type to listen these events. An `EntryEvent` object is passed to the listener callback method. See the following example. @@ -1734,7 +1741,7 @@ int main() { } ``` -A map-wide event is fired as a result of a map-wide operation. For example, `IMap::clear()` or `IMap::evictAll()`. You should use the `EntryListener` type to listen to these events. A `MapEvent` object is passed to the listener function. +A map-wide event is fired as a result of a map-wide operation. For example, `IMap::clear()` or `IMap::evictAll()`. You should use the `EntryListener` type to listen to these events. A `MapEvent` object is passed to the listener method. See the following example. @@ -1977,13 +1984,13 @@ Hazelcast offers the following ways for distributed query purposes: Assume that you have an `employee` map containing the values of `Employee` objects, as coded below. ```C++ -class Employee : public serialization::IdentifiedDataSerializable { +class Employee : public serialization::Portable { public: static const int TYPE_ID = 100; Employee() : active(false), salary(0.0) {} - Employee(int id, const std::string &name, bool active, double salary) : id(id), name(name), active(active), + Employee(int32_t id, const std::string &name, bool active, double salary) : id(id), name(name), active(active), salary(salary) {} virtual int getFactoryId() const { @@ -1994,14 +2001,14 @@ public: return TYPE_ID; } - virtual void writeData(serialization::ObjectDataOutput &writer) const { + virtual void writePortable(serialization::PortableWriter &writer) const { writer.writeInt(id); writer.writeUTF(&name); writer.writeBoolean(active); writer.writeDouble(salary); } - virtual void readData(serialization::ObjectDataInput &reader) { + virtual void readPortable(serialization::PortableReader &reader) { id = reader.readInt(); name = *reader.readUTF(); active = reader.readBoolean(); @@ -2009,32 +2016,19 @@ public: } private: - int id; + int32_t id; std::string name; bool active; double salary; }; ``` -Note that `Employee` is an `IdentifiedDataSerializable` object. If you just want to save the `Employee` objects as byte arrays on the map, you don't need to implement its equivalent on the server-side. However, if you want to query on the `employee` map, the server needs the `Employee` objects rather than byte array formats. Therefore, you need to implement its Java equivalent and its data serializable factory on the server side for server to reconstitute the objects from binary formats. After implementing the Java class and its factory, you need to add the factory to the data serializable factories or the portable factories by giving a factory `id`. The following is an example declarative configuration on the server. +Note that `Employee` is implementing `Portable`. As portable types are not deserialized on server side for querying, you don't need to implement its Java equivalent on the server side. -```xml - - ... - - - - mypackage.MyIdentifiedFactory - - - - ... - -``` - -Note that before starting the server, you need to compile the `Employee` and `MyIdentifiedFactory` classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#123-adding-user-library-to-classpath). +For type that are not of portable types, you need to implement its Java equivalent and its serializable factory on the server side for server to reconstitute the objects from binary formats. +In this case before starting the server, you need to compile the `Employee` and related factory classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#123-adding-user-library-to-classpath). -> **NOTE: You can also make this object `Portable` and implement its Java equivalent and portable factory on the server side. Note that querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** +> **NOTE: Querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.** #### 7.7.1.2. Querying by Combining Predicates with AND, OR, NOT @@ -2161,7 +2155,13 @@ If you want to sort the result before paging, you need to specify a comparator o Also, you can access a specific page more easily with the help of the `setPage` function. This way, if you make a query for the 100th page, for example, it will get all 100 pages at once instead of reaching the 100th page one by one using the `nextPage` function. -## 7.8. Raw Pointer API +## 7.8. Performance + +### 7.8.1. Partition Aware + +## 7.9. Monitoring and Logging + +## 7.10. Raw Pointer API When using C++ client you can have the ownership of raw pointers for the objects you create and return. This allows you to keep the objects in your library/application without any need for copy. @@ -2229,7 +2229,7 @@ value = vals->get(0); Using the raw pointer based API may improve the performance if you are using the API to return multiple values such as `values`, `keySet` and `entrySet`. In this case, the cost of deserialization is delayed until the item is actually accessed. -## 7.9. Mixed Object Types Supporting HazelcastClient +## 7.11. Mixed Object Types Supporting HazelcastClient Sometimes, you may need to use Hazelcast data structures with objects of different types. For example, you may want to put `int`, `string`, `IdentifiedDataSerializable`, etc. objects into the same Hazelcast `IMap` data structure. You can do this by using the mixed type adopted `HazelcastClient`. You can adopt the client in this way: ``` ClientConfig config; @@ -2258,7 +2258,7 @@ Mixed type support for near cache only exists when the in-memory format is BINAR The mixed type API uses the TypedData class at the user interface. -### 7.9.1. TypedData API +### 7.11.1. TypedData API The TypedData class is a wrapper class for the serialized binary data. It presents the following user APIs: ``` /** From 17b2877e2ed249baf680be605927ebdf59d0bb2f Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Fri, 9 Nov 2018 15:55:45 +0300 Subject: [PATCH 07/11] Added missing headings in the index. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 34dce30037..f5b760835e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ * [1.4. Basic Configuration](#14-basic-configuration) * [1.4.1. Configuring Hazelcast IMDG](#141-configuring-hazelcast-imdg) * [1.4.2. Configuring Hazelcast C++ Client](#142-configuring-hazelcast-cpp-client) + * [1.4.2.1. Group Settings](#1421-group-settings) + * [1.4.2.2. Network Settings](#1422-network-settings) * [1.5. Basic Usage](#15-basic-usage) * [1.6. Code Samples](#16-code-samples) * [2. Features](#2-features) @@ -376,7 +378,7 @@ supply this object to your client at the startup. If you run the Hazelcast IMDG members in a different server than the client, you most probably have configured the members' ports and cluster names as explained in the previous section. If you did, then you need to make certain changes to the network settings of your client. -### Group Settings +### 1.4.2.1 Group Settings **Programmatic:** ```C++ @@ -384,7 +386,7 @@ names as explained in the previous section. If you did, then you need to make ce config.getGroupConfig().setName("group name of your cluster"); ``` -### Network Settings +### 1.4.2.2. Network Settings You need to provide the IP address and port of at least one member in your cluster so the client can find it. From 444b998865bf4cd2b5ebdd48486b97ac3cf67d0d Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Mon, 12 Nov 2018 17:16:36 +0300 Subject: [PATCH 08/11] Added transactions section. --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f5b760835e..786b931399 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ * [7.4.11. Using Semaphore](#7411-using-semaphore) * [7.4.12. Using PN Counter](#7412-using-pn-counter) * [7.4.13. Using Flake ID Generator](#7413-using-flake-id-generator) + * [7.4.14. Using Transactions](#7414-using-transactions) * [7.5. Distributed Events](#75-distributed-events) * [7.5.1. Cluster Events](#751-cluster-events) * [7.5.1.1. Listening for Member Events](#7511-listening-for-member-events) @@ -561,6 +562,11 @@ Hazelcast C++ client supports the following data structures and features: * Flake Id Generator * Event Listeners * Entry Processor +* Transactional Map +* Transactional MultiMap +* Transactional Queue +* Transactional List +* Transactional Set * Query (Predicates) * Paging Predicate * Built-in Predicates @@ -1322,7 +1328,7 @@ A Queue usage example is shown below. std::cout << *queue.take() << std::endl; // Will print yetanotheritem ``` -## 7.4.5. Using Set +### 7.4.5. Using Set Hazelcast Set (`ISet`) is a distributed set which does not allow duplicate elements. For details, see the [Set section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#set) in the Hazelcast IMDG Reference Manual. @@ -1345,7 +1351,7 @@ A Set usage example is shown below. } ``` -## 7.4.6. Using List +### 7.4.6. Using List Hazelcast List (`IList`) is a distributed list which allows duplicate elements and preserves the order of elements. For details, see the [List section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#list) in the Hazelcast IMDG Reference Manual. @@ -1366,7 +1372,7 @@ A List usage example is shown below. list.clear(); ``` -## 7.4.7. Using Ringbuffer +### 7.4.7. Using Ringbuffer Hazelcast `Ringbuffer` is a replicated but not partitioned data structure that stores its data in a ring-like structure. You can think of it as a circular array with a given capacity. Each Ringbuffer has a tail and a head. The tail is where the items are added and the head is where the items are overwritten or expired. You can reach each element in a Ringbuffer using a sequence ID, which is mapped to the elements between the head and tail (inclusive) of the Ringbuffer. For details, see the [Ringbuffer section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#ringbuffer) in the Hazelcast IMDG Reference Manual. @@ -1385,7 +1391,7 @@ A Ringbuffer usage example is shown below. std::cout << *rb->readOne(sequence) << std::endl; ``` -## 7.4.8. Using Reliable Topic +### 7.4.8. Using Reliable Topic Hazelcast `ReliableTopic` is a distributed topic implementation backed up by the `Ringbuffer` data structure. For details, see the [Reliable Topic section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#reliable-topic) in the Hazelcast IMDG Reference Manual. @@ -1460,7 +1466,7 @@ int main() { } ``` -## 7.4.9 Using Lock +### 7.4.9 Using Lock Hazelcast Lock (`ILock`) is a distributed lock implementation. You can synchronize Hazelcast members and clients using a Lock. For details, see the [Lock section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#lock) in the Hazelcast IMDG Reference Manual. @@ -1479,7 +1485,7 @@ A Lock usage example is shown below. } ``` -## 7.4.10 Using Atomic Long +### 7.4.10 Using Atomic Long Hazelcast Atomic Long (`IAtomicLong`) is the distributed long which offers most of the operations such as `get`, `set`, `getAndSet`, `compareAndSet` and `incrementAndGet`. For details, see the [Atomic Long section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#iatomiclong) in the Hazelcast IMDG Reference Manual. @@ -1495,7 +1501,7 @@ An Atomic Long usage example is shown below. std::cout << "Count is:" << counter.get() << std::endl; // Counter is 1000000 ``` -## 7.4.11 Using Semaphore +### 7.4.11 Using Semaphore Hazelcast Semaphore (`ISemaphore`) is a distributed semaphore implementation. For details, see the [Semaphore section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#isemaphore) in the Hazelcast IMDG Reference Manual. @@ -1508,7 +1514,7 @@ A Semaphore usage example is shown below. std::cout << "Number of available permits: " << semaphore.availablePermits() << std::endl; // Number of available permits: 5 ``` -## 7.4.12 Using PN Counter +### 7.4.12 Using PN Counter Hazelcast `PNCounter` (Positive-Negative Counter) is a CRDT positive-negative counter implementation. It is an eventually consistent counter given there is no member failure. For details, see the [PN Counter section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter) in the Hazelcast IMDG Reference Manual. @@ -1528,7 +1534,7 @@ A PN Counter usage example is shown below. std::cout << "Decremented counter by one to: " << pnCounter->decrementAndGet() << std::endl; // Decremented counter by one to: 6 ``` -## 7.4.13 Using Flake ID Generator +### 7.4.13 Using Flake ID Generator Hazelcast `FlakeIdGenerator` is used to generate cluster-wide unique identifiers. Generated identifiers are long primitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to `2^63-1` (maximum signed long value). For details, see the [FlakeIdGenerator section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator) in the Hazelcast IMDG Reference Manual. @@ -1539,6 +1545,40 @@ A Flake ID Generator usage example is shown below. std::cout << "Id : " << generator.newId() << std::endl; // Id : ``` +### 7.4.14. Using Transactions + +Hazelcast C++ client provides transactional operations like beginning transactions, committing transactions and retrieving transactional data structures like the `TransactionalMap`, `TransactionalSet`, `TransactionalList`, `TransactionalQueue` and `TransactionalMultiMap`. + +You can create a `TransactionContext` object using the C++ client to begin, commit and rollback a transaction. You can obtain transaction-aware instances of queues, maps, sets, lists and multimaps via the `TransactionContext` object, work with them and commit or rollback in one shot. For details, see the [Transactions section](https://docs.hazelcast.org//docs/latest/manual/html-single/index.html#transactions) in the Hazelcast IMDG Reference Manual. + +```C++ + // Create a Transaction object and begin the transaction + TransactionContext context = client->newTransactionContext(); + context.beginTransaction(); + + // Get transactional distributed data structures + TransactionalMap txnMap = context.getMap("transactional-map"); + TransactionalMultiMap txnMultiMap = context.getMultiMap("transactional-multimap"); + TransactionalQueue txnQueue = context.getQueue("transactional-queue"); + TransactionalSet txnSet = context.getSet("transactional-set"); + + try { + boost::shared_ptr obj = txnQueue.poll(); + //process object + txnMap.put( "1", "value1" ); + txnMultiMap.put("2", "value2_1"); + txnMultiMap.put("2", "value2_2"); + txnSet.add( "value" ); + //do other things + + // Commit the above changes done in the cluster. + context.commitTransaction(); + } catch (...) { + context.rollbackTransaction(); + throw; + } +``` + ## 7.5. Distributed Events This chapter explains when various events are fired and describes how you can add event listeners on a Hazelcast C++ client. These events can be categorized as cluster and distributed data structure events. From c842174c4e77f9a04305a919b67c9fdaa68d78b7 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Mon, 12 Nov 2018 17:25:55 +0300 Subject: [PATCH 09/11] Update to transactions section. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 786b931399..a722730c8f 100644 --- a/README.md +++ b/README.md @@ -1579,6 +1579,12 @@ You can create a `TransactionContext` object using the C++ client to begin, comm } ``` +In a transaction, operations will not be executed immediately. Their changes will be local to the `TransactionContext` until committed. However, they will ensure the changes via locks. + +For the above example, when `map.put` is executed, no data will be put in the map but the key will be locked against changes. While committing, operations will be executed, the value will be put to the map and the key will be unlocked. + +The isolation level in Hazelcast Transactions is `READ_COMMITTED` on the level of a single partition. If you are in a transaction, you can read the data in your transaction and the data that is already committed. If you are not in a transaction, you can only read the committed data. + ## 7.5. Distributed Events This chapter explains when various events are fired and describes how you can add event listeners on a Hazelcast C++ client. These events can be categorized as cluster and distributed data structure events. From 7ad411e9f3b4e496cc8aaabbabeff93846c8cb87 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Thu, 15 Nov 2018 12:31:10 +0300 Subject: [PATCH 10/11] Updated based on Node.js Logger and Statistics PRs. --- README.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 138 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a722730c8f..61e1b637ba 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ * [1.1. Requirements](#11-requirements) * [1.2. Working with Hazelcast IMDG Clusters](#12-working-with-hazelcast-imdg-clusters) * [1.2.1. Setting Up a Hazelcast IMDG Cluster](#121-setting-up-a-hazelcast-imdg-cluster) - * [1.2.2. Running Standalone Jars](#122-running-standalone-jars) - * [1.2.3. Adding User Library to CLASSPATH](#123-adding-user-library-to-classpath) - * [1.2.4. Using hazelcast-member Tool](#124-using-hazelcast-member-tool) + * [1.2.1.1. Running Standalone Jars](#1211-running-standalone-jars) + * [1.2.1.2. Adding User Library to CLASSPATH](#1212-adding-user-library-to-classpath) + * [1.2.1.3. Using hazelcast-member Tool](#1213-using-hazelcast-member-tool) * [1.3. Downloading and Installing](#13-downloading-and-installing) * [1.4. Basic Configuration](#14-basic-configuration) * [1.4.1. Configuring Hazelcast IMDG](#141-configuring-hazelcast-imdg) @@ -82,7 +82,10 @@ * [7.7.1.4. Filtering with Paging Predicates](#7714-filtering-with-paging-predicates) * [7.8. Performance](#78-performance) * [7.8.1. Partition Aware](#781-partition-aware) + * [7.8.2. Near Cache](#782-near-cache) * [7.9. Monitoring and Logging](#79-monitoring-and-logging) + * [7.9.1. Enabling Client Statistics](#791-enabling-client-statistics) + * [7.9.2. Logging Configuration](#792-logging-configuration) * [7.10 Raw Pointer API](#710-raw-pointer-api) * [7.11 Mixed Object Types Supporting HazelcastClient](#711-mixed-object-types-supporting-hazelcastclient) * [7.11.1 TypedData API](#7111-typeddata-api) @@ -151,7 +154,7 @@ There are following options to start a Hazelcast IMDG cluster easily: We are going to download JARs from the website and run a standalone member for this guide. -#### 1.2.2. Running Standalone JARs +#### 1.2.1.1. Running Standalone JARs Follow the instructions below to create a Hazelcast IMDG cluster: @@ -174,7 +177,7 @@ Sep 06, 2018 10:50:23 AM com.hazelcast.core.LifecycleService INFO: [192.168.0.3]:5701 [dev] [3.10.4] [192.168.0.3]:5701 is STARTED ``` -#### 1.2.3. Adding User Library to CLASSPATH +#### 1.2.1.2. Adding User Library to CLASSPATH When you want to use features such as querying and language interoperability, you might need to add your own Java classes to the Hazelcast member in order to use them from your C++ client. This can be done by adding your own compiled code to the `CLASSPATH`. To do this, compile your code with the `CLASSPATH` and add the compiled files to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). Then, you can start your Hazelcast member by using the start scripts in the `bin` directory. The start scripts will automatically add your compiled classes to the `CLASSPATH`. @@ -197,7 +200,7 @@ The following is an example configuration when you are adding an `IdentifiedData ``` If you want to add a `Portable` class, you should use `` instead of `` in the above configuration. -#### 1.2.4. Using hazelcast-member Tool +#### 1.2.1.3. Using hazelcast-member Tool `hazelcast-member` is a tool to download and run Hazelcast IMDG members easily. If you have brew installed, run the following commands to instal this tool: @@ -343,11 +346,11 @@ We will go over some important configuration elements in the rest of this sectio - ``: Specifies which cluster this member belongs to. A member connects only to the other members that are in the same group as itself. As shown in the above configuration sample, there are `` and `` tags under the `` element with some pre-configured values. You may give your clusters different names so that they can live in the same network without disturbing each other. Note that the cluster name should be the same across all members and clients that belong - to the same cluster. `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility + to the same cluster. The `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility purposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later. - `` - ``: Specifies the port number to be used by the member when it starts. Its default value is 5701. You can specify another port number, and if - you set `auto-increment` to `true`, then Hazelcast will try subsequent ports until it finds an available port or `port-count` is reached. + you set `auto-increment` to `true`, then Hazelcast will try the subsequent ports until it finds an available port or the `port-count` is reached. - ``: Specifies the strategies to be used by the member to find other cluster members. Choose which strategy you want to use by setting its `enabled` attribute to `true` and the others to `false`. - ``: Members find each other by sending multicast requests to the specified address and port. It is very useful if IP addresses @@ -369,7 +372,7 @@ and the following sections for information about detailed network configurations **Programmatic configuration** An easy way to configure your Hazelcast C++ Client is to create a `ClientConfig` object and set the appropriate options. Then you need to -supply this object to your client at the startup. +pass this object to the client when starting it. ```C++ hazelcast::client::ClientConfig config; @@ -2207,8 +2210,134 @@ Also, you can access a specific page more easily with the help of the `setPage` ### 7.8.1. Partition Aware +Partition Aware ensures that the related entries exist on the same member. If the related data is on the same member, operations can be executed without the cost of extra network calls and extra wire data, and this improves the performance. This feature is provided by using the same partition keys for related data. + +Hazelcast has a standard way of finding out which member owns/manages each key object. The following operations are routed to the same member, since all of them are operating based on the same key `'key1'`. + +```C++ + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hazelcastInstance(config); + + hazelcast::client::IMap mapA = hazelcastInstance.getMap("mapA"); + hazelcast::client::IMap mapB = hazelcastInstance.getMap("mapB"); + hazelcast::client::IMap mapC = hazelcastInstance.getMap("mapC"); + + // since map names are different, operation will be manipulating + // different entries, but the operation will take place on the + // same member since the keys ("key1") are the same + mapA.put("key1", value); + mapB.get("key1"); + mapC.remove("key1"); +``` + +When the keys are the same, entries are stored on the same member. However, we sometimes want to have the related entries stored on the same member, such as a customer and his/her order entries. We would have a customers map with `customerId` as the key and an orders map with `orderId` as the key. Since `customerId` and `orderId` are different keys, a customer and his/her orders may fall into different members in your cluster. So how can we have them stored on the same member? We create an affinity between the customer and orders. If we make them part of the same partition then these entries will be co-located. We achieve this by making `orderId`s `PartitionAware`. + +```C++ +class OrderKey : public hazelcast::client::PartitionAware, + public hazelcast::client::serialization::IdentifiedDataSerializable { +public: + static const std::string desiredPartitionString; + + OrderKey() { + } + + OrderKey(int64_t orderId, int64_t customerId) : orderId(orderId), customerId(customerId) {} + + int64_t getOrderId() const { + return orderId; + } + + virtual const int64_t *getPartitionKey() const { + return &customerId; + } + + virtual int getFactoryId() const { + return 1; + } + + virtual int getClassId() const { + return 10; + } + + virtual void writeData(hazelcast::client::serialization::ObjectDataOutput &writer) const { + writer.writeLong(orderId); + writer.writeLong(customerId); + } + + virtual void readData(hazelcast::client::serialization::ObjectDataInput &reader) { + orderId = reader.readLong(); + customerId = reader.readLong(); + } + +private: + int64_t orderId; + int64_t customerId; +}; +``` + +Notice that `OrderKey` implements `PartitionAware` interface and that `getPartitionKey()` returns the `customerId`. This will make sure that the `Customer` entry and its `Order`s will be stored on the same member. + +```C++ + hazelcast::client::ClientConfig config; + hazelcast::client::HazelcastClient hazelcastInstance(config); + + hazelcast::client::IMap mapCustomers = hazelcastInstance.getMap( "customers" ); + hazelcast::client::IMap mapOrders = hazelcastInstance.getMap( "orders" ); + + // create the customer entry with customer id = 1 + mapCustomers.put( 1, customer ); + + // now create the orders for this customer + mapOrders.put( new OrderKey( 21, 1 ), order ); + mapOrders.put( new OrderKey( 22, 1 ), order ); + mapOrders.put( new OrderKey( 23, 1 ), order ); +``` + +For more details, see the [PartitionAware section](https://docs.hazelcast.org/docs/latest/manual/html-single/#partitionaware) in the Hazelcast IMDG Reference Manual. + +### 7.8.2. Near Cache + ## 7.9. Monitoring and Logging +### 7.9.1. Enabling Client Statistics + +You can enable the client statistics before starting your clients. There are two properties related to client statistics: + +- `hazelcast.client.statistics.enabled`: If set to `true`, it enables collecting the client statistics and sending them to the cluster. When it is `true` you can monitor the clients that are connected to your Hazelcast cluster, using Hazelcast Management Center. Its default value is `false`. + +- `hazelcast.client.statistics.period.seconds`: Period in seconds the client statistics are collected and sent to the cluster. Its default value is `3`. + +You can enable client statistics and set a non-default period in seconds as follows: + +```C++ + hazelcast::client::ClientConfig config; + config.setProperty(hazelcast::client::ClientProperties::STATISTICS_ENABLED, "true"); + config.setProperty(hazelcast::client::ClientProperties::STATISTICS_PERIOD_SECONDS, "4"); +``` + +After enabling the client statistics, you can monitor your clients using Hazelcast Management Center. Please refer to the [Monitoring Clients section](https://docs.hazelcast.org/docs/management-center/latest/manual/html/index.html#monitoring-clients) in the Hazelcast Management Center Reference Manual for more information on the client statistics. + +### 7.9.2. Logging Configuration + +By default, the Hazelcast logger prints out the INFO level and above logs to the standard output. The following log levels exist: +- FINEST (DEBUG) +- INFO +- WARNING +- SEVERE (FATAL) + +The order is in increasing order. Hence, for INFO level configuration the INFO, WARNING and SEVERE logs are written. + +In some applications you may want to use your custom logging statements, you may want to direct the logs to a file instead of standard output, and enable/disable certain log levels. This can be done using the LoggerConfig in the ClientConfig object which is used to configure the client. Each client may have a separate configuration. The customized options needs to be configured using a configuration file. E.g.: + +``` +clientConfig.getLoggerConfig().setConfigurationFileName("logger-config.txt"); +``` +The file name is relative path to the application working directory or should be an absolute path. The configuration file will use the format as supported by the configured logger type. Currently, only the easylogging++ (https://github.com/muflihun/easyloggingpp/tree/v8.91) logger is supported, hence the configuration should be done in accordance with the easylogging++ configuration: https://github.com/muflihun/easyloggingpp/tree/v8.91#configuration-file + +If you provide a non-existent or invalid logger configuration file, the library will fail fast by throwing exception::IllegalStateException with the cause of the problem. + +**Important Note:** If you configured the logger configuration file, then the ClientConfig::setLogLevel will not be effective since the levels will be controlled from the configuration file. + ## 7.10. Raw Pointer API When using C++ client you can have the ownership of raw pointers for the objects you create and return. This allows you to keep the objects in your library/application without any need for copy. From 599717cf2ff0a4c8f7943283b42e69c1f8da4581 Mon Sep 17 00:00:00 2001 From: ihsandemir Date: Thu, 15 Nov 2018 12:48:25 +0300 Subject: [PATCH 11/11] Review comment fixes. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 61e1b637ba..238c24fe1c 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ * [7.7.1.3. Querying with SQL](#7713-querying-with-sql) * [7.7.1.4. Filtering with Paging Predicates](#7714-filtering-with-paging-predicates) * [7.8. Performance](#78-performance) - * [7.8.1. Partition Aware](#781-partition-aware) - * [7.8.2. Near Cache](#782-near-cache) + * [7.8.1. Partition Aware](#781-partition-aware) + * [7.8.2. Near Cache](#782-near-cache) * [7.9. Monitoring and Logging](#79-monitoring-and-logging) * [7.9.1. Enabling Client Statistics](#791-enabling-client-statistics) * [7.9.2. Logging Configuration](#792-logging-configuration) @@ -637,7 +637,7 @@ When Hazelcast serializes an object into Data (byte array): 1. It first checks whether the object pointer is NULL. -2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type ID for the built-in types such as `byte`, `char`, `int32_t`, `bool`, `int64_t`, etc. If it does not match any of the built-in types, it may match the types offered by Hazelcast, i.e., IdentifiedDataSerializable, Portable or custom. The matching is done based on method parameter matching. +2. If the above check fails, then the object is serialized using the serializer that matches the object type. The object type is determined using the free function `int32_t getHazelcastTypeId(const T *object);`. This method returns the constant built-int serializer type ID for the built-in types such as `byte`, `char`, `int32_t`, `bool` and `int64_t`, etc. If it does not match any of the built-in types, it may match the types offered by Hazelcast, i.e., IdentifiedDataSerializable, Portable or custom. The matching is done based on method parameter matching. 3. If the above check fails, Hazelcast will use the registered Global Serializer if one exists. @@ -2074,9 +2074,9 @@ private: }; ``` -Note that `Employee` is implementing `Portable`. As portable types are not deserialized on server side for querying, you don't need to implement its Java equivalent on the server side. +Note that `Employee` is implementing `Portable`. As portable types are not deserialized on server side for querying, you don't need to implement its Java equivalent on the server side for querying. -For type that are not of portable types, you need to implement its Java equivalent and its serializable factory on the server side for server to reconstitute the objects from binary formats. +For the non-portable types, you need to implement its Java equivalent and its serializable factory on the server side for server to reconstitute the objects from binary formats. In this case before starting the server, you need to compile the `Employee` and related factory classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). See the [Adding User Library to CLASSPATH section](#123-adding-user-library-to-classpath). > **NOTE: Querying with `Portable` object is faster as compared to `IdentifiedDataSerializable`.**