blob: 325413b9f8646b26e6aa8961b435b13d9e47ca02 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.cache;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.cache.Cache;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.util.typedef.P2;
import org.apache.ignite.testframework.MvccFeatureChecker;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.apache.ignite.transactions.Transaction;
import org.jetbrains.annotations.Nullable;
import org.junit.Before;
import org.junit.Test;
import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
/**
* Basic store test.
*/
public abstract class GridCacheBasicStoreAbstractTest extends GridCommonAbstractTest {
/** Cache store. */
private static final GridCacheTestStore store = new GridCacheTestStore();
/** */
@Before
public void beforeGridCacheBasicStoreAbstractTest() {
MvccFeatureChecker.skipIfNotSupported(MvccFeatureChecker.Feature.CACHE_STORE);
}
/**
*
*/
protected GridCacheBasicStoreAbstractTest() {
super(true /*start grid. */);
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
store.resetTimestamp();
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
jcache().clear();
store.reset();
}
/** @return Caching mode. */
protected abstract CacheMode cacheMode();
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
MvccFeatureChecker.skipIfNotSupported(MvccFeatureChecker.Feature.CACHE_STORE);
IgniteConfiguration c = super.getConfiguration(igniteInstanceName);
CacheConfiguration cc = defaultCacheConfiguration();
cc.setCacheMode(cacheMode());
cc.setWriteSynchronizationMode(FULL_SYNC);
cc.setAtomicityMode(atomicityMode());
cc.setRebalanceMode(SYNC);
cc.setCacheStoreFactory(singletonFactory(store));
cc.setReadThrough(true);
cc.setWriteThrough(true);
cc.setLoadPreviousValue(true);
c.setCacheConfiguration(cc);
return c;
}
/**
* @return Cache atomicity mode.
*/
protected CacheAtomicityMode atomicityMode() {
return TRANSACTIONAL;
}
/**
* @throws IgniteCheckedException If failed.
*/
@Test
public void testNotExistingKeys() throws IgniteCheckedException {
IgniteCache<Integer, String> cache = jcache();
Map<Integer, String> map = store.getMap();
cache.put(100, "hacuna matata");
assertEquals(1, map.size());
cache.localEvict(Collections.singleton(100));
assertEquals(1, map.size());
assertEquals("hacuna matata", cache.getAndRemove(100));
assertTrue(map.isEmpty());
store.resetLastMethod();
assertNull(store.getLastMethod());
cache.remove(200);
assertEquals("remove", store.getLastMethod());
cache.get(300);
assertEquals("load", store.getLastMethod());
}
/** @throws Exception If test fails. */
@Test
public void testWriteThrough() throws Exception {
IgniteCache<Integer, String> cache = jcache();
Map<Integer, String> map = store.getMap();
assert map.isEmpty();
if (atomicityMode() == TRANSACTIONAL) {
try (Transaction tx = grid().transactions().txStart(OPTIMISTIC, REPEATABLE_READ)) {
for (int i = 1; i <= 10; i++) {
cache.put(i, Integer.toString(i));
checkLastMethod(null);
}
tx.commit();
}
}
else {
Map<Integer, String> putMap = new HashMap<>();
for (int i = 1; i <= 10; i++)
putMap.put(i, Integer.toString(i));
cache.putAll(putMap);
}
checkLastMethod("putAll");
assert cache.size() == 10;
for (int i = 1; i <= 10; i++) {
String val = map.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
}
store.resetLastMethod();
if (atomicityMode() == TRANSACTIONAL) {
try (Transaction tx = grid().transactions().txStart()) {
for (int i = 1; i <= 10; i++) {
String val = cache.getAndRemove(i);
checkLastMethod(null);
assert val != null;
assert val.equals(Integer.toString(i));
}
tx.commit();
checkLastMethod("removeAll");
}
}
else {
Set<Integer> keys = new HashSet<>();
for (int i = 1; i <= 10; i++)
keys.add(i);
cache.removeAll(keys);
checkLastMethod("removeAll");
}
assert map.isEmpty();
}
/** @throws Exception If test failed. */
@Test
public void testReadThrough() throws Exception {
IgniteCache<Integer, String> cache = jcache();
Map<Integer, String> map = store.getMap();
assert map.isEmpty();
if (atomicityMode() == TRANSACTIONAL) {
try (Transaction tx = grid().transactions().txStart(OPTIMISTIC, REPEATABLE_READ)) {
for (int i = 1; i <= 10; i++)
cache.put(i, Integer.toString(i));
checkLastMethod(null);
tx.commit();
}
}
else {
Map<Integer, String> putMap = new HashMap<>();
for (int i = 1; i <= 10; i++)
putMap.put(i, Integer.toString(i));
cache.putAll(putMap);
}
checkLastMethod("putAll");
for (int i = 1; i <= 10; i++) {
String val = map.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
}
cache.clear();
assert cache.localSize() == 0;
assert cache.localSize() == 0;
assert map.size() == 10;
for (int i = 1; i <= 10; i++) {
// Read through.
String val = cache.get(i);
checkLastMethod("load");
assert val != null;
assert val.equals(Integer.toString(i));
}
assert cache.size() == 10;
cache.clear();
assert cache.localSize() == 0;
assert cache.localSize() == 0;
assert map.size() == 10;
Set<Integer> keys = new HashSet<>();
for (int i = 1; i <= 10; i++)
keys.add(i);
// Read through.
Map<Integer, String> vals = cache.getAll(keys);
checkLastMethod("loadAll");
assert vals != null;
assert vals.size() == 10;
for (int i = 1; i <= 10; i++) {
String val = vals.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
}
// Write through.
cache.removeAll(keys);
checkLastMethod("removeAll");
assert cache.localSize() == 0;
assert cache.localSize() == 0;
assert map.isEmpty();
}
/** @throws Exception If test failed. */
@Test
public void testLoadCache() throws Exception {
IgniteCache<Integer, String> cache = jcache();
int cnt = 1;
cache.loadCache(null, cnt);
checkLastMethod("loadAllFull");
assert !(cache.localSize() == 0);
Map<Integer, String> map = cache.getAll(keySet(cache));
assert map.size() == cnt : "Invalid map size: " + map.size();
// Recheck last method to make sure
// values were read from cache.
checkLastMethod("loadAllFull");
int start = store.getStart();
for (int i = start; i < start + cnt; i++) {
String val = map.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
}
}
/** @throws Exception If test failed. */
@Test
public void testLoadCacheWithPredicate() throws Exception {
IgniteCache<Integer, String> cache = jcache();
int cnt = 10;
cache.loadCache(new P2<Integer, String>() {
@Override public boolean apply(Integer key, String val) {
// Accept only even numbers.
return key % 2 == 0;
}
}, cnt);
checkLastMethod("loadAllFull");
Map<Integer, String> map = cache.getAll(keySet(cache));
assert map.size() == cnt / 2 : "Invalid map size: " + map.size();
// Recheck last method to make sure
// values were read from cache.
checkLastMethod("loadAllFull");
int start = store.getStart();
for (int i = start; i < start + cnt; i++) {
String val = map.get(i);
if (i % 2 == 0) {
assert val != null;
assert val.equals(Integer.toString(i));
}
else
assert val == null;
}
}
/** @throws Exception If test failed. */
@Test
public void testReloadCache() throws Exception {
IgniteCache<Integer, String> cache = jcache();
cache.loadCache(null, 0);
assert cache.size() == 0;
checkLastMethod("loadAllFull");
Set<Integer> keys = new HashSet<>();
for (int i = 1; i <= 10; i++) {
keys.add(i);
cache.put(i, Integer.toString(i));
checkLastMethod("put");
}
assert cache.size() == 10;
loadAll(cache, keys, true);
checkLastMethod("loadAll");
assert cache.size() == 10;
store.resetLastMethod();
for (int i = 1; i <= 10; i++) {
String val = cache.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
cache.clear();
cache.loadCache(new P2<Integer, String>() {
@Override public boolean apply(Integer k, String v) {
// Only accept even numbers.
return k % 2 == 0;
}
}, 10);
checkLastMethod("loadAllFull");
store.resetLastMethod();
assertEquals(5, cache.size());
for (Cache.Entry<Integer, String> entry : cache) {
String val = entry.getValue();
assert val != null;
assert val.equals(Integer.toString(entry.getKey()));
assert entry.getKey() % 2 == 0;
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
/** @throws Exception If test failed. */
@Test
public void testReloadAll() throws Exception {
IgniteCache<Integer, String> cache = jcache();
assert cache.size() == 0;
Map<Integer, String> vals = new HashMap<>();
for (int i = 1; i <= 10; i++)
vals.put(i, Integer.toString(i));
loadAll(cache, vals.keySet(), true);
assert cache.size() == 0 : "Cache is not empty.";
checkLastMethod("loadAll");
cache.putAll(vals);
checkLastMethod("putAll");
assert cache.size() == 10;
loadAll(cache, vals.keySet(), true);
checkLastMethod("loadAll");
assert cache.size() == 10;
store.resetLastMethod();
for (int i = 1; i <= 10; i++) {
String val = cache.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
for (int i = 1; i <= 10; i++)
store.write(new CacheEntryImpl<>(i, "reloaded-" + i));
loadAll(cache, vals.keySet(), true);
checkLastMethod("loadAll");
store.resetLastMethod();
assert cache.size() == 10;
for (int i = 1; i <= 10; i++) {
String val = cache.get(i);
assert val != null;
assert val.equals("reloaded-" + i);
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
}
/** @throws Exception If test failed. */
@Test
public void testReload() throws Exception {
IgniteCache<Integer, String> cache = jcache();
assert cache.size() == 0;
Map<Integer, String> vals = new HashMap<>();
for (int i = 1; i <= 10; i++)
vals.put(i, Integer.toString(i));
loadAll(cache, vals.keySet(), true);
assert cache.size() == 0;
checkLastMethod("loadAll");
cache.putAll(vals);
checkLastMethod("putAll");
assert cache.size() == 10;
load(cache, 1, true);
String val = cache.localPeek(1);
assert val != null;
assert "1".equals(val);
checkLastMethod("load");
assert cache.size() == 10;
store.resetLastMethod();
for (int i = 1; i <= 10; i++) {
val = cache.get(i);
assert val != null;
assert val.equals(Integer.toString(i));
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
for (int i = 1; i <= 10; i++)
store.write(new CacheEntryImpl<>(i, "reloaded-" + i));
store.resetLastMethod();
assert cache.size() == 10;
for (int i = 1; i <= 10; i++) {
load(cache, i, true);
val = cache.localPeek(i);
checkLastMethod("load");
assertEquals("reloaded-" + i, val);
store.resetLastMethod();
String cached = cache.get(i);
assert cached != null;
assert cached.equals(val) : "Cached value mismatch [expected=" + val + ", cached=" + cached + ']';
// Make sure that value is coming from cache, not from store.
checkLastMethod(null);
}
}
/** @param mtd Expected last method value. */
private void checkLastMethod(@Nullable String mtd) {
String lastMtd = store.getLastMethod();
if (mtd == null)
assert lastMtd == null : "Last method must be null: " + lastMtd;
else {
assert lastMtd != null : "Last method must be not null";
assert lastMtd.equals(mtd) : "Last method does not match [expected=" + mtd + ", lastMtd=" + lastMtd + ']';
}
}
}