Hazelcast: Near cached Map.put with TTL does not work as expected for version 3.7.4

Created on 17 Jan 2017  路  3Comments  路  Source: hazelcast/hazelcast

Test case: instantiate a client with near cache. put a map entry with ttl 2 seconds. get from map and populate near cache. Wait 2 seconds. The map.get should return null upon ttl expiry.

This test case fails. test code:

package com.hazelcast.client.map.impl.nearcache;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.config.NearCacheConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastTestSupport;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;


public class TTLTest {
    public static void main(String[] args) {
        Hazelcast.newHazelcastInstance();
        int ttlTime = 2000; //msecs
        String mapName = "SomeMap";
        ClientConfig config = new ClientConfig();
        config.addNearCacheConfig(new NearCacheConfig(mapName));
        HazelcastInstance client = HazelcastClient.newHazelcastClient(config);
        final IMap<Object, Object> map = client.getMap(mapName);
        map.put(1, "value1", ttlTime, TimeUnit.MILLISECONDS);

        Object firstValue = map.get(1);
        System.out.println("First value:" + firstValue);
        assertEquals("value1", firstValue);

        try {
            Thread.sleep(ttlTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // trigger eviction
        Object secondValue = map.get(1);
        System.out.println("Second value:" + secondValue);

        HazelcastTestSupport.assertTrueEventually(new AssertTask() {
            @Override
            public void run()
                    throws Exception {
                Object value = map.get(1);
                System.out.println("Value is " + value);
                assertNull(value);
            }
        });

        System.out.println("Test finished");

        HazelcastClient.shutdownAll();
    }
}

Internal Client Core Test-Failure

All 3 comments

We analysed the case further and it comes out that the test were not correct. The map put with ttl behaves different when near cache is enabled.

When no-near cache, the client will see the eviction after the ttl expires when it does the next get.
When near cache is enabled, the client will NOT see the eviction after ttl passes and it will continue to see the value from local near-cache. Hence, the actual correct test is:

package com.hazelcast.client.map.impl.nearcache;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.config.NearCacheConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastTestSupport;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
 * Created by ihsan on 17/01/17.
 */
public class TTLTest {
    public static void main(String[] args) {
        Hazelcast.newHazelcastInstance();

        int ttlTime = 2000; //msecs
        String mapName = "SomeMap";
        ClientConfig config = new ClientConfig();
        config.addNearCacheConfig(new NearCacheConfig(mapName));
        HazelcastInstance client = HazelcastClient.newHazelcastClient(config);
        final IMap<Object, Object> map = client.getMap(mapName);
        map.put(1, "value1", ttlTime, TimeUnit.MILLISECONDS);

        Object firstValue = map.get(1);
        System.out.println("First value:" + firstValue);
        assertEquals("value1", firstValue);

        try {
            Thread.sleep(ttlTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // trigger eviction
        Object secondValue = map.get(1);
        System.out.println("Second value:" + secondValue);

        HazelcastTestSupport.assertTrueAllTheTime(new AssertTask() {
            @Override
            public void run()
                    throws Exception {
                Object value = map.get(1);
                System.out.println("Value is " + value);
                assertEquals("value1", value);
            }
        }, 20);

        System.out.println("Test finished");

        HazelcastClient.shutdownAll();
    }
}

Despite of near cache doesn't guarantee strong consistency, would expect that it gets invalidated even in this case. Will appreciate any meaningful description. Couldn't find any so far.

Could solve the problem by adding a EntryExpiredListener to the map:

...
map.addEntryListener((EntryExpiredListener) event -> invalidateNearCache(map, event), false);
...
public static void invalidateNearCache(final IMap<Object, Object> map, EntryEvent event) {
    if (map instanceof NearCachedClientMapProxy) {
        System.out.println("Invalidating key " + event.getKey());
        NearCachedClientMapProxy nearCache = (NearCachedClientMapProxy) map;
        nearCache.getNearCache().invalidate(event.getKey());
    }
}
Was this page helpful?
0 / 5 - 0 ratings