Hazelcast Near Cache - hazelcast

I have a setup of two node hazelcast 3.2 with each node containing 500 MB of data.I have configured client side Near cache.
I have following questions
How to verify that data is fetched from near cache hashmap?
Some of my maps have only one entry i.e., size=1, which sometimes on
heavy load takes 20 to 30 ms to fetch the data?
The time taken on heavy load is between 4ms to 20 ms.If it is
fetching from near cache,what will be the optimal fetch time in
millisecond.
What is the best GC policy to be set on the server?
My server side config
mapCfg = new MapConfig();
mapCfg.setName("TL_MAP_2");
mapCfg.setBackupCount(1);
mapCfg.setReadBackupData(true);//enabled as it is sync backup
mapCfg.setInMemoryFormat(InMemoryFormat.BINARY);
cfg.addMapConfig(mapCfg);
My client side code is a singleton
public enum CacheService {
INSTANCE;
private static ClientConfig clientConfig = null;
private static HazelcastInstance cacheClient = null;
private static Set<String> nearcaches = new HashSet<String>(Arrays.asList("TL_MAP_1","TL_MAP_2","TL_MAP_3","TL_MAP_4","TL_MAP_5","TL_MAP_6","TL_MAP_7","TL_MAP_8","TL_MAP_9","TL_MAP_10"));
static{
if(clientConfig ==null || cacheClient ==null){
clientConfig = new ClientConfig();
clientConfig.getGroupConfig().setName("dev").setPassword("dev-pass");
clientConfig.getNetworkConfig().addAddress("XXX.XX.XXX.XXX:5701","XXX.XX.XXX.XXX:5702").setSmartRouting(true);;
clientConfig.setExecutorPoolSize(120);
Iterator<String> iterator = nearcaches.iterator();
String near = null;
while(iterator.hasNext()){
near = iterator.next();
clientConfig.addNearCacheConfig(near, new NearCacheConfig().setInMemoryFormat(InMemoryFormat.OBJECT));
}
cacheClient = HazelcastClient.newHazelcastClient(clientConfig);
}
}
public HazelcastInstance getCacheClient(){
return cacheClient;
}
public Map get(String cacheName) {
Map cacheMap = cacheClient.getMap(cacheName);
if (nearcaches.contains(cacheName)) {
NearCacheStats stats = ((IMap) cacheMap).getLocalMapStats()
.getNearCacheStats();
logger.info("Near Cache HITS for map " + cacheName + " ::"
+ stats.getHits());
}
return cacheMap;
}
}

Related

Hazelcast IMap TTL Expiry

How to invoke a method to sync the data to some DB or Kafka once the TTL set is expired during the put method of IMap class.
eg:IMap.put(key,value,TTL,TimeUnit.SECONDS);
if the above TTL is set to like 10 seconds i must call some store or some mechanism where i could sync that key and value to DB or Kafka in real time. As of now when i tried the store method it is immediately calling the method instead of 10 seconds wait time.
You may set an EntryExpiredListener to your map config.
It feeds on two sources of expiration based eviction, they are max-idle-seconds and time-to-live-seconds.
Example Listener class:
#Slf4j
public class MyExpiredEntryListener implements EntryExpiredListener<String, String>, MapListener {
#Override
public void entryExpired(EntryEvent<String, String> event) {
log.info("entry Expired {}", event);
}
}
You can add this config via programmatically or you may set mapconfig via xml config file.
Example usage:
public static void main(String[] args) {
Config config = new Config();
MapConfig mapConfig = config.getMapConfig("myMap");
mapConfig.setTimeToLiveSeconds(10);
mapConfig.setEvictionPolicy(EvictionPolicy.RANDOM);
HazelcastInstance hz = Hazelcast.newHazelcastInstance(config);
IMap<String, String> map = hz.getMap("myMap");
map.addEntryListener(new MyExpiredEntryListener(), true);
for (int i = 0; i < 100; i++) {
String uuid = UUID.randomUUID().toString();
map.put(uuid, uuid);
}
}
You will see the logs like below when running this implementation.
entry Expired EntryEvent{entryEventType=EXPIRED, member=Member [192.168.1.1]:5701 - ca76c6d8-abe0-4efe-a6a6-24330657675b this, name='myMap', key=70ee594c-ffea-4584-aefe-1148b9fcdf9f, oldValue=70ee594c-ffea-4584-aefe-1148b9fcdf9f, value=null, mergingValue=null}
Also, you can use other entry listeners according to your requirements.

