/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.impl;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.MapStoreConfig;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.*;
import com.hazelcast.impl.partition.PartitionInfo;
import com.hazelcast.monitor.LocalMapStats;
import com.hazelcast.nio.Data;
import com.hazelcast.query.SqlPredicate;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import static com.hazelcast.nio.IOUtil.toData;
import static org.junit.Assert.*;
@RunWith(com.hazelcast.util.RandomBlockJUnit4ClassRunner.class)
public class MapStoreTest extends TestUtil {
@BeforeClass
public static void init() throws Exception {
System.setProperty(GroupProperties.PROP_WAIT_SECONDS_BEFORE_JOIN, "1");
System.setProperty(GroupProperties.PROP_VERSION_CHECK_ENABLED, "false");
Hazelcast.shutdownAll();
}
@After
public void cleanup() throws Exception {
Hazelcast.shutdownAll();
}
@Test
public void testPersistentQueue() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
Config config = newConfig("themap", testMapStore, 0);
config.getQueueConfig("default").setBackingMapRef("themap");
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IQueue q1 = h1.getQueue("default");
for (int i = 0; i < 100; i++) {
q1.put("value" + i);
}
assertEquals(100, q1.size());
assertEquals(100, testMapStore.store.size());
h1.getLifecycleService().shutdown();
h1 = Hazelcast.newHazelcastInstance(config);
q1 = h1.getQueue("default");
assertEquals(100, q1.size());
for (int i = 0; i < 100; i++) {
assertEquals("value" + i, q1.take());
}
for (int i = 0; i < 100; i++) {
q1.put("value" + i);
}
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
IQueue q2 = h2.getQueue("default");
assertEquals(100, q2.size());
h1.getLifecycleService().shutdown();
assertEquals(100, q2.size());
for (int i = 0; i < 100; i++) {
assertEquals("value" + i, q2.take());
}
}
@Test
public void testGetAllKeys() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
Map store = testMapStore.getStore();
Set keys = new HashSet();
int size = 1000;
for (int i = 0; i < size; i++) {
store.put(i, "value" + i);
keys.add(i);
}
Config config = newConfig(testMapStore, 2);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
IMap map1 = h1.getMap("default");
IMap map2 = h2.getMap("default");
assertEquals("value1", map1.get(1));
assertEquals("value1", map2.get(1));
assertEquals(1000, map1.size());
assertEquals(1000, map2.size());
HazelcastInstance h3 = Hazelcast.newHazelcastInstance(config);
IMap map3 = h3.getMap("default");
assertEquals("value1", map1.get(1));
assertEquals("value1", map2.get(1));
assertEquals("value1", map3.get(1));
assertEquals(1000, map1.size());
assertEquals(1000, map2.size());
assertEquals(1000, map3.size());
h3.getLifecycleService().shutdown();
assertEquals("value1", map1.get(1));
assertEquals("value1", map2.get(1));
assertEquals(1000, map1.size());
assertEquals(1000, map2.size());
}
@Test
public void testThreeMemberInit() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
testMapStore.setLoadAllKeys(true);
Map store = testMapStore.getStore();
Set keys = new HashSet();
int size = 10000;
for (int i = 0; i < size; i++) {
store.put(i, "value" + i);
keys.add(i);
}
final String mapName = "mymap";
Config config = newConfig(mapName, testMapStore, 2);
final HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
final HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
final HazelcastInstance h3 = Hazelcast.newHazelcastInstance(config);
final HazelcastInstance[] instances = new HazelcastInstance[]{h1, h2, h3};
final Random random = new Random();
final CountDownLatch latch = new CountDownLatch(50);
for (int i = 0; i < 50; i++) {
new Thread(new Runnable() {
public void run() {
instances[random.nextInt(3)].getMap(mapName);
latch.countDown();
}
}).start();
}
assertTrue(latch.await(100, TimeUnit.SECONDS));
instances[0].getMap(mapName);
instances[1].getMap(mapName);
instances[2].getMap(mapName);
assertEquals("After load:", 15, testMapStore.callCount.get());
IMap map1 = h1.getMap(mapName);
IMap map2 = h2.getMap(mapName);
IMap map3 = h3.getMap(mapName);
for (int i = 0; i < size; i++) {
assertEquals("value" + i, map3.get(i));
}
assertEquals("After gets:", 15, testMapStore.callCount.get());
}
@Test
public void testThreeMemberGetAll() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
testMapStore.setLoadAllKeys(false);
Map store = testMapStore.getStore();
Set keys = new HashSet();
int size = 1000;
for (int i = 0; i < size; i++) {
store.put(i, "value" + i);
keys.add(i);
}
Config config = newConfig(testMapStore, 2);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
IMap map1 = h1.getMap("default");
IMap map2 = h2.getMap("default");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL_KEYS, testMapStore.waitForEvent(5));
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL_KEYS, testMapStore.waitForEvent(5));
final CountDownLatch l = new CountDownLatch(1);
map1.addEntryListener(new EntryAdapter() {
public void entryAdded(EntryEvent entryEvent) {
assertEquals("value1", entryEvent.getValue());
l.countDown();
}
}, 1, true);
assertEquals("value1", map1.get(1));
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(5));
assertTrue(l.await(10, TimeUnit.SECONDS));
assertEquals("value1", map1.get(1));
assertEquals(null, testMapStore.waitForEvent(3));
Map loaded = map1.getAll(keys);
assertEquals(size, loaded.size());
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL, testMapStore.waitForEvent(5));
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL, testMapStore.waitForEvent(5));
loaded = map2.getAll(keys);
assertEquals(size, loaded.size());
assertEquals(null, testMapStore.waitForEvent(5));
for (int i = 0; i < size; i++) {
map1.evict(i);
}
assertEquals(0, map1.size());
HazelcastInstance h3 = Hazelcast.newHazelcastInstance(config);
Set<Member> owners = new HashSet<Member>();
for (Object key : loaded.keySet()) {
owners.add(h1.getPartitionService().getPartition(key).getOwner());
}
loaded = map1.getAll(keys);
assertEquals(size, loaded.size());
for (Member owner : owners) {
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL, testMapStore.waitForEvent(5));
}
assertEquals(0, testMapStore.getEventCount());
loaded = map2.getAll(keys);
assertEquals(size, loaded.size());
assertEquals(null, testMapStore.waitForEvent(5));
}
@Test
public void testOneMemberWriteThroughTxnalFailingStore() {
FailAwareMapStore testMapStore = new FailAwareMapStore();
testMapStore.setFail(false);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
Transaction txn = h1.getTransaction();
txn.begin();
assertEquals(0, map.size());
assertEquals(0, testMapStore.dbSize());
map.put("1", "value1");
map.put("2", "value2");
txn.commit();
assertEquals(2, map.size());
assertEquals(2, testMapStore.dbSize());
txn = h1.getTransaction();
txn.begin();
assertEquals(2, map.size());
assertEquals(2, testMapStore.dbSize());
map.put("3", "value3");
assertEquals(3, map.size());
assertEquals(2, testMapStore.dbSize());
testMapStore.setFail(true);
map.put("4", "value4");
try {
txn.commit();
fail("Should not commit the txn");
} catch (Exception e) {
}
assertEquals(2, map.size());
assertEquals(2, testMapStore.dbSize());
map.evict("1");
map.evict("2");
assertEquals(0, map.size());
assertEquals(2, testMapStore.dbSize());
Set keys = new HashSet();
keys.add("1");
keys.add("2");
try {
map.getAll(keys);
fail();
} catch (Exception e) {
}
assertEquals(0, map.size());
assertEquals(2, testMapStore.dbSize());
testMapStore.setFail(false);
map.getAll(keys);
assertEquals(2, map.size());
assertEquals(2, testMapStore.dbSize());
}
@Test
public void testOneMemberWriteThroughFailingStore() throws Exception {
FailAwareMapStore testMapStore = new FailAwareMapStore();
testMapStore.setFail(true);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(0, map.size());
try {
map.get("1");
fail("should have thrown exception");
} catch (Exception e) {
}
assertEquals(1, testMapStore.loads.get());
try {
map.get("1");
fail("should have thrown exception");
} catch (Exception e) {
}
assertEquals(2, testMapStore.loads.get());
try {
map.put("1", "value");
fail("should have thrown exception");
} catch (Exception e) {
}
assertEquals(1, testMapStore.stores.get());
}
@Test
public void testTwoMemberWriteThrough2() throws Exception {
TestMapStore testMapStore = new TestMapStore(1000, 0, 0);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
IMap map1 = h1.getMap("default");
IMap map2 = h2.getMap("default");
for (int i = 0; i < 1000; i++) {
map1.put(i, "value" + i);
}
assertEquals(1000, testMapStore.getStore().size());
assertEquals(1000, map1.size());
assertEquals(1000, map2.size());
testMapStore.assertAwait(10);
// 1000 put-load 1000 put-store call and 2 loadAllKeys
assertEquals(2002, testMapStore.callCount.get());
}
@Test
public void testTwoMemberWriteThrough() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
Employee employee = new Employee("joe", 25, true, 100.00);
Employee employee2 = new Employee("jay", 35, false, 100.00);
testMapStore.insert("1", employee);
IMap map = h1.getMap("default");
map.addIndex("name", false);
assertEquals(0, map.size());
assertEquals(employee, map.get("1"));
assertEquals(employee, testMapStore.getStore().get("1"));
assertEquals(1, map.size());
Collection values = map.values(new SqlPredicate("name = 'joe'"));
assertEquals(1, values.size());
assertEquals(employee, values.iterator().next());
map.put("2", employee2);
assertEquals(employee2, testMapStore.getStore().get("2"));
assertEquals(2, testMapStore.getStore().size());
assertEquals(2, map.size());
map.remove("2");
assertEquals(1, testMapStore.getStore().size());
assertEquals(1, map.size());
testMapStore.assertAwait(10);
assertEquals(6, testMapStore.callCount.get());
}
@Test
public void testOneMemberWriteThrough() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
Employee employee = new Employee("joe", 25, true, 100.00);
Employee newEmployee = new Employee("ali", 26, true, 1000);
testMapStore.insert("1", employee);
testMapStore.insert("2", employee);
testMapStore.insert("3", employee);
testMapStore.insert("4", employee);
testMapStore.insert("5", employee);
testMapStore.insert("6", employee);
testMapStore.insert("8", employee);
IMap map = h1.getMap("default");
map.addIndex("name", false);
assertEquals(0, map.size());
assertTrue(map.tryLock("1"));
assertEquals(employee, map.get("1"));
assertEquals(employee, testMapStore.getStore().get("1"));
assertEquals(1, map.size());
assertEquals(employee, map.put("2", newEmployee));
assertEquals(newEmployee, testMapStore.getStore().get("2"));
assertEquals(2, map.size());
Collection values = map.values(new SqlPredicate("name = 'joe'"));
assertEquals(1, values.size());
assertEquals(employee, values.iterator().next());
map.remove("1");
map.put("1", employee, 1, TimeUnit.SECONDS);
Thread.sleep(2000);
assertEquals(employee, testMapStore.getStore().get("1"));
assertEquals(employee, map.get("1"));
map.evict("2");
assertEquals(newEmployee, map.get("2"));
assertEquals(employee, map.tryLockAndGet("3", 1, TimeUnit.SECONDS));
assertEquals(employee, map.put("3", newEmployee));
assertEquals(newEmployee, map.get("3"));
assertEquals(employee, map.remove("4"));
assertEquals(employee, map.tryLockAndGet("5", 1, TimeUnit.SECONDS));
assertEquals(employee, map.remove("5"));
assertEquals(employee, map.putIfAbsent("6", newEmployee));
assertEquals(employee, map.get("6"));
assertEquals(employee, testMapStore.getStore().get("6"));
assertNull(map.get("7"));
assertFalse(map.containsKey("7"));
assertNull(map.putIfAbsent("7", employee));
assertEquals(employee, map.get("7"));
assertEquals(employee, testMapStore.getStore().get("7"));
assertTrue(map.containsKey("8"));
assertEquals(employee, map.get("8"));
}
@Test
public void testOneMemberWriteThroughWithLRU() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 0);
config.getMapConfig("default").setMaxSize(10).setEvictionPolicy("LRU");
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
for (int i = 0; i < 20; i++) {
map.put(i, new Employee("joe", i, true, 100.00));
}
assertTrue(map.size() > 5);
assertTrue(map.size() <= 10);
}
@Test
public void testOneMemberWriteThroughWithIndex() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 0);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
testMapStore.insert("1", "value1");
IMap map = h1.getMap("default");
assertEquals(0, map.size());
assertTrue(map.tryLock("1", 1, TimeUnit.SECONDS));
assertEquals("value1", map.get("1"));
map.unlock("1");
assertEquals("value1", map.put("1", "value2"));
assertEquals("value2", map.get("1"));
assertEquals("value2", testMapStore.getStore().get("1"));
assertEquals(1, map.size());
assertTrue(map.evict("1"));
assertEquals(0, map.size());
assertEquals(1, testMapStore.getStore().size());
assertEquals("value2", map.get("1"));
assertEquals(1, map.size());
map.remove("1");
assertEquals(0, map.size());
assertEquals(0, testMapStore.getStore().size());
testMapStore.assertAwait(1);
assertEquals(1, testMapStore.getInitCount());
assertEquals("default", testMapStore.getMapName());
assertEquals(h1, testMapStore.getHazelcastInstance());
}
@Test
public void testOneMemberFailingWriteBehind() throws Exception {
FailAwareMapStore testMapStore = new FailAwareMapStore();
Config config = newConfig(testMapStore, 2);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(0, map.size());
CMap cmap = getCMap(h1, "default");
assertEquals(0, testMapStore.db.size());
testMapStore.setFail(true);
map.put("1", "value1");
Thread.sleep(3000);
BlockingQueue listener = new LinkedBlockingQueue();
testMapStore.addListener(listener);
cmap.startCleanup(false);
assertNotNull(listener.poll(20, TimeUnit.SECONDS));
assertEquals(1, map.size());
assertEquals(0, testMapStore.db.size());
testMapStore.setFail(false);
cmap.startCleanup(false);
assertNotNull(listener.poll(20, TimeUnit.SECONDS));
assertEquals(1, testMapStore.db.size());
assertEquals("value1", testMapStore.db.get("1"));
}
@Test
public void testOneMemberFlushOnShutdown() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 200);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map1 = h1.getMap("default");
assertEquals(0, map1.size());
for (int i = 0; i < 100; i++) {
map1.put(i, i);
}
assertEquals(100, map1.size());
assertEquals(0, testMapStore.getStore().size());
h1.getLifecycleService().shutdown();
assertEquals(100, testMapStore.getStore().size());
assertEquals(1, testMapStore.getDestroyCount());
}
@Test
public void testOneMemberFlush() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 200);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(0, map.size());
for (int i = 0; i < 100; i++) {
map.put(i, i);
}
assertEquals(100, map.size());
assertEquals(0, testMapStore.getStore().size());
getConcurrentMapManager(h1).flush(Prefix.MAP + "default");
assertEquals(100, testMapStore.getStore().size());
assertEquals(100, map.size());
}
@Test
public void testOneMemberWriteBehind2() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 1);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL_KEYS, testMapStore.waitForEvent(1));
assertEquals(0, map.size());
map.put("1", "value1");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(1));
assertEquals(TestEventBasedMapStore.STORE_EVENTS.STORE, testMapStore.waitForEvent(2));
assertEquals(1, map.size());
assertEquals(1, testMapStore.getStore().size());
map.remove("1");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.DELETE, testMapStore.waitForEvent(2));
assertEquals(0, map.size());
assertEquals(0, testMapStore.getStore().size());
}
@Test
public void testOneMemberWriteBehind() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 2);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
testMapStore.insert("1", "value1");
IMap map = h1.getMap("default");
assertEquals(0, map.size());
assertEquals("value1", map.get("1"));
assertEquals("value1", map.put("1", "value2"));
assertEquals("value2", map.get("1"));
// store should have the old data as we will write-behind
assertEquals("value1", testMapStore.getStore().get("1"));
assertEquals(1, map.size());
assertTrue(map.evict("1"));
assertEquals("value2", testMapStore.getStore().get("1"));
assertEquals(0, map.size());
assertEquals(1, testMapStore.getStore().size());
assertEquals("value2", map.get("1"));
assertEquals(1, map.size());
map.remove("1");
// store should have the old data as we will delete-behind
assertEquals(1, testMapStore.getStore().size());
assertEquals(0, map.size());
testMapStore.assertAwait(12);
assertEquals(0, testMapStore.getStore().size());
}
@Test
public void testWriteBehindBackupLoaded() throws Exception {
TestMapStore testMapStore = new TestMapStore(1, 1, 1);
testMapStore.setLoadAllKeys(false);
Config config = newConfig(testMapStore, 20);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config);
testMapStore.insert("1", "value1");
IMap map = h1.getMap("default");
assertEquals(0, map.size());
map.lock("1");
assertEquals("value1", map.get("1"));
assertEquals(1, map.size());
Data key1 = toData("1");
int partitionId = getConcurrentMapManager(h1).getPartitionId(key1);
PartitionInfo p1 = getConcurrentMapManager(h1).getPartitionInfo(partitionId);
PartitionInfo p2 = getConcurrentMapManager(h2).getPartitionInfo(partitionId);
assertEquals(p1, p2);
CMap cmap1 = getCMap(h1, "default");
Record rec1 = cmap1.getRecord(key1);
CMap cmap2 = getCMap(h2, "default");
Record rec2 = cmap2.getRecord(key1);
assertNotNull(rec1.getValueData());
assertNotNull(rec2.getValueData());
assertEquals(rec1.getLock(), rec2.getLock());
h1.getLifecycleService().shutdown();
IMap map2 = h2.getMap("default");
assertEquals(1, map2.size());
map2.putTransient("2", "value2", 100 * 24 * 6000, TimeUnit.SECONDS);
CMap cmap = getCMap(h2, "default");
Data key = toData("2");
Record record = cmap.getRecord(key);
assertFalse(record.isDirty());
assertTrue(record.isValid());
assertTrue(record.isActive());
assertEquals("value2", record.getValue());
map2.put("2", "value22");
assertTrue(record.isDirty());
assertTrue(record.isValid());
assertTrue(record.isActive());
map2.putTransient("2", "value222", 100 * 24 * 6000, TimeUnit.SECONDS);
assertTrue(record.isDirty());
assertTrue(record.isValid());
assertTrue(record.isActive());
}
@Test
public void testOneMemberWriteBehindWithEvictions() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
Config config = newConfig(testMapStore, 2);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL_KEYS, testMapStore.waitForEvent(20));
for (int i = 0; i < 100; i++) {
map.put(i, "value" + i);
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(10));
}
assertEquals(TestEventBasedMapStore.STORE_EVENTS.STORE_ALL, testMapStore.waitForEvent(20));
assertEquals(100, testMapStore.getStore().size());
for (int i = 0; i < 100; i++) {
map.evict(i);
}
// we should not receive any store event.
assertEquals(null, testMapStore.waitForEvent(10));
assertEquals(100, testMapStore.getStore().size());
for (int i = 0; i < 100; i++) {
map.put(i, "value" + i);
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(10));
}
for (int i = 0; i < 100; i++) {
map.evict(i);
}
for (int i = 0; i < 100; i++) {
assertEquals(TestEventBasedMapStore.STORE_EVENTS.STORE, testMapStore.waitForEvent(10));
}
assertEquals(null, testMapStore.waitForEvent(10));
assertEquals(100, testMapStore.getStore().size());
assertEquals(0, map.size());
for (int i = 0; i < 100; i++) {
map.put(i, "value" + i);
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(10));
}
for (int i = 0; i < 100; i++) {
map.remove(i);
}
assertEquals(TestEventBasedMapStore.STORE_EVENTS.DELETE_ALL, testMapStore.waitForEvent(20));
assertEquals(0, testMapStore.getStore().size());
assertEquals(0, map.size());
assertEquals(null, testMapStore.waitForEvent(10));
}
@Test
public void testOneMemberWriteBehindWithMaxIdle() throws Exception {
TestEventBasedMapStore testMapStore = new TestEventBasedMapStore();
Config config = newConfig(testMapStore, 1);
config.getMapConfig("default").setMaxIdleSeconds(4);
HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config);
IMap map = h1.getMap("default");
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD_ALL_KEYS, testMapStore.waitForEvent(20));
for (int i = 0; i < 10; i++) {
map.put(i, "value" + i);
assertEquals(TestEventBasedMapStore.STORE_EVENTS.LOAD, testMapStore.waitForEvent(10));
}
CMap cmap = getCMap(h1, "default");
cmap.startCleanup(true);
assertEquals(TestEventBasedMapStore.STORE_EVENTS.STORE_ALL, testMapStore.waitForEvent(10));
Thread.sleep(5000);
cmap.startCleanup(true);
assertEquals(null, testMapStore.waitForEvent(1));
assertEquals(10, testMapStore.getStore().size());
assertEquals(0, map.size());
cmap.startCleanup(true);
assertEquals(null, testMapStore.waitForEvent(10));
}
@Test
public void issue587CallMapLoaderDuringRemoval() {
final AtomicInteger loadCount = new AtomicInteger(0);
final AtomicInteger storeCount = new AtomicInteger(0);
final AtomicInteger deleteCount = new AtomicInteger(0);
class SimpleMapStore<K, V> implements MapStore<K, V> {
SimpleMapStore(ConcurrentMap<K, V> store) {
this.store = store;
}
public V load(K key) {
V value = store.get(key);
loadCount.incrementAndGet();
return value;
}
public Map<K, V> loadAll(Collection<K> keys) {
Map<K, V> result = new HashMap<K, V>();
for (K key : keys) {
V value = load(key);
if (value != null) {
result.put(key, value);
}
}
return result;
}
public Set<K> loadAllKeys() {
Set<K> keys = store.keySet();
return keys;
}
//
// MapStore methods
//
public void store(K key, V value) {
storeCount.incrementAndGet();
store.put(key, value);
}
public void delete(K key) {
deleteCount.incrementAndGet();
store.remove(key);
}
public void storeAll(Map<K, V> map) {
store.putAll(map);
}
public void deleteAll(Collection<K> keys) {
for (K key : keys) {
store.remove(key);
}
}
private final ConcurrentMap<K, V> store;
}
final ConcurrentMap<String, Long> store = new ConcurrentHashMap<String, Long>();
final MapStore<String, Long> myMapStore = new SimpleMapStore<String, Long>(store);
Config config = new Config();
config
.getMapConfig("myMap")
.setMapStoreConfig(new MapStoreConfig()
//.setWriteDelaySeconds(1)
.setImplementation(myMapStore));
HazelcastInstance hc = Hazelcast.newHazelcastInstance(config);
try {
store.put("one", 1l);
store.put("two", 2l);
assertEquals(0, loadCount.get());
assertEquals(0, storeCount.get());
assertEquals(0, deleteCount.get());
IMap<String, Long> myMap = hc.getMap("myMap");
assertEquals(1l, myMap.get("one").longValue());
assertEquals(2l, myMap.get("two").longValue());
assertEquals(2, loadCount.get());
assertEquals(0, storeCount.get());
assertEquals(0, deleteCount.get());
assertNull(myMap.remove("ten"));
assertEquals(3, loadCount.get());
assertEquals(0, storeCount.get());
assertEquals(0, deleteCount.get());
myMap.put("three", 3L);
myMap.put("four", 4L);
assertEquals(5, loadCount.get());
assertEquals(2, storeCount.get());
assertEquals(0, deleteCount.get());
myMap.remove("one");
assertEquals(2, storeCount.get());
assertEquals(1, deleteCount.get());
assertEquals(5, loadCount.get());
} finally {
Hazelcast.shutdownAll();
}
}
@Test
public void storedQueueWithDelaySecondsActionPollAndTake() throws InterruptedException {
final ConcurrentMap<Long, String> STORE =
new ConcurrentHashMap<Long, String>();
STORE.put(1l, "Event1");
STORE.put(2l, "Event2");
STORE.put(3l, "Event3");
STORE.put(4l, "Event4");
STORE.put(5l, "Event5");
STORE.put(6l, "Event6");
Config config = new Config();
config
.getMapConfig("queue-map")
.setMapStoreConfig(new MapStoreConfig()
.setWriteDelaySeconds(1)
.setImplementation(new MapStore<Long, String>() {
public String load(Long key) {
String value = STORE.get(key);
return value;
}
public Map<Long, String> loadAll(Collection<Long> keys) {
Map<Long, String> result = new HashMap<Long, String>();
for (Long key : keys) {
String value = load(key);
if (value != null) {
result.put(key, value);
}
}
return result;
}
public Set<Long> loadAllKeys() {
return STORE.keySet();
}
public void store(Long key, String value) {
STORE.put(key, value);
}
public void storeAll(Map<Long, String> map) {
for (Map.Entry<Long, String> entry : map.entrySet()) {
store(entry.getKey(), entry.getValue());
}
}
public void delete(Long key) {
STORE.remove(key);
}
public void deleteAll(Collection<Long> keys) {
for (Long key : STORE.keySet()) {
delete(key);
}
}
}));
config.getQueueConfig("tasks").setBackingMapRef("queue-map");
HazelcastInstance h = Hazelcast.newHazelcastInstance(config);
IQueue q = h.getQueue("tasks");
assertEquals(STORE.size(), q.size());
// assertEquals(STORE.get(1l), q.poll());
assertEquals(STORE.get(1l), q.take());
assertEquals(STORE.get(2l), q.take());
}
@Test
public void testIssue583MapReplaceShouldTriggerMapStore() {
class SimpleMapStore<K, V> implements MapStore<K, V> {
SimpleMapStore(ConcurrentMap<K, V> store) {
this.store = store;
}
public V load(K key) {
V value = store.get(key);
return value;
}
public Map<K, V> loadAll(Collection<K> keys) {
Map<K, V> result = new HashMap<K, V>();
for (K key : keys) {
V value = store.get(key);
if (value != null) {
result.put(key, value);
}
}
return result;
}
public Set<K> loadAllKeys() {
return store.keySet();
}
//
// MapStore methods
//
public void store(K key, V value) {
store.put(key, value);
}
public void delete(K key) {
store.remove(key);
}
public void storeAll(Map<K, V> map) {
store.putAll(map);
}
public void deleteAll(Collection<K> keys) {
for (K key : keys) {
store.remove(key);
}
}
private final ConcurrentMap<K, V> store;
}
final ConcurrentMap<String, Long> store = new ConcurrentHashMap<String, Long>();
final MapStore<String, Long> myMapStore = new SimpleMapStore<String, Long>(store);
Config config = new Config();
config
.getMapConfig("myMap")
.setMapStoreConfig(new MapStoreConfig()
.setImplementation(myMapStore));
HazelcastInstance hc = Hazelcast.newHazelcastInstance(config);
IMap<String, Long> myMap = hc.getMap("myMap");
myMap.put("one", 1L);
assertEquals(1L, myMap.get("one").longValue());
assertEquals(1L, store.get("one").longValue());
myMap.putIfAbsent("two", 2L);
assertEquals(2L, myMap.get("two").longValue());
assertEquals(2L, store.get("two").longValue());
myMap.putIfAbsent("one", 5L);
assertEquals(1L, myMap.get("one").longValue());
assertEquals(1L, store.get("one").longValue());
myMap.replace("one", 1L, 111L);
assertEquals(111L, myMap.get("one").longValue());
assertEquals(111L, store.get("one").longValue());
myMap.replace("one", 1L);
assertEquals(1L, myMap.get("one").longValue());
assertEquals(1L, store.get("one").longValue());
}
@Test
public void issue614() {
final ConcurrentMap<Long, String> STORE =
new ConcurrentHashMap<Long, String>();
STORE.put(1l, "Event1");
STORE.put(2l, "Event2");
STORE.put(3l, "Event3");
STORE.put(4l, "Event4");
STORE.put(5l, "Event5");
STORE.put(6l, "Event6");
Config config = new Config();
config
.getMapConfig("map")
.setMapStoreConfig(new MapStoreConfig()
.setWriteDelaySeconds(1)
.setImplementation(new MapStore<Long, String>() {
public String load(Long key) {
String value = STORE.get(key);
return value;
}
public Map<Long, String> loadAll(Collection<Long> keys) {
Map<Long, String> result = new HashMap<Long, String>();
for (Long key : keys) {
String value = load(key);
if (value != null) {
result.put(key, value);
}
}
return result;
}
public Set<Long> loadAllKeys() {
return STORE.keySet();
}
public void store(Long key, String value) {
STORE.put(key, value);
}
public void storeAll(Map<Long, String> map) {
for (Map.Entry<Long, String> entry : map.entrySet()) {
store(entry.getKey(), entry.getValue());
}
}
public void delete(Long key) {
STORE.remove(key);
}
public void deleteAll(Collection<Long> keys) {
for (Long key : STORE.keySet()) {
delete(key);
}
}
}));
HazelcastInstance h = Hazelcast.newHazelcastInstance(config);
IMap map = h.getMap("map");
Collection collection = map.values();
for (Object o : collection) {
}
LocalMapStats localMapStats = map.getLocalMapStats();
assertEquals(0, localMapStats.getDirtyEntryCount());
}
protected Config newConfig(Object storeImpl, int writeDelaySeconds) {
return newConfig("default", storeImpl, writeDelaySeconds);
}
protected Config newConfig(String mapName, Object storeImpl, int writeDelaySeconds) {
Config config = new XmlConfigBuilder().build();
MapConfig mapConfig = config.getMapConfig(mapName);
MapStoreConfig mapStoreConfig = new MapStoreConfig();
mapStoreConfig.setImplementation(storeImpl);
mapStoreConfig.setWriteDelaySeconds(writeDelaySeconds);
mapConfig.setMapStoreConfig(mapStoreConfig);
return config;
}
public static class TestMapStore implements MapLoaderLifecycleSupport, MapStore {
final Map store = new ConcurrentHashMap();
final CountDownLatch latchStore;
final CountDownLatch latchStoreAll;
final CountDownLatch latchDelete;
final CountDownLatch latchDeleteAll;
final CountDownLatch latchLoad;
final CountDownLatch latchLoadAllKeys;
final CountDownLatch latchLoadAll;
final AtomicInteger callCount = new AtomicInteger();
final AtomicInteger initCount = new AtomicInteger();
final AtomicInteger destroyCount = new AtomicInteger();
private HazelcastInstance hazelcastInstance;
private Properties properties;
private String mapName;
private boolean loadAllKeys = true;
public TestMapStore() {
this(0, 0, 0, 0, 0, 0);
}
public TestMapStore(int expectedStore, int expectedDelete, int expectedLoad) {
this(expectedStore, 0, expectedDelete, 0, expectedLoad, 0);
}
public TestMapStore(int expectedStore, int expectedStoreAll, int expectedDelete,
int expectedDeleteAll, int expectedLoad, int expectedLoadAll) {
this(expectedStore, expectedStoreAll, expectedDelete, expectedDeleteAll,
expectedLoad, expectedLoadAll, 0);
}
public TestMapStore(int expectedStore, int expectedStoreAll, int expectedDelete,
int expectedDeleteAll, int expectedLoad, int expectedLoadAll,
int expectedLoadAllKeys) {
latchStore = new CountDownLatch(expectedStore);
latchStoreAll = new CountDownLatch(expectedStoreAll);
latchDelete = new CountDownLatch(expectedDelete);
latchDeleteAll = new CountDownLatch(expectedDeleteAll);
latchLoad = new CountDownLatch(expectedLoad);
latchLoadAll = new CountDownLatch(expectedLoadAll);
latchLoadAllKeys = new CountDownLatch(expectedLoadAllKeys);
}
public void init(HazelcastInstance hazelcastInstance, Properties properties, String mapName) {
this.hazelcastInstance = hazelcastInstance;
this.properties = properties;
this.mapName = mapName;
initCount.incrementAndGet();
}
public boolean isLoadAllKeys() {
return loadAllKeys;
}
public void setLoadAllKeys(boolean loadAllKeys) {
this.loadAllKeys = loadAllKeys;
}
public void destroy() {
destroyCount.incrementAndGet();
}
public int getInitCount() {
return initCount.get();
}
public int getDestroyCount() {
return destroyCount.get();
}
public HazelcastInstance getHazelcastInstance() {
return hazelcastInstance;
}
public String getMapName() {
return mapName;
}
public Properties getProperties() {
return properties;
}
public void assertAwait(int seconds) throws Exception {
assertTrue(latchStore.await(seconds, TimeUnit.SECONDS));
assertTrue(latchStoreAll.await(seconds, TimeUnit.SECONDS));
assertTrue(latchDelete.await(seconds, TimeUnit.SECONDS));
assertTrue(latchDeleteAll.await(seconds, TimeUnit.SECONDS));
assertTrue(latchLoad.await(seconds, TimeUnit.SECONDS));
assertTrue(latchLoadAll.await(seconds, TimeUnit.SECONDS));
}
Map getStore() {
return store;
}
public void insert(Object key, Object value) {
store.put(key, value);
}
public void store(Object key, Object value) {
store.put(key, value);
callCount.incrementAndGet();
latchStore.countDown();
}
public Set loadAllKeys() {
callCount.incrementAndGet();
latchLoadAllKeys.countDown();
if (!loadAllKeys) return null;
return store.keySet();
}
public Object load(Object key) {
callCount.incrementAndGet();
latchLoad.countDown();
return store.get(key);
}
public void storeAll(Map map) {
store.putAll(map);
callCount.incrementAndGet();
latchStoreAll.countDown();
}
public void delete(Object key) {
store.remove(key);
callCount.incrementAndGet();
latchDelete.countDown();
}
public Map loadAll(Collection keys) {
Map map = new HashMap(keys.size());
for (Object key : keys) {
Object value = store.get(key);
if (value != null) {
map.put(key, value);
}
}
callCount.incrementAndGet();
latchLoadAll.countDown();
return map;
}
public void deleteAll(Collection keys) {
for (Object key : keys) {
store.remove(key);
}
callCount.incrementAndGet();
latchDeleteAll.countDown();
}
}
public static class TestEventBasedMapStore<K, V> implements MapLoaderLifecycleSupport, MapStore<K, V> {
protected enum STORE_EVENTS {
STORE, STORE_ALL, DELETE, DELETE_ALL, LOAD, LOAD_ALL, LOAD_ALL_KEYS
}
protected final Map<K, V> store = new ConcurrentHashMap();
protected final BlockingQueue events = new LinkedBlockingQueue();
protected final AtomicInteger callCount = new AtomicInteger();
protected final AtomicInteger initCount = new AtomicInteger();
protected HazelcastInstance hazelcastInstance;
protected Properties properties;
protected String mapName;
protected boolean loadAllKeys = true;
public void init(HazelcastInstance hazelcastInstance, Properties properties, String mapName) {
this.hazelcastInstance = hazelcastInstance;
this.properties = properties;
this.mapName = mapName;
initCount.incrementAndGet();
}
public void destroy() {
}
public int getEventCount() {
return events.size();
}
public int getInitCount() {
return initCount.get();
}
public boolean isLoadAllKeys() {
return loadAllKeys;
}
public void setLoadAllKeys(boolean loadAllKeys) {
this.loadAllKeys = loadAllKeys;
}
public HazelcastInstance getHazelcastInstance() {
return hazelcastInstance;
}
public String getMapName() {
return mapName;
}
public Properties getProperties() {
return properties;
}
Object waitForEvent(int seconds) throws InterruptedException {
return events.poll(seconds, TimeUnit.SECONDS);
}
Map getStore() {
return store;
}
public void insert(K key, V value) {
store.put(key, value);
}
public void store(K key, V value) {
store.put(key, value);
callCount.incrementAndGet();
events.offer(STORE_EVENTS.STORE);
}
public V load(K key) {
callCount.incrementAndGet();
events.offer(STORE_EVENTS.LOAD);
return store.get(key);
}
public void storeAll(Map map) {
store.putAll(map);
callCount.incrementAndGet();
events.offer(STORE_EVENTS.STORE_ALL);
}
public void delete(K key) {
store.remove(key);
callCount.incrementAndGet();
events.offer(STORE_EVENTS.DELETE);
}
public Set<K> loadAllKeys() {
callCount.incrementAndGet();
events.offer(STORE_EVENTS.LOAD_ALL_KEYS);
if (!loadAllKeys) return null;
return store.keySet();
}
public Map loadAll(Collection keys) {
Map map = new HashMap(keys.size());
for (Object key : keys) {
Object value = store.get(key);
if (value != null) {
map.put(key, value);
}
}
callCount.incrementAndGet();
events.offer(STORE_EVENTS.LOAD_ALL);
return map;
}
public void deleteAll(Collection keys) {
for (Object key : keys) {
store.remove(key);
}
callCount.incrementAndGet();
events.offer(STORE_EVENTS.DELETE_ALL);
}
}
public static class FailAwareMapStore implements MapStore {
final Map db = new ConcurrentHashMap();
final AtomicLong deletes = new AtomicLong();
final AtomicLong deleteAlls = new AtomicLong();
final AtomicLong stores = new AtomicLong();
final AtomicLong storeAlls = new AtomicLong();
final AtomicLong loads = new AtomicLong();
final AtomicLong loadAlls = new AtomicLong();
final AtomicLong loadAllKeys = new AtomicLong();
final AtomicBoolean shouldFail = new AtomicBoolean(false);
final List<BlockingQueue> listeners = new CopyOnWriteArrayList<BlockingQueue>();
public void addListener(BlockingQueue obj) {
listeners.add(obj);
}
public void notifyListeners() {
for (BlockingQueue listener : listeners) {
listener.offer(new Object());
}
}
public void delete(Object key) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
db.remove(key);
}
} finally {
deletes.incrementAndGet();
notifyListeners();
}
}
public void setFail(boolean shouldFail) {
this.shouldFail.set(shouldFail);
}
public int dbSize() {
return db.size();
}
public boolean dbContainsKey(Object key) {
return db.containsKey(key);
}
public Object dbGet(Object key) {
return db.get(key);
}
public void store(Object key, Object value) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
db.put(key, value);
}
} finally {
stores.incrementAndGet();
notifyListeners();
}
}
public Set loadAllKeys() {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
return db.keySet();
}
} finally {
loadAllKeys.incrementAndGet();
}
}
public Object load(Object key) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
return db.get(key);
}
} finally {
loads.incrementAndGet();
}
}
public void storeAll(Map map) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
db.putAll(map);
}
} finally {
storeAlls.incrementAndGet();
notifyListeners();
}
}
public Map loadAll(Collection keys) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
Map results = new HashMap();
for (Object key : keys) {
Object value = db.get(key);
if (value != null) {
results.put(key, value);
}
}
return results;
}
} finally {
loadAlls.incrementAndGet();
notifyListeners();
}
}
public void deleteAll(Collection keys) {
try {
if (shouldFail.get()) {
throw new RuntimeException();
} else {
for (Object key : keys) {
db.remove(key);
}
}
} finally {
deleteAlls.incrementAndGet();
notifyListeners();
}
}
}
@Test
public void testMapLoaderInitialization() {
Config config = new Config();
MapConfig mapConfig = config.getMapConfig("testMapLoader-*");
MapStoreConfig msConfig = new MapStoreConfig();
mapConfig.setMapStoreConfig(msConfig);
msConfig.setEnabled(true);
Config configSuper = new Config();
configSuper.setLiteMember(true);
configSuper.addMapConfig(mapConfig);
final int initialKeys = 5;
msConfig.setImplementation(new MapLoader() {
public Object load(Object key) {
return "Value: " + key;
}
public Map loadAll(Collection keys) {
Map map = new HashMap(keys.size());
for (Object key : keys) {
map.put(key, load(key));
}
return map;
}
public Set loadAllKeys() {
Set keys = new HashSet(3);
for (int i = 0; i < initialKeys; i++) {
keys.add(i);
}
return keys;
}
});
final HazelcastInstance member = Hazelcast.newHazelcastInstance(config);
final HazelcastInstance superClient = Hazelcast.newHazelcastInstance(configSuper);
Hazelcast.newHazelcastInstance(config);
assertEquals(initialKeys, member.getMap("testMapLoader-1").size());
assertEquals(initialKeys, superClient.getMap("testMapLoader-1").size());
assertEquals(initialKeys, superClient.getMap("testMapLoader-2").size());
assertEquals(initialKeys, member.getMap("testMapLoader-2").size());
Hazelcast.shutdownAll();
}
@Test
/**
* Issue 816.
*/
public void testMapRemoveWithWriteBehindMapStore() {
Config c = new Config();
TestMapStore mapStore = new TestMapStore();
mapStore.setLoadAllKeys(false);
for (int i = 1; i < 5; i++) {
mapStore.store(i, "value" + i);
}
c.getMapConfig("test").setMapStoreConfig(new MapStoreConfig().setEnabled(true)
.setWriteDelaySeconds(100).setImplementation(mapStore));
HazelcastInstance hz = Hazelcast.newHazelcastInstance(c);
Map map = hz.getMap("test");
assertEquals("value1", map.get(1));
assertEquals("value1", map.remove(1));
assertNull(map.get(1));
assertEquals("value2", map.get(2));
assertEquals("value2", map.remove(2));
assertFalse(map.containsKey(2));
assertEquals("value3", map.get(3));
assertEquals("value3", map.remove(3));
assertNull(map.put(3, "valuex"));
assertEquals("value4", map.get(4));
assertEquals("value4", map.remove(4));
assertNull(map.remove(4));
}
}