blob: 033d21ad39e1d8be9d357da7c5a653f0cbd20182 [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.index;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.metric.IoStatisticsHolder;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.persistence.tree.CorruptedTreeException;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.testframework.ListeningTestLogger;
import org.apache.ignite.testframework.LogListener;
import org.apache.ignite.testframework.MessageOrderLogListener;
import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
import static java.lang.String.format;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
/** */
public class H2TreeCorruptedTreeExceptionTest extends GridCommonAbstractTest {
/** */
private static final String IDX_NAME = "A_IDX";
/** */
private static final String GRP_NAME = "cacheGrp";
/** */
private static final String VERY_SENS_STR_DATA = "here_comes_very_sensitive_data@#123#321#@";
/** */
private final AtomicBoolean failWithCorruptTree = new AtomicBoolean(false);
/** */
private final LogListener logListener = new MessageOrderLogListener(
format(
".*?Tree is corrupted.*?cacheId=-1578586276, cacheName=SQL_PUBLIC_A, indexName=%s, groupName=%s" +
", msg=Runtime failure on row: Row@.*?key: 1, val: .*?%s.*",
IDX_NAME,
GRP_NAME,
IGNITE_TO_STRING_INCLUDE_SENSITIVE
)
);
/** */
private final LogListener logSensListener = new MessageOrderLogListener(format(".*%s.*", VERY_SENS_STR_DATA));
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(gridName);
cfg.setConsistentId(gridName);
cfg.setCacheConfiguration(new CacheConfiguration<>()
.setName(DEFAULT_CACHE_NAME)
.setAffinity(new RendezvousAffinityFunction().setPartitions(1))
.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
);
ListeningTestLogger listeningTestLog = new ListeningTestLogger(false, log);
listeningTestLog.registerListener(logListener);
listeningTestLog.registerListener(logSensListener);
cfg.setGridLogger(listeningTestLog);
return cfg;
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
stopAllGrids();
cleanPersistenceDir();
BPlusTree.testHndWrapper = (tree, hnd) -> {
if (hnd instanceof BPlusTree.Insert) {
PageHandler<Object, BPlusTree.Result> delegate = (PageHandler<Object, BPlusTree.Result>)hnd;
return new PageHandler<Object, BPlusTree.Result>() {
@Override public BPlusTree.Result run(
int cacheId,
long pageId,
long page,
long pageAddr,
PageIO io,
Boolean walPlc,
Object arg,
int intArg,
IoStatisticsHolder statHolder
) throws IgniteCheckedException {
BPlusTree.Result res =
delegate.run(cacheId, pageId, page, pageAddr, io, walPlc, arg, intArg, statHolder);
if (failWithCorruptTree.get() && tree.name().contains(IDX_NAME))
throw new RuntimeException("test exception");
return res;
}
};
}
else
return hnd;
};
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
cleanPersistenceDir();
clearGridToStringClassCache();
BPlusTree.testHndWrapper = null;
super.afterTest();
}
/** */
@Test
@WithSystemProperty(key = IGNITE_TO_STRING_INCLUDE_SENSITIVE, value = "false")
public void testCorruptedTree() throws Exception {
IgniteEx srv = startGrid(0);
srv.cluster().active(true);
IgniteCache<Integer, Integer> cache = srv.getOrCreateCache(DEFAULT_CACHE_NAME);
cache.query(new SqlFieldsQuery("create table a (col1 varchar primary key, col2 varchar) with " +
"\"CACHE_GROUP=" + GRP_NAME + "\""));
cache.query(new SqlFieldsQuery("create index " + IDX_NAME + " on a(col2)"));
failWithCorruptTree.set(true);
try {
cache.query(new SqlFieldsQuery("insert into a(col1, col2) values (1, ?1)")
.setArgs(VERY_SENS_STR_DATA));
fail("Cache operations are expected to fail");
}
catch (Throwable e) {
assertTrue(X.hasCause(e, CorruptedTreeException.class));
}
assertTrue(logListener.check());
assertFalse(logSensListener.check());
System.setProperty(IGNITE_TO_STRING_INCLUDE_SENSITIVE, Boolean.TRUE.toString());
clearGridToStringClassCache();
logListener.reset();
logSensListener.reset();
try {
cache.query(new SqlFieldsQuery("insert into a(col1, col2) values (2, ?1)")
.setArgs(VERY_SENS_STR_DATA));
}
catch (Throwable e) {
assertTrue(X.hasCause(e, CorruptedTreeException.class));
}
assertFalse(logListener.check());
assertTrue(logSensListener.check());
}
}