blob: 932ee4e3b3d297e0046f922e4fd78bc2020e7551 [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 com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.metric.IoStatisticsType;
import org.apache.ignite.internal.processors.metric.GridMetricManager;
import org.apache.ignite.internal.processors.metric.MetricRegistry;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.spi.metric.Metric;
import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
import org.junit.Assert;
import org.junit.Test;
import static org.apache.ignite.internal.metric.IoStatisticsCacheSelfTest.logicalReads;
import static org.apache.ignite.internal.metric.IoStatisticsMetricsLocalMXBeanImplSelfTest.resetAllIoMetrics;
import static org.apache.ignite.internal.metric.IoStatisticsType.CACHE_GROUP;
import static org.apache.ignite.internal.metric.IoStatisticsType.HASH_INDEX;
import static org.apache.ignite.internal.metric.IoStatisticsType.SORTED_INDEX;
import static org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.queryProcessor;
import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
/**
* A set of basic tests for caches with indexes.
*/
public class IoStatisticsBasicIndexSelfTest extends AbstractIndexingCommonTest {
/** */
private static final int NUMBER_OF_PK_SORTED_INDEXES = 1;
/** */
private static final Set<String> PK_HASH_INDEXES = Sets.newHashSet(DEFAULT_CACHE_NAME);
/** */
private Collection<QueryIndex> indexes;
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
assertNotNull(indexes);
IgniteConfiguration igniteCfg = super.getConfiguration(igniteInstanceName);
LinkedHashMap<String, String> fields = new LinkedHashMap<>();
fields.put("keyStr", String.class.getName());
fields.put("keyLong", Long.class.getName());
fields.put("keyPojo", Pojo.class.getName());
fields.put("valStr", String.class.getName());
fields.put("valLong", Long.class.getName());
fields.put("valPojo", Pojo.class.getName());
Set<String> keyFields = new HashSet<>();
keyFields.add("keyStr");
keyFields.add("keyLong");
keyFields.add("keyPojo");
CacheConfiguration<Key, Val> ccfg = new CacheConfiguration<Key, Val>(DEFAULT_CACHE_NAME)
.setQueryEntities(Collections.singleton(
new QueryEntity()
.setKeyType(Key.class.getName())
.setValueType(Val.class.getName())
.setKeyFields(keyFields)
.setFields(fields)
.setIndexes(indexes)
));
igniteCfg.setCacheConfiguration(ccfg);
igniteCfg.setDataStorageConfiguration(new DataStorageConfiguration()
.setDefaultDataRegionConfiguration(
new DataRegionConfiguration().setPersistenceEnabled(true)
)
);
return igniteCfg;
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
stopAllGrids();
cleanPersistenceDir();
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
cleanPersistenceDir();
indexes = null;
super.afterTest();
}
/**
* @throws Exception In case of failure.
*/
@Test
public void testNoIndexes() throws Exception {
indexes = Collections.emptyList();
startGrid();
grid().cluster().active(true);
populateCache();
checkStat();
checkAll();
checkStat();
}
/**
* @throws Exception In case of failure.
*/
@Test
public void testAllIndexes() throws Exception {
indexes = Arrays.asList(
new QueryIndex("keyStr"),
new QueryIndex("keyLong"),
new QueryIndex("keyPojo"),
new QueryIndex("valStr"),
new QueryIndex("valLong"),
new QueryIndex("valPojo")
);
startGrid();
grid().cluster().active(true);
populateCache();
checkStat();
checkAll();
checkStat();
}
/** Checks that {@link MetricRegistry} with the sorted index IO statistics removed on index drop. */
@Test
public void testMetricRegistryRemovedOnIndexDrop() throws Exception {
indexes = Collections.emptyList();
startGrid();
grid().cluster().active(true);
execute(grid(), "CREATE TABLE t(id int, name varchar, primary key (id))");
execute(grid(), "CREATE INDEX MY_IDX ON t(name)");
MetricRegistry mreg =
grid().context().metric().registry(metricName(SORTED_INDEX.metricGroupName(), "SQL_PUBLIC_T", "MY_IDX"));
assertTrue(mreg.iterator().hasNext());
assertNotNull(mreg.findMetric("name"));
execute(grid(), "DROP INDEX MY_IDX");
mreg = grid().context().metric().registry(metricName(SORTED_INDEX.metricGroupName(), "SQL_PUBLIC_T", "MY_IDX"));
assertFalse(mreg.iterator().hasNext());
}
/** */
private void checkStat() throws Exception {
GridMetricManager mmgr = grid().context().metric();
Set<String> hashIndexes = deriveStatisticNames(grid(), HASH_INDEX);
Assert.assertEquals(PK_HASH_INDEXES, hashIndexes);
Set<String> sortedIndexCaches = deriveStatisticNames(grid(), SORTED_INDEX);
Assert.assertEquals(1, sortedIndexCaches.size());
Set<String> sortedIdxNames = deriveStatisticSubNames(grid(), SORTED_INDEX);
Assert.assertEquals(sortedIndexCaches.toString(), indexes.size() + NUMBER_OF_PK_SORTED_INDEXES,
sortedIdxNames.size());
for (String idxName : sortedIdxNames) {
Long logicalReads = logicalReads(mmgr, SORTED_INDEX, metricName(DEFAULT_CACHE_NAME, idxName));
Assert.assertNotNull(idxName, logicalReads);
Assert.assertTrue(logicalReads > 0);
}
resetAllIoMetrics(grid());
}
/** */
private void checkAll() {
IgniteCache<Key, Val> cache = grid().cache(DEFAULT_CACHE_NAME);
checkRemovePut(cache);
checkSelectAll(cache);
checkSelectStringEqual(cache);
checkSelectLongEqual(cache);
checkSelectStringRange(cache);
checkSelectLongRange(cache);
}
/** */
private void populateCache() {
IgniteCache<Key, Val> cache = grid().cache(DEFAULT_CACHE_NAME);
for (int i = 0; i < 100; i++)
cache.put(key(i), val(i));
}
/**
* @param cache Cache.
*/
private void checkRemovePut(IgniteCache<Key, Val> cache) {
final int INT = 24;
assertEquals(val(INT), cache.get(key(INT)));
cache.remove(key(INT));
assertNull(cache.get(key(INT)));
cache.put(key(INT), val(INT));
assertEquals(val(INT), cache.get(key(INT)));
}
/**
* @param cache Cache.
*/
private void checkSelectAll(IgniteCache<Key, Val> cache) {
List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val")).getAll();
assertEquals(100, data.size());
for (List<?> row : data) {
Key key = (Key)row.get(0);
Val val = (Val)row.get(1);
long i = key.keyLong;
assertEquals(key(i), key);
assertEquals(val(i), val);
}
}
/**
* @param cache Cache.
*/
private void checkSelectStringEqual(IgniteCache<Key, Val> cache) {
final String STR = "foo011";
final long LONG = 11;
List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where keyStr = ?")
.setArgs(STR))
.getAll();
assertEquals(1, data.size());
List<?> row = data.get(0);
assertEquals(key(LONG), row.get(0));
assertEquals(val(LONG), row.get(1));
}
/**
* @param cache Cache.
*/
private void checkSelectLongEqual(IgniteCache<Key, Val> cache) {
final long LONG = 42;
List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where valLong = ?")
.setArgs(LONG))
.getAll();
assertEquals(1, data.size());
List<?> row = data.get(0);
assertEquals(key(LONG), row.get(0));
assertEquals(val(LONG), row.get(1));
}
/**
* @param cache Cache.
*/
private void checkSelectStringRange(IgniteCache<Key, Val> cache) {
final String PREFIX = "foo06";
List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where keyStr like ?")
.setArgs(PREFIX + "%"))
.getAll();
assertEquals(10, data.size());
for (List<?> row : data) {
Key key = (Key)row.get(0);
Val val = (Val)row.get(1);
long i = key.keyLong;
assertEquals(key(i), key);
assertEquals(val(i), val);
assertTrue(key.keyStr.startsWith(PREFIX));
}
}
/**
* @param cache Cache.
*/
private void checkSelectLongRange(IgniteCache<Key, Val> cache) {
final long RANGE_START = 70;
final long RANGE_END = 80;
List<List<?>> data = cache.query(
new SqlFieldsQuery("select _key, _val from Val where valLong >= ? and valLong < ?")
.setArgs(RANGE_START, RANGE_END))
.getAll();
assertEquals(10, data.size());
for (List<?> row : data) {
Key key = (Key)row.get(0);
Val val = (Val)row.get(1);
long i = key.keyLong;
assertEquals(key(i), key);
assertEquals(val(i), val);
assertTrue(i >= RANGE_START && i < RANGE_END);
}
}
/**
* Extract all tracked subNames for given statistics type .
*
* @param statType Type of statistics which tracked names need to extract.
* @return Set of present names for given statType
*/
public Set<String> deriveStatisticSubNames(IgniteEx ignite, IoStatisticsType statType) {
assert statType != null;
return ioStats(ignite, statType).map(m -> {
if (m.name().startsWith(CACHE_GROUP.metricGroupName()))
return m.findMetric("grpId").getAsString();
if (m.name().startsWith(HASH_INDEX.metricGroupName()) ||
m.name().startsWith(SORTED_INDEX.metricGroupName()))
return m.findMetric("indexName").getAsString();
throw new IgniteException("Wrong metric registry " + m.name());
}).collect(Collectors.toSet());
}
/**
* Extract all tracked names for given statistics type.
*
* @param ignite Node instance.
* @param statType Type of statistics which tracked names need to extract.
* @return Set of present names for given statType
*/
public Set<String> deriveStatisticNames(IgniteEx ignite, IoStatisticsType statType) {
assert statType != null;
Stream<ReadOnlyMetricRegistry> grpsStream = ioStats(ignite, statType);
return grpsStream.flatMap(grp -> StreamSupport.stream(grp.spliterator(), false))
.filter(m -> m.name().endsWith("name"))
.map(Metric::getAsString)
.collect(Collectors.toSet());
}
/** @return Stream of MetricGroup for specified {@link statType}. */
private Stream<ReadOnlyMetricRegistry> ioStats(IgniteEx ignite, IoStatisticsType statType) {
GridMetricManager mmgr = ignite.context().metric();
return StreamSupport.stream(mmgr.spliterator(), false)
.filter(grp -> grp.name().startsWith(statType.metricGroupName()));
}
/**
* @param i Index to generate key.
* @return generated key.
*/
private static Key key(long i) {
return new Key(String.format("foo%03d", i), i, new Pojo(i));
}
/**
* @param i Index to generate key.
* @return generated key.
*/
private static Val val(long i) {
return new Val(String.format("bar%03d", i), i, new Pojo(i));
}
/** */
private static class Key {
/** */
private String keyStr;
/** */
private long keyLong;
/** */
private Pojo keyPojo;
/**
* @param str String.
* @param aLong Long.
* @param pojo Pojo.
*/
private Key(String str, long aLong, Pojo pojo) {
keyStr = str;
keyLong = aLong;
keyPojo = pojo;
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Key key = (Key)o;
return keyLong == key.keyLong &&
Objects.equals(keyStr, key.keyStr) &&
Objects.equals(keyPojo, key.keyPojo);
}
/** {@inheritDoc} */
@Override public int hashCode() {
return Objects.hash(keyStr, keyLong, keyPojo);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Key.class, this);
}
}
/** */
private static class Val {
/** */
private String valStr;
/** */
private long valLong;
/** */
private Pojo valPojo;
/**
* @param str String.
* @param aLong Long.
* @param pojo Pojo.
*/
private Val(String str, long aLong, Pojo pojo) {
valStr = str;
valLong = aLong;
valPojo = pojo;
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Val val = (Val)o;
return valLong == val.valLong &&
Objects.equals(valStr, val.valStr) &&
Objects.equals(valPojo, val.valPojo);
}
/** {@inheritDoc} */
@Override public int hashCode() {
return Objects.hash(valStr, valLong, valPojo);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Val.class, this);
}
}
/** */
private static class Pojo {
/** */
private long pojoLong;
/**
* @param pojoLong Long.
*/
private Pojo(long pojoLong) {
this.pojoLong = pojoLong;
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Pojo pojo = (Pojo)o;
return pojoLong == pojo.pojoLong;
}
/** {@inheritDoc} */
@Override public int hashCode() {
return Objects.hash(pojoLong);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Pojo.class, this);
}
}
/**
* Execute query on given node.
*
* @param node Node.
* @param sql Statement.
*/
private List<List<?>> execute(Ignite node, String sql, Object... args) {
SqlFieldsQuery qry = new SqlFieldsQuery(sql)
.setArgs(args)
.setSchema("PUBLIC");
return queryProcessor(node).querySqlFields(qry, true).getAll();
}
}