You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by na...@apache.org on 2021/03/26 07:42:15 UTC

[ignite] branch master updated: IGNITE-14255: Added the ability to rotate collecting performance statistics (#8840)

This is an automated email from the ASF dual-hosted git repository.

namelchev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 50f7657  IGNITE-14255: Added the ability to rotate collecting performance statistics (#8840)
50f7657 is described below

commit 50f765746b1ac964f53c93f5638e7f83369b43fc
Author: Sergei Ryzhov <s....@gmail.com>
AuthorDate: Fri Mar 26 10:41:53 2021 +0300

    IGNITE-14255: Added the ability to rotate collecting performance statistics (#8840)
---
 .../PerformanceStatisticsCommand.java              |   4 +
 .../PerformanceStatisticsSubCommand.java           |   3 +
 .../util/PerformanceStatisticsCommandTest.java     |  34 ++++++
 .../FilePerformanceStatisticsWriter.java           |  10 +-
 .../PerformanceStatisticsMBeanImpl.java            |   5 +
 .../PerformanceStatisticsProcessor.java            |  52 +++++++++
 .../util/distributed/DistributedProcess.java       |   7 +-
 .../VisorPerformanceStatisticsOperation.java       |   3 +
 .../VisorPerformanceStatisticsTask.java            |   5 +
 .../ignite/mxbean/PerformanceStatisticsMBean.java  |   4 +
 .../AbstractPerformanceStatisticsTest.java         |  27 ++++-
 .../PerformanceStatisticsRotateFileTest.java       | 118 +++++++++++++++++++++
 .../IgniteBasicWithPersistenceTestSuite.java       |   2 +
 ...ridCommandHandlerClusterByClassTest_help.output |   3 +
 ...andHandlerClusterByClassWithSSLTest_help.output |   3 +
 15 files changed, 274 insertions(+), 6 deletions(-)

diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsCommand.java
index 6887f71..87c7005 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsCommand.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsCommand.java
@@ -31,6 +31,7 @@ import org.apache.ignite.mxbean.PerformanceStatisticsMBean;
 
 import static org.apache.ignite.internal.commandline.CommandList.PERFORMANCE_STATISTICS;
 import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.ROTATE;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.START;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.STATUS;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.STOP;
@@ -92,6 +93,9 @@ public class PerformanceStatisticsCommand extends AbstractCommand<Object> {
         Command.usage(log, "Stop collecting performance statistics in the cluster:",
             PERFORMANCE_STATISTICS, STOP.toString());
 
+        Command.usage(log, "Rotate collecting performance statistics in the cluster:",
+            PERFORMANCE_STATISTICS, ROTATE.toString());
+
         Command.usage(log, "Get status of collecting performance statistics in the cluster:",
             PERFORMANCE_STATISTICS, STATUS.toString());
     }
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsSubCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsSubCommand.java
index 3f33200..3dc30ee 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsSubCommand.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/performancestatistics/PerformanceStatisticsSubCommand.java
@@ -32,6 +32,9 @@ public enum PerformanceStatisticsSubCommand {
     /** Sub-command to stop collecting performance statistics in the cluster. */
     STOP("stop", VisorPerformanceStatisticsOperation.STOP),
 
+    /** Sub-command to rotate collecting performance statistics in the cluster. */
+    ROTATE("rotate", VisorPerformanceStatisticsOperation.ROTATE),
+
     /** Sub-command to get status of collecting performance statistics in the cluster. */
     STATUS("status", VisorPerformanceStatisticsOperation.STATUS);
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/PerformanceStatisticsCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/PerformanceStatisticsCommandTest.java
index 04da5de..6669714 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/PerformanceStatisticsCommandTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/PerformanceStatisticsCommandTest.java
@@ -18,17 +18,23 @@
 package org.apache.ignite.util;
 
 import org.apache.ignite.internal.commandline.CommandList;
+import org.apache.ignite.internal.util.typedef.G;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
 import static org.apache.ignite.internal.commandline.CommandList.PERFORMANCE_STATISTICS;
+import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.ROTATE;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.START;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.STATUS;
 import static org.apache.ignite.internal.commandline.performancestatistics.PerformanceStatisticsSubCommand.STOP;
+import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.TIMEOUT;
 import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.cleanPerformanceStatisticsDir;
+import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.statisticsFiles;
 import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.waitForStatisticsEnabled;
 import static org.apache.ignite.internal.visor.performancestatistics.VisorPerformanceStatisticsTask.STATUS_DISABLED;
 import static org.apache.ignite.internal.visor.performancestatistics.VisorPerformanceStatisticsTask.STATUS_ENABLED;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
 
 /** Tests {@link CommandList#PERFORMANCE_STATISTICS} command. */
 public class PerformanceStatisticsCommandTest extends GridCommandHandlerClusterByClassAbstractTest {
@@ -62,6 +68,13 @@ public class PerformanceStatisticsCommandTest extends GridCommandHandlerClusterB
         assertEquals(EXIT_CODE_OK, res);
         assertEquals(STATUS_DISABLED, lastOperationResult);
 
+        res = execute(PERFORMANCE_STATISTICS.text(), ROTATE.toString());
+
+        assertEquals(EXIT_CODE_UNEXPECTED_ERROR, res);
+        assertEquals(null, lastOperationResult);
+
+        assertEquals(0, statisticsFiles().size());
+
         res = execute(PERFORMANCE_STATISTICS.text(), START.toString());
 
         assertEquals(EXIT_CODE_OK, res);
@@ -73,6 +86,27 @@ public class PerformanceStatisticsCommandTest extends GridCommandHandlerClusterB
         assertEquals(EXIT_CODE_OK, res);
         assertEquals(STATUS_ENABLED, lastOperationResult);
 
+        res = execute(PERFORMANCE_STATISTICS.text(), ROTATE.toString());
+
+        assertEquals(EXIT_CODE_OK, res);
+        assertEquals("Rotated.", lastOperationResult);
+
+        assertTrue(waitForCondition(() -> {
+                try {
+                    return statisticsFiles().size() == G.allGrids().size() * 2;
+                }
+                catch (Exception e) {
+                    fail();
+
+                    return false;
+                }
+            }, TIMEOUT));
+
+        res = execute(PERFORMANCE_STATISTICS.text(), STATUS.toString());
+
+        assertEquals(EXIT_CODE_OK, res);
+        assertEquals(STATUS_ENABLED, lastOperationResult);
+
         res = execute(PERFORMANCE_STATISTICS.text(), STOP.toString());
 
         assertEquals(EXIT_CODE_OK, res);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/FilePerformanceStatisticsWriter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/FilePerformanceStatisticsWriter.java
index d3e0472..ec90642 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/FilePerformanceStatisticsWriter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/FilePerformanceStatisticsWriter.java
@@ -102,6 +102,9 @@ public class FilePerformanceStatisticsWriter {
     /** Factory to provide I/O interface. */
     private final FileIOFactory fileIoFactory = new RandomAccessFileIOFactory();
 
+    /** Performance statistics file. */
+    private final File file;
+
     /** Performance statistics file I/O. */
     private final FileIO fileIo;
 
@@ -136,7 +139,7 @@ public class FilePerformanceStatisticsWriter {
     public FilePerformanceStatisticsWriter(GridKernalContext ctx) throws IgniteCheckedException, IOException {
         log = ctx.log(getClass());
 
-        File file = resolveStatisticsFile(ctx);
+        file = resolveStatisticsFile(ctx);
 
         fileIo = fileIoFactory.create(file);
 
@@ -309,6 +312,11 @@ public class FilePerformanceStatisticsWriter {
         });
     }
 
+    /** @return Performance statistics file. */
+    File file() {
+        return file;
+    }
+
     /**
      * @param op Operation type.
      * @param recSize Record size.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsMBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsMBeanImpl.java
index 8b6a236..37a0ee6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsMBeanImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsMBeanImpl.java
@@ -44,6 +44,11 @@ public class PerformanceStatisticsMBeanImpl implements PerformanceStatisticsMBea
     }
 
     /** {@inheritDoc} */
+    @Override public void rotate() throws IgniteCheckedException {
+        ctx.performanceStatistics().rotateCollectStatistics();
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean started() {
         return ctx.performanceStatistics().enabled();
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsProcessor.java
index eec7c1b..ccb5d74 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsProcessor.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.performancestatistics;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.EventListener;
 import java.util.UUID;
@@ -32,6 +33,7 @@ import org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
 import org.apache.ignite.internal.processors.metastorage.DistributedMetastorageLifecycleListener;
 import org.apache.ignite.internal.processors.metastorage.ReadableDistributedMetaStorage;
 import org.apache.ignite.internal.util.GridIntList;
+import org.apache.ignite.internal.util.distributed.DistributedProcess;
 import org.apache.ignite.internal.util.lang.GridPlainRunnable;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -41,6 +43,7 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.IgniteFeatures.allNodesSupports;
 import static org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage.IGNITE_INTERNAL_KEY_PREFIX;
+import static org.apache.ignite.internal.util.distributed.DistributedProcess.DistributedProcessType.PERFORMANCE_STATISTICS_ROTATE;
 
 /**
  * Performance statistics processor.
@@ -66,6 +69,9 @@ public class PerformanceStatisticsProcessor extends GridProcessorAdapter {
     /** Performance statistics state listeners. */
     private final ArrayList<PerformanceStatisticsStateListener> lsnrs = new ArrayList<>();
 
+    /** Rotate performance statistics process. */
+    private final DistributedProcess<Serializable, Serializable> rotateProc;
+
     /** @param ctx Kernal context. */
     public PerformanceStatisticsProcessor(GridKernalContext ctx) {
         super(ctx);
@@ -103,6 +109,14 @@ public class PerformanceStatisticsProcessor extends GridProcessorAdapter {
             if (U.isLocalNodeCoordinator(ctx.discovery()))
                 ctx.cache().cacheDescriptors().values().forEach(desc -> cacheStart(desc.cacheId(), desc.cacheName()));
         });
+
+        rotateProc = new DistributedProcess<>(ctx, PERFORMANCE_STATISTICS_ROTATE,
+            req -> ctx.closure().callLocalSafe(() -> {
+                rotateWriter();
+
+                return null;
+            }),
+            (id, res, err) -> {});
     }
 
     /** Registers state listener. */
@@ -214,6 +228,21 @@ public class PerformanceStatisticsProcessor extends GridProcessorAdapter {
         metastorage.write(PERF_STAT_KEY, false);
     }
 
+    /**
+     * Rotate collecting performance statistics.
+     *
+     * @throws IgniteCheckedException If rotation failed.
+     */
+    public void rotateCollectStatistics() throws IgniteCheckedException {
+        if (ctx.isStopping())
+            throw new NodeStoppingException("Operation has been cancelled (node is stopping)");
+
+        if (!enabled())
+            throw new IgniteCheckedException("Performance statistics collection not started.");
+
+        rotateProc.start(UUID.randomUUID(), null);
+    }
+
     /** @return {@code True} if collecting performance statistics is enabled. */
     public boolean enabled() {
         return writer != null;
@@ -278,6 +307,29 @@ public class PerformanceStatisticsProcessor extends GridProcessorAdapter {
         log.info("Performance statistics writer stopped.");
     }
 
+    /** Rotate performance statistics writer. */
+    private void rotateWriter() throws Exception {
+        FilePerformanceStatisticsWriter oldWriter = null;
+
+        synchronized (mux) {
+            if (writer == null)
+                return;
+
+            FilePerformanceStatisticsWriter newWriter = new FilePerformanceStatisticsWriter(ctx);
+
+            newWriter.start();
+
+            oldWriter = writer;
+
+            writer = newWriter;
+
+            oldWriter.stop();
+        }
+
+        if (log.isInfoEnabled() && oldWriter != null)
+            log.info("Performance statistics writer rotated[writtenFile=" + oldWriter.file() + "].");
+    }
+
     /** Writes statistics through passed writer. */
     private void write(Consumer<FilePerformanceStatisticsWriter> c) {
         FilePerformanceStatisticsWriter writer = this.writer;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/distributed/DistributedProcess.java b/modules/core/src/main/java/org/apache/ignite/internal/util/distributed/DistributedProcess.java
index 7cfa0fc..7d9e6ae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/distributed/DistributedProcess.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/distributed/DistributedProcess.java
@@ -440,6 +440,11 @@ public class DistributedProcess<I extends Serializable, R extends Serializable>
         /**
          * Cache group encyption key change perform phase.
          */
-        CACHE_GROUP_KEY_CHANGE_FINISH
+        CACHE_GROUP_KEY_CHANGE_FINISH,
+
+        /**
+         * Rotate performance statistics.
+         */
+        PERFORMANCE_STATISTICS_ROTATE
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsOperation.java
index ad3f738..8b40f8e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsOperation.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsOperation.java
@@ -27,6 +27,9 @@ public enum VisorPerformanceStatisticsOperation {
     /** Stop collecting performance statistics in the cluster. */
     STOP,
 
+    /** Rotate collecting performance statistics in the cluster. */
+    ROTATE,
+
     /** Get status of collecting performance statistics in the cluster. */
     STATUS;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsTask.java
index b9052df..9c5c6eb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/performancestatistics/VisorPerformanceStatisticsTask.java
@@ -72,6 +72,11 @@ public class VisorPerformanceStatisticsTask extends VisorOneNodeTask<VisorPerfor
 
                         return "Stopped.";
 
+                    case ROTATE:
+                        ignite.context().performanceStatistics().rotateCollectStatistics();
+
+                        return "Rotated.";
+
                     case STATUS:
 
                         return ignite.context().performanceStatistics().enabled() ? STATUS_ENABLED : STATUS_DISABLED;
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
index 88e03e8..2c736d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
@@ -34,6 +34,10 @@ public interface PerformanceStatisticsMBean {
     @MXBeanDescription("Stop collecting performance statistics in the cluster.")
     public void stop() throws IgniteCheckedException;
 
+    /** Rotate performance statistics in the cluster. */
+    @MXBeanDescription("Rotate performance statistics in the cluster.")
+    public void rotate() throws IgniteCheckedException;
+
     /** @return {@code True} if performance statistics collection is started. */
     @MXBeanDescription("True if performance statistics collection is started.")
     public boolean started();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/AbstractPerformanceStatisticsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/AbstractPerformanceStatisticsTest.java
index 4a29989..6867974 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/AbstractPerformanceStatisticsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/AbstractPerformanceStatisticsTest.java
@@ -81,8 +81,17 @@ public abstract class AbstractPerformanceStatisticsTest extends GridCommonAbstra
         waitForStatisticsEnabled(true);
     }
 
-    /** Stops and reads collecting performance statistics. */
-    public static void stopCollectStatisticsAndRead(TestHandler... handlers) throws Exception {
+    /** Rotate file collecting performance statistics. */
+    public static void rotateCollectStatistics() throws Exception {
+        List<Ignite> grids = G.allGrids();
+
+        assertFalse(grids.isEmpty());
+
+        statisticsMBean(grids.get(0).name()).rotate();
+    }
+
+    /** Stops collecting performance statistics. */
+    public static void stopCollectStatistics() throws Exception {
         List<Ignite> grids = G.allGrids();
 
         assertFalse(grids.isEmpty());
@@ -90,10 +99,20 @@ public abstract class AbstractPerformanceStatisticsTest extends GridCommonAbstra
         statisticsMBean(grids.get(0).name()).stop();
 
         waitForStatisticsEnabled(false);
+    }
+
+    /** Stops and reads collecting performance statistics. */
+    public static void stopCollectStatisticsAndRead(TestHandler... handlers) throws Exception {
+        stopCollectStatistics();
 
         File dir = U.resolveWorkDirectory(U.defaultWorkDirectory(), PERF_STAT_DIR, false);
 
-        new FilePerformanceStatisticsReader(handlers).read(singletonList(dir));
+        readFiles(singletonList(dir), handlers);
+    }
+
+    /** Reads collecting performance statistics files. */
+    public static void readFiles(List<File> files, TestHandler... handlers) throws Exception {
+        new FilePerformanceStatisticsReader(handlers).read(files);
     }
 
     /** Wait for statistics started/stopped in the cluster. */
@@ -130,7 +149,7 @@ public abstract class AbstractPerformanceStatisticsTest extends GridCommonAbstra
     }
 
     /** @return Performance statistics files. */
-    protected static List<File> statisticsFiles() throws Exception {
+    public static List<File> statisticsFiles() throws Exception {
         File perfStatDir = U.resolveWorkDirectory(U.defaultWorkDirectory(), PERF_STAT_DIR, false);
 
         return FilePerformanceStatisticsReader.resolveFiles(singletonList(perfStatDir));
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsRotateFileTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsRotateFileTest.java
new file mode 100644
index 0000000..8010441
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/performancestatistics/PerformanceStatisticsRotateFileTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.performancestatistics;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.LogListener;
+import org.junit.Test;
+
+import static org.apache.ignite.testframework.GridTestUtils.assertThrows;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.apache.ignite.testframework.LogListener.matches;
+
+/**
+ * Performance statistics file rotation tests.
+ */
+public class PerformanceStatisticsRotateFileTest extends AbstractPerformanceStatisticsTest {
+    /** Nodes count. */
+    private static final int NODES_CNT = 2;
+
+    /** Listener test logger. */
+    private static ListeningTestLogger listeningLog;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setCacheConfiguration(defaultCacheConfiguration());
+        cfg.setGridLogger(listeningLog);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        listeningLog = new ListeningTestLogger(log);
+
+        startGrids(NODES_CNT - 1);
+
+        startClientGrid(NODES_CNT);
+    }
+
+    /** @throws Exception If failed. */
+    @Test
+    public void testRotateFile() throws Exception {
+        assertThrows(log, AbstractPerformanceStatisticsTest::rotateCollectStatistics, IgniteException.class,
+            "Performance statistics collection not started.");
+
+        startCollectStatistics();
+
+        int cnt = 3;
+
+        List<File> filesBefore = new ArrayList<>();
+
+        for (int i = 0; i < cnt; i++) {
+            G.allGrids().forEach(ignite -> ignite.cache(DEFAULT_CACHE_NAME).get(0));
+
+            LogListener lsnr = matches("Performance statistics writer rotated")
+                .times(NODES_CNT)
+                .build();
+
+            listeningLog.registerListener(lsnr);
+
+            List<File> files = statisticsFiles();
+
+            files.removeAll(filesBefore);
+
+            filesBefore.addAll(files);
+
+            rotateCollectStatistics();
+
+            assertTrue(waitForCondition(lsnr::check, TIMEOUT));
+
+            checkFiles(files, NODES_CNT, NODES_CNT);
+        }
+
+        stopCollectStatistics();
+
+        checkFiles(statisticsFiles(), NODES_CNT * (cnt + 1), NODES_CNT * cnt);
+    }
+
+    /** Checks files and operations count. */
+    private void checkFiles(List<File> files, int expFiles, int expOps) throws Exception {
+        AtomicInteger ops = new AtomicInteger();
+
+        readFiles(files, new TestHandler() {
+            @Override public void cacheOperation(UUID nodeId, OperationType type, int cacheId, long startTime,
+                long duration) {
+                ops.incrementAndGet();
+            }
+        });
+
+        assertEquals(expFiles, files.size());
+        assertEquals(expOps, ops.get());
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicWithPersistenceTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicWithPersistenceTestSuite.java
index fd51913..3041a4c 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicWithPersistenceTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicWithPersistenceTestSuite.java
@@ -48,6 +48,7 @@ import org.apache.ignite.internal.processors.performancestatistics.CacheStartTes
 import org.apache.ignite.internal.processors.performancestatistics.ForwardReadTest;
 import org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsMultipleStartTest;
 import org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsPropertiesTest;
+import org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsRotateFileTest;
 import org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsSelfTest;
 import org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsThinClientTest;
 import org.apache.ignite.internal.processors.performancestatistics.StringCacheTest;
@@ -103,6 +104,7 @@ import org.junit.runners.Suite;
 
     PerformanceStatisticsSelfTest.class,
     PerformanceStatisticsThinClientTest.class,
+    PerformanceStatisticsRotateFileTest.class,
     TopologyChangesTest.class,
     IgniteClusterIdTagTest.class,
     StringCacheTest.class,
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index 75dea27..2881bab 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -263,6 +263,9 @@ If the file name isn't specified the output file name is: '<typeId>.bin'
   Stop collecting performance statistics in the cluster:
     control.(sh|bat) --performance-statistics stop
 
+  Rotate collecting performance statistics in the cluster:
+    control.(sh|bat) --performance-statistics rotate
+
   Get status of collecting performance statistics in the cluster:
     control.(sh|bat) --performance-statistics status
 
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index 75dea27..2881bab 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -263,6 +263,9 @@ If the file name isn't specified the output file name is: '<typeId>.bin'
   Stop collecting performance statistics in the cluster:
     control.(sh|bat) --performance-statistics stop
 
+  Rotate collecting performance statistics in the cluster:
+    control.(sh|bat) --performance-statistics rotate
+
   Get status of collecting performance statistics in the cluster:
     control.(sh|bat) --performance-statistics status