/* * 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.ArrayList; import java.util.Collection; import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.query.Query; import org.apache.ignite.cache.query.QueryDetailMetrics; import org.apache.ignite.cache.query.ScanQuery; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.cache.query.SqlQuery; import org.apache.ignite.cache.query.TextQuery; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.util.lang.GridAbsPredicate; import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; /** * Tests for cache query details metrics. */ public abstract class CacheAbstractQueryDetailMetricsSelfTest extends GridCommonAbstractTest { /** */ private static final int QRY_DETAIL_METRICS_SIZE = 3; /** Grid count. */ protected int gridCnt; /** Cache mode. */ protected CacheMode cacheMode; /** */ private static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { startGridsMultiThreaded(gridCnt); IgniteCache<Integer, String> cacheA = grid(0).cache("A"); IgniteCache<Integer, String> cacheB = grid(0).cache("B"); for (int i = 0; i < 100; i++) { cacheA.put(i, String.valueOf(i)); cacheB.put(i, String.valueOf(i)); } } /** {@inheritDoc} */ @Override protected void afterTest() throws Exception { stopAllGrids(); } /** * @param cacheName Cache name. * @return Cache configuration. */ private CacheConfiguration<Integer, String> configureCache(String cacheName) { CacheConfiguration<Integer, String> ccfg = defaultCacheConfiguration(); ccfg.setName(cacheName); ccfg.setCacheMode(cacheMode); ccfg.setWriteSynchronizationMode(FULL_SYNC); ccfg.setIndexedTypes(Integer.class, String.class); ccfg.setStatisticsEnabled(true); ccfg.setQueryDetailMetricsSize(QRY_DETAIL_METRICS_SIZE); return ccfg; } /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); TcpDiscoverySpi disco = new TcpDiscoverySpi(); disco.setIpFinder(ipFinder); cfg.setDiscoverySpi(disco); cfg.setCacheConfiguration(configureCache("A"), configureCache("B")); return cfg; } /** * Test metrics for SQL fields queries. * * @throws Exception In case of error. */ public void testSqlFieldsQueryMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from String"); checkQueryMetrics(cache, qry); } /** * Test metrics for SQL fields queries. * * @throws Exception In case of error. */ public void testSqlFieldsQueryNotFullyFetchedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from String"); qry.setPageSize(10); checkQueryNotFullyFetchedMetrics(cache, qry, false); } /** * Test metrics for failed SQL queries. * * @throws Exception In case of error. */ public void testSqlFieldsQueryFailedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from UNKNOWN"); checkQueryFailedMetrics(cache, qry); } /** * Test metrics eviction. * * @throws Exception In case of error. */ public void testQueryMetricsEviction() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); // Execute several DIFFERENT queries with guaranteed DIFFERENT time of execution. cache.query(new SqlFieldsQuery("select * from String")).getAll(); Thread.sleep(100); cache.query(new SqlFieldsQuery("select count(*) from String")).getAll(); Thread.sleep(100); cache.query(new SqlFieldsQuery("select * from String limit 1")).getAll(); Thread.sleep(100); cache.query(new SqlFieldsQuery("select * from String limit 2")).getAll(); Thread.sleep(100); cache.query(new ScanQuery()).getAll(); Thread.sleep(100); cache.query(new SqlQuery("String", "from String")).getAll(); waitingFor(cache, "size", QRY_DETAIL_METRICS_SIZE); for (int i = 0; i < QRY_DETAIL_METRICS_SIZE; i++) checkMetrics(cache, QRY_DETAIL_METRICS_SIZE, i, 1, 1, 0, false); // Check that collected metrics contains correct items: metrics for last 3 queries. Collection<? extends QueryDetailMetrics> metrics = cache.queryDetailMetrics(); String lastMetrics = ""; for (QueryDetailMetrics m : metrics) lastMetrics += m.queryType() + " " + m.query() + ";"; assertTrue(lastMetrics.contains("SQL_FIELDS select * from String limit 2;")); assertTrue(lastMetrics.contains("SCAN A;")); assertTrue(lastMetrics.contains("SQL from String;")); cache = grid(0).context().cache().jcache("B"); cache.query(new SqlFieldsQuery("select * from String")).getAll(); cache.query(new SqlFieldsQuery("select count(*) from String")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 1")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 2")).getAll(); cache.query(new ScanQuery()).getAll(); cache.query(new SqlQuery("String", "from String")).getAll(); waitingFor(cache, "size", QRY_DETAIL_METRICS_SIZE); for (int i = 0; i < QRY_DETAIL_METRICS_SIZE; i++) checkMetrics(cache, QRY_DETAIL_METRICS_SIZE, i, 1, 1, 0, false); if (gridCnt > 1) { cache = grid(1).context().cache().jcache("A"); cache.query(new SqlFieldsQuery("select * from String")).getAll(); cache.query(new SqlFieldsQuery("select count(*) from String")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 1")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 2")).getAll(); cache.query(new ScanQuery()).getAll(); cache.query(new SqlQuery("String", "from String")).getAll(); waitingFor(cache, "size", QRY_DETAIL_METRICS_SIZE); for (int i = 0; i < QRY_DETAIL_METRICS_SIZE; i++) checkMetrics(cache, QRY_DETAIL_METRICS_SIZE, i, 1, 1, 0, false); cache = grid(1).context().cache().jcache("B"); cache.query(new SqlFieldsQuery("select * from String")).getAll(); cache.query(new SqlFieldsQuery("select count(*) from String")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 1")).getAll(); cache.query(new SqlFieldsQuery("select * from String limit 2")).getAll(); cache.query(new ScanQuery()).getAll(); cache.query(new SqlQuery("String", "from String")).getAll(); waitingFor(cache, "size", QRY_DETAIL_METRICS_SIZE); for (int i = 0; i < QRY_DETAIL_METRICS_SIZE; i++) checkMetrics(cache, QRY_DETAIL_METRICS_SIZE, i, 1, 1, 0, false); } } /** */ private static class Worker extends Thread { /** */ private final IgniteCache cache; /** */ private final Query qry; /** */ Worker(IgniteCache cache, Query qry) { this.cache = cache; this.qry = qry; } /** {@inheritDoc} */ @Override public void run() { cache.query(qry).getAll(); } } /** * Test metrics if queries executed from several threads. * * @throws Exception In case of error. */ public void testQueryMetricsMultithreaded() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); Collection<Worker> workers = new ArrayList<>(); int repeat = 10; for (int k = 0; k < repeat; k++) { // Execute as match queries as history size to avoid eviction. for (int i = 1; i <= QRY_DETAIL_METRICS_SIZE; i++) workers.add(new Worker(cache, new SqlFieldsQuery("select * from String limit " + i))); } for (Worker worker : workers) worker.start(); for (Worker worker : workers) worker.join(); for (int i = 0; i < QRY_DETAIL_METRICS_SIZE; i++) checkMetrics(cache, QRY_DETAIL_METRICS_SIZE, i, repeat, repeat, 0, false); } /** * Test metrics for Scan queries. * * @throws Exception In case of error. */ public void testScanQueryMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); ScanQuery<Integer, String> qry = new ScanQuery<>(); checkQueryMetrics(cache, qry); } /** * Test metrics for Scan queries. * * @throws Exception In case of error. */ public void testScanQueryNotFullyFetchedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); ScanQuery<Integer, String> qry = new ScanQuery<>(); qry.setPageSize(10); checkQueryNotFullyFetchedMetrics(cache, qry, true); } /** * Test metrics for failed Scan queries. * * @throws Exception In case of error. */ public void testScanQueryFailedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); ScanQuery<Integer, String> qry = new ScanQuery<>(Integer.MAX_VALUE); checkQueryFailedMetrics(cache, qry); } /** * Test metrics for Scan queries. * * @throws Exception In case of error. */ public void testSqlQueryMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlQuery<Integer, String> qry = new SqlQuery<>("String", "from String"); checkQueryMetrics(cache, qry); } /** * Test metrics for Scan queries. * * @throws Exception In case of error. */ public void testSqlQueryNotFullyFetchedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlQuery<Integer, String> qry = new SqlQuery<>("String", "from String"); qry.setPageSize(10); checkQueryNotFullyFetchedMetrics(cache, qry, true); } /** * Test metrics for failed Scan queries. * * @throws Exception In case of error. */ public void testSqlQueryFailedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlQuery<Integer, String> qry = new SqlQuery<>("Long", "from Long"); checkQueryFailedMetrics(cache, qry); } /** * Test metrics for Sql queries. * * @throws Exception In case of error. */ public void testTextQueryMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); TextQuery qry = new TextQuery<>("String", "1"); checkQueryMetrics(cache, qry); } /** * Test metrics for Sql queries. * * @throws Exception In case of error. */ public void testTextQueryNotFullyFetchedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); TextQuery qry = new TextQuery<>("String", "1"); qry.setPageSize(10); checkQueryNotFullyFetchedMetrics(cache, qry, true); } /** * Test metrics for failed Scan queries. * * @throws Exception In case of error. */ public void testTextQueryFailedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); TextQuery qry = new TextQuery<>("Unknown", "zzz"); checkQueryFailedMetrics(cache, qry); } /** * Test metrics for SQL cross cache queries. * * @throws Exception In case of error. */ public void testSqlFieldsCrossCacheQueryMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from \"B\".String"); checkQueryMetrics(cache, qry); } /** * Test metrics for SQL cross cache queries. * * @throws Exception In case of error. */ public void testSqlFieldsCrossCacheQueryNotFullyFetchedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from \"B\".String"); qry.setPageSize(10); checkQueryNotFullyFetchedMetrics(cache, qry, false); } /** * Test metrics for failed SQL cross cache queries. * * @throws Exception In case of error. */ public void testSqlFieldsCrossCacheQueryFailedMetrics() throws Exception { IgniteCache<Integer, String> cache = grid(0).context().cache().jcache("A"); SqlFieldsQuery qry = new SqlFieldsQuery("select * from \"G\".String"); checkQueryFailedMetrics(cache, qry); } /** * Check metrics. * * @param cache Cache to check metrics. * @param sz Expected size of metrics. * @param idx Index of metrics to check. * @param execs Expected number of executions. * @param completions Expected number of completions. * @param failures Expected number of failures. * @param first {@code true} if metrics checked for first query only. */ private void checkMetrics(IgniteCache<Integer, String> cache, int sz, int idx, int execs, int completions, int failures, boolean first) { Collection<? extends QueryDetailMetrics> metrics = cache.queryDetailMetrics(); assertNotNull(metrics); assertEquals(sz, metrics.size()); QueryDetailMetrics m = new ArrayList<>(metrics).get(idx); info("Metrics: " + m); assertEquals("Executions", execs, m.executions()); assertEquals("Completions", completions, m.completions()); assertEquals("Failures", failures, m.failures()); assertTrue(m.averageTime() >= 0); assertTrue(m.maximumTime() >= 0); assertTrue(m.minimumTime() >= 0); if (first) assertTrue("On first execution minTime == maxTime", m.minimumTime() == m.maximumTime()); } /** * @param cache Cache. * @param qry Query. */ private void checkQueryMetrics(IgniteCache<Integer, String> cache, Query qry) { // Execute query. cache.query(qry).getAll(); checkMetrics(cache, 1, 0, 1, 1, 0, true); // Execute again with the same parameters. cache.query(qry).getAll(); checkMetrics(cache, 1, 0, 2, 2, 0, false); } /** * @param cache Cache. * @param qry Query. * @param waitingForCompletion Waiting for query completion. */ private void checkQueryNotFullyFetchedMetrics(IgniteCache<Integer, String> cache, Query qry, boolean waitingForCompletion) throws IgniteInterruptedCheckedException { // Execute query. cache.query(qry).iterator().next(); if (waitingForCompletion) waitingFor(cache, "completions", 1); checkMetrics(cache, 1, 0, 1, 1, 0, true); // Execute again with the same parameters. cache.query(qry).iterator().next(); if (waitingForCompletion) waitingFor(cache, "completions", 2); checkMetrics(cache, 1, 0, 2, 2, 0, false); } /** * @param cache Cache. * @param qry Query. */ private void checkQueryFailedMetrics(IgniteCache<Integer, String> cache, Query qry) { try { // Execute invalid query. cache.query(qry).getAll(); } catch (Exception ignored) { // No-op. } checkMetrics(cache, 1, 0, 1, 0, 1, true); try { // Execute invalid query again with the same parameters. cache.query(qry).getAll(); } catch (Exception ignored) { // No-op. } checkMetrics(cache, 1, 0, 2, 0, 2, true); } /** * @param cache Cache. * @param cond Condition to check. * @param exp Expected value. */ private static void waitingFor(final IgniteCache<Integer, String> cache, final String cond, final int exp) throws IgniteInterruptedCheckedException { GridTestUtils.waitForCondition(new GridAbsPredicate() { @Override public boolean apply() { Collection<? extends QueryDetailMetrics> metrics = cache.queryDetailMetrics(); switch (cond) { case "size": return metrics.size() == exp; case "completions": int completions = 0; for (QueryDetailMetrics m : metrics) completions += m.completions(); return completions == exp; default: return true; } } }, 5000); } }