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

NSAmelchev 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 b502bad1a30 IGNITE-28668 Keep snapshot creation metrics on each node 
(#13119)
b502bad1a30 is described below

commit b502bad1a30ca103f3ee085a78a408808447f46b
Author: Vladimir Steshin <[email protected]>
AuthorDate: Fri Jul 3 10:45:16 2026 +0300

    IGNITE-28668 Keep snapshot creation metrics on each node (#13119)
---
 .../apache/ignite/util/GridCommandHandlerTest.java |   9 +-
 .../snapshot/IgniteSnapshotManager.java            |  28 +++--
 .../snapshot/IgniteClusterSnapshotSelfTest.java    | 124 ++++++++++++---------
 .../IncrementalSnapshotWarnAtomicCachesTest.java   |  57 +++++-----
 4 files changed, 121 insertions(+), 97 deletions(-)

diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
index 1baad66b3c5..872fb04cce0 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
@@ -3163,11 +3163,14 @@ public class GridCommandHandlerTest extends 
GridCommandHandlerClusterPerMethodAb
 
         MetricRegistry metrics = 
ig.context().metric().registry(SNAPSHOT_METRICS);
 
-        LongMetric opEndTimeMetric = metrics.findMetric("LastSnapshotEndTime");
-        BooleanSupplier endTimeMetricPredicate = () -> opEndTimeMetric.value() 
> 0;
+        assertTrue(waitForCondition(() -> metrics.findMetric("LastRequestId") 
!= null, getTestTimeout(), 50));
 
         String reqId = metrics.findMetric("LastRequestId").getAsString();
-        assertFalse(F.isEmpty(reqId));
+
+        assert reqId != null;
+
+        LongMetric opEndTimeMetric = metrics.findMetric("LastSnapshotEndTime");
+        BooleanSupplier endTimeMetricPredicate = () -> opEndTimeMetric.value() 
> 0;
 
         // Make sure the operation ID has been shown to the user.
         assertContains(log, testOut.toString(), reqId);
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
index 365886b366d..c6e25c2baa3 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
@@ -773,6 +773,14 @@ public class IgniteSnapshotManager extends 
GridCacheSharedManagerAdapter
                 "Another snapshot operation in progress [req=" + req + ", 
curr=" + curSnpOp + ']'));
         }
 
+        // Keeps the metrics on each server node.
+        if (!cctx.localNode().isClient()) {
+            if (clusterSnpFut == null)
+                clusterSnpFut = new ClusterSnapshotFuture(req.reqId, 
req.snpName, req.incremental() ? req.incrementIndex() : null);
+
+            lastFuture(req.incremental(), clusterSnpFut);
+        }
+
         SnapshotOperation snpOp = new SnapshotOperation(req,
             new SnapshotFileTree(cctx.kernalContext(), req.snapshotName(), 
req.snapshotPath()));
 
@@ -2050,11 +2058,6 @@ public class IgniteSnapshotManager extends 
GridCacheSharedManagerAdapter
                 snpFut0 = new ClusterSnapshotFuture(UUID.randomUUID(), name, 
incIdx);
 
                 clusterSnpFut = snpFut0;
-
-                if (incremental)
-                    lastSeenIncSnpFut = snpFut0;
-                else
-                    lastSeenSnpFut = snpFut0;
             }
 
             Set<String> cacheGrpNames0 = cacheGrpNames == null ? null : new 
HashSet<>(cacheGrpNames);
@@ -2131,17 +2134,18 @@ public class IgniteSnapshotManager extends 
GridCacheSharedManagerAdapter
 
             U.error(log, SNAPSHOT_FAILED_MSG, e);
 
-            ClusterSnapshotFuture errSnpFut = new ClusterSnapshotFuture(name, 
e);
-
-            if (incremental)
-                lastSeenIncSnpFut = errSnpFut;
-            else
-                lastSeenSnpFut = errSnpFut;
-
             return new IgniteFinishedFutureImpl<>(e);
         }
     }
 
