blob: f7b87d185dcabe9f3a6e6c8434586c708def832d [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.distributed.dht;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.testframework.ListeningTestLogger;
import org.apache.ignite.testframework.LogListener;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
* Test correct behaviour of class to validate partitions update counters and
* cache sizes during exchange process
* {@link org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionsStateValidator}.
*/
public class GridCachePartitionsUpdateCountersAndSizeTest extends GridCommonAbstractTest {
/** Cache name. */
private static final String CACHE_NAME = "cacheTest";
/** Listener for parsing patterns in log. */
private ListeningTestLogger testLog;
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
cfg.setConsistentId(igniteInstanceName);
cfg.setCacheConfiguration(new CacheConfiguration(CACHE_NAME)
.setBackups(2)
.setAffinity(new RendezvousAffinityFunction(false, 32))
);
if (igniteInstanceName.endsWith("0"))
cfg.setGridLogger(testLog);
return cfg;
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
stopAllGrids();
testLog = new ListeningTestLogger(log);
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
testLog.clearListeners();
super.afterTest();
}
/**
* Method that shows the results of partitions state validation for all possible cases:
* @param inconsistentCnt - {@code true} if it is expected that partition counters are inconsistent.
* @param inconsistentSize - {@code true} if it is expected that partition sizes are inconsistent.
* @throws Exception If failed.
*/
private void startThreeNodesGrid(boolean inconsistentCnt, boolean inconsistentSize) throws Exception {
SizeCounterLogListener lsnr = new SizeCounterLogListener();
IgniteEx ignite = startGrids(3);
ignite.cluster().state(ClusterState.ACTIVE);
awaitPartitionMapExchange();
testLog.registerListener(lsnr);
// Populate cache to increment update counters.
for (int i = 0; i < 1000; i++)
ignite.cache(CACHE_NAME).put(i, i);
if (inconsistentCnt) {
// Modify update counter for some partition.
ignite.cachex(CACHE_NAME).context().topology().localPartitions().get(0).updateCounter(99L);
}
if (inconsistentSize) {
// Modify update size for some partition.
ignite.cachex(CACHE_NAME).context().topology().localPartitions().get(0).dataStore()
.clear(ignite.cachex(CACHE_NAME).context().cacheId());
}
// Trigger exchange.
startGrid(3);
awaitPartitionMapExchange();
// Nothing should happen (just log error message) and we're still able
// to put data to corrupted cache.
ignite.cache(CACHE_NAME).put(0, 0);
if (inconsistentCnt && !inconsistentSize)
assertTrue("Counters inconsistent message found", lsnr.checkCnt());
if (!inconsistentCnt && inconsistentSize)
assertTrue("Size inconsistent message found", lsnr.checkSize());
if (inconsistentCnt && inconsistentSize)
assertTrue("Both counters and sizes message found", lsnr.check());
if (!inconsistentCnt && !inconsistentSize) {
assertFalse("Counters and Size inconsistent message found!",
lsnr.check() || lsnr.checkCnt() || lsnr.checkSize());
}
}
/**
* 1. Only partition counters are inconsistent.
*
* @throws Exception If failed.
*/
@Test
public void testValidationfPartitionCountersInconsistent() throws Exception {
startThreeNodesGrid(true, false);
}
/**
* 2. Only partition size are inconsistent.
*
* @throws Exception If failed.
*/
@Test
public void testValidationfPartitionSizeInconsistent() throws Exception {
startThreeNodesGrid(false, true);
}
/**
* 3. Partition counters and size are inconsistent.
*
* @throws Exception If failed.
*/
@Test
public void testValidationBothPartitionSizesAndCountersAreInconsistent() throws Exception {
startThreeNodesGrid(true, true);
}
/**
* 4. No one partition counters and size are inconsistent.
*
* @throws Exception If failed.
*/
@Test
public void testValidationBothPatririonSixeAndCountersAreConsistent() throws Exception {
startThreeNodesGrid(false, false);
}
/**
* Overraided class LogListener for find specific patterns.
*/
private static class SizeCounterLogListener extends LogListener {
/** Pattern for Counters inconsistent message.*/
final Pattern patCnt = Pattern.compile("(\\d)=(\\d{1,2})");
/** Pattern for Size inconsistent message.*/
final Pattern patSz = Pattern.compile("(\\d)=(\\d{1,2})");
/** Pattern for Both counters and sizes message*/
final Pattern patCntSz = Pattern.compile("consistentId=dht.GridCachePartitionsUpdateCountersAndSizeTest" +
"\\d meta=\\[updCnt=(\\d{2}), size=(\\d{1,2})");
/** if finded substring in log for inconsistent counters.*/
boolean cn;
/** if finded substring in log for inconsistent partition size.*/
boolean sz;
/** return true if inconsistent counters.*/
public boolean checkCnt() {
return cn;
}
/** return true if inconsistent partition size.*/
public boolean checkSize() {
return sz;
}
/** {@inheritDoc} */
@Override public boolean check() {
return cn && sz;
}
/** {@inheritDoc} */
@Override public void reset() {
//no op
}
/** {@inheritDoc} */
@Override public void accept(String s) {
HashSet<Long> setCnt = new HashSet<>();
HashSet<Long> setSize = new HashSet<>();
if (s.contains("Partitions update counters are inconsistent for Part 0: ")) {
Matcher m = patCnt.matcher(s);
while (m.find())
setCnt.add(Long.parseLong(m.group(2)));
}
else if (s.contains("Partitions cache sizes are inconsistent for Part 0: ")) {
Matcher m = patSz.matcher(s);
while (m.find())
setSize.add(Long.parseLong(m.group(2)));
}
else if (s.contains("Partitions cache size and update counters are inconsistent for Part 0:")) {
Matcher m = patCntSz.matcher(s);
while (m.find()) {
setCnt.add(Long.parseLong(m.group(1)));
setSize.add(Long.parseLong(m.group(2)));
}
}
if (setCnt.size() == 2 && setCnt.contains(32L) && setCnt.contains(99L))
cn = true;
if (setSize.size() == 2 && setSize.contains(0L) && setSize.contains(32L))
sz = true;
}
}
}