Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMap.delete operations don't update the LocalMapStats #26212

Closed
rickymemphis opened this issue Jan 11, 2024 · 1 comment
Closed

IMap.delete operations don't update the LocalMapStats #26212

rickymemphis opened this issue Jan 11, 2024 · 1 comment
Labels
Source: Community PR or issue was opened by a community user Type: Defect

Comments

@rickymemphis
Copy link

rickymemphis commented Jan 11, 2024

Hello,

We noticed that the LocalMapStats.getLastUpdateTime() method returns incorrect results when the IMap.delete operations are executed.

Expected behavior
We would expect that the LastUpdateTime metric is updated when entries are removed from a distributed map.
Here is a test that reproduces the issue. Could you please check?

The test is a JUnit5 test executed on a Mac and Java 11.

One of my colleagues reported this issue a while ago, but only part of the problem described was fixed.
The issue I refer to is this

package test;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.MapStoreConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.map.MapStore;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class TestMap22 {

	private static final String mapName = "testMap" + TestMap22.class.getSimpleName();

	private HazelcastInstance hazelcastInstance;

	private IMap<String, String> iMap;

	private RecordingTimeMapStore store;

	@BeforeEach
	public void setUp() throws Exception {

		// create map store config
		store = new RecordingTimeMapStore();
		MapStoreConfig mapStoreConfig = new MapStoreConfig();
		mapStoreConfig.setEnabled(true);
		mapStoreConfig.setWriteDelaySeconds(1);
		mapStoreConfig.setWriteCoalescing(true);
		mapStoreConfig.setImplementation(store);

		// create config
		Config config = new Config();
		MapConfig mapConfig = config.getMapConfig(mapName);
		mapConfig.setMapStoreConfig(mapStoreConfig);

		// start Hazelcast and init map
		hazelcastInstance = Hazelcast.newHazelcastInstance(config);
		iMap = hazelcastInstance.getMap(mapName);
	}

	@AfterEach
	public void tearDown() throws Exception {
		hazelcastInstance.getLifecycleService().terminate();
	}


	@Test
	public void testDelete() throws InterruptedException {

		iMap.put("k1", "v1");

		long lastUpdateTimeDistributedMap = iMap.getLocalMapStats().getLastUpdateTime();
		long lastUpdateTimeMapStore = store.getLastUpdateTime();
		Thread.sleep(2 * 1000);

		iMap.delete("k1");
		Thread.sleep(5 * 1000);

		assertAll(
				() -> assertTrue(iMap.getLocalMapStats().getLastUpdateTime() - lastUpdateTimeDistributedMap > 0),
				() -> assertTrue(store.getLastUpdateTime() - lastUpdateTimeMapStore > 0)
		);
	}

	@Test
	public void testDelete_nonExistentKey() throws InterruptedException {

		iMap.put("k1", "v1");

		long lastUpdateTimeDistributedMap = iMap.getLocalMapStats().getLastUpdateTime();
		long lastUpdateTimeMapStore = store.getLastUpdateTime();
		Thread.sleep(2 * 1000);

		iMap.delete("nonExistentKey");
		Thread.sleep(5 * 1000);

		assertAll(
				() -> assertTrue(iMap.getLocalMapStats().getLastUpdateTime() - lastUpdateTimeDistributedMap == 0),
				() -> assertTrue(store.getLastUpdateTime() - lastUpdateTimeMapStore == 0)
		);
	}

}

class RecordingTimeMapStore implements MapStore<String, String> {

	private final AtomicLong lastUpdateTime = new AtomicLong();

	@Override
	public void store(String key, String value) {
		lastUpdateTime.set(System.currentTimeMillis());
	}

	@Override
	public void storeAll(Map<String, String> map) {
		lastUpdateTime.set(System.currentTimeMillis());
	}

	@Override
	public void delete(String key) {
		lastUpdateTime.set(System.currentTimeMillis());
	}

	@Override
	public void deleteAll(Collection<String> keys) {
		lastUpdateTime.set(System.currentTimeMillis());
	}

	@Override
	public String load(String key) {
		return null;
	}

	@Override
	public Map<String, String> loadAll(Collection<String> keys) {
		return null;
	}

	@Override
	public Set<String> loadAllKeys() {
		return null;
	}

	public long getLastUpdateTime() {
		return lastUpdateTime.get();
	}

}
@rickymemphis
Copy link
Author

Hi Everyone,

I have proposed a solution in this PR

Please let me know what you think about it.

Best,
Riccardo

@AyberkSorgun AyberkSorgun added the Source: Community PR or issue was opened by a community user label Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Source: Community PR or issue was opened by a community user Type: Defect
Projects
None yet
Development

No branches or pull requests

2 participants