Why is the Hazelcast Near Cache out of sync with the EntryUpdatedListener even though they're in the same process?

I understand that Near Caches are not guaranteed to be synchronized real-time when the value is updated elsewhere on some other node.
However I do expect it to be in sync with the EntryUpdatedListener that is on the same node and therefore the same process - or am I missing something?
Sequence of events:
Cluster of 1 node modifies the same key/value, flipping a value from X to Y and back to X on an interval every X seconds.
A client connects to this cluster node and adds an EntryUpdatedListener to observe the flipping value.
Client receives the EntryUpdatedEvent and prints the value given - as expected, it gives the value recently set.
Client immediately does a map.get for the same key (which should hit the near cache), and it prints a STALE value.
I find this strange - it means that two "channels" within the same client process are showing inconsistent versions of data. I would only expect this between different processes.
Below is my reproducer code:
public class ClusterTest {
private static final int OLD_VALUE = 10000;
private static final int NEW_VALUE = 88888;
private static final int KEY = 5;
private static final int NUMBER_OF_ENTRIES = 10;
public static void main(String[] args) throws Exception {
HazelcastInstance instance = Hazelcast.newHazelcastInstance();
IMap map = instance.getMap("test");
for (int i = 0; i < NUMBER_OF_ENTRIES; i++) {
map.put(i, 0);
}
System.out.println("Size of map = " + map.size());
boolean flag = false;
while(true) {
int value = flag ? OLD_VALUE : NEW_VALUE;
flag = !flag;
map.put(KEY, value);
System.out.println("Set a value of [" + value + "]: ");
Thread.sleep(1000);
}
}
}
public class ClientTest {
public static void main(String[] args) throws InterruptedException {
HazelcastInstance instance = HazelcastClient.newHazelcastClient(new ClientConfig().addNearCacheConfig(new NearCacheConfig("test")));
IMap map = instance.getMap("test");
System.out.println("Size of map = " + map.size());
map.addEntryListener(new MyEntryListener(instance), true);
new CountDownLatch(1).await();
}
static class MyEntryListener
implements EntryAddedListener,
EntryUpdatedListener,
EntryRemovedListener {
private HazelcastInstance instance;
public MyEntryListener(HazelcastInstance instance) {
this.instance = instance;
}
#Override
public void entryAdded(EntryEvent event) {
System.out.println("Entry Added:" + event);
}
#Override
public void entryRemoved(EntryEvent event) {
System.out.println("Entry Removed:" + event);
}
#Override
public void entryUpdated(EntryEvent event) {
Object o = instance.getMap("test").get(event.getKey());
boolean equals = o.equals(event.getValue());
String s = "Event matches what has been fetched = " + equals;
if (!equals) {
s += ", EntryEvent value has delivered: " + (event.getValue()) + ", and an explicit GET has delivered:" + o;
}
System.out.println(s);
}
}
}
The output from the client:
INFO: hz.client_0 [dev] [3.11.1] HazelcastClient 3.11.1 (20181218 - d294f31) is CLIENT_CONNECTED
Jun 20, 2019 4:58:15 PM com.hazelcast.internal.diagnostics.Diagnostics
INFO: hz.client_0 [dev] [3.11.1] Diagnostics disabled. To enable add -Dhazelcast.diagnostics.enabled=true to the JVM arguments.
Size of map = 10
Event matches what has been fetched = true
Event matches what has been fetched = false, EntryEvent value has delivered: 88888, and an explicit GET has delivered:10000
Event matches what has been fetched = true
Event matches what has been fetched = true
Event matches what has been fetched = false, EntryEvent value has delivered: 10000, and an explicit GET has delivered:88888
Near Cache has Eventual Consistency guarantee, while Listeners work in a fire & forget fashion. That's why there are two different mechanisms for both. Also, batching for near cache events reduces the network traffic and keeps the eventing system less busy (this helps when there are too many invalidations or clients), as a tradeoff it may increase the delay of individual invalidations. If you are confident that your system can handle each invalidation event, you can disable batching.
You need to configure the property on member side as events are generated on cluster members and sent to clients.

