| /* |
| * 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.performancestatistics; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| import java.util.function.Consumer; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType; |
| import org.apache.ignite.internal.processors.performancestatistics.FilePerformanceStatisticsWriter; |
| import org.apache.ignite.internal.processors.performancestatistics.OperationType; |
| import org.apache.ignite.internal.util.GridIntList; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.internal.util.typedef.internal.CU; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.lang.IgniteUuid; |
| import org.apache.ignite.logger.java.JavaLogger; |
| import org.apache.ignite.testframework.junits.GridTestKernalContext; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import static java.util.stream.Collectors.joining; |
| import static org.apache.ignite.internal.processors.performancestatistics.FilePerformanceStatisticsWriter.PERF_STAT_DIR; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.CACHE_GET; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.CACHE_PUT; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.CACHE_START; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.JOB; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.QUERY; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.QUERY_READS; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.TASK; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.TX_COMMIT; |
| import static org.apache.ignite.internal.processors.performancestatistics.OperationType.TX_ROLLBACK; |
| import static org.apache.ignite.testframework.GridTestUtils.waitForCondition; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Tests the performance statistics printer. |
| */ |
| public class PerformanceStatisticsPrinterTest { |
| /** Test node ID. */ |
| private static final UUID NODE_ID = UUID.randomUUID(); |
| |
| /** */ |
| @Before |
| public void beforeTest() throws Exception { |
| U.delete(new File(U.defaultWorkDirectory())); |
| } |
| |
| /** */ |
| @After |
| public void afterTest() throws Exception { |
| U.delete(new File(U.defaultWorkDirectory())); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testOperationsFilter() throws Exception { |
| createStatistics(writer -> { |
| writer.cacheStart(0, "cache"); |
| writer.cacheOperation(CACHE_GET, 0, 0, 0); |
| writer.transaction(GridIntList.asList(0), 0, 0, true); |
| writer.transaction(GridIntList.asList(0), 0, 0, false); |
| writer.query(GridCacheQueryType.SQL_FIELDS, "query", 0, 0, 0, true); |
| writer.queryReads(GridCacheQueryType.SQL_FIELDS, NODE_ID, 0, 0, 0); |
| writer.task(new IgniteUuid(NODE_ID, 0), "task", 0, 0, 0); |
| writer.job(new IgniteUuid(NODE_ID, 0), 0, 0, 0, true); |
| }); |
| |
| List<OperationType> expOps = F.asList(CACHE_START, CACHE_GET, TX_COMMIT, TX_ROLLBACK, QUERY, QUERY_READS, |
| TASK, JOB); |
| |
| checkOperationFilter(null, expOps); |
| checkOperationFilter(F.asList(CACHE_START), F.asList(CACHE_START)); |
| checkOperationFilter(F.asList(TASK, JOB), F.asList(TASK, JOB)); |
| checkOperationFilter(F.asList(CACHE_PUT), Collections.emptyList()); |
| } |
| |
| /** */ |
| private void checkOperationFilter(List<OperationType> opsArg, List<OperationType> expOps) throws Exception { |
| List<String> args = new LinkedList<>(); |
| |
| if (opsArg != null) { |
| args.add("--ops"); |
| |
| args.add(opsArg.stream().map(Enum::toString).collect(joining(","))); |
| } |
| |
| List<OperationType> ops = new LinkedList<>(expOps); |
| |
| readStatistics(args, json -> { |
| OperationType op = OperationType.valueOf(json.get("op").asText()); |
| |
| assertTrue("Unexpected operation: " + op, ops.remove(op)); |
| |
| UUID nodeId = UUID.fromString(json.get("nodeId").asText()); |
| |
| assertEquals(NODE_ID, nodeId); |
| }); |
| |
| assertTrue("Expected operations:" + ops, ops.isEmpty()); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testStartTimeFilter() throws Exception { |
| long startTime1 = 10; |
| long startTime2 = 20; |
| |
| createStatistics(writer -> { |
| for (long startTime : new long[] {startTime1, startTime2}) { |
| writer.cacheOperation(CACHE_GET, 0, startTime, 0); |
| writer.transaction(GridIntList.asList(0), startTime, 0, true); |
| writer.transaction(GridIntList.asList(0), startTime, 0, false); |
| writer.query(GridCacheQueryType.SQL_FIELDS, "query", 0, startTime, 0, true); |
| writer.task(new IgniteUuid(NODE_ID, 0), "", startTime, 0, 0); |
| writer.job(new IgniteUuid(NODE_ID, 0), 0, startTime, 0, true); |
| } |
| }); |
| |
| checkStartTimeFilter(null, null, F.asList(startTime1, startTime2)); |
| checkStartTimeFilter(null, startTime1, F.asList(startTime1)); |
| checkStartTimeFilter(startTime2, null, F.asList(startTime2)); |
| checkStartTimeFilter(startTime1, startTime2, F.asList(startTime1, startTime2)); |
| checkStartTimeFilter(startTime2 + 1, null, Collections.emptyList()); |
| checkStartTimeFilter(null, startTime1 - 1, Collections.emptyList()); |
| } |
| |
| /** */ |
| private void checkStartTimeFilter(Long fromArg, Long toArg, List<Long> expTimes) throws Exception { |
| List<OperationType> opsWithStartTime = F.asList(CACHE_GET, TX_COMMIT, TX_ROLLBACK, QUERY, TASK, JOB); |
| |
| List<String> args = new LinkedList<>(); |
| |
| if (fromArg != null) { |
| args.add("--from"); |
| |
| args.add(fromArg.toString()); |
| } |
| |
| if (toArg != null) { |
| args.add("--to"); |
| |
| args.add(toArg.toString()); |
| } |
| |
| Map<Long, List<OperationType>> opsByTime = new HashMap<>(); |
| |
| for (Long time : expTimes) |
| opsByTime.put(time, new LinkedList<>(opsWithStartTime)); |
| |
| readStatistics(args, json -> { |
| OperationType op = OperationType.valueOf(json.get("op").asText()); |
| |
| if (opsWithStartTime.contains(op)) { |
| long startTime = json.get("startTime").asLong(); |
| |
| assertTrue("Unexpected startTime: " + startTime, opsByTime.containsKey(startTime)); |
| assertTrue("Unexpected operation: " + op, opsByTime.get(startTime).remove(op)); |
| } |
| }); |
| |
| assertTrue("Expected operations: " + opsByTime, opsByTime.values().stream().allMatch(List::isEmpty)); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testCachesFilter() throws Exception { |
| String cache1 = "cache1"; |
| String cache2 = "cache2"; |
| |
| createStatistics(writer -> { |
| for (String cache : new String[] {cache1, cache2}) { |
| writer.cacheStart(CU.cacheId(cache), cache); |
| writer.cacheOperation(CACHE_GET, CU.cacheId(cache), 0, 0); |
| writer.transaction(GridIntList.asList(CU.cacheId(cache)), 0, 0, true); |
| writer.transaction(GridIntList.asList(CU.cacheId(cache)), 0, 0, false); |
| } |
| }); |
| |
| checkCachesFilter(null, new String[] {cache1, cache2}); |
| checkCachesFilter(new String[] {cache1}, new String[] {cache1}); |
| checkCachesFilter(new String[] {cache2}, new String[] {cache2}); |
| checkCachesFilter(new String[] {cache1, cache2}, new String[] {cache1, cache2}); |
| checkCachesFilter(new String[] {"unknown_cache"}, new String[0]); |
| } |
| |
| /** */ |
| private void checkCachesFilter(String[] cachesArg, String[] expCaches) throws Exception { |
| List<OperationType> cacheIdOps = F.asList(CACHE_START, CACHE_GET, TX_COMMIT, TX_ROLLBACK); |
| |
| List<String> args = new LinkedList<>(); |
| |
| if (cachesArg != null) { |
| args.add("--caches"); |
| |
| args.add(String.join(",", cachesArg)); |
| } |
| |
| Map<Integer, List<OperationType>> opsById = new HashMap<>(); |
| |
| for (String cache : expCaches) |
| opsById.put(CU.cacheId(cache), new LinkedList<>(cacheIdOps)); |
| |
| readStatistics(args, json -> { |
| OperationType op = OperationType.valueOf(json.get("op").asText()); |
| |
| Integer id = null; |
| |
| if (OperationType.cacheOperation(op) || op == CACHE_START) |
| id = json.get("cacheId").asInt(); |
| else if (OperationType.transactionOperation(op)) { |
| assertTrue(json.get("cacheIds").isArray()); |
| assertEquals(1, json.get("cacheIds").size()); |
| |
| id = json.get("cacheIds").get(0).asInt(); |
| } |
| else |
| fail("Unexpected operation: " + op); |
| |
| assertTrue("Unexpected cache id: " + id, opsById.containsKey(id)); |
| assertTrue("Unexpected operation: " + op, opsById.get(id).remove(op)); |
| }); |
| |
| assertTrue("Expected operations: " + opsById, opsById.values().stream().allMatch(List::isEmpty)); |
| } |
| |
| /** Writes statistics through passed writer. */ |
| private void createStatistics(Consumer<FilePerformanceStatisticsWriter> c) throws Exception { |
| FilePerformanceStatisticsWriter writer = new FilePerformanceStatisticsWriter(new TestKernalContext(NODE_ID)); |
| |
| writer.start(); |
| |
| waitForCondition(() -> U.field((Object)U.field(writer, "fileWriter"), "runner") != null, 30_000); |
| |
| c.accept(writer); |
| |
| writer.stop(); |
| } |
| |
| /** |
| * @param args Additional program arguments. |
| * @param c Consumer to handle operations. |
| * @throws Exception If failed. |
| */ |
| private void readStatistics(List<String> args, Consumer<JsonNode> c) throws Exception { |
| File perfStatDir = new File(U.defaultWorkDirectory(), PERF_STAT_DIR); |
| |
| assertTrue(perfStatDir.exists()); |
| |
| File out = new File(U.defaultWorkDirectory(), "report.txt"); |
| |
| U.delete(out); |
| |
| List<String> pArgs = new LinkedList<>(); |
| |
| pArgs.add(perfStatDir.getAbsolutePath()); |
| pArgs.add("--out"); |
| pArgs.add(out.getAbsolutePath()); |
| |
| pArgs.addAll(args); |
| |
| PerformanceStatisticsPrinter.main(pArgs.toArray(new String[0])); |
| |
| assertTrue(out.exists()); |
| |
| ObjectMapper mapper = new ObjectMapper(); |
| |
| try (BufferedReader reader = new BufferedReader(new FileReader(out))) { |
| String line; |
| |
| while ((line = reader.readLine()) != null) { |
| JsonNode json = mapper.readTree(line); |
| |
| assertTrue(json.isObject()); |
| |
| UUID nodeId = UUID.fromString(json.get("nodeId").asText()); |
| |
| assertEquals(NODE_ID, nodeId); |
| |
| c.accept(json); |
| } |
| } |
| } |
| |
| /** Test kernal context. */ |
| private static class TestKernalContext extends GridTestKernalContext { |
| /** Node ID. */ |
| private final UUID nodeId; |
| |
| /** @param nodeId Node ID. */ |
| public TestKernalContext(UUID nodeId) { |
| super(new JavaLogger()); |
| |
| this.nodeId = nodeId; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public UUID localNodeId() { |
| return nodeId; |
| } |
| } |
| } |