+    /** Sets last seen snapshot future. */
+    private void lastFuture(boolean incremental, ClusterSnapshotFuture 
futToSet) {
+        if (incremental)
+            lastSeenIncSnpFut = futToSet;
+        else
+            lastSeenSnpFut = futToSet;
+    }
+
     /** Writes a warning message if an incremental snapshot contains atomic 
caches. */
     void warnAtomicCachesInIncrementalSnapshot(String snpName, int incIdx, 
Collection<String> cacheGrps) {
         List<String> warnCaches = new ArrayList<>();
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotSelfTest.java
index ee23e278a7d..570761dac22 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotSelfTest.java
@@ -849,81 +849,99 @@ public class IgniteClusterSnapshotSelfTest extends 
AbstractSnapshotSelfTest {
     /** @throws Exception If fails. */
     @Test
     public void testClusterSnapshotMetrics() throws Exception {
-        String newSnapshotName = SNAPSHOT_NAME + "_new";
         CountDownLatch deltaApply = new CountDownLatch(1);
         CountDownLatch deltaBlock = new CountDownLatch(1);
         IgniteEx ignite = startGridsWithCache(2, dfltCacheCfg, 
CACHE_KEYS_RANGE);
 
-        MetricRegistry mreg0 = 
ignite.context().metric().registry(SNAPSHOT_METRICS);
-
-        LongMetric startTime = mreg0.findMetric("LastSnapshotStartTime");
-        LongMetric endTime = mreg0.findMetric("LastSnapshotEndTime");
-        ObjectGauge<String> snpName = mreg0.findMetric("LastSnapshotName");
-        ObjectGauge<String> errMsg = 
mreg0.findMetric("LastSnapshotErrorMessage");
-        ObjectGauge<List<String>> snpList = 
mreg0.findMetric("LocalSnapshotNames");
+        startClientGrid();
 
         // Snapshot process will be blocked when delta partition files 
processing starts.
         snp(ignite).localSnapshotSenderFactory(
             blockingLocalSnapshotSender(ignite, deltaApply, deltaBlock));
 
-        assertEquals("Snapshot start time must be undefined prior to snapshot 
operation started.",
-            0, startTime.value());
-        assertEquals("Snapshot end time must be undefined to snapshot 
operation started.",
-            0, endTime.value());
-        assertTrue("Snapshot name must not exist prior to snapshot operation 
started.", snpName.value().isEmpty());
-        assertTrue("Snapshot error message must null prior to snapshot 
operation started.", errMsg.value().isEmpty());
-        assertTrue("Snapshots on local node must not exist", 
snpList.value().isEmpty());
+        for (Ignite g : G.allGrids()) {
+            MetricRegistry mreg = 
((IgniteEx)g).context().metric().registry(SNAPSHOT_METRICS);
+
+            LongMetric startTime = mreg.findMetric("LastSnapshotStartTime");
+            LongMetric endTime = mreg.findMetric("LastSnapshotEndTime");
+            ObjectGauge<String> snpName = mreg.findMetric("LastSnapshotName");
+            ObjectGauge<String> errMsg = 
mreg.findMetric("LastSnapshotErrorMessage");
+            ObjectGauge<List<String>> snpList = 
mreg.findMetric("LocalSnapshotNames");
+
+            if (g.cluster().localNode().isClient()) {
+                assertNull("Snapshot start time must not be created on a 
client node.", startTime);
+                assertNull("Snapshot end time must not be created on a client 
node.", endTime);
+                assertNull("Snapshot name must not be created on a client 
node.", snpName);
+                assertNull("Snapshot error message must not be created on a 
client node.", errMsg);
+                assertNull("Snapshot local names must not be created on a 
client node.", snpList);
+
+                continue;
+            }
+
+            assertEquals("Snapshot start time must be undefined prior to 
snapshot operation started.",
+                0, startTime.value());
+            assertEquals("Snapshot end time must be undefined to snapshot 
operation started.",
+                0, endTime.value());
+            assertTrue("Snapshot name must not exist prior to snapshot 
operation started.", snpName.value().isEmpty());
+            assertTrue("Snapshot error message must null prior to snapshot 
operation started.", errMsg.value().isEmpty());
+            assertTrue("Snapshots on local node must not exist", 
snpList.value().isEmpty());
+        }
 
         long cutoffStartTime = U.currentTimeMillis();
 
-        IgniteFuture<Void> fut0 = snp(ignite).createSnapshot(SNAPSHOT_NAME, 
null, false, onlyPrimary);
+        IgniteFuture<Void> fut = snp(ignite).createSnapshot(SNAPSHOT_NAME, 
null, false, onlyPrimary);
+
+        for (Ignite g : G.allGrids()) {
+            MetricRegistry mreg = 
((IgniteEx)g).context().metric().registry(SNAPSHOT_METRICS);
 
-        U.await(deltaApply);
+            LongMetric startTime = mreg.findMetric("LastSnapshotStartTime");
+            LongMetric endTime = mreg.findMetric("LastSnapshotEndTime");
+            ObjectGauge<String> snpName = mreg.findMetric("LastSnapshotName");
+            ObjectGauge<String> errMsg = 
mreg.findMetric("LastSnapshotErrorMessage");
 
-        assertTrue("Snapshot start time must be set prior to snapshot 
operation started " +
-            "[startTime=" + startTime.value() + ", cutoffTime=" + 
cutoffStartTime + ']',
-            startTime.value() >= cutoffStartTime);
-        assertEquals("Snapshot end time must be zero prior to snapshot 
operation started.",
-            0, endTime.value());
-        assertEquals("Snapshot name must be set prior to snapshot operation 
started.",
-            SNAPSHOT_NAME, snpName.value());
-        assertTrue("Snapshot error message must null prior to snapshot 
operation started.",
-            errMsg.value().isEmpty());
+            if (g.cluster().localNode().isClient()) {
+                assertNull("Snapshot start time must not be created on a 
client node.", startTime);
+                assertNull("Snapshot end time must not be created on a client 
node.", endTime);
+                assertNull("Snapshot name must not be created on a client 
node.", snpName);
+                assertNull("Snapshot error message must not be created on a 
client node.", errMsg);
 
-        IgniteFuture<Void> fut1 = snp(grid(1)).createSnapshot(newSnapshotName, 
null, false, onlyPrimary);
+                continue;
+            }
 
-        assertThrowsWithCause((Callable<Object>)fut1::get, 
IgniteException.class);
+            assertTrue("Snapshot start time must be set prior to snapshot 
operation started " +
+                    "[startTime=" + startTime.value() + ", cutoffTime=" + 
cutoffStartTime + ']',
+                waitForCondition(() -> startTime.value() >= cutoffStartTime, 
getTestTimeout(), 30));
 
-        MetricRegistry mreg1 = 
grid(1).context().metric().registry(SNAPSHOT_METRICS);
+            assertTrue("Snapshot end time must be zero prior to snapshot 
operation started.",
+                waitForCondition(() -> 0 == endTime.value(), 30));
 
-        LongMetric startTime1 = mreg1.findMetric("LastSnapshotStartTime");
-        LongMetric endTime1 = mreg1.findMetric("LastSnapshotEndTime");
-        ObjectGauge<String> snpName1 = mreg1.findMetric("LastSnapshotName");
-        ObjectGauge<String> errMsg1 = 
mreg1.findMetric("LastSnapshotErrorMessage");
+            assertEquals("Snapshot name must be set prior to snapshot 
operation started.",
+                SNAPSHOT_NAME, snpName.value());
 
-        assertTrue("Snapshot start time must be greater than zero for finished 
snapshot.",
-            startTime1.value() > 0);
-        assertEquals("Snapshot end time must zero for failed on start 
snapshots.",
-            0, endTime1.value());
-        assertEquals("Snapshot name must be set when snapshot operation 
already finished.",
-            newSnapshotName, snpName1.value());
-        assertNotNull("Concurrent snapshot operation must failed.",
-            errMsg1.value());
+            assertTrue("Snapshot error message must null prior to snapshot 
operation started.",
+                errMsg.value().isEmpty());
+        }
 
         deltaBlock.countDown();
 
-        fut0.get();
-
-        assertTrue("Snapshot start time must be greater than zero for finished 
snapshot.",
-            startTime.value() > 0);
-        assertTrue("Snapshot end time must be greater than zero for finished 
snapshot.",
-            endTime.value() > 0);
-        assertEquals("Snapshot name must be set when snapshot operation 
already finished.",
-            SNAPSHOT_NAME, snpName.value());
-        assertTrue("Concurrent snapshot operation must finished successfully.",
-            errMsg.value().isEmpty());
-        assertEquals("Only the first snapshot must be created and stored on 
disk.",
-            Collections.singletonList(SNAPSHOT_NAME), snpList.value());
+        fut.get();
+
+        for (Ignite g : G.allGrids()) {
+            MetricRegistry mreg = 
((IgniteEx)g).context().metric().registry(SNAPSHOT_METRICS);
+
+            LongMetric startTime = mreg.findMetric("LastSnapshotStartTime");
+            LongMetric endTime = mreg.findMetric("LastSnapshotEndTime");
+
+            if (g.cluster().localNode().isClient()) {
+                assertNull("Snapshot start time must not be created on a 
client node.", startTime);
+                assertNull("Snapshot end time must not be created on a client 
node.", endTime);
+
+                continue;
+            }
+
+            assertTrue(waitForCondition(() -> endTime.value() != 0L && 
startTime.value() != 0 && endTime.value() > startTime.value(),
+                getTestTimeout()));
+        }
     }
 
     /** @throws Exception If fails. */
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/incremental/IncrementalSnapshotWarnAtomicCachesTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/incremental/IncrementalSnapshotWarnAtomicCachesTest.java
index 1806cee36a7..68911b39dbc 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/incremental/IncrementalSnapshotWarnAtomicCachesTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/incremental/IncrementalSnapshotWarnAtomicCachesTest.java
@@ -19,6 +19,7 @@ package 
org.apache.ignite.internal.processors.cache.persistence.snapshot.increme
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -112,8 +113,8 @@ public class IncrementalSnapshotWarnAtomicCachesTest 
extends GridCommonAbstractT
 
     /** */
     public void 
checkCachesSnapshotCreationAndRestore(CacheConfiguration<Integer, Integer>... 
ccfgs) throws Exception {
-        List<Integer> allWarnCaches = new ArrayList<>();
-        Map<String, List<Integer>> warnCachesByGrps = new HashMap<>();
+        List<String> allWarnCaches = new ArrayList<>();
+        Map<String, List<String>> warnCachesByGrps = new HashMap<>();
 
         for (CacheConfiguration<?, ?> ccfg: ccfgs) {
             if (ccfg.getAtomicityMode() == CacheAtomicityMode.ATOMIC && 
ccfg.getBackups() > 0) {
@@ -122,37 +123,25 @@ public class IncrementalSnapshotWarnAtomicCachesTest 
extends GridCommonAbstractT
                 warnCachesByGrps.compute(grpName, (grp, caches) -> {
                     caches = caches == null ? new ArrayList<>() : caches;
 
-                    int cacheNum = 
Integer.parseInt(ccfg.getName().replace("cache", ""));
+                    caches.add(ccfg.getName());
 
-                    caches.add(cacheNum);
-
-                    allWarnCaches.add(cacheNum);
+                    allWarnCaches.add(ccfg.getName());
 
                     return caches;
                 });
             }
         }
 
-        checkWarnMessageOnCreateSnapshot(cachesPattern(allWarnCaches), ccfgs);
-        checkWarnMessageOnRestoreSnapshot(cachesPattern(allWarnCaches), null);
+        checkWarnMessageOnCreateSnapshot(allWarnCaches, ccfgs);
+        checkWarnMessageOnRestoreSnapshot(allWarnCaches, null);
 
         for (String grp: warnCachesByGrps.keySet())
-            
checkWarnMessageOnRestoreSnapshot(cachesPattern(warnCachesByGrps.get(grp)), 
F.asList(grp));
-    }
-
-    /** Transforms cache numbers to cache pattern. For example, [0, 1] -> 
cache[0,1], cache[0,1]. */
-    private @Nullable String cachesPattern(List<Integer> cacheNums) {
-        if (cacheNums.isEmpty())
-            return null;
-
-        return cacheNums.stream()
-            .map(c -> "cache" + cacheNums)
-            .collect(Collectors.joining(", "));
+            checkWarnMessageOnRestoreSnapshot(warnCachesByGrps.get(grp), 
F.asList(grp));
     }
 
     /** */
     private void checkWarnMessageOnCreateSnapshot(
-        @Nullable String warnAtomicCaches,
+        Collection<String> warnAtomicCaches,
         CacheConfiguration<Integer, Integer>... ccfgs
     ) throws Exception {
         this.ccfgs = ccfgs;
@@ -172,25 +161,25 @@ public class IncrementalSnapshotWarnAtomicCachesTest 
extends GridCommonAbstractT
 
         g.snapshot().createSnapshot(SNP).get(getTestTimeout());
 
-        assertTrue(warnAtomicCaches, lsnr.check());
+        assertTrue(lsnr.check());
 
         for (CacheConfiguration<Integer, Integer> c: ccfgs) {
             for (int i = 1_000; i < 2_000; i++)
                 g.cache(c.getName()).put(i, i);
         }
 
-        lsnr = warnLogListener(warnAtomicCaches, warnAtomicCaches == null ? 0 
: 1);
+        lsnr = warnLogListener(warnAtomicCaches, warnAtomicCaches.isEmpty() ? 
0 : 3);
 
         lsnLogger.registerListener(lsnr);
 
         g.snapshot().createIncrementalSnapshot(SNP).get(getTestTimeout());
 
-        assertTrue(warnAtomicCaches, lsnr.check());
+        assertTrue(lsnr.check(getTestTimeout()));
     }
 
     /** */
     private void checkWarnMessageOnRestoreSnapshot(
-        @Nullable String warnAtomicCaches,
+        Collection<String> warnAtomicCaches,
         @Nullable Collection<String> restoreCacheGrps
     ) throws Exception {
         stopAllGrids();
@@ -211,26 +200,36 @@ public class IncrementalSnapshotWarnAtomicCachesTest 
extends GridCommonAbstractT
 
         g.snapshot().restoreSnapshot(SNP, 
restoreCacheGrps).get(getTestTimeout());
 
-        assertTrue(warnAtomicCaches + " " + restoreCacheGrps, lsnr.check());
+        assertTrue(lsnr.check());
 
         g.destroyCaches(g.cacheNames());
 
         awaitPartitionMapExchange();
 
-        lsnr = warnLogListener(warnAtomicCaches, warnAtomicCaches == null ? 0 
: 1);
+        lsnr = warnLogListener(warnAtomicCaches, warnAtomicCaches.isEmpty() ? 
0 : 1);
 
         lsnLogger.registerListener(lsnr);
 
         g.snapshot().restoreSnapshot(SNP, restoreCacheGrps, 
1).get(getTestTimeout());
 
-        assertTrue(warnAtomicCaches + " " + restoreCacheGrps, lsnr.check());
+        assertTrue(lsnr.check());
     }
 
     /** */
-    private LogListener warnLogListener(@Nullable String atomicCaches, int 
times) {
+    private LogListener warnLogListener(Collection<String> atomicCaches, int 
times) {
+        String cachesStr = null;
+
+        if (atomicCaches.size() == 1)
+            cachesStr = F.first(atomicCaches);
+        else if (atomicCaches.size() > 1) {
+            cachesStr = "((" + String.join(", ", atomicCaches) + ')';
+
+            cachesStr += "|(" + 
atomicCaches.stream().sorted(Comparator.reverseOrder()).collect(Collectors.joining(",
 ")) + "))";
+        }
+
         Pattern p = Pattern.compile(
             "Incremental snapshot \\[snpName=" + SNP + ", incIdx=1] contains 
ATOMIC caches with backups:"
-            + (atomicCaches == null ? "" : " \\[" + atomicCaches) + ']');
+            + (cachesStr == null ? "" : " \\[" + cachesStr) + "]");
 
         return LogListener.matches(p).times(times).build();
     }

Reply via email to