/*
* Copyright Terracotta, Inc.
*
* 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 org.ehcache.clustered.replication;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.PersistentCacheManager;
import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder;
import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder;
import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder;
import org.ehcache.clustered.common.Consistency;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.terracotta.testing.rules.BasicExternalCluster;
import org.terracotta.testing.rules.Cluster;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.LongStream;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* The point of this test is to assert proper data read after fail-over handling.
*/
@RunWith(Parameterized.class)
public class BasicClusteredCacheOpsReplicationWithMultipleClientsTest {
private static final String RESOURCE_CONFIG =
"<config xmlns:ohr='http://www.terracotta.org/config/offheap-resource'>"
+ "<ohr:offheap-resources>"
+ "<ohr:resource name=\"primary-server-resource\" unit=\"MB\">16</ohr:resource>"
+ "</ohr:offheap-resources>" +
"</config>\n";
private static PersistentCacheManager CACHE_MANAGER1;
private static PersistentCacheManager CACHE_MANAGER2;
private static Cache<Long, BlobValue> CACHE1;
private static Cache<Long, BlobValue> CACHE2;
@Parameters(name = "consistency={0}")
public static Consistency[] data() {
return Consistency.values();
}
@Parameter
public Consistency cacheConsistency;
@ClassRule
public static Cluster CLUSTER =
new BasicExternalCluster(new File("build/cluster"), 2, Collections.emptyList(), "", RESOURCE_CONFIG, "");
@Before
public void startServers() throws Exception {
CLUSTER.getClusterControl().startAllServers();
CLUSTER.getClusterControl().waitForActive();
CLUSTER.getClusterControl().waitForRunningPassivesInStandby();
final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder
= CacheManagerBuilder.newCacheManagerBuilder()
.with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm-replication"))
.autoCreate()
.defaultServerResource("primary-server-resource"));
CACHE_MANAGER1 = clusteredCacheManagerBuilder.build(true);
CACHE_MANAGER2 = clusteredCacheManagerBuilder.build(true);
CacheConfiguration<Long, BlobValue> config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, BlobValue.class,
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(500, EntryUnit.ENTRIES)
.with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB)))
.add(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency))
.build();
CACHE1 = CACHE_MANAGER1.createCache("clustered-cache", config);
CACHE2 = CACHE_MANAGER2.createCache("clustered-cache", config);
}
@After
public void tearDown() throws Exception {
CACHE_MANAGER1.close();
CACHE_MANAGER2.close();
CACHE_MANAGER2.destroy();
}
@Test(timeout=180000)
public void testCRUD() throws Exception {
Random random = new Random();
LongStream longStream = random.longs(1000);
Set<Long> added = new HashSet<>();
longStream.forEach(x -> {
CACHE1.put(x, new BlobValue());
added.add(x);
});
Set<Long> readKeysByCache2BeforeFailOver = new HashSet<>();
added.forEach(x -> {
if (CACHE2.get(x) != null) {
readKeysByCache2BeforeFailOver.add(x);
}
});
CLUSTER.getClusterControl().terminateActive();
Set<Long> readKeysByCache1AfterFailOver = new HashSet<>();
added.forEach(x -> {
if (CACHE1.get(x) != null) {
readKeysByCache1AfterFailOver.add(x);
}
});
assertThat(readKeysByCache2BeforeFailOver.size(), greaterThanOrEqualTo(readKeysByCache1AfterFailOver.size()));
readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(CACHE2.get(y), notNullValue()));
}
@Test(timeout=180000)
@Ignore //TODO: FIXME: FIX THIS RANDOMLY FAILING TEST
public void testBulkOps() throws Exception {
List<Cache<Long, BlobValue>> caches = new ArrayList<>();
caches.add(CACHE1);
caches.add(CACHE2);
Map<Long, BlobValue> entriesMap = new HashMap<>();
Random random = new Random();
LongStream longStream = random.longs(1000);
longStream.forEach(x -> entriesMap.put(x, new BlobValue()));
caches.forEach(cache -> cache.putAll(entriesMap));
Set<Long> keySet = entriesMap.keySet();
Set<Long> readKeysByCache2BeforeFailOver = new HashSet<>();
keySet.forEach(x -> {
if (CACHE2.get(x) != null) {
readKeysByCache2BeforeFailOver.add(x);
}
});
CLUSTER.getClusterControl().terminateActive();
Set<Long> readKeysByCache1AfterFailOver = new HashSet<>();
keySet.forEach(x -> {
if (CACHE1.get(x) != null) {
readKeysByCache1AfterFailOver.add(x);
}
});
assertThat(readKeysByCache2BeforeFailOver.size(), greaterThanOrEqualTo(readKeysByCache1AfterFailOver.size()));
readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(CACHE2.get(y), notNullValue()));
}
@Test(timeout=180000)
public void testClear() throws Exception {
List<Cache<Long, BlobValue>> caches = new ArrayList<>();
caches.add(CACHE1);
caches.add(CACHE2);
Map<Long, BlobValue> entriesMap = new HashMap<>();
Random random = new Random();
LongStream longStream = random.longs(1000);
longStream.forEach(x -> entriesMap.put(x, new BlobValue()));
caches.forEach(cache -> cache.putAll(entriesMap));
Set<Long> keySet = entriesMap.keySet();
Set<Long> readKeysByCache2BeforeFailOver = new HashSet<>();
keySet.forEach(x -> {
if (CACHE2.get(x) != null) {
readKeysByCache2BeforeFailOver.add(x);
}
});
CACHE1.clear();
CLUSTER.getClusterControl().terminateActive();
readKeysByCache2BeforeFailOver.forEach(x -> assertThat(CACHE2.get(x), nullValue()));
}
private static class BlobValue implements Serializable {
private final byte[] data = new byte[10 * 1024];
}
}