| /* |
| * 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.geode.internal.cache; |
| |
| import static java.util.concurrent.TimeUnit.NANOSECONDS; |
| import static org.apache.geode.internal.statistics.StatisticsClockFactory.disabledClock; |
| import static org.apache.geode.test.micrometer.MicrometerAssertions.assertThat; |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| import static org.mockito.quality.Strictness.STRICT_STUBS; |
| |
| import java.util.Collection; |
| import java.util.function.LongSupplier; |
| |
| import io.micrometer.core.instrument.Gauge; |
| import io.micrometer.core.instrument.Meter; |
| import io.micrometer.core.instrument.MeterRegistry; |
| import io.micrometer.core.instrument.Timer; |
| import io.micrometer.core.instrument.simple.SimpleMeterRegistry; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.junit.MockitoJUnit; |
| import org.mockito.junit.MockitoRule; |
| |
| import org.apache.geode.Statistics; |
| import org.apache.geode.StatisticsFactory; |
| import org.apache.geode.cache.DataPolicy; |
| import org.apache.geode.internal.statistics.StatisticsClock; |
| |
| public class RegionPerfStatsTest { |
| |
| private static final String REGION_NAME = "region1"; |
| private static final String TEXT_ID = "textId"; |
| private static final DataPolicy DATA_POLICY = DataPolicy.PERSISTENT_REPLICATE; |
| private static final long CLOCK_VALUE = 5L; |
| |
| private MeterRegistry meterRegistry; |
| private CachePerfStats cachePerfStats; |
| private InternalRegion region; |
| private RegionPerfStats regionPerfStats; |
| private Statistics statistics; |
| private StatisticsClock statisticsClock; |
| |
| @Rule |
| public MockitoRule mockitoRule = MockitoJUnit.rule().strictness(STRICT_STUBS); |
| private StatisticsFactory statisticsFactory; |
| |
| @Before |
| public void setUp() { |
| meterRegistry = new SimpleMeterRegistry(); |
| cachePerfStats = mock(CachePerfStats.class); |
| statistics = mock(Statistics.class); |
| region = mock(InternalRegion.class); |
| when(region.getName()).thenReturn(REGION_NAME); |
| when(region.getDataPolicy()).thenReturn(DATA_POLICY); |
| statisticsFactory = mock(StatisticsFactory.class); |
| when(statisticsFactory.createAtomicStatistics(any(), any())).thenReturn(statistics); |
| statisticsClock = mock(StatisticsClock.class); |
| |
| regionPerfStats = new RegionPerfStats(statisticsFactory, TEXT_ID, statisticsClock, |
| cachePerfStats, region, meterRegistry); |
| } |
| |
| @After |
| public void closeStats() { |
| if (regionPerfStats != null) { |
| regionPerfStats.close(); |
| } |
| } |
| |
| @Test |
| public void createsStatisticsUsingTextId() { |
| StatisticsFactory statisticsFactory = mock(StatisticsFactory.class); |
| when(statisticsFactory.createAtomicStatistics(any(), any())).thenReturn(mock(Statistics.class)); |
| |
| new RegionPerfStats(statisticsFactory, TEXT_ID, disabledClock(), cachePerfStats, region, |
| meterRegistry); |
| |
| verify(statisticsFactory).createAtomicStatistics(any(), eq(TEXT_ID)); |
| } |
| |
| @Test |
| public void constructor_createsEntriesGauge_taggedWithRegionName() { |
| assertThat(entriesGauge()) |
| .as("geode.cache.entries gauge") |
| .hasTag("region", REGION_NAME); |
| } |
| |
| @Test |
| public void constructor_createsEntriesGauge_taggedWithDataPolicy() { |
| assertThat(entriesGauge()) |
| .as("geode.cache.entries gauge") |
| .hasTag("data.policy", DATA_POLICY.toString()); |
| } |
| |
| @Test |
| public void constructor_createsCacheGetsHitTimer_taggedWithRegionName() { |
| assertThat(cacheGetsHitTimer()) |
| .as("geode.cache.gets timer with tag result=hit") |
| .hasTag("region", REGION_NAME); |
| } |
| |
| @Test |
| public void constructor_createsCacheGetsMissTimer_taggedWithRegionName() { |
| assertThat(cacheGetsMissTimer()) |
| .as("geode.cache.gets timer with tag result=miss") |
| .hasTag("region", REGION_NAME); |
| } |
| |
| @Test |
| public void suppliesEntryCountStatWithRegionLocalSize() { |
| ArgumentCaptor<LongSupplier> supplierCaptor = ArgumentCaptor.forClass(LongSupplier.class); |
| |
| verify(statistics).setLongSupplier(eq(RegionPerfStats.entryCountId), supplierCaptor.capture()); |
| |
| when(region.getLocalSize()).thenReturn(92); |
| |
| assertThat(supplierCaptor.getValue().getAsLong()) |
| .as("Accumulated value of supplier") |
| .isEqualTo(92); |
| } |
| |
| @Test |
| public void incEntryCount_incrementsCachePerfStatsEntryCount() { |
| regionPerfStats.incEntryCount(2); |
| |
| verify(cachePerfStats).incEntryCount(2); |
| } |
| |
| @Test |
| public void entryCountGaugeFetchesValueFromRegionLocalSize() { |
| when(region.getLocalSize()).thenReturn(3); |
| |
| Gauge entriesGauge = meterRegistry |
| .find("geode.cache.entries") |
| .tag("region", REGION_NAME) |
| .gauge(); |
| |
| assertThat(entriesGauge.value()).isEqualTo(3); |
| } |
| |
| @Test |
| public void endGetForClient_recordsHitTimerCountAndTotalTime_ifCacheHitAndClockEnabled() { |
| when(statisticsClock.isEnabled()).thenReturn(true); |
| when(statisticsClock.getTime()).thenReturn(CLOCK_VALUE); |
| |
| regionPerfStats.endGetForClient(0, false); |
| |
| assertThat(cacheGetsHitTimer()) |
| .as("geode.cache.gets timer with tag result=hit") |
| .hasCount(1) |
| .hasTotalTime(NANOSECONDS, CLOCK_VALUE); |
| } |
| |
| @Test |
| public void endGetForClient_doesNotRecordMissTimerCountOrTotalTime_ifCacheHitAndClockEnabled() { |
| when(statisticsClock.isEnabled()).thenReturn(true); |
| when(statisticsClock.getTime()).thenReturn(CLOCK_VALUE); |
| |
| regionPerfStats.endGetForClient(0, false); |
| |
| assertThat(cacheGetsMissTimer()) |
| .as("geode.cache.gets timer with tag result=miss") |
| .hasCount(0) |
| .hasTotalTime(NANOSECONDS, 0); |
| } |
| |
| @Test |
| public void endGetForClient_recordsHitTimerCountOnly_ifCacheHitAndClockDisabled() { |
| when(statisticsClock.isEnabled()).thenReturn(false); |
| |
| regionPerfStats.endGetForClient(0, false); |
| |
| assertThat(cacheGetsHitTimer()) |
| .as("geode.cache.gets timer with tag result=hit") |
| .hasCount(1) |
| .hasTotalTime(NANOSECONDS, 0); |
| } |
| |
| @Test |
| public void endGetForClient_recordsMissTimerCountAndTotalTime_ifCacheMissAndClockEnabled() { |
| when(statisticsClock.isEnabled()).thenReturn(true); |
| when(statisticsClock.getTime()).thenReturn(CLOCK_VALUE); |
| |
| regionPerfStats.endGetForClient(0, true); |
| |
| assertThat(cacheGetsMissTimer()) |
| .as("geode.cache.gets timer with tag result=miss") |
| .hasCount(1) |
| .hasTotalTime(NANOSECONDS, CLOCK_VALUE); |
| } |
| |
| @Test |
| public void endGetForClient_doesNotRecordHitTimerCountOrTotalTime_ifCacheMissAndClockEnabled() { |
| when(statisticsClock.isEnabled()).thenReturn(true); |
| when(statisticsClock.getTime()).thenReturn(CLOCK_VALUE); |
| |
| regionPerfStats.endGetForClient(0, true); |
| |
| assertThat(cacheGetsHitTimer()) |
| .as("geode.cache.gets timer with tag result=hit") |
| .hasCount(0) |
| .hasTotalTime(NANOSECONDS, 0); |
| } |
| |
| @Test |
| public void endGetForClient_recordsMissTimerCountOnly_ifCacheMissAndClockDisabled() { |
| when(statisticsClock.isEnabled()).thenReturn(false); |
| |
| regionPerfStats.endGetForClient(0, true); |
| |
| assertThat(cacheGetsMissTimer()) |
| .as("geode.cache.gets timer with tag result=miss") |
| .hasCount(1) |
| .hasTotalTime(NANOSECONDS, 0); |
| } |
| |
| @Test |
| public void close_removesEntriesGaugeFromTheRegistry() { |
| assertThat(metersNamed("geode.cache.entries")) |
| .as("entries gauge before closing the stats") |
| .hasSize(1); |
| |
| regionPerfStats.close(); |
| |
| assertThat(metersNamed("geode.cache.entries")) |
| .as("entries gauge after closing the stats") |
| .hasSize(0); |
| |
| regionPerfStats = null; |
| } |
| |
| @Test |
| public void close_removesCacheGetsTimersFromTheRegistry() { |
| assertThat(metersNamed("geode.cache.gets")) |
| .as("cache gets timer before closing the stats") |
| .hasSize(2); |
| |
| regionPerfStats.close(); |
| |
| assertThat(metersNamed("geode.cache.gets")) |
| .as("cache gets timer after closing the stats") |
| .hasSize(0); |
| |
| regionPerfStats = null; |
| } |
| |
| @Test |
| public void close_doesNotRemoveMetersItDoesNotOwn() { |
| String foreignMeterName = "some.meter.not.created.by.the.gateway.receiver.stats"; |
| |
| Timer.builder(foreignMeterName) |
| .register(meterRegistry); |
| |
| regionPerfStats.close(); |
| |
| assertThat(metersNamed(foreignMeterName)) |
| .as("foreign meter after closing the stats") |
| .hasSize(1); |
| |
| regionPerfStats = null; |
| } |
| |
| @Test |
| public void close_closesMeters() { |
| Gauge entriesGauge = mock(Gauge.class); |
| Timer cacheGetsHitTimer = mock(Timer.class); |
| Timer cacheGetsMissTimer = mock(Timer.class); |
| regionPerfStats = new RegionPerfStats(statisticsFactory, TEXT_ID, statisticsClock, |
| cachePerfStats, region, mock(MeterRegistry.class), entriesGauge, cacheGetsHitTimer, |
| cacheGetsMissTimer); |
| |
| regionPerfStats.close(); |
| |
| verify(entriesGauge).close(); |
| verify(cacheGetsHitTimer).close(); |
| verify(cacheGetsMissTimer).close(); |
| |
| regionPerfStats = null; |
| } |
| |
| private Collection<Meter> metersNamed(String meterName) { |
| return meterRegistry |
| .find(meterName) |
| .meters(); |
| } |
| |
| private Gauge entriesGauge() { |
| return meterRegistry |
| .find("geode.cache.entries") |
| .gauge(); |
| } |
| |
| private Timer cacheGetsHitTimer() { |
| return cacheGetsTimer("hit"); |
| } |
| |
| private Timer cacheGetsMissTimer() { |
| return cacheGetsTimer("miss"); |
| } |
| |
| private Timer cacheGetsTimer(String resultTagValue) { |
| return meterRegistry |
| .find("geode.cache.gets") |
| .tag("result", resultTagValue) |
| .timer(); |
| } |
| } |