adding Cassandra as sink in Flink error : All host(s) tried for query failed

I was following up with an example at https://ci.apache.org/projects/flink/flink-docs-release-1.4/dev/connectors/cassandra.html to connect Cassandra as sink in Flink
My code for is shown below
public class writeToCassandra {
private static final String CREATE_KEYSPACE_QUERY = "CREATE KEYSPACE test WITH replication= {'class':'SimpleStrategy', 'replication_factor':1};";
private static final String createTable = "CREATE TABLE test.cassandraData(id varchar, heart_rate varchar, PRIMARY KEY(id));" ;
private final static Collection<String> collection = new ArrayList<>(50);
static {
for (int i = 1; i <= 50; ++i) {
collection.add("element " + i);
}
}
public static void main(String[] args) throws Exception {
//setting the env variable to local
StreamExecutionEnvironment envrionment = StreamExecutionEnvironment.createLocalEnvironment(1);
DataStream<Tuple2<String, String>> dataStream = envrionment
.fromCollection(collection)
.map(new MapFunction<String, Tuple2<String, String>>() {
final String mapped = " mapped ";
String[] splitted;
#Override
public Tuple2<String, String> map(String s) throws Exception {
splitted = s.split("\\s+");
return Tuple2.of(
UUID.randomUUID().toString(),
splitted[0] + mapped + splitted[1]
);
}
});
CassandraSink.addSink(dataStream)
.setQuery("INSERT INTO test.cassandraData(id,heart_rate) values (?,?);")
.setHost("127.0.0.1")
.build();
envrionment.execute();
} //main
} //writeToCassandra
I am getting the following error
Caused by: com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: /127.0.0.1:9042 (com.datastax.driver.core.exceptions.TransportException: [/127.0.0.1] Cannot connect))
at com.datastax.driver.core.ControlConnection.reconnectInternal(ControlConnection.java:231)
Not sure if this is always required, but the way that I set up my CassandraSink is like this:
CassandraSink
.addSink(dataStream)
.setClusterBuilder(new ClusterBuilder() {
#Override
protected Cluster buildCluster(Cluster.Builder builder) {
return Cluster.builder()
.addContactPoints(myListOfCassandraUrlsString.split(","))
.withPort(portNumber)
.build();
}
})
.build();
I have annotated POJOs that are returned by the dataStream so I don't need the query, but you would just include ".setQuery(...)" after the ".addSink(...)" line.
The exception simply indicates that the example program cannot reach the C* database.
flink-cassandra-connector offers streaming API to connect to designated C* database. Thus, you need to have a C* instance running.
Each streaming job is pushed/serialized to the node that Task Manager runs at. In your example, you assume C* is running on the same node as the TM node. An alternative is to change the C* address from 127.0.0.1 to a public address.

Hazelcast 3.6 - Map get element

