diff --git a/docs/_docs/monitoring-metrics/new-metrics.adoc b/docs/_docs/monitoring-metrics/new-metrics.adoc index 19a2ca3121ef8..0850d9ae33116 100644 --- a/docs/_docs/monitoring-metrics/new-metrics.adoc +++ b/docs/_docs/monitoring-metrics/new-metrics.adoc @@ -394,6 +394,7 @@ Register name: `io.dataregion.{data_region_name}` |DirtyPages | long| Number of pages in memory not yet synchronized with persistent storage. |EmptyDataPages| long| Calculates empty data pages count for region. It counts only totally free pages that can be reused (e. g. pages that are contained in reuse bucket of free list). |EvictionRate| hitrate| Eviction rate (pages per second). +|EvictionsStarted | boolean | True if page eviction was triggered due to data region memory pressure. |LargeEntriesPagesCount| long| Count of pages that fully ocupied by large entries that go beyond page size |OffHeapSize| long| Offheap size in bytes. |OffheapUsedSize| long| Offheap used size in bytes. diff --git a/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java b/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java index a1539e1e8b77e..28d7c4004bfb8 100644 --- a/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java +++ b/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java @@ -241,4 +241,11 @@ public interface DataRegionMetrics { * @return Total used offheap size in bytes. */ public long getOffheapUsedSize(); + + /** + * Data region eviction-started flag. + * + * @return {@code true} if page eviction was triggered due to data region memory pressure. + */ + public boolean isEvictionsStarted(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java index 549ffc399f731..1e962331f1156 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java @@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMetricsImpl; import org.apache.ignite.internal.processors.metric.MetricRegistryImpl; import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric; +import org.apache.ignite.internal.processors.metric.impl.BooleanMetricImpl; import org.apache.ignite.internal.processors.metric.impl.HitRateMetric; import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric; import org.apache.ignite.internal.processors.metric.impl.LongAdderWithDelegateMetric; @@ -178,6 +179,9 @@ private static LongAdderMetricDelegate delegate(LongAdderMetric delegate) { @Nullable private final PeriodicHistogramMetricImpl pageTsHistogram; + /** Metric indicating whether page eviction has started. */ + private final BooleanMetricImpl evictionsStarted; + /** * Same as {@link #DataRegionMetricsImpl(DataRegionConfiguration, GridKernalContext, DataRegionMetricsProvider)} * but uses a no-op implementation for the {@link DataRegionMetricsProvider}. @@ -281,6 +285,9 @@ public DataRegionMetricsImpl( mreg.longMetric("MaxSize", "Maximum memory region size in bytes defined by its data region.") .value(dataRegionCfg.getMaxSize()); + evictionsStarted = mreg.booleanMetric("EvictionsStarted", + "True if page eviction was triggered due to data region memory pressure."); + if (persistenceEnabled) { // Reserve 1 sec, page ts can be slightly lower than currentTimeMillis, due to applied to ts mask. This // reservation mainly affects only tests (we can check buckets more predictevely). @@ -875,4 +882,15 @@ public Collection pagesTimestampHistogramView() { return list; } + + /** {@inheritDoc} */ + @Override public boolean isEvictionsStarted() { + return evictionsStarted.value(); + } + + /** */ + public void onPageEvictionsStarted() { + if (!evictionsStarted.value()) + evictionsStarted.value(true); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java index 316f98fe8753b..b56b9a9baf844 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java @@ -92,6 +92,9 @@ public class DataRegionMetricsSnapshot implements DataRegionMetrics { /** */ private final long offHeapUsedSize; + /** */ + private final boolean evictionsStarted; + /** * @param metrics Metrics instance to take a copy. */ @@ -119,6 +122,7 @@ public DataRegionMetricsSnapshot(DataRegionMetrics metrics) { replacedPage = metrics.getPagesReplaced(); offHeapSize = metrics.getOffHeapSize(); offHeapUsedSize = metrics.getOffheapUsedSize(); + evictionsStarted = metrics.isEvictionsStarted(); } /** {@inheritDoc} */ @@ -235,4 +239,9 @@ public DataRegionMetricsSnapshot(DataRegionMetrics metrics) { @Override public long getOffheapUsedSize() { return offHeapUsedSize; } + + /** {@inheritDoc} */ + @Override public boolean isEvictionsStarted() { + return evictionsStarted; + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java index d8b14ce718a1f..a20a38e7093aa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java @@ -1246,6 +1246,8 @@ public void ensureFreeSpace(DataRegion memPlc) throws IgniteCheckedException { return; while (memPlc.evictionTracker().evictionRequired()) { + memPlc.metrics().onPageEvictionsStarted(); + warnFirstEvict(memPlc.config()); memPlc.evictionTracker().evictDataPage(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgniteDataStorageMetricsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgniteDataStorageMetricsSelfTest.java index 9cff28d988252..dffaf8a29a9a2 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgniteDataStorageMetricsSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgniteDataStorageMetricsSelfTest.java @@ -63,14 +63,17 @@ import org.apache.ignite.spi.metric.HistogramMetric; import org.apache.ignite.spi.metric.LongMetric; import org.apache.ignite.testframework.ListeningTestLogger; +import org.apache.ignite.testframework.LogListener; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; import static java.util.Collections.emptyList; +import static org.apache.commons.lang3.StringUtils.repeat; import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; import static org.apache.ignite.cluster.ClusterState.ACTIVE; +import static org.apache.ignite.configuration.DataPageEvictionMode.RANDOM_LRU; import static org.apache.ignite.internal.processors.cache.CacheGroupMetricsImpl.CACHE_GROUP_METRICS_PREFIX; import static org.apache.ignite.internal.processors.cache.persistence.DataStorageMetricsImpl.DATASTORAGE_METRIC_PREFIX; import static org.apache.ignite.internal.processors.cache.persistence.wal.serializer.RecordV1Serializer.HEADER_RECORD_SIZE; @@ -89,7 +92,10 @@ public class IgniteDataStorageMetricsSelfTest extends GridCommonAbstractTest { private static final String GROUP2 = "grp2"; /** */ - private static final String NO_PERSISTENCE = "no-persistence"; + private static final String NO_PERSISTENCE_1 = "no-persistence-1"; + + /** */ + private static final String NO_PERSISTENCE_2 = "no-persistence-2"; /** */ private static final String PERSISTENCE_REGION_1 = "persistence-1"; @@ -137,7 +143,14 @@ public class IgniteDataStorageMetricsSelfTest extends GridCommonAbstractTest { .setMaxSize(maxRegionSize) .setPersistenceEnabled(false) .setMetricsEnabled(true) - .setName(NO_PERSISTENCE)) + .setName(NO_PERSISTENCE_1) + .setPageEvictionMode(RANDOM_LRU), + new DataRegionConfiguration() + .setMaxSize(maxRegionSize) + .setPersistenceEnabled(false) + .setMetricsEnabled(true) + .setName(NO_PERSISTENCE_2) + .setPageEvictionMode(RANDOM_LRU)) .setWalMode(WALMode.LOG_ONLY) .setMetricsEnabled(true); @@ -148,7 +161,8 @@ public class IgniteDataStorageMetricsSelfTest extends GridCommonAbstractTest { cfg.setCacheConfiguration( cacheConfiguration(GROUP1, "cache", PARTITIONED, ATOMIC, 1, null), cacheConfiguration(GROUP2, "cache2", PARTITIONED, ATOMIC, 1, PERSISTENCE_REGION_2), - cacheConfiguration(null, "cache-np", PARTITIONED, ATOMIC, 1, NO_PERSISTENCE)); + cacheConfiguration(null, "cache-np", PARTITIONED, ATOMIC, 1, NO_PERSISTENCE_1), + cacheConfiguration(null, "cache-np2", PARTITIONED, ATOMIC, 1, NO_PERSISTENCE_2)); cfg.setGridLogger(listeningLog); @@ -191,7 +205,7 @@ private CacheConfiguration cacheConfiguration( ccfg.setDataRegionName(dataRegName); ccfg.setAffinity(new RendezvousAffinityFunction(false, 32)); - if (NO_PERSISTENCE.equals(dataRegName)) + if (NO_PERSISTENCE_1.equals(dataRegName)) ccfg.setDiskPageCompression(null); return ccfg; @@ -226,7 +240,7 @@ public void testPersistenceMetrics() throws Exception { assertTrue(memMetrics.getDirtyPages() > 0); assertTrue(memMetrics.getPagesFillFactor() > 0); - memMetrics = ig.dataRegionMetrics("no-persistence"); + memMetrics = ig.dataRegionMetrics(NO_PERSISTENCE_1); assertNotNull(memMetrics); assertTrue(memMetrics.getTotalAllocatedPages() > 0); @@ -505,6 +519,63 @@ public void testWalTotalSizeWithArchiveTurnedOff() throws Exception { checkWalArchiveAndTotalSize(n, false); } + /** + * Verifies that the 'EvictionsStarted' metric is tracked per data region and becomes {@code true} only for the + * region where page eviction is triggered, remaining unaffected by evictions in other regions. If eviction starts + * in multiple regions, the metric becomes {@code true} independently for each of them. + */ + @Test + public void testEvictionsStartedMetric() throws Exception { + IgniteEx ignite = startGrid(0); + + ignite.cluster().state(ClusterState.ACTIVE); + + String template = "Page-based evictions started. Consider increasing 'maxSize' on Data Region configuration: "; + + LogListener lsnr = LogListener.matches(template + NO_PERSISTENCE_1).build(); + + listeningLog.registerListener(lsnr); + + DataRegionMetrics memMetrics1 = ignite.dataRegionMetrics(NO_PERSISTENCE_1); + DataRegionMetrics memMetrics2 = ignite.dataRegionMetrics(NO_PERSISTENCE_2); + + assertNotNull(memMetrics1); + assertNotNull(memMetrics2); + + assertFalse(memMetrics1.isEvictionsStarted()); + assertFalse(memMetrics2.isEvictionsStarted()); + + IgniteCache cacheNp1 = ignite.cache("cache-np"); + IgniteCache cacheNp2 = ignite.cache("cache-np2"); + + String big = repeat('X', 256 * 1024); + + int entryCnt = 0; + + for (int i = 0; i < 1_000_000 && !lsnr.check(); i++) { + cacheNp1.put(i, new Person("first-" + i + "-" + big, "last-" + i + "-" + big)); + + entryCnt++; + } + + assertTrue(lsnr.check()); + + memMetrics1 = ignite.dataRegionMetrics(NO_PERSISTENCE_1); + memMetrics2 = ignite.dataRegionMetrics(NO_PERSISTENCE_2); + + assertTrue(memMetrics1.isEvictionsStarted()); + assertFalse(memMetrics2.isEvictionsStarted()); + + for (int i = 0; i < entryCnt + 10; i++) + cacheNp2.put(i, new Person("first-" + i + "-" + big, "last-" + i + "-" + big)); + + memMetrics1 = ignite.dataRegionMetrics(NO_PERSISTENCE_1); + memMetrics2 = ignite.dataRegionMetrics(NO_PERSISTENCE_2); + + assertTrue(memMetrics1.isEvictionsStarted()); + assertTrue(memMetrics2.isEvictionsStarted()); + } + /** * Populates a cache w/32 KB of data. *