Este contenido no está disponible en el idioma seleccionado.
Chapter 16. The Hot Rod Interface
16.1. About Hot Rod
Hot Rod is a binary TCP client-server protocol used in Red Hat JBoss Data Grid. It was created to overcome deficiencies in other client/server protocols, such as Memcached.
Hot Rod will failover on a server cluster that undergoes a topology change. Hot Rod achieves this by providing regular updates to clients about the cluster topology.
Hot Rod enables clients to do smart routing of requests in partitioned or distributed Red Hat JBoss Data Grid server clusters. To do this, Hot Rod allows clients to determine the partition that houses a key and then communicate directly with the server that has the key. This functionality relies on Hot Rod updating the cluster topology with clients, and that the clients use the same consistent hash algorithm as the servers.
Red Hat JBoss Data Grid contains a server module that implements the Hot Rod protocol. The Hot Rod protocol facilitates faster client and server interactions in comparison to other text-based protocols and allows clients to make decisions about load balancing, failover and data location operations.
16.2. Hot Rod Headers
16.2.1. Hot Rod Header Data Types
All keys and values used for Hot Rod in Red Hat JBoss Data Grid are stored as byte arrays. Certain header values, such as those for REST and Memcached, are stored using the following data types instead:
Data Type | Size | Details |
---|---|---|
vInt | Between 1-5 bytes. | Unsigned variable length integer values. |
vLong | Between 1-9 bytes. | Unsigned variable length long values. |
string | - | Strings are always represented using UTF-8 encoding. |
16.2.2. Request Header
When using Hot Rod to access Red Hat JBoss Data Grid, the contents of the request header consist of the following:
Field Name | Data Type/Size | Details |
---|---|---|
Magic | 1 byte | Indicates whether the header is a request header or response header. |
Message ID | vLong | Contains the message ID. Responses use this unique ID when responding to a request. This allows Hot Rod clients to implement the protocol in an asynchronous manner. |
Version | 1 byte | Contains the Hot Rod server version. |
Opcode | 1 byte | Contains the relevant operation code. In a request header, opcode can only contain the request operation codes. |
Cache Name Length | vInt |
Stores the length of the cache name. If Cache Name Length is set to |
Cache Name | string | Stores the name of the target cache for the specified operation. This name must match the name of a predefined cache in the cache configuration file. |
Flags | vInt | Contains a numeric value of variable length that represents flags passed to the system. Each bit represents a flag, except the most significant bit, which is used to determine whether more bytes must be read. Using a bit to represent each flag facilitates the representation of flag combinations in a condensed manner. |
Client Intelligence | 1 byte | Contains a value that indicates the client capabilities to the server. |
Topology ID | vInt |
Contains the last known view ID in the client. Basic clients supply the value |
16.2.3. Response Header
When using Hot Rod to access Red Hat JBoss Data Grid, the contents of the response header consist of the following:
Field Name | Data Type | Details |
---|---|---|
Magic | 1 byte | Indicates whether the header is a request or response header. |
Message ID | vLong | Contains the message ID. This unique ` ID` is used to pair the response with the original request. This allows Hot Rod clients to implement the protocol in an asynchronous manner. |
Opcode | 1 byte | Contains the relevant operation code. In a response header, opcode can only contain the response operation codes. |
Status | 1 byte | Contains a code that represents the status of the response. |
Topology Change Marker | 1 byte | Contains a marker byte that indicates whether the response is included in the topology change information. |
16.2.4. Topology Change Headers
16.2.4.1. Topology Change Headers
When using Hot Rod to access Red Hat JBoss Data Grid, response headers respond to changes in the cluster or view formation by looking for clients that can distinguish between different topologies or hash distributions. The Hot Rod server compares the current topology ID
and the topology ID
sent by the client and, if the two differ, it returns a new topology ID
.
16.2.4.2. Topology Change Marker Values
The following is a list of valid values for the Topology Change Marker
field in a response header:
Value | Details |
---|---|
0 | No topology change information is added. |
1 | Topology change information is added. |
16.2.4.3. Topology Change Headers for Topology-Aware Clients
The response header sent to topology-aware clients when a topology change is returned by the server includes the following elements:
Response Header Fields | Data Type/Size | Details |
---|---|---|
Response Header with Topology Change Marker | variable | Refer to Response Header. |
Topology ID | vInt | Topology ID. |
Num Servers in Topology | vInt | Contains the number of Hot Rod servers running in the cluster. This value can be a subset of the entire cluster if only some nodes are running Hot Rod servers. |
mX: Host/IP Length | vInt | Contains the length of the hostname or IP address of an individual cluster member. Variable length allows this element to include hostnames, IPv4 and IPv6 addresses. |
mX: Host/IP Address | string | Contains the hostname or IP address of an individual cluster member. The Hot Rod client uses this information to access the individual cluster member. |
mX: Port | Unsigned Short. 2 bytes | Contains the port used by Hot Rod clients to communicate with the cluster member. |
The three entries with the prefix mX
, are repeated for each server in the topology. The first server in the topology’s information fields will be prefixed with m1
and the numerical value is incremented by one for each additional server till the value of X
equals the number of servers specified in the num servers in topology
field.
16.2.4.4. Topology Change Headers for Hash Distribution-Aware Clients
The response header sent to clients when a topology change is returned by the server includes the following elements:
Field | Data Type/Size | Details |
---|---|---|
Response Header with Topology Change Marker | variable | Refer to Response Header. |
Topology ID | vInt | Topology ID. |
Number Key Owners | Unsigned short. 2 bytes. |
Contains the number of globally configured copies for each distributed key. Contains the value |
Hash Function Version | 1 byte |
Contains a pointer to the hash function in use. Contains the value |
Hash Space Size | vInt |
Contains the modulus used by JBoss Data Grid for all module arithmetic related to hash code generation. Clients use this information to apply the correct hash calculations to the keys. Contains the value |
Number servers in topology | vInt | Contains the number of [path]_ Hot Rod_ servers running in the cluster. This value can be a subset of the entire cluster if only some nodes are running [path]_ Hot Rod_ servers. This value also represents the number of host to port pairings included in the header. |
Number Virtual Nodes Owners | vInt |
Contains the number of configured virtual nodes. Contains the value |
mX: Host/IP Length | vInt | Contains the length of the hostname or [path]_ IP_ address of an individual cluster member. Variable length allows this element to include hostnames, [path]_ IPv4_ and [path]_ IPv6_ addresses. |
mX: Host/IP Address | string | Contains the hostname or [path]_ IP_ address of an individual cluster member. The [path]_ Hot Rod_ client uses this information to access the individual cluster member. |
mX: Port | Unsigned short. 2 bytes. | Contains the port used by [path]_ Hot Rod_ clients to communicate with the cluster member. |
Hash Function Version | 1 byte | 0x03 |
Number of Segments in Topology | vInt | Total number of segments in the topology. |
Number of Owners in Segment | 1 byte | This can be either 0, 1, or 2 owners. |
First Wwner’s Index | vInt | Given the list of all nodes, the position of this owner in this list. This is only present if number of owners for this segment is 1 or 2. |
Second Owner’s Index | vInt | Given the list of all nodes, the position of this owner in this list. This is only present if number of owners for this segment is 2. |
Even though it is possible to have more than 2 owners per segment, the Hot Rod protocol limits the number of owners to send for efficiency reasons.
The three entries with the prefix mX
, are repeated for each server in the topology. The first server in the topology’s information fields will be prefixed with m1
and the numerical value is incremented by one for each additional server till the value of X
equals the number of servers specified in the num servers in topology
field.
16.3. Hot Rod Operations
16.3.1. Hot Rod Operations
The following are valid operations when using Hot Rod protocol 1.3 to interact with Red Hat JBoss Data Grid:
- Authenticate
- AuthMechList
- BulkGet
- BulkKeysGet
- Clear
- ContainsKey
- Exec
- Get
- GetAll
- GetWithMetadata
- GetWithVersion
- IterationEnd
- IterationNext
- IterationStart
- Ping
- Put
- PutAll
- PutIfAbsent
- Query
- Remove
- RemoveIfUnmodified
- Replace
- ReplaceIfUnmodified
- Stats
- Size
When using the RemoteCache API to call the Hot Rod client’s Put , PutIfAbsent , Replace , and ReplaceWithVersion operations, if lifespan is set to a value greater than 30 days, the value is treated as UNIX time and represents the number of seconds since the date 1/1/1970.
16.3.2. Hot Rod Authenticate Operation
The purpose of this operation is to authenticate a client against a server using SASL. The authentication process, depending on the chosen mech, might be a multi-step operation. Once complete the connection becomes authenticated.
The Authenticate
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Mech | String | String containing the name of the mech chosen by the client for authentication. Empty on the successive invocations. |
Response length | vInt | Length of the SASL client response. |
Response data | byte array | The SASL client response. |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Completed | byte | 0 if further processing is needed, or 1 if authentication is complete. |
Challenge length | vInt | Length of the SASL server challenge. |
Challenge data | byte array | The SASL server challenge. |
16.3.3. Hot Rod AuthMechList Operation
The purpose of this operation is to obtain the list of valid SASL authentication mechs supported by the server. The client will then need to issue an Authenticate
request with the preferred mech.
The AuthMechList
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | Variable | Request header |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Header | Variable | Response header |
Mech count | vInt | The number of mechs. |
Mech | String | String containing the name of the SASL mech in its IANA-registered form (e.g. GSSAPI, CRAM-MD5, etc) |
The Mech
value recurs for each supported mech.
16.3.4. Hot Rod BulkGet Operation
A Hot Rod BulkGet
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request Header. |
Entry Count | vInt | Contains the maximum number of Red Hat JBoss Data Grid entries to be returned by the server. The entry is the key and value pair. |
The response header for this operation contains one of the following response statuses:
Field | Data Type | Details |
---|---|---|
Header | variable | Response Header |
More | vInt |
Represents if more entries must be read from the stream. While |
Key Length | vInt | Contains the length of the key. |
Key | byte array | Contains the key value. |
Value Length | vInt | Contains the length of the value. |
Value | byte array | Contains the value. |
For each entry that was requested, a More
, Key Size
, Key
, Value Size
and Value
entry is appended to the response.
16.3.5. Hot Rod BulkKeysGet Operation
A Hot Rod BulkKeysGet
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Scope | vInt |
|
The response header for this operation contains one of the following response statuses:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Response Status | 1 byte |
|
More | 1 byte |
One byte representing whether more keys need to be read from the stream. When set to |
Key Length | vInt | Length of key |
Key | byte array | Retrieved key. |
More | 1 byte | One byte representing whether more entries need to be read from the stream. So, when it’s set to 1, it means that an entry follows, whereas when it’s set to 0, it’s the end of stream and no more entries are left to read. |
The values Key Length
and Key
recur for each key.
16.3.6. Hot Rod Clear Operation
The clear
operation format includes only a header.
Valid response statuses for this operation are as follows:
Response Status | Details |
---|---|
0x00 | Red Hat JBoss Data Grid was successfully cleared. |
16.3.7. Hot Rod ContainsKey Operation
A Hot Rod ContainsKey
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | - | - |
Key Length | vInt |
Contains the length of the key. The vInt data type is used because of its size (up to |
Key | Byte array | Contains a key, the corresponding value of which is requested. |
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | Successful operation. |
0x02 | The key does not exist. |
The response for this operation is empty.
16.3.8. Hot Rod Exec Operation
The Exec
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Script | String | Name of the script to execute. |
Parameter Count | vInt | The number of parameters. |
Parameter Name (per parameter) | String | The name of the parameter. |
Parameter Length (per parameter) | vInt | The length of the parameter. |
Parameter Value (per parameter) | byte array | The value of the parameter. |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Response status | 1 byte | 0x00 if the execution completed successfully. 0x85 if the server resulted in an error. |
Value Length | vInt | If success, length of return value. |
Value | byte array | If success, the result of the execution. |
16.3.9. Hot Rod Get Operation
A Hot Rod Get
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | Variable | Request Header |
Key Length | vInt |
Contains the length of the key. The vInt data type is used because of its size (up to |
Key | Byte array | Contains a key, the corresponding value of which is requested. |
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | Successful operation. |
0x02 | The key does not exist. |
The format of the get
operation’s response when the key is found is as follows:
Field | Data Type | Details |
---|---|---|
Header | Variable | Response Header |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | Contains the requested value. |
16.3.10. Hot Rod GetAll Operation
Bulk operation to get all entries that map to a given set of keys.
A Hot Rod GetAll
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header |
Key Count | vInt | How many keys to find entities for. |
Key Length | vInt | Length of key. |
Key | byte array | Retrieved key. |
The Key Length
and Key
values recur for each key.
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header |
Entry count | vInt | How many entries are being returned. |
Key Length | vInt | Length of key. |
Key | byte array | Retrieved key. |
Value Length | vInt | Length of value. |
Value | byte array | Retrieved value. |
The Key Length
, Key
, Value Length
, and Value
entries recur per key and value.
16.3.11. Hot Rod GetWithMetadata Operation
A Hot Rod GetWithMetadata
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt |
Length of key. Note that the size of a vInt can be up to five bytes, which theoretically can produce bigger numbers than |
Key | byte array | Byte array containing the key whose value is being requested. |
The response header for this operation contains one of the following response statuses:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Response status | 1 byte |
|
Flag | 1 byte |
A flag indicating whether the response contains expiration information. The value of the flag is obtained as a bitwise OR operation between |
Created | Long |
(optional) a Long representing the timestamp when the entry was created on the server. This value is returned only if the flag’s |
Lifespan | vInt |
(optional) a vInt representing the lifespan of the entry in seconds. This value is returned only if the flag’s |
LastUsed | Long |
(optional) a Long representing the timestamp when the entry was last accessed on the server. This value is returned only if the flag’s |
MaxIdle | vInt |
(optional) a vInt representing the maxIdle of the entry in seconds. This value is returned only if the flag’s |
Entry Version | 8 bytes | Unique value of an existing entry modification. The protocol does not mandate that entry_version values are sequential, however they need to be unique per update at the key level. |
Value Length | vInt | If success, length of value. |
Value | byte array | If success, the requested value. |
16.3.12. Hot Rod GetWithVersion Operation
A Hot Rod GetWithVersion
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | Variable | Request Header |
Key Length | vInt |
Contains the length of the key. The vInt data type is used because of its size (up to |
Key | Byte array | Contains a key, the corresponding value of which is requested. |
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | Successful operation. |
0x02 | The key does not exist. |
The format of the GetWithVersion
operation’s response when the key is found is as follows:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header |
Entry Version | 8 bytes | Unique value of an existing entry’s modification. The protocol does not mandate that entry_version values are sequential. They just need to be unique per update at the key level. |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | Contains the requested value. |
16.3.13. Hot Rod IterationEnd Operation
The IterationEnd
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
iterationId | String | The unique id of the iteration. |
The following are the valid response values returned from this operation:
Response Status | Details |
---|---|
0x00 | Successful operation. |
0x05 | Error for non existent iterationId. |
16.3.14. Hot Rod IterationNext Operation
The IterationNext
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
IterationId | String | The unique id of the iteration. |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Finished segments size | vInt | Size of the bitset representing segments that were finished iterating. |
Finished segments | byte array | Bitset encoding of the segments that were finished iterating. |
Entry count | vInt | How many entries are being returned. |
Number of value projections | vInt | Number of projections for the values. |
Metadata | 1 byte | If set, entry has metadata associated. |
Expiration | 1 byte |
A flag indicating whether the response contains expiration information. The value of the flag is obtained as a bitwise OR operation between |
Created | Long |
(optional) a Long representing the timestamp when the entry was created on the server. This value is returned only if the flag’s |
Lifespan | vInt |
(optional) a vInt representing the lifespan of the entry in seconds. This value is returned only if the flag’s |
LastUsed | Long |
(optional) a Long representing the timestamp when the entry was last accessed on the server. This value is returned only if the flag’s |
MaxIdle | vInt |
(optional) a vInt representing the maxIdle of the entry in seconds. This value is returned only if the flag’s |
Entry Version | 8 bytes | Unique value of an existing entry’s modification. Only present if Metadata flag is set. |
Key Length | vInt | Length of key. |
Key | byte array | Retrieved key. |
Value Length | vInt | Length of value. |
Value | byte array | Retrieved value. |
For each entry the Metadata
, Expiration
, Created
, Lifespan
, LastUsed
, MaxIdle
, Entry Version
, Key Length
, Key
, Value Length
, and Value
fields recur.
16.3.15. Hot Rod IterationStart Operation
The IterationStart
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Segments size | signed vInt | Size of the bitset encoding of the segments ids to iterate on. The size is the maximum segment id rounded to nearest multiple of 8. A value -1 indicates no segment filtering is to be done |
Segments | byte array | (Optional) Contains the segments ids bitset encoded, where each bit with value 1 represents a segment in the set. Byte order is little-endian. Example: segments [1,3,12,13] would result in the following encoding: 00001010 00110000 size: 16 bits first byte: represents segments from 0 to 7, from which 1 and 3 are set second byte: represents segments from 8 to 15, from which 12 and 13 are set
More details in the |
FilterConverter size | signed vInt |
The size of the String representing a |
FilterConverter | UTF-8 byte array |
(Optional) |
Parameters size | byte |
The number of parameters of the filter. Only present when |
Parameters | byte[][] |
An array of parameters. Each parameter is a byte array. Only present if |
BatchSize | vInt | Number of entries to transfers from the server at one go. |
Metadata | 1 byte | 1 if metadata is to be returned for each entry, 0 otherwise. |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
IterationId | String | The unique id of the iteration. |
16.3.16. Hot Rod Ping Operation
The ping
is an application level request to check for server availability.
Valid response statuses for this operation are as follows:
Response Status | Details |
---|---|
0x00 | Successful ping without any errors. |
16.3.17. Hot Rod Put Operation
The put
operation request format includes the following:
.
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | - | Contains the length of the key. |
Key | Byte array | Contains the key value. |
TimeUnits | Byte |
Time units of lifespan (first 4 bits) and maxIdle (last 4 bits). Special units 0x00 = SECONDS 0x01 = MILLISECONDS 0x02 = NANOSECONDS 0x03 = MICROSECONDS 0x04 = MINUTES 0x05 = HOURS 0x06 = DAYS 0x07 = DEFAULT 0x08 = INFINITE |
Lifespan | vInt |
Duration which the entry is allowed to life. Only sent when time unit is not |
Max Idle | vInt |
Duration that each entry can be idle before it’s evicted from the cache. Only sent when time unit is not |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | The requested value. |
The following are the valid response values returned from this operation:
.
Response Status | Details |
---|---|
0x00 | The value was successfully stored. |
0x03 | The value was successfully stored, and the previous value follows. |
An empty response is the default response for this operation. However, if ForceReturnPreviousValue
is passed, the previous value and key are returned. If the previous key and value do not exist, the value length would contain the value 0
.
16.3.18. Hot Rod PutAll Operation
Bulk operation to put all key value entries into the cache at the same time.
The PutAll
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
TimeUnits | Byte |
Time units of lifespan (first 4 bits) and maxIdle (last 4 bits). Special units 0x00 = SECONDS 0x01 = MILLISECONDS 0x02 = NANOSECONDS 0x03 = MICROSECONDS 0x04 = MINUTES 0x05 = HOURS 0x06 = DAYS 0x07 = DEFAULT 0x08 = INFINITE |
Lifespan | vInt |
Duration which the entry is allowed to life. Only sent when time unit is not |
Max Idle | vInt |
Duration that each entry can be idle before it’s evicted from the cache. Only sent when time unit is not |
Entry count | vInt | How many entries are being inserted. |
Key Length | vInt | Length of key. |
Key | byte array | Retrieved key. |
Value Length | vInt | Length of value. |
Value | byte array | Retrieved value. |
The Key Length
, Key
, Value Length
, and Value
fields repeat for each entry that will be placed.
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | Successful operation, indicating all keys were successfully put. |
16.3.19. Hot Rod PutIfAbsent Operation
The putIfAbsent
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt | Contains the length of the key. |
Key | Byte array | Contains the key value. |
TimeUnits | Byte |
Time units of lifespan (first 4 bits) and maxIdle (last 4 bits). Special units 0x00 = SECONDS 0x01 = MILLISECONDS 0x02 = NANOSECONDS 0x03 = MICROSECONDS 0x04 = MINUTES 0x05 = HOURS 0x06 = DAYS 0x07 = DEFAULT 0x08 = INFINITE |
Lifespan | vInt |
Duration which the entry is allowed to life. Only sent when time unit is not |
Max Idle | vInt |
Duration that each entry can be idle before it’s evicted from the cache. Only sent when time unit is not |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | Contains the requested value. |
The following are the valid response values returned from this operation:
.
Response Status | Details |
---|---|
0x00 | The value was successfully stored. |
0x01 | The key was present, therefore the value was not stored. The current value of the key is returned. |
0x04 | The operation failed because the key was present and its value follows in the response. |
An empty response is the default response for this operation. However, if ForceReturnPreviousValue
is passed, the previous value and key are returned. If the previous key and value do not exist, the value length would contain the value 0
.
16.3.20. Hot Rod Query Operation
The Query
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Query Length | vInt | The length of the Protobuf encoded query object. |
Query | Byte array | Byte array containing the Protobuf encoded query object, having a length specified by previous field. |
The following are the valid response values returned from this operation:
Response Status | Data | Details |
---|---|---|
Header | variable | Response header. |
Response payload Length | vInt | The length of the Protobuf encoded response object. |
Response payload | Byte array | Byte array containing the Protobuf encoded response object, having a length specified by previous field. |
The Hot Rod Query
operation request and response types are defined in the org/infinispan/query/remote/client/query.proto
resource filed, found inside infinispan-remote-query-client.jar.
16.3.21. Hot Rod Remove Operation
A Hot RodRemove
operation uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt |
Contains the length of the key. The vInt data type is used because of its size (up to |
Key | Byte array | Contains a key, the corresponding value of which is requested. |
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | Successful operation. |
0x02 | The key does not exist. |
0x03 | The key was removed, and the previous or removed value follows in the response. |
Normally, the response header for this operation is empty. However, if ForceReturnPreviousValue
is passed, the response header contains either:
- The value and length of the previous key.
-
The value length
0
and the response status0x02
to indicate that the key does not exist.
The remove operation’s response header contains the previous value and the length of the previous value for the provided key if ForceReturnPreviousValue
is passed. If the key does not exist or the previous value was null, the value length is 0
.
16.3.22. Hot Rod RemoveIfUnmodified Operation
The RemoveIfUnmodified
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt | Contains the length of the key. |
Key | Byte array | Contains the key value. |
Entry Version | 8 bytes | The version number for the entry. |
The following are the valid response values returned from this operation:
Response Status | Details |
---|---|
0x00 | The entry was replaced or removed. |
0x01 | The entry replace or remove was unsuccessful because the key was modified. |
0x02 | The key does not exist. |
0x03 | The key was removed, and the previous or replaced value follows in the response. |
0x04 | The entry remove was unsuccessful because the key was modified, and the modified value follows in the response. |
An empty response is the default response for this operation. However, if ForceReturnPreviousValue
is passed, the previous value and key are returned. If the previous key and value do not exist, the value length would contain the value 0
.
16.3.23. Hot Rod Replace Operation
The replace
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt | Contains the length of the key. |
Key | Byte array | Contains the key value. |
TimeUnits | Byte |
Time units of lifespan (first 4 bits) and maxIdle (last 4 bits). Special units 0x00 = SECONDS 0x01 = MILLISECONDS 0x02 = NANOSECONDS 0x03 = MICROSECONDS 0x04 = MINUTES 0x05 = HOURS 0x06 = DAYS 0x07 = DEFAULT 0x08 = INFINITE |
Lifespan | vInt |
Duration which the entry is allowed to life. Only sent when time unit is not |
Max Idle | vInt |
Duration that each entry can be idle before it’s evicted from the cache. Only sent when time unit is not |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | Contains the requested value. |
The following are the valid response values returned from this operation:
Response Status | Details |
---|---|
0x00 | The value was successfully stored. |
0x01 | The value was not stored because the key does not exist. |
0x03 | The value was successfully replaced, and the previous or replaced value follows in the response. |
An empty response is the default response for this operation. However, if ForceReturnPreviousValue
is passed, the previous value and key are returned. If the previous key and value do not exist, the value length would contain the value 0
.
16.3.24. Hot Rod ReplaceIfUnmodified Operation
The ReplaceIfUnmodified
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header. |
Key Length | vInt |
Length of key. Note that the size of a vint can be up to 5 bytes which in theory can produce bigger numbers than |
Key | byte array | Byte array containing the key whose value is being requested. |
TimeUnits | Byte |
Time units of lifespan (first 4 bits) and maxIdle (last 4 bits). Special units 0x00 = SECONDS 0x01 = MILLISECONDS 0x02 = NANOSECONDS 0x03 = MICROSECONDS 0x04 = MINUTES 0x05 = HOURS 0x06 = DAYS 0x07 = DEFAULT 0x08 = INFINITE |
Lifespan | vInt |
Duration which the entry is allowed to life. Only sent when time unit is not |
Max Idle | vInt |
Duration that each entry can be idle before it’s evicted from the cache. Only sent when time unit is not |
Entry Version | 8 bytes |
Use the value returned by |
Value Length | vInt | Length of value. |
Value | byte array | Value to be stored. |
The response header for this operation contains one of the following response statuses:
Response Status | Details |
---|---|
0x00 | The value was successfully stored. |
0x01 | Replace did not happen because key had been modified. |
0x02 | Replace did not happen because key does not exist. |
0x03 | The key was replaced, and the previous or replaced value follows in the response. |
0x04 | The entry replace was unsuccessful because the key was modified, and the modified value follows in the response. |
The following are the valid response values returned from this operation:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Previous value length | vInt | If force return previous value flag was sent in the request, the length of the previous value will be returned. If the key does not exist, value length would be 0. If no flag was sent, no value length would be present. |
Previous value | byte array | If force return previous value flag was sent in the request and the key was replaced, previous value. |
16.3.25. Hot Rod ReplaceWithVersion Operation
The ReplaceWithVersion
operation request format includes the following:
In the RemoteCache API, the Hot Rod ReplaceWithVersion
operation uses the ReplaceIfUnmodified
operation. As a result, these two operations are exactly the same in JBoss Data Grid.
Field | Data Type | Details |
---|---|---|
Header | - | - |
Key Length | vInt | Contains the length of the key. |
Key | Byte array | Contains the key value. |
Lifespan | vInt |
Contains the number of seconds before the entry expires. If the number of seconds exceeds thirty days, the value is treated as UNIX time (i.e. the number of seconds since the date |
Max Idle | vInt |
Contains the number of seconds an entry is allowed to remain idle before it is evicted from the cache. If this entry is set to |
Entry Version | 8 bytes | The version number for the entry. |
Value Length | vInt | Contains the length of the value. |
Value | Byte array | Contains the requested value. |
The following are the valid response values returned from this operation:
Response Status | Details |
---|---|
0x00 | Returned status if the entry was replaced or removed. |
0x01 | Returns status if the entry replace or remove was unsuccessful because the key was modified. |
0x02 | Returns status if the key does not exist. |
An empty response is the default response for this operation. However, if ForceReturnPreviousValue
is passed, the previous value and key are returned. If the previous key and value do not exist, the value length would contain the value 0
.
16.3.26. Hot Rod Stats Operation
This operation returns a summary of all available statistics. For each returned statistic, a name and value is returned in both string and UTF-8 formats.
The following are supported statistics for this operation:
Name | Details |
---|---|
timeSinceStart | Contains the number of seconds since Hot Rod started. |
currentNumberOfEntries | Contains the number of entries that currently exist in the Hot Rod server. |
totalNumberOfEntries | Contains the total number of entries stored in the Hot Rod server. |
stores | Contains the number of put operations attempted. |
retrievals | Contains the number of get operations attempted. |
hits | Contains the number of get hits. |
misses | Contains the number of get misses. |
removeHits | Contains the number of remove hits. |
removeMisses | Contains the number of removal misses. |
globalCurrentNumberOfEntries | Number of entries currently across the Hot Rod cluster. |
globalStores | Total number of put operations across the Hot Rod cluster. |
globalRetrievals | Total number of get operations across the Hot Rod cluster. |
globalHits | Total number of get hits across the Hot Rod cluster. |
globalMisses | Total number of get misses across the Hot Rod cluster. |
globalRemoveHits | Total number of removal hits across the Hot Rod cluster. |
globalRemoveMisses | Total number of removal misses across the Hot Rod cluster. |
Any of the statistics beginning with global
are not available if Hot Rod is running in local mode.
The response header for this operation contains the following:
Name | Data Type | Details |
---|---|---|
Header | variable | Response Header. |
Number of Stats | vInt | Contains the number of individual statistics returned. |
Name Length | vInt | Contains the length of the named statistic. |
Name | string | Contains the name of the statistic. |
Value Length | vInt | Contains the length of the value. |
Value | string | Contains the statistic value. |
The values Name Length
, Name
, Value Length
and Value
recur for each statistic requested.
16.3.27. Hot Rod Size Operation
The Size
operation request format includes the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Request header |
The response header for this operation contains the following:
Field | Data Type | Details |
---|---|---|
Header | variable | Response header. |
Size | vInt | Size of the remote cache, which is calculated globally in the clustered set ups, and if present, takes cache store contents into account as well. |
16.4. Hot Rod Operation Values
16.4.1. Hot Rod Operation Values
The following is a list of valid opcode
values for a request header and their corresponding response header values:
Operation | Request Operation Code | Response Operation Code |
---|---|---|
put | 0x01 | 0x02 |
get | 0x03 | 0x04 |
putIfAbsent | 0x05 | 0x06 |
replace | 0x07 | 0x08 |
replaceIfUnmodified | 0x09 | 0x0A |
remove | 0x0B | 0x0C |
removeIfUnmodified | 0x0D | 0x0E |
containsKey | 0x0F | 0x10 |
clear | 0x13 | 0x14 |
stats | 0x15 | 0x16 |
ping | 0x17 | 0x18 |
bulkGet | 0x19 | 0x1A |
getWithMetadata | 0x1B | 0x1C |
bulkKeysGet | 0x1D | 0x1E |
query | 0x1F | 0x20 |
authMechList | 0x21 | 0x22 |
auth | 0x23 | 0x24 |
addClientListener | 0x25 | 0x26 |
removeClientListener | 0x27 | 0x28 |
size | 0x29 | 0x2A |
exec | 0x2B | 0x2C |
putAll | 0x2D | 0x2E |
getAll | 0x2F | 0x30 |
iterationStart | 0x31 | 0x32 |
iterationNext | 0x33 | 0x34 |
iterationEnd | 0x35 | 0x36 |
Additionally, if the response header opcode
value is 0x50
, it indicates an error response.
16.4.2. Magic Values
The following is a list of valid values for the Magic
field in request and response headers:
Value | Details |
---|---|
0xA0 | Cache request marker. |
0xA1 | Cache response marker. |
16.4.3. Status Values
The following is a table that contains all valid values for the Status
field in a response header:
Value | Details |
---|---|
0x00 | No error. |
0x01 | Not put/removed/replaced. |
0x02 | Key does not exist. |
0x06 | Success status and compatibility mode is enabled. |
0x07 | Success status and return previous value, with compatibility mode is enabled. |
0x08 | Not executed and return previous value, with compatibility mode is enabled. |
0x81 | Invalid Magic value or Message ID. |
0x82 | Unknown command. |
0x83 | Unknown version. |
0x84 | Request parsing error. |
0x85 | Server error. |
0x86 | Command timed out. |
16.4.4. Client Intelligence Values
The following is a list of valid values for Client Intelligence
in a request header:
Value | Details |
---|---|
0x01 | Indicates a basic client that does not require any cluster or hash information. |
0x02 | Indicates a client that is aware of topology and requires cluster information. |
0x03 | Indicates a client that is aware of hash and distribution and requires both the cluster and hash information. |
16.4.5. Flag Values
The following is a list of valid flag
values in the request header:
Value | Details |
---|---|
0x0001 | ForceReturnPreviousValue |
16.4.6. Hot Rod Error Handling
Field | Data Type | Details |
---|---|---|
Error Opcode | - | Contains the error operation code. |
Error Status Number | - |
Contains a status number that corresponds to the |
Error Message Length | vInt | Contains the length of the error message. |
Error Message | string |
Contains the actual error message. If an |
16.5. Hot Rod Remote Events
16.5.1. Hot Rod Remote Events
Clients may register Remote Event Listeners, allowing them to receive updates on events happening in the server. As soon as a client listener has been added events are generated and sent, allowing the client to receive all events that have occurred after adding the listener.
16.5.2. Hot Rod Add Client Listener for Remote Events
Adding client listeners for remote events uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request Header. |
Listener ID | byte array | Listener identifier. |
Include state | byte | When this byte is set to 1, cached state is sent back to remote clients when either adding a cache listener for the first time, or when the node where a remote listener is registered changes in a clustered environment. When enabled, state is sent back as cache entry created events to the clients. If set to 0, no state is sent back to the client when adding a listener, nor it gets state when the node where the listener is registered changes. |
Key/value filter factory name | String | Optional name of the key/value filter factory to be used with this listener. The factory is used to create key/value filter instances which allow events to be filtered directly in the Hot Rod server, avoiding sending events that the client is not interested in. If no factory is to be used, the length of the string is 0. |
Key/value filter factory parameter count | byte | The key/value filter factory, when creating a filter instance, can take an arbitrary number of parameters, enabling the factory to be used to create different filter instances dynamically. This count field indicates how many parameters will be passed to the factory. If no factory name was provided, this field is not present in the request. |
Key/value filter factory parameter (per parameter) | byte array | Key/value filter factory parameter. |
Converter factory name | String | Optional name of the converter factory to be used with this listener. The factory is used to transform the contents of the events sent to clients. By default, when no converter is in use, events are well defined, according to the type of event generated. However, there might be situations where users want to add extra information to the event, or they want to reduce the size of the events. In these cases, a converter can be used to transform the event contents. The given converter factory name produces converter instances to do this job. If no factory is to be used, the length of the string is 0. |
Converter factory parameter count | byte | The converter factory, when creating a converter instance, can take an arbitrary number of parameters, enabling the factory to be used to create different converter instances dynamically. This count field indicates how many parameters will be passed to the factory. If no factory name was provided, this field is not present in the request. |
Converter factory parameter (per parameter) | byte array | Converter factory parameter. |
Use raw data | byte | If filter/converter parameters should be raw binary, then 1, otherwise 0. |
The format of the operation’s response is as follows:
Field | Data Type | Details |
---|---|---|
Header | Variable | Response Header. |
16.5.3. Hot Rod Remote Client Listener for Remote Events
Removing a previously added client listener uses the following request format:
Field | Data Type | Details |
---|---|---|
Header | variable | Request Header. |
Listener ID | byte array | Listener Identifier |
The format of the operation’s response is as follows:
Field | Data Type | Details |
---|---|---|
Header | Variable | Response Header. |
16.5.4. Hot Rod Event Header
Each remote event uses a header that adheres to the following format:
Field Name | Size | Value |
---|---|---|
Magic | 1 byte | 0xA1 = response |
Message ID | vLong | ID of event |
Opcode | 1 byte | A code responding to the Event type: 0x60 = cache entry created event 0x61 = cache entry modified event 0x62 = cache entry removed event 0x50 = error |
Status | 1 byte | Status of the response, with the following possible values: 0x00 = No error |
Topology Change Marker | 1 byte | Since events are not associated with a particular incoming topology ID to be able to decide whether a new topology is required to be sent or not, new topologies will never be sent with events. Hence, this marker will always have 0 value for events. |
16.5.5. Hot Rod Cache Entry Created Event
The CacheEntryCreated
event includes the following:
Field Name | Size | Value |
---|---|---|
Header | variable |
Event header with |
Listener ID | byte array | Listener for which this event is directed |
Custom Marker | byte | Custom event marker. For created events, this is 0. |
Command Retried | byte | Marker for events that are result of retried commands. If command is retried, it returns 1, otherwise 0. |
Key | byte array | Created key. |
Version | long | Version of the created entry. This version information can be used to make conditional operations on this cache entry. |
16.5.6. Hot Rod Cache Entry Modified Event
The CacheEntryModified
event includes the following:
Field Name | Size | Value |
---|---|---|
Header | variable |
Event header with |
Listener ID | byte array | Listener for which this event is directed |
Custom Marker | byte | Custom event marker. For created events, this is 0. |
Command Retried | byte | Marker for events that are result of retried commands. If command is retried, it returns 1, otherwise 0. |
Key | byte array | Modified key. |
Version | long | Version of the modified entry. This version information can be used to make conditional operations on this cache entry. |
16.5.7. Hot Rod Cache Entry Removed Event
The CacheEntryRemoved
event includes the following:
Field Name | Size | Value |
---|---|---|
Header | variable |
Event header with |
Listener ID | byte array | Listener for which this event is directed |
Custom Marker | byte | Custom event marker. For created events, this is 0. |
Command Retried | byte | Marker for events that are result of retried commands. If command is retried, it returns 1, otherwise 0. |
Key | byte array | Removed key. |
16.5.8. Hot Rod Custom Event
The Custom
event includes the following:
Field Name | Size | Value |
---|---|---|
Header | variable | Event header with event specific operation code |
Listener ID | byte array | Listener for which this event is directed |
Custom Marker | byte | Custom event marker. For custom events whose event data needs to be unmarshalled before returning to user the value is 1. For custom events that need to return the event data as-is to the user, the value is 2. |
Event Data | byte array | Custom event data. If the custom marker is 1, the bytes represent the marshalled version of the instance returned by the converter. If custom marker is 2, it represents the byte array, as returned by the converter. |
16.6. Put Request Example
The following is the coded request from a sample put
request using Hot Rod:
Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
8 | 0xA0 | 0x09 | 0x41 | 0x01 | 0x07 | 0x4D ('M') | 0x79 ('y') | 0x43 ('C') |
16 | 0x61 ('a') | 0x63 ('c') | 0x68 ('h') | 0x65 ('e') | 0x00 | 0x03 | 0x00 | 0x00 |
24 | 0x00 | 0x05 | 0x48 ('H') | 0x65 ('e') | 0x6C ('l') | 0x6C ('l') | 0x6F ('o') | 0x00 |
32 | 0x00 | 0x05 | 0x57 ('W') | 0x6F ('o') | 0x72 ('r') | 0x6C ('l') | 0x64 ('d') | - |
The following table contains all header fields and their values for the example request:
Field Name | Byte | Value |
---|---|---|
Magic | 0 | 0xA0 |
Version | 2 | 0x41 |
Cache Name Length | 4 | 0x07 |
Flag | 12 | 0x00 |
Topology ID | 14 | 0x00 |
Transaction ID | 16 | 0x00 |
Key | 18-22 | 'Hello' |
Max Idle | 24 | 0x00 |
Value | 26-30 | 'World' |
Message ID | 1 | 0x09 |
Opcode | 3 | 0x01 |
Cache Name | 5-11 | 'MyCache' |
Client Intelligence | 13 | 0x03 |
Transaction Type | 15 | 0x00 |
Key Field Length | 17 | 0x05 |
Lifespan | 23 | 0x00 |
Value Field Length | 25 | 0x05 |
The following is a coded response for the sample put
request:
Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
8 | 0xA1 | 0x09 | 0x01 | 0x00 | 0x00 | - | - | - |
The following table contains all header fields and their values for the example response:
Field Name | Byte | Value |
---|---|---|
Magic | 0 | 0xA1 |
Opcode | 2 | 0x01 |
Topology Change Marker | 4 | 0x00 |
Message ID | 1 | 0x09 |
Status | 3 | 0x00 |
16.7. Hot Rod Java Client
16.7.1. Hot Rod Java Client
Hot Rod is a binary, language neutral protocol. A Java client is able to interact with a server via the Hot Rod protocol using the Hot Rod Java Client API.
16.7.2. Hot Rod Java Client Download
Use the following steps to download the JBoss Data Grid Hot Rod Java Client:
Procedure: Download Hot Rod Java Client
- Log into the Customer Portal at https://access.redhat.com.
- Click the Downloads button near the top of the page.
- In the Product Downloads page, click Red Hat JBoss Data Grid.
- Select the appropriate JBoss Data Grid version from the Version: drop down menu.
- Locate the Red Hat JBoss Data Grid 7.2 Hot Rod Java Client entry and click the corresponding Download link.
16.7.3. Hot Rod Java Client Configuration
The Hot Rod Java client is configured both programmatically and externally using a configuration file or a properties file. The following example illustrate creation of a client instance using the available Java fluent API:
Client Instance Creation
org.infinispan.client.hotrod.configuration.ConfigurationBuilder cb = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder(); cb.tcpNoDelay(true) .connectionPool() .numTestsPerEvictionRun(3) .testOnBorrow(false) .testOnReturn(false) .testWhileIdle(true) .addServer() .host("localhost") .port(11222); RemoteCacheManager rmc = new RemoteCacheManager(cb.build());
Configuring the Hot Rod Java client using a properties file
To configure the Hot Rod Java client, edit the hotrod-client.properties file on the classpath.
The following example shows the possible content of the hotrod-client.properties file.
Configuration
infinispan.client.hotrod.transport_factory = org.infinispan.client.hotrod.impl.transport.tcp.TcpTransportFactory infinispan.client.hotrod.server_list = 127.0.0.1:11222 infinispan.client.hotrod.marshaller = org.infinispan.commons.marshall.jboss.GenericJBossMarshaller infinispan.client.hotrod.async_executor_factory = org.infinispan.client.hotrod.impl.async.DefaultAsyncExecutorFactory infinispan.client.hotrod.default_executor_factory.pool_size = 1 infinispan.client.hotrod.default_executor_factory.queue_size = 10000 infinispan.client.hotrod.hash_function_impl.1 = org.infinispan.client.hotrod.impl.consistenthash.ConsistentHashV1 infinispan.client.hotrod.tcp_no_delay = true infinispan.client.hotrod.ping_on_startup = true infinispan.client.hotrod.request_balancing_strategy = org.infinispan.client.hotrod.impl.transport.tcp.RoundRobinBalancingStrategy infinispan.client.hotrod.key_size_estimate = 64 infinispan.client.hotrod.value_size_estimate = 512 infinispan.client.hotrod.force_return_values = false infinispan.client.hotrod.tcp_keep_alive = true ## below is connection pooling config maxActive=-1 maxTotal = -1 maxIdle = -1 whenExhaustedAction = 1 timeBetweenEvictionRunsMillis=120000 minEvictableIdleTimeMillis=300000 testWhileIdle = true minIdle = 1
The TCPKEEPALIVE
configuration is enabled/disabled on the Hot Rod Java client either through a config property as seen in the example (infinispan.client.hotrod.tcp_keep_alive = true/false
or programmatically through the org.infinispan.client.hotrod.ConfigurationBuilder.tcpKeepAlive()
method.
Either of the following two constructors must be used in order for the properties file to be consumed by Red Hat JBoss Data Grid:
-
new RemoteCacheManager(boolean start)
-
new RemoteCacheManager()
16.7.4. Hot Rod Java Client Basic API
The following code shows how the client API can be used to store or retrieve information from a Hot Rod server using the Hot Rod Java client. This example assumes that a Hot Rod server has been started bound to the default location, localhost:11222
.
Basic API
//API entry point, by default it connects to localhost:11222 BasicCacheContainer cacheContainer = new RemoteCacheManager(); //obtain a handle to the remote default cache BasicCache<String, String> cache = cacheContainer.getCache(); //now add something to the cache and ensure it is there cache.put("car", "ferrari"); assert cache.get("car").equals("ferrari"); //remove the data cache.remove("car"); assert !cache.containsKey("car") : "Value must have been removed!";
The RemoteCacheManager
corresponds to DefaultCacheManager
, and both implement BasicCacheContainer
.
This API facilitates migration from local calls to remote calls via Hot Rod. This can be done by switching between DefaultCacheManager
and RemoteCacheManager
, which is simplified by the common BasicCacheContainer
interface.
All keys can be retrieved from the remote cache using the keySet()
method. If the remote cache is a distributed cache, the server will start a Map/Reduce job to retrieve all keys from clustered nodes and return all keys to the client.
Use this method with caution if there are a large number of keys.
Set keys = remoteCache.keySet();
16.7.5. Hot Rod Java Client Versioned API
To ensure data consistency, Hot Rod stores a version number that uniquely identifies each modification. Using getVersioned
, clients can retrieve the value associated with the key as well as the current version.
When using the Hot Rod Java client, a RemoteCacheManager
provides instances of the RemoteCache
interface that accesses the named or default cache on the remote cluster. This extends the Cache
interface to which it adds new methods, including the versioned API.
Using Versioned Methods
// To use the versioned API, remote classes are specifically needed RemoteCacheManager remoteCacheManager = new RemoteCacheManager(); RemoteCache<String, String> remoteCache = remoteCacheManager.getCache(); remoteCache.put("car", "ferrari"); VersionedValue valueBinary = remoteCache.getWithMetadata("car"); // removal only takes place only if the version has not been changed // in between. (a new version is associated with 'car' key on each change) assert remoteCache.removeWithVersion("car", valueBinary.getVersion()); assert !remoteCache.containsKey("car");
Using Replace
remoteCache.put("car", "ferrari"); VersionedValue valueBinary = remoteCache.getWithMetadata("car"); assert remoteCache.replaceWithVersion("car", "lamborghini", valueBinary.getVersion());
16.7.6. Cluster-Wide Dynamic Cache Creation with Hot Rod Java Client
If a cache needs to be created dynamically from a client, use the createCache()
method as follows:
BasicCache<String, String> cache = remoteCacheManager.administration().createCache("newCacheName", "newTemplate");
While a cache created this way will be available on all nodes in the cluster, it will also be ephemeral: shutting down the entire cluster and restarting it will not automatically recreate the caches. To make the caches persistent, use the PERMANENT
flag as follows:
BasicCache<String, String> cache = remoteCacheManager.administration().withFlags(AdminFlag.PERMANENT).createCache("newCacheName", "newTemplate");
In order for the above to work, global state must be enabled and a suitable configuration storage selected. The available configuration stores are:
-
VOLATILE
: as the name implies, this configuration storage does not supportPERMANENT
caches. -
OVERLAY
: this stores configurations in the global shared state persistent path in a file named caches.xml. -
MANAGED
: this is only supported in server deployments, and will storePERMANENT
caches in the server model. -
CUSTOM
: a custom configuration store.
16.8. Hot Rod C++ Client
16.8.1. Hot Rod C++ Client
The Hot Rod C++ client enables C++ runtime applications to connect and interact with Red Hat JBoss Data Grid remote servers, and to read or write data to remote caches. The Hot Rod C++ client supports all three levels of client intelligence and is supported on the following platforms:
- Red Hat Enterprise Linux 6, 64-bit
Red Hat Enterprise Linux 7, 64-bit
The Hot Rod C++ client is available as a Technology Preview on 64-bit Windows with Visual Studio 2015.
16.8.2. Hot Rod C++ Client Formats
The Hot Rod C++ client is available in the following two library formats:
- Static library
- Shared/Dynamic library
Static Library
The static library is statically linked to an application. This increases the size of the final executable. The application is self-contained and it does not need to ship a separate library.
Shared/Dynamic Library
Shared/Dynamic libraries are dynamically linked to an application at runtime. The library is stored in a separate file and can be upgraded separately from the application, without recompiling the application.
This can only happen if the library’s major version is equal to the one against which the application was linked at compile time, indicating that it is binary compatible.
16.8.3. Hot Rod C++ Client Prerequisites
The following table details requirements needed to use the Hot Rod C++ Client depending on the underlying OS:
Operating System | Hot Rod C++ Client Prerequisites |
---|---|
RHEL 6, 64-bit | C++ 03 compiler with support for shared_ptr TR1 (GCC 4.0+) |
RHEL 7, 64-bit | C++ 11 compiler (GCC 4.8.1) |
Windows 7 x64 | C 11 compiler (Visual Studio 2015, Microsoft Visual C 2013 Redistributable Package for the x64 platform) |
16.8.4. Installing the Hot Rod C++ Client
16.8.4.1. Hot Rod C++ Client Download and Installation
The Hot Rod C++ client is distributed in two file types, based on the Operating System where the client will be used:
- RHEL servers install via an RPM distribution.
- Windows servers install via a zip distribution.
16.8.4.2. Hot Rod C++ Client RHEL Download and Installation
To install the client perform the following steps:
- Ensure your Red Hat Enterprise Linux (RHEL) system is registered to your account using Red Hat Subscription Manager. For more information, refer to the Red Hat Subscription Management documentation.
Using Red Hat Subscription Manager, enable the appropriate repository based on your version of RHEL:
Table 16.77. RHSM Repositories RHEL Version Repo Name RHEL 6
jb-datagrid-7.2-for-rhel-6-server-rpms
RHEL 7
jb-datagrid-7.2-for-rhel-7-server-rpms
For instance, to enable the RHEL 7 repo the following command would be used:
subscription-manager repos --enable=jb-datagrid-7.2-for-rhel-7-server-rpms
For RHEL 7 you also need to enable the
rhel-7-server-optional-rpms
repo which provides the requiredprotobuf-devel
andprotobuf-static
RPMs:subscription-manager repos --enable=rhel-7-server-optional-rpms
Once the appropriate repos have been added the C++ client RPM may be installed with:
yum install jdg-cpp-client
16.8.4.3. Hot Rod C++ Client Windows Download and Installation
The Hot Rod C++ Client for Windows is included in a separate zip file jboss-datagrid-<version>-hotrod-cpp-WIN-x86_64.zip under Red Hat JBoss Data Grid binaries on the Red Hat Customer Portal at https://access.redhat.com.
Once downloaded the C++ Client may be installed by extracing the zip file to the desired location on the system.
16.8.5. Utilizing the Protobuf Compiler with the Hot Rod C++ Client
16.8.5.1. Using the Protobuf Compiler in RHEL 7
The C++ Hot Rod client channel in RHEL 7 includes the Protobuf compiler. The following instructions detail using this compiler:
- Ensure that the C++ channel has been added to the RHEL system, as outlined in Hot Rod C++ Client RHEL Download and Installation.:
Install the
protobuf
rpm:yum install protobuf
Add the included protobuf libraries to the library path. These libraries are included in
/opt/lib64
by default:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/lib64
Compile the desired protobuf files into C++ header and source files:
/bin/protoc --cpp_out dllexport_decl=HR_PROTO_EXPORT:/path/to/output/ $FILE
NoteHR_PROTO_EXOPRT
is a macro defined within the Hot Rod client code, and will be expanded when the files are subsequently compiled.- The resulting header and source files will be generated in the designated output directory, allowing them to be referenced and compiled as normal with the specific application code.
For additional information on Protobuf refer to Protobuf Encoding.
16.8.5.2. Using the Protobuf Compiler in Windows
The C++ Hot Rod client for Windows ships with the precompiled Hot Rod components along with the Protobuf compiler included. For many users the included components may be used without the need for additional compilation; however, should any .proto files require compiling the following instructions document this process:
- Extract the jboss-datagrid-<version>-hotrod-cpp-client-WIN-x86_64.zip locally to the filesystem.
- Open a command prompt and navigate to the newly extracted directory.
Compile the desired protobuf files into C++ header and source files:
bin\protoc --cpp_out dllexport_decl=HR_PROTO_EXPORT:path\to\output\ $FILE
NoteHR_PROTO_EXOPRT
is a macro defined within the Hot Rod client code, and will be expanded when the files are subsequently compiled.- The resulting header and source files will be generated in the designated output directory, allowing them to be referenced and compiled as normal with the specific application code.
For additional information on Protobuf refer to Protobuf Encoding.
16.8.6. Hot Rod C++ Client Configuration
The Hot Rod C++ client interacts with a remote Hot Rod server using the RemoteCache API. To initiate communication with a particular Hot Rod server, configure RemoteCache and choose the specific cache on the Hot Rod server.
Use the ConfigurationBuilder API to configure:
- The initial set of servers to connect to.
- Connection pooling attributes.
- Connection/Socket timeouts and TCP nodelay.
- Hot Rod protocol version.
Sample C++ main executable file configuration
The following example shows how to use the ConfigurationBuilder to configure a RemoteCacheManager
and how to obtain the default remote cache:
SimpleMain.cpp
#include "infinispan/hotrod/ConfigurationBuilder.h" #include "infinispan/hotrod/RemoteCacheManager.h" #include "infinispan/hotrod/RemoteCache.h" #include <stdlib.h> using namespace infinispan::hotrod; int main(int argc, char** argv) { ConfigurationBuilder b; b.addServer().host("127.0.0.1").port(11222); RemoteCacheManager cm(builder.build()); RemoteCache<std::string, std::string> cache = cm.getCache<std::string, std::string>(); return 0; }
16.8.7. Hot Rod C++ Client API
The RemoteCacheManager is a starting point to obtain a reference to a RemoteCache. The RemoteCache API can interact with a remote Hot Rod server and the specific cache on that server.
Using the RemoteCache reference obtained in the previous example, it is possible to put, get, replace and remove values in a remote cache. It is also possible to perform bulk operations, such as retrieving all of the keys, and clearing the cache.
When a RemoteCacheManager is stopped, all resources in use are released.
SimpleMain.cpp
RemoteCache<std::string, std::string> rc = cm.getCache<std::string, std::string>(); std::string k1("key13"); std::string v1("boron"); // put rc.put(k1, v1); std::auto_ptr<std::string> rv(rc.get(k1)); rc.putIfAbsent(k1, v1); std::auto_ptr<std::string> rv2(rc.get(k1)); std::map<HR_SHARED_PTR<std::string>,HR_SHARED_PTR<std::string> > map = rc.getBulk(0); std::cout << "getBulk size" << map.size() << std::endl; .. . cm.stop();
16.8.8. Hot Rod C++ Client Asynchronous API
The Hot Rod C++ client offers asynchronous versions of many of the synchronous methods, allowing non-blocking methods for interacting with remote caches.
These methods follow the same naming convention as the synchronous methods, except that Async
is appended to the end of each method. Asynchronous methods return a std::future
containing the result of the operation. If a method were to return a std::string
, instead it will return a std::future < std::string* >
A list of asynchronous methods are below:
-
clearAsync
-
getAsync
-
putAsync
-
putAllAsync
-
putIfAbsentAsync
-
removeAsync
-
removeWithVersionAsync
-
replaceAsync
-
replaceWithVersionAsync
Hot Rod C++ Asynchronous API Example
The following example demonstrates using these methods:
#include "infinispan/hotrod/ConfigurationBuilder.h" #include "infinispan/hotrod/RemoteCacheManager.h" #include "infinispan/hotrod/RemoteCache.h" #include "infinispan/hotrod/Version.h" #include "infinispan/hotrod/JBasicMarshaller.h" #include <iostream> #include <thread> #include <future> using namespace infinispan::hotrod; int main(int argc, char** argv) { ConfigurationBuilder builder; builder.addServer().host(argc > 1 ? argv[1] : "127.0.0.1").port(argc > 2 ? atoi(argv[2]) : 11222).protocolVersion(Configuration::PROTOCOL_VERSION_24); RemoteCacheManager cacheManager(builder.build(), false); auto *km = new BasicMarshaller<std::string>(); auto *vm = new BasicMarshaller<std::string>(); auto cache = cacheManager.getCache<std::string, std::string>(km, &Marshaller<std::string>::destroy, vm, &Marshaller<std::string>::destroy ); cacheManager.start(); std::string ak1("asyncK1"); std::string av1("asyncV1"); std::string ak2("asyncK2"); std::string av2("asyncV2"); cache.clear(); // Put ak1,av1 in async thread std::future<std::string*> future_put= cache.putAsync(ak1,av1); // Get the value in this thread std::string* arv1= cache.get(ak1); // Now wait for put completion future_put.wait(); // All is synch now std::string* arv11= cache.get(ak1); if (!arv11 || arv11->compare(av1)) { std::cout << "fail: expected " << av1 << "got " << (arv11 ? *arv11 : "null") << std::endl; return 1; } // Read ak1 again, but in async way and test that the result is the same std::future<std::string*> future_ga= cache.getAsync(ak1); std::string* arv2= future_ga.get(); if (!arv2 || arv2->compare(av1)) { std::cerr << "fail: expected " << av1 << " got " << (arv2 ? *arv2 : "null") << std::endl; return 1; } // Now user pass a simple lambda func that set a flag to true when the put completes bool flag=false; std::future<std::string*> future_put1= cache.putAsync(ak2,av2,0,0,[&] (std::string *v){flag=true; return v;}); // The put is not completed here so flag must be false if (flag) { std::cerr << "fail: expected false got true" << std::endl; return 1; } // Now wait for put completion future_put1.wait(); // The user lambda must be executed so flag must be true if (!flag) { std::cerr << "fail: expected true got false" << std::endl; return 1; } // Same test for get flag=false; // Now user pass a simple lambda func that set a flag to true when the put completes std::future<std::string*> future_get1= cache.getAsync(ak2,[&] (std::string *v){flag=true; return v;}); // The get is not completed here so flag must be false if (flag) { std::cerr << "fail: expected false got true" << std::endl; return 1; } // Now wait for get completion future_get1.wait(); if (!flag) { std::cerr << "fail: expected true got false" << std::endl; return 1; } std::string* arv3= future_get1.get(); if (!arv3 || arv3->compare(av2)) { std::cerr << "fail: expected " << av2 << " got " << (arv3 ? *arv3 : "null") << std::endl; return 1; } cacheManager.stop(); }
16.8.9. Hot Rod C++ Client Remote Event Listeners
The Hot Rod C++ client supports remote cache listeners, and these may be added using the add_listener
function on the ClientCacheListener
.
Remote Event Listeners are a Technology Preview feature of the Hot Rod C++ client in Red Hat JBoss Data Grid 7.2.
This function takes a listener for each event type(create, modify, remove, expire, or custom). For more information on Remote Event Listeners refer to Remote Event Listeners (Hot Rod). An example of this is provided below:
ConfigurationBuilder builder; builder.balancingStrategyProducer(nullptr); builder.addServer().host("127.0.0.1").port(11222); builder.protocolVersion(Configuration::PROTOCOL_VERSION_24); RemoteCacheManager cacheManager(builder.build(), false); cacheManager.start(); JBasicMarshaller<int> *km = new JBasicMarshaller<int>(); JBasicMarshaller<std::string> *vm = new JBasicMarshaller<std::string>(); RemoteCache<int, std::string> cache = cacheManager.getCache<int, std::string>(km, &Marshaller<int>::destroy, vm, &Marshaller<std::string>::destroy); cache.clear(); std::vector<std::vector<char> > filterFactoryParams; std::vector<std::vector<char> > converterFactoryParams; CacheClientListener<int, std::string> cl(cache); int createdCount=0, modifiedCount=0, removedCount=0, expiredCount=0; // We're using future and promise to have a basic listeners/main thread synch int setFutureEventKey=0; std::promise<void> promise; std::function<void(ClientCacheEntryCreatedEvent<int>)> listenerCreated = [&createdCount, &setFutureEventKey, &promise](ClientCacheEntryCreatedEvent<int> e) { createdCount++; if (setFutureEventKey==e.getKey()) promise.set_value(); }; std::function<void(ClientCacheEntryModifiedEvent<int>)> listenerModified = [&modifiedCount, &setFutureEventKey, &promise](ClientCacheEntryModifiedEvent <int> e) { modifiedCount++; if (setFutureEventKey==e.getKey()) promise.set_value(); }; std::function<void(ClientCacheEntryRemovedEvent<int>)> listenerRemoved = [&removedCount, &setFutureEventKey, &promise](ClientCacheEntryRemovedEvent <int> e) { removedCount++; if (setFutureEventKey==e.getKey()) promise.set_value(); }; std::function<void(ClientCacheEntryExpiredEvent<int>)> listenerExpired = [&expiredCount, &setFutureEventKey, &promise](ClientCacheEntryExpiredEvent <int> e) { expiredCount++; if (setFutureEventKey==e.getKey()) promise.set_value(); }; cl.add_listener(listenerCreated); cl.add_listener(listenerModified); cl.add_listener(listenerRemoved); cl.add_listener(listenerExpired); cache.addClientListener(cl, filterFactoryParams, converterFactoryParams);
16.8.10. Hot Rod C++ Client Working with Sites
Multiple Red Hat JBoss Data Grid Server clusters may be deployed so that each cluster belongs to a different site. Such deployments are done to enable data to be backed up from one cluster to another, potentially in a different geographical location. C++ client implementation can failover between nodes within a cluster, along with failing over to a different cluster entirely, should the original cluster become nonresponsive. To be able to failover between clusters all Red Hat JBoss Data Grid Servers must be configured with Cross-Datacenter replication. Instructions for this procedure are found in the Red Hat JBoss Data Grid Administration and Configuration Guide.
Once failed over the client will remain connected to the alternative cluster until this new cluster becomes unavailable, in which case it will throw an exception. If the original cluster becomes operational, the client will not switch over automatically. To switch back to the original cluster use the switchToDefaultCluster()
method mentioned below.
Once Cross-Datacenter replication has been configured on the servers, the client has to provide the alternative clusters' configuration with at least one host/port pair details for each of the clusters configured. For example:
#include "infinispan/hotrod/ConfigurationBuilder.h" #include "infinispan/hotrod/RemoteCacheManager.h" #include "infinispan/hotrod/RemoteCache.h" #include <stdlib.h> using namespace infinispan::hotrod; int main(int argc, char** argv) { ConfigurationBuilder b; b.addServer().host("127.0.0.1").port(11222); b.addCluster("nyc").addClusterNode("127.0.0.1", 11322); RemoteCacheManager cm(builder.build()); RemoteCache<std::string, std::string> cache = cm.getCache<std::string, std::string>(); return 0; }
16.8.10.1. Manual Cluster Switch
In addition to automatic site failover, C++ clients may switch between clusters by calling either of the following methods:
-
switchToCluster(clusterName)
- Forces the client to switch to the pre-defined cluster name passed in. -
switchToDefaultCluster
- Forces the client to switch to the initial servers defined in the client configuration.
16.8.11. Performing Remote Queries via the Hot Rod C++ Client
The Hot Rod C++ client allows remote querying, using Google’s Protocol Buffers, once the RemoteCacheManager
has been configured with the Protobuf marshaller.
Performing Remote Queries is a Technology Preview feature of the Hot Rod C++ client in Red Hat JBoss Data Grid 7.2.
Enable Remote Querying on the Hot Rod C++ Client
Obtain a connection to the remote Red Hat JBoss Data Grid server:
#include "addressbook.pb.h" #include "bank.pb.h" #include <infinispan/hotrod/BasicTypesProtoStreamMarshaller.h> #include <infinispan/hotrod/ProtoStreamMarshaller.h> #include "infinispan/hotrod/ConfigurationBuilder.h" #include "infinispan/hotrod/RemoteCacheManager.h" #include "infinispan/hotrod/RemoteCache.h" #include "infinispan/hotrod/Version.h" #include "infinispan/hotrod/query.pb.h" #include "infinispan/hotrod/QueryUtils.h" #include <vector> #include <tuple> #define PROTOBUF_METADATA_CACHE_NAME "___protobuf_metadata" #define ERRORS_KEY_SUFFIX ".errors" using namespace infinispan::hotrod; using namespace org::infinispan::query::remote::client; std::string read(std::string file) { std::ifstream t(file); std::stringstream buffer; buffer << t.rdbuf(); return buffer.str(); } int main(int argc, char** argv) { std::cout << "Tests for Query" << std::endl; ConfigurationBuilder builder; builder.addServer().host(argc > 1 ? argv[1] : "127.0.0.1").port(argc > 2 ? atoi(argv[2]) : 11222).protocolVersion(Configuration::PROTOCOL_VERSION_24); RemoteCacheManager cacheManager(builder.build(), false); cacheManager.start();
Create the Protobuf metadata cache with the Protobuf Marshaller:
// This example continues the previous codeblock // Create the Protobuf Metadata cache peer with a Protobuf marshaller auto *km = new BasicTypesProtoStreamMarshaller<std::string>(); auto *vm = new BasicTypesProtoStreamMarshaller<std::string>(); auto metadataCache = cacheManager.getCache<std::string, std::string>( km, &Marshaller<std::string>::destroy, vm, &Marshaller<std::string>::destroy,PROTOBUF_METADATA_CACHE_NAME, false);
Install the data model in the Protobuf metadata cache:
// This example continues the previous codeblock // Install the data model into the Protobuf metadata cache metadataCache.put("sample_bank_account/bank.proto", read("proto/bank.proto")); if (metadataCache.containsKey(ERRORS_KEY_SUFFIX)) { std::cerr << "fail: error in registering .proto model" << std::endl; return -1; }
This step adds data to the cache for the purposes of this demonstration, and may be ignored when simply querying a remote cache:
// This example continues the previous codeblock // Fill the cache with the application data: two users Tom and Jerry testCache.clear(); sample_bank_account::User_Address a; sample_bank_account::User user1; user1.set_id(3); user1.set_name("Tom"); user1.set_surname("Cat"); user1.set_gender(sample_bank_account::User_Gender_MALE); sample_bank_account::User_Address * addr= user1.add_addresses(); addr->set_street("Via Roma"); addr->set_number(3); addr->set_postcode("202020"); testCache.put(3, user1); user1.set_id(4); user1.set_name("Jerry"); user1.set_surname("Mouse"); addr->set_street("Via Milano"); user1.set_gender(sample_bank_account::User_Gender_MALE); testCache.put(4, user1);
Query the remote cache:
// This example continues the previous codeblock // Simple query to get User objects { QueryRequest qr; std::cout << "Query: from sample_bank_account.User" << std::endl; qr.set_jpqlstring("from sample_bank_account.User"); QueryResponse resp = testCache.query(qr); std::vector<sample_bank_account::User> res; unwrapResults(resp, res); for (auto i : res) { std::cout << "User(id=" << i.id() << ",name=" << i.name() << ",surname=" << i.surname() << ")" << std::endl; } } cacheManager.stop(); return 0; }
Additional Query Examples
The following examples are included to demonstrate more complicated queries, and may be used on the same dataset found in the above procedure.
Using a query with a conditional
// Simple query to get User objects with where condition { QueryRequest qr; std::cout << "from sample_bank_account.User u where u.addresses.street=\"Via Milano\"" << std::endl; qr.set_jpqlstring("from sample_bank_account.User u where u.addresses.street=\"Via Milano\""); QueryResponse resp = testCache.query(qr); std::vector<sample_bank_account::User> res; unwrapResults(resp, res); for (auto i : res) { std::cout << "User(id=" << i.id() << ",name=" << i.name() << ",surname=" << i.surname() << ")" << std::endl; } }
Using a query with a projection
// Simple query to get projection (name, surname) { QueryRequest qr; std::cout << "Query: select u.name, u.surname from sample_bank_account.User u" << std::endl; qr.set_jpqlstring( "select u.name, u.surname from sample_bank_account.User u"); QueryResponse resp = testCache.query(qr); //Typed resultset std::vector<std::tuple<std::string, std::string> > prjRes; unwrapProjection(resp, prjRes); for (auto i : prjRes) { std::cout << "Name: " << std::get<0> (i) << " Surname: " << std::get<1> (i) << std::endl; } }
16.8.12. Using the Near Cache with the Hot Rod C++ Client
Near caches are optional caches for the Hot Rod C++ client that keep recently accessed data close to the user, providing faster access to data that is accessed frequently. This cache acts as a local Hot Rod client cache that are synchronized with the remote server in the background.
Near caches are enabled programmatically on the ConfigurationBuilder
by using the nearCache()
method, as seen in the following example:
int main(int argc, char** argv) { ConfigurationBuilder confBuilder; confBuilder.addServer().host("127.0.0.1").port(11222); confBuilder.protocolVersion(Configuration::PROTOCOL_VERSION_24); confBuilder.balancingStrategyProducer(nullptr); // Enable the near cache support confBuilder.nearCache().mode(NearCacheMode::INVALIDATED).maxEntries(4);
The following methods are used to configure the near cache’s behavior:
-
nearCache()
- defines aNearCacheConfigurationBuilder
which may be modified further. -
mode(NearCacheMode mode)
- requires aNearCacheMode
be passed in. Defaults toDISABLED
, indicating no near cache is enabled. -
maxEntries(int maxEntries)
- indicates the maximum number of entries for the near cache to contain. Once the near cache is full, the oldest entry will be evicted. Setting this value to0
defines an unbounded near cache.
Entries in the near cache are kept aligned with the remote cache via events. If a change occurs in the server then an appropriate event is sent to the client, which will update the near cache accordingly.
16.8.13. Script Execution Using the Hot Rod C++ Client
The Hot Rod C++ client allows tasks to be executed directly on JBoss Data Grid servers via Remote Execution. This feature executes logic close to the data, utilizing the resources of all nodes in the cluster. Tasks may be deployed to the server instances, and may then be executed programmatically.
Remote Execution is a Technology Preview feature of the Hot Rod C++ client in Red Hat JBoss Data Grid 7.2.
Installing a Task
Tasks may be installed on the server by being using the put(std::string name, std::string script)
method of the ___script_cache
. The extension of the script name determines the engine used to execute the script; however, this may be overridden by metadata in the script itself.
The following example demonstrates installing scripts:
Installing a Task with the C++ Client
#include "infinispan/hotrod/ConfigurationBuilder.h" #include "infinispan/hotrod/RemoteCacheManager.h" #include "infinispan/hotrod/RemoteCache.h" #include "infinispan/hotrod/Version.h" #include "infinispan/hotrod/JBasicMarshaller.h" using namespace infinispan::hotrod; int main(int argc, char** argv) { // Configure the client ConfigurationBuilder builder; builder.addServer().host("127.0.0.1").port(11222).protocolVersion( Configuration::PROTOCOL_VERSION_24); RemoteCacheManager cacheManager(builder.build(), false); try { // Create the cache with the given marshallers auto *km = new JBasicMarshaller<std::string>(); auto *vm = new JBasicMarshaller<std::string>(); RemoteCache<std::string, std::string> cache = cacheManager.getCache< std::string, std::string>(km, &Marshaller<std::string>::destroy, vm, &Marshaller<std::string>::destroy, std::string("namedCache")); cacheManager.start(); // Obtain a reference to the ___script_cache RemoteCache<std::string, std::string> scriptCache = cacheManager.getCache<std::string, std::string>( "___script_cache", false); // Install on the server the getValue script std::string getValueScript( "// mode=local,language=javascript\n " "var cache = cacheManager.getCache(\"namedCache\");\n " "var ct = cache.get(\"accessCounter\");\n " "var c = ct==null ? 0 : parseInt(ct);\n " "cache.put(\"accessCounter\",(++c).toString());\n " "cache.get(\"privateValue\") "); std::string getValueScriptName("getValue.js"); std::string pGetValueScriptName = JBasicMarshaller<std::string>::addPreamble(getValueScriptName); std::string pGetValueScript = JBasicMarshaller<std::string>::addPreamble(getValueScript); scriptCache.put(pGetValueScriptName, pGetValueScript); // Install on the server the get access counter script std::string getAccessScript( "// mode=local,language=javascript\n " "var cache = cacheManager.getCache(\"namedCache\");\n " "cache.get(\"accessCounter\")"); std::string getAccessScriptName("getAccessCounter.js"); std::string pGetAccessScriptName = JBasicMarshaller<std::string>::addPreamble(getAccessScriptName); std::string pGetAccessScript = JBasicMarshaller<std::string>::addPreamble(getAccessScript); scriptCache.put(pGetAccessScriptName, pGetAccessScript);
Executing a Task
Once installed, a task may be executed by using the execute(std::string name, std::map<std::string, std::string> args)
method, passing in the name of the script to execute, along with any arguments that are required for execution.
The following example demonstrates executing a script:
Executing a Script with the C++ Client
// The following is a continuation of the above example cache.put("privateValue", "Counted Access Value"); std::map<std::string, std::string> s; // Execute the getValue script std::vector<unsigned char> execValueResult = cache.execute( getValueScriptName, s); // Execute the getAccess script std::vector<unsigned char> execAccessResult = cache.execute( getAccessScriptName, s); std::string value( JBasicMarshallerHelper::unmarshall<std::string>( (char*) execValueResult.data())); std::string access( JBasicMarshallerHelper::unmarshall<std::string>( (char*) execAccessResult.data())); std::cout << "Returned value is '" << value << "' and has been accessed: " << access << " times." << std::endl; } catch (const Exception& e) { std::cout << "is: " << typeid(e).name() << '\n'; std::cerr << "fail unexpected exception: " << e.what() << std::endl; return 1; } cacheManager.stop(); return 0; }
16.9. Hot Rod C# Client
16.9.1. Hot Rod C# Client
The Hot Rod C# client allows .NET runtime applications to connect and interact with Red Hat JBoss Data Grid servers. This client is aware of the cluster topology and hashing scheme, and can access an entry on the server in a single hop similar to the Hot Rod Java and Hot Rod C++ clients.
The Hot Rod C# client is compatible with 64-bit operating systems on which the .NET Framework is supported by Microsoft. Visual Studio 2015 and .NET 4.6.2 are prerequisites for the Hot Rod C# client.
16.9.2. Hot Rod C# Client Download and Installation
The Hot Rod C# client is included in a .msi file jboss-datagrid-<version>-hotrod-dotnet-client.msi packed for download with Red Hat JBoss Data Grid. To install the Hot Rod C# client, execute the following instructions.
Installing the Hot Rod C# Client
As an administrator, navigate to the location where the Hot Rod C# .msi file is downloaded. Run the .msi file to launch the windows installer and then click Next.
Figure 16.1. Hot Rod C# Client Setup Welcome
Review the end-user license agreement. Select the I accept the terms in the License Agreement check box and then click Next.
Figure 16.2. Hot Rod C# Client End-User License Agreement
To change the default directory, click Change… or click Next to install in the default directory.
Figure 16.3. Hot Rod C# Client Destination Folder
Click Install to begin the Hot Rod C# client installation.
Figure 16.4. Hot Rod C# Client Begin Installation
Click Finish to complete the Hot Rod C# client installation.
Figure 16.5. Hot Rod C# Client Setup Completion
16.9.3. Creating a Hot Rod C# .NET Project
To use the Hot Rod C# client in a .NET project the following steps must be performed:
Configure the Hot Rod C# Project
Add the Path Environment Variables
The PATH environment variable must have the following folders added:
C:\path\to\infinispan-hotrod-dotnet 8.5.0.Final\bin C:\path\to\infinispan-hotrod-dotnet 8.5.0.Final\lib
Remove Prefer 32 bit
On the Project properties, under the Build tab, ensure that Prefer 32 bit is unchecked.
Add the Hot Rod C# dlls
- On the Solution Explorer view select Project.
- Select References.
- Right-click on references and select Add Reference.
- In the window presented, click Browse and navigate to the C:\path\to\infinispan-hotrod-dotnet 8.5.0.Final\lib\hotrodcs.dll file.
- Click OK.
The Hot Rod C# API may now be used in the .NET project.
16.9.4. Hot Rod C# Client Configuration
The Hot Rod C# client is configured programmatically using the ConfigurationBuilder. Configure the host and the port to which the client should connect.
Sample C# file configuration
The following example shows how to use the ConfigurationBuilder to configure a RemoteCacheManager
.
C# configuration
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Infinispan.HotRod; using Infinispan.HotRod.Config; namespace simpleapp { class Program { static void Main(string[] args) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.AddServer() .Host(args.Length > 1 ? args[0] : "127.0.0.1") .Port(args.Length > 2 ? int.Parse(args[1]) : 11222); Configuration config = builder.Build(); RemoteCacheManager cacheManager = new RemoteCacheManager(config); [...] } } }
16.9.5. Hot Rod C# Client API
The RemoteCacheManager
is a starting point to obtain a reference to a RemoteCache.
The following example shows retrieval of a default cache from the server and a few basic operations.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Infinispan.HotRod; using Infinispan.HotRod.Config; namespace simpleapp { class Program { static void Main(string[] args) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.AddServer() .Host(args.Length > 1 ? args[0] : "127.0.0.1") .Port(args.Length > 2 ? int.Parse(args[1]) : 11222); Configuration config = builder.Build(); RemoteCacheManager cacheManager = new RemoteCacheManager(config); cacheManager.Start(); // Retrieve a reference to the default cache. IRemoteCache<String, String> cache = cacheManager.GetCache<String, String>(); // Add entries. cache.Put("key1", "value1"); cache.PutIfAbsent("key1", "anotherValue1"); cache.PutIfAbsent("key2", "value2"); cache.PutIfAbsent("key3", "value3"); // Retrive entries. Console.WriteLine("key1 -> " + cache.Get("key1")); // Bulk retrieve key/value pairs. int limit = 10; IDictionary<String, String> result = cache.GetBulk(limit); foreach (KeyValuePair<String, String> kv in result) { Console.WriteLine(kv.Key + " -> " + kv.Value); } // Remove entries. cache.Remove("key2"); Console.WriteLine("key2 -> " + cache.Get("key2")); cacheManager.Stop(); } } }
16.9.6. Hot Rod C# Client Asynchronous API
The Hot Rod C# client offers asynchronous versions of many of the synchronous methods, allowing non-blocking methods for interacting with remote caches.
These methods follow the same naming convention as the synchronous methods, except that Async is appended to the end of each method. Asynchronous methods return a Task
containing the result of the operation. If a method were to return a String
, instead it will return a Task<String>
A list of asynchronous methods are below:
-
ClearAsync
-
GetAsync
-
PutAsync
-
PutAllAsync
-
PutIfAbsentAsync
-
RemoveAsync
-
RemoveWithVersionAsync
-
ReplaceAsync
-
ReplaceWithVersionAsync
Hot Rod C# Asynchronous API Example
The following example demonstrates using these methods:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Infinispan.HotRod; using Infinispan.HotRod.Config; namespace simpleapp { class Program { static void Main(string[] args) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.AddServer() .Host(args.Length > 1 ? args[0] : "127.0.0.1") .Port(args.Length > 2 ? int.Parse(args[1]) : 11222); Configuration config = builder.Build(); RemoteCacheManager cacheManager = new RemoteCacheManager(config); IRemoteCache<String,String> cache = cacheManager.GetCache<String,String>(); // Add Entries Async cache.PutAsync("key1","value1"); cache.PutAsync("key2","value2"); // Retrieve Entries Async Task<string> futureExec = cache.GetAsync("key1"); string result = futureExec.Result; } } }
16.9.7. Hot Rod C# Client Remote Event Listeners
The Hot Rod C# client supports remote cache listeners, and these may be added using the addListener
method on the ClientListener
.
Remote Event Listeners is a Technology Preview feature of the Hot Rod C# client in Red Hat JBoss Data Grid 7.2.
This method takes a listener for each event type(create
, modify
, remove
, expire
, or custom
). For more information on Remote Event Listeners refer to Remote Event Listeners (Hot Rod). An example of a modifiedEvent is provided below:
[...] private static void modifiedEventAction(Event.ClientCacheEntryModifiedEvent<string> e) { ++modifiedEventCounter; modifiedSemaphore.Release(); } [...] public void ModifiedEventTest() { IRemoteCache<string, string> cache = remoteManager.GetCache<string, string>(); cache.Clear(); Event.ClientListener<string, string> cl = new Event.ClientListener<string, string>(); cl.filterFactoryName = ""; cl.converterFactoryName = ""; cl.addListener(modifiedEventAction); cache.addClientListener(cl, new string[] { }, new string[] { }, null); }
16.9.8. Hot Rod C# Client Working with Sites
Multiple Red Hat JBoss Data Grid Server clusters may be deployed so that each cluster belongs to a different site. Such deployments are done to enable data to be backed up from one cluster to another, potentially in a different geographical location. The C# client implementation can failover between nodes within a cluster, along with failing over to a different cluster entirely, should the original cluster become nonresponsive. To be able to failover between clusters all Red Hat JBoss Data Grid Servers must be configured with Cross-Datacenter replication. Instructions for this procedure are found in the Red Hat JBoss Data Grid Administration and Configuration Guide.
Once failed over the client will remain connected to the alternative cluster until this new cluster becomes unavailable, in which case it will throw an exception. If the original cluster becomes operational, the client will not switch over automatically. To switch back to the original cluster use the SwitchToDefaultCluster()
method mentioned below.
Once Cross-Datacenter replication has been configured on the servers, the client has to provide the alternative clusters' configuration with at least one host/port pair details for each of the clusters configured. For example:
ConfigurationBuilder conf1 = new ConfigurationBuilder(); conf1.AddServer().Host("127.0.0.1").Port(11222); conf1.AddCluster("nyc").AddClusterNode("127.0.0.1", 11322); RemoteCacheManager manager1 = new RemoteCacheManager(conf1.Build(), true); ConfigurationBuilder conf2 = new ConfigurationBuilder(); conf2.AddServer().Host("127.0.0.1").Port(11322); conf2.AddCluster("lon").AddClusterNode("127.0.0.1", 11222); RemoteCacheManager remoteManager = new RemoteCacheManager(conf2.Build(), true);
16.9.8.1. Manual Cluster Switch
In addition to automatic site failover, C++ clients may switch between clusters by calling either of the following methods:
-
SwitchToCluster(clusterName)
- Forces the client to switch to the pre-defined cluster name passed in. -
SwitchToDefaultCluster()
- Forces the client to switch to the initial servers defined in the client configuration.
16.9.9. Performing Remote Queries via the Hot Rod C# Client
The Hot Rod C# client allows remote querying, using Google’s Protocol Buffers, once the RemoteCacheManager
has been configured with the Protobuf marshaller.
Performing Remote Queries is a Technology Preview feature of the Hot Rod C# Client in Red Hat JBoss Data Grid 7.2.
Enable Remote Querying on the Hot Rod C# Client
Obtain a connection to the remote JBoss Data Grid server, passing the Protobuf marshaller into the configuration:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Infinispan.HotRod; using Infinispan.HotRod.Config; using Google.Protobuf; using Org.Infinispan.Protostream; using Org.Infinispan.Query.Remote.Client; using QueryExampleBankAccount; using System.IO; namespace Query { /// <summary> /// This sample code shows how to perform Infinispan queries using the C# client /// </summary> class Query { static void Main(string[] args) { // Cache manager setup RemoteCacheManager remoteManager; const string ERRORS_KEY_SUFFIX = ".errors"; const string PROTOBUF_METADATA_CACHE_NAME = "___protobuf_metadata"; ConfigurationBuilder conf = new ConfigurationBuilder(); conf.AddServer().Host("127.0.0.1").Port(11222).ConnectionTimeout(90000).SocketTimeout(6000); conf.Marshaller(new BasicTypesProtoStreamMarshaller()); remoteManager = new RemoteCacheManager(conf.Build(), true); IRemoteCache<String, String> metadataCache = remoteManager.GetCache<String, String>(PROTOBUF_METADATA_CACHE_NAME); IRemoteCache<int, User> testCache = remoteManager.GetCache<int, User>("namedCache");
Install any protobuf entities model:
// This example continues the previous codeblock // Installing the entities model into the Infinispan __protobuf_metadata cache metadataCache.Put("sample_bank_account/bank.proto", File.ReadAllText("resources/proto2/bank.proto")); if (metadataCache.ContainsKey(ERRORS_KEY_SUFFIX)) { Console.WriteLine("fail: error in registering .proto model"); Environment.Exit(-1); }
This step adds data to the cache for the purposes of this demonstration, and may be ignored when simply querying a remote cache:
// This example continues the previous codeblock // The application cache must contain entities only testCache.Clear(); // Fill the application cache User user1 = new User(); user1.Id = 4; user1.Name = "Jerry"; user1.Surname = "Mouse"; User ret = testCache.Put(4, user1);
Query the remote cache:
// This example continues the previous codeblock // Run a query QueryRequest qr = new QueryRequest(); qr.JpqlString = "from sample_bank_account.User"; QueryResponse result = testCache.Query(qr); List<User> listOfUsers = new List<User>(); unwrapResults(result, listOfUsers); }
To process the results convert the protobuf matter into C# objects. The following method demonstrates this conversion:
// Convert Protobuf matter into C# objects private static bool unwrapResults<T>(QueryResponse resp, List<T> res) where T : IMessage<T> { if (resp.ProjectionSize > 0) { // Query has select return false; } for (int i = 0; i < resp.NumResults; i++) { WrappedMessage wm = resp.Results.ElementAt(i); if (wm.WrappedBytes != null) { WrappedMessage wmr = WrappedMessage.Parser.ParseFrom(wm.WrappedBytes); if (wmr.WrappedMessageBytes != null) { System.Reflection.PropertyInfo pi = typeof(T).GetProperty("Parser"); MessageParser<T> p = (MessageParser<T>)pi.GetValue(null); T u = p.ParseFrom(wmr.WrappedMessageBytes); res.Add(u); } } } return true; } } }
16.9.10. Using the Near Cache with the Hot Rod C# Client
Near caches are optional caches for the Hot Rod C# client that keep recently accessed data close to the user, providing faster access to data that is accessed frequently. This cache acts as a local Hot Rod client cache that is synchronized with the remote server in the background.
Near caches are enabled programmatically on the ConfigurationBuilder
by using the NearCache()
method, as seen in the following example:
ConfigurationBuilder conf = new ConfigurationBuilder(); conf.AddServer().Host("127.0.0.1").Port(11222) // Define a Near Cache that contains up to 10 entries .NearCache().Mode(NearCacheMode.INVALIDATED).MaxEntries(10);
The following methods are used to configure the near cache’s behavior:
-
NearCache()
- defines aNearCacheConfigurationBuilder
which may be modified further. -
Mode(NearCacheMode mode)
- requires aNearCacheMode
be passed in. Defaults toDISABLED
, indicating no near cache is enabled. -
MaxEntries(int maxEntries)
- indicates the maximum number of entries for the near cache to contain. Once the near cache is full, the oldest entry will be evicted. Setting this value to0
defines an unbounded near cache.
Entries in the near cache are kept aligned with the remote cache via events. If a change occurs in the server then an appropriate event is sent to the client, which will update the near cache accordingly.
16.9.11. Script Execution Using the Hot Rod C# Client
The Hot Rod C# client allows tasks to be executed directly on Red Hat JBoss Data Grid servers via Remote Execution. This feature executes logic close to the data, utilizing the resources of all nodes in the cluster. Tasks may be deployed to the server instances, and may then be executed programmatically.
Installing a Task
Tasks may be installed on the server by being using the Put(string name, string script)
method of the ___script_cache
. The extension of the script name determines the engine used to execute the script; however, this may be overridden by metadata in the script itself.
The following example demonstrates installing scripts:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Infinispan.HotRod; using Infinispan.HotRod.Config; namespace RemoteExec { /// <summary> /// This sample code shows how to perform a server remote execution using the C# client /// </summary> class RemoteExec { static void Main(string[] args) { // Cache manager setup RemoteCacheManager remoteManager; IMarshaller marshaller; ConfigurationBuilder conf = new ConfigurationBuilder(); conf.AddServer().Host("127.0.0.1").Port(11222).ConnectionTimeout(90000).SocketTimeout(6000); marshaller = new JBasicMarshaller(); conf.Marshaller(marshaller); remoteManager = new RemoteCacheManager(conf.Build(), true); // Install the .js code into the Infinispan __script_cache const string SCRIPT_CACHE_NAME = "___script_cache"; string valueScriptName = "getValue.js"; string valueScript = "// mode=local,language=javascript\n " + "var cache = cacheManager.getCache(\"namedCache\");\n " + "var ct = cache.get(\"accessCounter\");\n " + "var c = ct==null ? 0 : parseInt(ct);\n " + "cache.put(\"accessCounter\",(++c).toString());\n " + "cache.get(\"privateValue\") "; string accessScriptName = "getAccess.js"; string accessScript = "// mode=local,language=javascript\n " + "var cache = cacheManager.getCache(\"namedCache\");\n " + "cache.get(\"accessCounter\")"; IRemoteCache<string, string> scriptCache = remoteManager.GetCache<string, string>(SCRIPT_CACHE_NAME); IRemoteCache<string, string> testCache = remoteManager.GetCache<string, string>("namedCache"); scriptCache.Put(valueScriptName, valueScript); scriptCache.Put(accessScriptName, accessScript);
Executing a Task
Once installed, a task may be executed by using the Execute(string name, Dictionary<string, string> scriptArgs)
method, passing in the name of the script to execute, along with any arguments that are required for execution.
The following example demonstrates running the scripts:
// This example continues the previous codeblock testCache.Put("privateValue", "Counted Access Value"); Dictionary<string, string> scriptArgs = new Dictionary<string, string>(); byte[] ret1 = testCache.Execute(valueScriptName, scriptArgs); string value = (string)marshaller.ObjectFromByteBuffer(ret1); byte[] ret2 = testCache.Execute(accessScriptName, scriptArgs); string accessCount = (string)marshaller.ObjectFromByteBuffer(ret2); Console.Write("Return value is '" + value + "' and has been accessed '" + accessCount + "' times."); } } }
Script execution using the Hot Rod C# Client is a Technology Preview Feature in JBoss Data Grid 7.2.
16.9.12. String Marshaller for Interoperability
To use the string compatibility marshaller, pass an instance of CompatibilityMarshaller
to the Marshaller()
method of the ConfigurationBuilder
object similar to this:
ConfigurationBuilder builder = new ConfigurationBuilder(); builder.Marshaller(new CompatibilityMarshaller()); RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build(), true); IRemoteCache<String, String> cache = cacheManager.GetCache<String, String>(); [....] cache.Put("key", "value"); [...] cache.Get("key"); [...]
Attempts to store or retrieve non-string key/values will result in a HotRodClientException
being thrown.
16.10. Hot Rod Node.js Client
16.10.1. Hot Rod Node.js Client
The Hot Rod Node.js client is an asynchronous event-driven client allowing Node.js users to communicate to Red Hat JBoss Data Grid servers. This client supports many of the features in the Java client, including the ability to execute and store scripts, utilize cache listeners, and receive the full cluster topology.
The asynchronous operation results are represented with Promise
instances, allowing the client to easily chain multiple invocations together and centralizing error handling.
16.10.2. Installing the Hot Rod Node.js Client
The Hot Rod Node.js client is included in a standalone distribution that you download separately to Red Hat JBoss Data Grid.
Procedure: Installing the Hot Rod Node.js Client
-
Download the
jboss-datagrid-7.2.x-nodejs-client.zip
from the Red Hat Customer Portal. - Extract the downloaded archive.
Use
npm
to install the provided tarball, as seen in the following command:npm install /path/to/jboss-datagrid-7.2.x-nodejs-client/infinispan-7.2.3-Final-redhat-00002.tgz
16.10.3. Hot Rod Node.js Requirements
The Hot Rod Node.js client has the following requirements:
- Node.js version 0.10 or higher.
- Red Hat JBoss Data Grid server instance 7.0.0 or higher.
16.10.4. Hot Rod Node.js Basic Functionality
The following example shows how to connect to a Red Hat JBoss Data Grid server and perform basic operations, such as putting and retrieving data. The following example assumes that a Red Hat JBoss Data Grid server is available at the default location of localhost:11222
:
var infinispan = require('infinispan'); // Obtain a connection to the JBoss Data Grid server // As no cache is specified all operations will occur on the 'default' cache var connected = infinispan.client({port: 11222, host: '127.0.0.1'}); connected.then(function (client) { // Attempt to put a value in the cache. var clientPut = client.put('key', 'value'); // Retrieve the value just placed var clientGet = clientPut.then( function() { return client.get('key'); }); // Print out the value that was retrieved var showGet = clientGet.then( function(value) { console.log('get(key)=' + value); }); // Disconnect from the server return showGet.finally( function() { return client.disconnect(); }); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
Connecting to a Named Cache
To connect to a specific cache the cacheName
attribute may be defined when specifying the location of the Red Hat JBoss Data Grid server instance, as seen in the following example:
var infinispan = require('infinispan'); // Obtain a connection to the JBoss Data Grid server // and connect to namedCache var connected = infinispan.client( {port: 11222, host: '127.0.0.1'}, {cacheName: 'namedCache'}); connected.then(function (client) { // Log the result of the connection console.log('Connected to `namedCache`'); // Disconnect from the server return client.disconnect(); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
Using Data Sets
In addition to placing single entries the putAll
and getAll
methods may be used to place or retrieve a set of data. The following example walks through these operations:
var infinispan = require('infinispan'); // Obtain a connection to the JBoss Data Grid server // As no cache is specified all operations will occur on the 'default' cache var connected = infinispan.client({port: 11222, host: '127.0.0.1'}); connected.then(function (client) { var data = [ {key: 'multi1', value: 'v1'}, {key: 'multi2', value: 'v2'}, {key: 'multi3', value: 'v3'}]; // Place all of the key/value pairs in the cache var clientPutAll = client.putAll(data); // Obtain the values for two of the keys var clientGetAll = clientPutAll.then( function() { return client.getAll(['multi2', 'multi3']); }); // Print out the values obtained. var showGetAll = clientGetAll.then( function(entries) { console.log('getAll(multi2, multi3)=%s', JSON.stringify(entries)); } ); // Obtain an iterator for the cache var clientIterator = showGetAll.then( function() { return client.iterator(1); }); // Iterate over the entries in the cache, printing the values var showIterated = clientIterator.then( function(it) { function loop(promise, fn) { // Simple recursive loop over iterator's next() call return promise.then(fn).then(function (entry) { return !entry.done ? loop(it.next(), fn) : entry.value; }); } return loop(it.next(), function (entry) { console.log('iterator.next()=' + JSON.stringify(entry)); return entry; }); } ); // Clear the cache of all values var clientClear = showIterated.then( function() { return client.clear(); }); // Disconnect from the server return clientClear.finally( function() { return client.disconnect(); }); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
16.10.5. Hot Rod Node.js Conditional Operations
The Hot Rod protocol stores metadata in addition to each value associated with the keys.
The getWithMetadata
retrieves the value and metadata for the key.
The following example demonstrates utilizing this metadata:
var infinispan = require('infinispan'); // Obtain a connection to the JBoss Data Grid server // As no cache is specified all operations will occur on the 'default' cache var connected = infinispan.client({port: 11222, host: '127.0.0.1'}); connected.then(function (client) { // Attempt to put a value in the cache if it does not exist var clientPut = client.putIfAbsent('cond', 'v0'); // Print out the result of the put operation var showPut = clientPut.then( function(success) { console.log(':putIfAbsent(cond)=' + success); }); // Replace the value in the cache var clientReplace = showPut.then( function() { return client.replace('cond', 'v1'); } ); // Print out the result of the replace var showReplace = clientReplace.then( function(success) { console.log('replace(cond)=' + success); }); // Obtain the value and metadata var clientGetMetaForReplace = showReplace.then( function() { return client.getWithMetadata('cond'); }); // Replace the value only if the version matches var clientReplaceWithVersion = clientGetMetaForReplace.then( function(entry) { console.log('getWithMetadata(cond)=' + JSON.stringify(entry)); return client.replaceWithVersion('cond', 'v2', entry.version); } ); // Print out the result of the previous replace var showReplaceWithVersion = clientReplaceWithVersion.then( function(success) { console.log('replaceWithVersion(cond)=' + success); }); // Obtain the value and metadata var clientGetMetaForRemove = showReplaceWithVersion.then( function() { return client.getWithMetadata('cond'); }); // Remove the value only if the version matches var clientRemoveWithVersion = clientGetMetaForRemove.then( function(entry) { console.log('getWithMetadata(cond)=' + JSON.stringify(entry)); return client.removeWithVersion('cond', entry.version); } ); // Print out the result of the previous remove var showRemoveWithVersion = clientRemoveWithVersion.then( function(success) { console.log('removeWithVersion(cond)=' + success)}); // Disconnect from the server return showRemoveWithVersion.finally( function() { return client.disconnect(); }); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
16.10.6. Hot Rod Node.js Data Sets
The client may specify multiple server addresses when a connection is defined. When multiple servers are defined it will loop through each one until a successful connection to a node is obtained. An example of this configuration is below:
var infinispan = require('infinispan'); // Accepts multiple addresses and fails over if connection not possible var connected = infinispan.client( [{port: 99999, host: '127.0.0.1'}, {port: 11222, host: '127.0.0.1'}]); connected.then(function (client) { // Obtain a list of all members in the cluster var members = client.getTopologyInfo().getMembers(); // Print out the list of members console.log('Connected to: ' + JSON.stringify(members)); // Disconnect from the server return client.disconnect(); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
16.10.7. Hot Rod Node.js Remote Events
The Hot Rod Node.js client supports remote cache listeners, and these may be added using the addListener
method. This method takes the event type (create
, modify
, remove
, or expiry
) and the function callback as parameter. For more information on Remote Event Listeners refer to Remote Event Listeners (Hot Rod). An example of this is shown below:
var infinispan = require('infinispan'); var Promise = require('promise'); var connected = infinispan.client({port: 11222, host: '127.0.0.1'}); connected.then(function (client) { var clientAddListenerCreate = client.addListener( 'create', function(key) { console.log('[Event] Created key: ' + key); }); var clientAddListeners = clientAddListenerCreate.then( function(listenerId) { // Multiple callbacks can be associated with a single client-side listener. // This is achieved by registering listeners with the same listener id // as shown in the example below. var clientAddListenerModify = client.addListener( 'modify', function(key) { console.log('[Event] Modified key: ' + key); }, {listenerId: listenerId}); var clientAddListenerRemove = client.addListener( 'remove', function(key) { console.log('[Event] Removed key: ' + key); }, {listenerId: listenerId}); return Promise.all([clientAddListenerModify, clientAddListenerRemove]); }); var clientCreate = clientAddListeners.then( function() { return client.putIfAbsent('eventful', 'v0'); }); var clientModify = clientCreate.then( function() { return client.replace('eventful', 'v1'); }); var clientRemove = clientModify.then( function() { return client.remove('eventful'); }); var clientRemoveListener = Promise.all([clientAddListenerCreate, clientRemove]).then( function(values) { var listenerId = values[0]; return client.removeListener(listenerId); }); return clientRemoveListener.finally( function() { return client.disconnect(); }); }).catch(function(error) { console.log("Got error: " + error.message); });
16.10.8. Hot Rod Node.js Working with Clusters
Red Hat JBoss Data Grid server instances may be clustered together to provide failover and capabilities for scaling up. While working with a cluster is very similar to using a single instance there are a few considerations:
- The client only needs to know about a single server’s address to receive information about the entire server cluster, regardless of the cluster size.
- For distributed caches, key-based operations are routed in the cluster using the same consistent hash algorithms used by the server. This means that the client can locate where any particular key resides without the need for extra network hops.
- For distributed caches, multi-key or key-less operations are routed in round-robin fashion.
- For replicated and invalidated caches, all operations are routed in round-robin fashion, regardless of whether they are key-based or multi-key/key-less.
All routing and failover is transparent to the client, so operations executed against a cluster look identical to the code examples performed above.
The cluster topology can be obtained using the following example:
var infinispan = require('infinispan'); var connected = infinispan.client({port: 11322, host: '127.0.0.1'}); connected.then(function (client) { var members = client.getTopologyInfo().getMembers(); // Should show all expected cluster members console.log('Connected to: ' + JSON.stringify(members)); // Add your own operations here... return client.disconnect(); }).catch(function(error) { // Log any errors received console.log("Got error: " + error.message); });
16.10.9. Hot Rod Node.js Working with Sites
Multiple Red Hat JBoss Data Grid Server clusters may be deployed so that each cluster belongs to a different site. Such deployments are done to enable data to be backed up from one cluster to another, potentially in a different geographical location. The Node.js client implementation can failover between nodes within a cluster, along with failing over to a different cluster entirely, should the original cluster become nonresponsive. To be able to failover between clusters all Red Hat JBoss Data Grid Servers must be configured with Cross-Datacenter replication. Instructions for this procedure are found in the Red Hat JBoss Data Grid Administration and Configuration Guide.
Once failed over the client will remain connected to the alternative cluster until this new cluster becomes unavailable, in which case it will try any other clusters defined, including the original server settings.
Once Cross-Datacenter replication has been configured on the servers, the client has to provide the alternative clusters' configuration with at least one host/port pair details for each of the clusters configured. For example:
var connected = infinispan.client({port: 11322, host: '127.0.0.1'}, { clusters: [ { name: 'LON', servers: [{port: 1234, host: 'LONA1'}] }, { name: 'NYC', servers: [{port: 2345, host: 'NYCB1'}, {port: 3456, host: 'NYCB2'}] } ] });
16.10.9.1. Manual Cluster Switch
In addition to automatic site failover, Node.js clients may switch between site clusters manually by calling either of the following methods:
-
switchToCluster(clusterName)
- Forces the client to switch to the pre-defined cluster name passed in. -
switchToDefaultCluster()
- Forces the client to switch to the initial servers defined in the client configuration.
For example, to manually switch to the NYC cluster the following could be used:
var connected = infinispan.client({port: 11322, host: '127.0.0.1'}, { clusters: [ { name: 'LON', servers: [{port: 1234, host: 'LONA1'}] }, { name: 'NYC', servers: [{port: 2345, host: 'NYCB1'}, {port: 3456, host: 'NYCB2'}] } ] }); connected.then(function (client) { var switchToB = client.getTopologyInfo().switchToCluster('NYC'); [...] });
16.10.10. Memory Profiling
You can profile how much memory Hot Rod Node.js client consumes with the following programs:
-
infinispan_memory_many_get.js
profiles memory usage using multipleGET
requests. -
infinispan_memory_one_get.js
profiles memory usage using oneGET
request.
These programs are located in the memory-profiling
directory of the client package.
To run the memory profiling programs, do the following:
node --expose-gc memory-profiling/infinispan_memory_many_get.js
You must pass the --expose-gc
parameter so that the programs can access the global garbage collector.
Tip: Use Google Chrome Developer Tools to visualize heap dumps. Load heap dumps from the Memory tab. This tab lets you compare multiple snapshots, which is useful for finding objects that have been kept in memory between points in time.
16.10.10.1. Avoiding Memory Issues with Promises
If the Node.js client creates many Promise
instances the client can consume too much memory, which degrades performance.
The following program is an example where too many Promise
instances are created. In this example, a user stores data and then generates multiple retrievals. The results are printed when all of the retrievals are complete, which results in increased memory consumption.
var _ = require('underscore'); var infinispan = require('infinispan'); var Promise = require('promise'); var heapdump = require('heapdump'); var connected = infinispan.client({port: 11222, host: '127.0.0.1'},{cacheName: 'namedCache'}); console.log("Connected to JDG server"); connected.then(function (client) { var sessionA = "Key"; var clientPut = client.put(sessionA, "test"); var clientTemp = clientPut; return clientTemp.then(function() { var initialHeapUsed = process.memoryUsage().heapUsed; console.log("process.memoryUsage().heapUsed: " + initialHeapUsed); heapdump.writeSnapshot('/tmp/' + Date.now() + '.heapsnapshot'); var temp = []; var numOps = 10000; // 500000 _.map(_.range(numOps), function(i) { temp.push(client.get(sessionA).then(function(value) { console.log("value " + value); })); }); var promesas = Promise.all(temp); var completed = promesas.then(function() { console.log("Promises completed"); }); temp = null; promesas = null; return completed.then(function() { global.gc(); console.log("process.memoryUsage().heapUsed (begin): " + initialHeapUsed); console.log("process.memoryUsage().heapUsed: "+process.memoryUsage().heapUsed); global.gc(); console.log("process.memoryUsage().heapUsed: "+process.memoryUsage().heapUsed); heapdump.writeSnapshot('/tmp/' + Date.now() + '.heapsnapshot'); return client.disconnect(); }); }); }).catch(function(err) { console.log("connect error", err); });
The following output shows the increased memory consumption that resulted from having too many Promise
instances created for the data retrieval:
node --expose-gc test.js ... process.memoryUsage().heapUsed (begin): 5620856 process.memoryUsage().heapUsed: 14368456 process.memoryUsage().heapUsed: 14274008
To avoid memory issues with multiple Promise
instances, you can either use Promise
instances in the platform or generate a new Promise
instance, depending on your version of Node.js.
Using Platform Promises
Recent Node.js versions include promise objects so that you do not need to load the promise library with the following line:
var Promise = require('promise')
If you remove that line from the preceding example and then run it with a Node.js version such as 8.11, the memory profiling results are as follows:
$ node --version v8.11.1 $ node --expose-gc test.js ... process.memoryUsage().heapUsed (begin): 6379448 process.memoryUsage().heapUsed: 6749056 process.memoryUsage().heapUsed: 6614560
Generating an Extra Promise
Older Node.js versions can generate a new Promise
after the collection of promise objects has been handled, as in the following example:
var _ = require('underscore'); var infinispan = require('infinispan'); var Promise = require('promise'); var heapdump = require('heapdump'); var connected = infinispan.client({port: 11222, host: '127.0.0.1'},{cacheName: 'namedCache'}); console.log("Connected to JDG server"); connected.then(function (client) { var sessionA = "Key"; var clientPut=client.put(sessionA, "test"); var clientTemp = clientPut; return clientTemp.then(function() { var initialHeapUsed = process.memoryUsage().heapUsed; console.log("process.memoryUsage().heapUsed: " + initialHeapUsed); heapdump.writeSnapshot('/tmp/' + Date.now() + '.heapsnapshot'); var temp = []; var numOps = 10000; // 500000 _.map(_.range(numOps), function(i) { temp.push(client.get(sessionA).then(function(value) { console.log("value " + value); })); }); var promesas = Promise.all(temp); var completed = promesas.then(function() { console.log("Promises completed"); }); temp = null; promesas = null; var getAfterAll = completed.then(function() { return client.get(sessionA); }); var logGet = getAfterAll.then(function(value) { console.log("[get after all] value: " + value); }) return logGet.then(function() { global.gc(); console.log("process.memoryUsage().heapUsed (begin): " + initialHeapUsed); console.log("process.memoryUsage().heapUsed: "+process.memoryUsage().heapUsed); global.gc(); console.log("process.memoryUsage().heapUsed: "+process.memoryUsage().heapUsed); heapdump.writeSnapshot('/tmp/' + Date.now() + '.heapsnapshot'); return client.disconnect(); }); }); }).catch(function(err) { console.log("connect error", err); });
The preceding example has the following the memory profiling results:
$ node --version v0.10.48 $ node --expose-gc test.js ... process.memoryUsage().heapUsed (begin): 5735864 process.memoryUsage().heapUsed: 4054352 process.memoryUsage().heapUsed: 4050064
16.11. Interoperability Between Hot Rod C++ and Hot Rod Java Client
Red Hat JBoss Data Grid provides interoperability between Hot Rod Java and Hot Rod C++ clients to access structured data. This is made possible by structuring and serializing data using Google’s Protobuf format.
For example, using interoperability between languages would allow a Hot Rod C++ client to write the following Person
object structured and serialized using Protobuf, and the Hot Rod Java client can read the same Person
object structured as Protobuf.
Using Interoperability Between Languages
package sample; message Person { required int32 age = 1; required string name = 2; }
Interoperability between C++ and Hot Rod Java Client is fully supported for primitive data types, strings, and byte arrays, as Protobuf and Protostream are not required for these types of interoperability.
16.12. Compatibility Between Server and Hot Rod Client Versions
Hot Rod clients, such as the Hot Rod Java, Hot Rod C++, and Hot Rod C#, are compatible with different versions of Red Hat JBoss Data Grid server. The server should be of the latest version in order to run with different Hot Rod clients.
It is recommended to use the same version of the Hot Rod client and the Red Hat JBoss Data Grid server, except in a case of migration or upgrade, to prevent any known problems.
Consider the following scenarios.
Scenario 1: Server running on a newer version than the Hot Rod client.
The following will be the impact on the client side:
- client will not have advantage of the latest protocol improvements.
- client might run into known issues which are fixed for the server-side version.
- client can only use the functionalities available in its current version and the previous versions.
Scenario 2: Hot Rod client running on a newer version than the server.
In this case, when a Hot Rod client connects to a Red Hat JBoss Data Grid server, the connection will be rejected with an exception error. The client can be downgraded to a known protocol version by either setting the client side property infinispan.client.hotrod.protocol_version
, or by using the ConfigurationBuilder
's protocolVersion(String version)
method. When downgraded the client version using either of these methods a String
containing the desired version should be passed in. In this case the client is able to connect to the server, but will be restricted to the functionality of that version. Any command which is not supported by this protocol version will not work and throw an exception; in addition, the topology information might be inefficient in this case.
Downgrading Client Hot Rod Protocol Version
The following code snippet demonstrates how to downgrade this version using the protocolVersion(String version)
method:
Configuration config = new ConfigurationBuilder() [...] .protocolVersion("2.2") .build();
It is not recommended to use this approach without guidance from Red Hat support.
The following table details the compatibility between different Hot Rod client and server versions.
Red Hat JBoss Data Grid Server Version | Hot Rod Protocol Version |
---|---|
Red Hat JBoss Data Grid 7.2.0 | Hot Rod 2.5 and later |
Red Hat JBoss Data Grid 7.1.0 | Hot Rod 2.5 and later |
Red Hat JBoss Data Grid 7.0.0 | Hot Rod 2.5 and later |