I have tried to make a simple demo using hazelcast 3.6 version. Basically I started the console application , then I added an element to the cluster from a client code.
public class GettingStartedClient {
public static void main(String[] args) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.addAddress("127.0.0.1:5704");
HazelcastInstance client = HazelcastClient.newHazelcastClient(clientConfig);
IMap<Integer, String> map = client.getMap("customers");
System.out.println("Map Size:" + map.size());
for (int i = 0; i < map.size(); i++) {
System.out.println(map.get(i+1));
}
map.put(0, "Emre");
}
}
Later, Using the command line I switch to ns customer and I execute m.get , m.values and m.keys commands. m.values and m.keys are returning the element in the map however m.get returns null.
hazelcast[customers] > m.keys
0
Total 1
hazelcast[customers] > m.values
Emre
Total 1
hazelcast[customers] > m.get 0
null
Am I missing someting ? Any help is appreciated.
The key used in the Demo App is not Integer but String. So in order to make it work, your client code should be like this :
public static void main(String[] args) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.addAddress("127.0.0.1:5704");
HazelcastInstance client = HazelcastClient.newHazelcastClient(clientConfig);
IMap<String, String> map = client.getMap("customers");
System.out.println("Map Size:" + map.size());
for (int i = 0; i < map.size(); i++) {
System.out.println(map.get(i+1));
}
map.put("0", "Emre");
}
You can checkout the source code of the demo app from github :
https://github.com/hazelcast/hazelcast/blob/master/hazelcast/src/main/java/com/hazelcast/console/ConsoleApp.java
Hazelcast console assume the key to be a string, and tries to find the key "0" which doesn't exist

Remove/Delete all/one item from StackExchange.Redis cache

I am using StackExchange.Redis client with Azure Redis Cache Service. Here is my class,
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
public RedisCacheService(ISettings settings)
{
_settings = settings;
var connectionMultiplexer = ConnectionMultiplexer.Connect(settings.RedisConnection);
_cache = connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
// How to remove one
}
public void Clear()
{
// How to remove all
}
}
Update: From the help of Marc, Here is my final class
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
private static ConnectionMultiplexer _connectionMultiplexer;
static RedisCacheService()
{
var connection = ConfigurationManager.AppSettings["RedisConnection"];
_connectionMultiplexer = ConnectionMultiplexer.Connect(connection);
}
public RedisCacheService(ISettings settings)
{
_settings = settings;
_cache = _connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
_cache.KeyDelete(key);
}
public void Clear()
{
var endpoints = _connectionMultiplexer.GetEndPoints(true);
foreach (var endpoint in endpoints)
{
var server = _connectionMultiplexer.GetServer(endpoint);
server.FlushAllDatabases();
}
}
}
Now I don't know how to remove all items or single item from redis cache.
To remove a single item:
_cache.KeyDelete(key);
To remove all involves the FLUSHDB or FLUSHALL redis command; both are available in StackExchange.Redis; but, for reasons discussed here, they are not on the IDatabase API (because: they affect servers, not logical databases).
As per the "So how do I use them?" on that page:
server.FlushDatabase(); // to wipe a single database, 0 by default
server.FlushAllDatabases(); // to wipe all databases
(quite possibly after using GetEndpoints() on the multiplexer)
I could not able to flush database in Azure Redis Cache, got this error:
This operation is not available unless admin mode is enabled: FLUSHDB
Instead iterate through all keys to delete:
var endpoints = connectionMultiplexer.GetEndPoints();
var server = connectionMultiplexer.GetServer(endpoints.First());
//FlushDatabase didn't work for me: got error admin mode not enabled error
//server.FlushDatabase();
var keys = server.Keys();
foreach (var key in keys)
{
Console.WriteLine("Removing Key {0} from cache", key.ToString());
_cache.KeyDelete(key);
}
Both answers by #Rasi and #Marc Gravell contain pieces of code needed.
Based on above, here is working snippet assuming there is just 1 server:
You need to connect to redis with allowAdmin=true, one way to obtain such options is to assign AllowAdmin to already parsed string:
var options = ConfigurationOptions.Parse("server:6379");
options.AllowAdmin = true;
var redis = ConnectionMultiplexer.Connect(options);
Then to flush all databases:
var endpoints = redis.GetEndPoints();
var server = redis.GetServer(endpoints[0]);
server.FlushAllDatabases();
Above will work on any redis deployment, not just Azure.
You can delete hash as well ie if you want to clear specific value from any cached list.
For example, we have an emp list and inside with different department as cached.
public static void DeleteHash(string key, string cacheSubKey)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
Cache.HashDelete(key, cacheSubKey);
}
so you can pass Key name and cache subkey as well.

Resources