| /* |
| * 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.execute.metrics; |
| |
| import static java.util.concurrent.TimeUnit.NANOSECONDS; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionExceptionsId; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionsCompletedId; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionsCompletedProcessingTimeId; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionsHasResultCompletedProcessingTimeId; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionsHasResultRunningId; |
| import static org.apache.geode.internal.cache.execute.metrics.FunctionStatsImpl.functionExecutionsRunningId; |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.assertj.core.api.Assertions.catchThrowable; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.verify; |
| |
| import io.micrometer.core.instrument.MeterRegistry; |
| import io.micrometer.core.instrument.Timer; |
| import io.micrometer.core.instrument.simple.SimpleMeterRegistry; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.mockito.Mock; |
| import org.mockito.junit.MockitoJUnit; |
| import org.mockito.junit.MockitoRule; |
| import org.mockito.quality.Strictness; |
| |
| import org.apache.geode.Statistics; |
| |
| public class FunctionStatsImplTest { |
| private static final String FUNCTION_ID = "functionId"; |
| |
| @Rule |
| public MockitoRule mockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); |
| |
| @Mock |
| private Statistics statistics; |
| |
| @Mock |
| private FunctionServiceStats functionServiceStats; |
| |
| private MeterRegistry meterRegistry; |
| |
| @Before |
| public void setUp() { |
| meterRegistry = new SimpleMeterRegistry(); |
| } |
| |
| @Test |
| public void constructor_registersSuccessTimer() { |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| assertThat(successTimer()) |
| .as("geode.function.executions timer with tags function=%s, succeeded=true", FUNCTION_ID) |
| .isNotNull(); |
| assertThat(successTimer().getId().getDescription()) |
| .as("success timer description") |
| .isEqualTo("Count and total time of successful function executions"); |
| } |
| |
| @Test |
| public void constructor_registersFailureTimer() { |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| assertThat(failureTimer()) |
| .as("geode.function.executions timer with tags function=%s, succeeded=false", FUNCTION_ID) |
| .isNotNull(); |
| assertThat(failureTimer().getId().getDescription()) |
| .as("failure timer description") |
| .isEqualTo("Count and total time of failed function executions"); |
| } |
| |
| @Test |
| public void constructor_throws_ifMeterRegistryIsNull() { |
| Throwable thrown = catchThrowable( |
| () -> new FunctionStatsImpl(FUNCTION_ID, null, statistics, functionServiceStats)); |
| |
| assertThat(thrown) |
| .isInstanceOf(NullPointerException.class); |
| } |
| |
| @Test |
| public void endFunctionExecution_incrementsSuccessTimerCount() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.endFunctionExecution(0, false); |
| |
| assertThat(successTimer().count()) |
| .as("Success timer count") |
| .isEqualTo(1); |
| } |
| |
| @Test |
| public void endFunctionExecution_incrementsSuccessTimerTotalTime() { |
| long endTime = 5; |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, endTime, |
| false); |
| |
| long startTime = 0; |
| functionStats.endFunctionExecution(startTime, false); |
| |
| long elapsedNanos = endTime - startTime; |
| assertThat(successTimer().totalTime(NANOSECONDS)) |
| .as("Success timer total time") |
| .isEqualTo(elapsedNanos); |
| } |
| |
| @Test |
| public void endFunctionExecution_noResult_clockStatsDisabled_incrementsStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, 0, |
| false); |
| |
| functionStats.endFunctionExecution(0, false); |
| |
| verify(statistics) |
| .incInt(functionExecutionsCompletedId(), 1); |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics, never()) |
| .incLong(eq(functionExecutionsCompletedProcessingTimeId()), anyLong()); |
| verify(statistics, never()) |
| .incInt(eq(functionExecutionsHasResultRunningId()), anyInt()); |
| verify(statistics, never()) |
| .incLong(eq(functionExecutionsHasResultCompletedProcessingTimeId()), anyLong()); |
| } |
| |
| @Test |
| public void endFunctionExecution_hasResult_clockStatsDisabled_incrementsStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, 0, |
| false); |
| |
| functionStats.endFunctionExecution(0, true); |
| |
| verify(statistics) |
| .incInt(functionExecutionsCompletedId(), 1); |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics, never()) |
| .incLong(eq(functionExecutionsCompletedProcessingTimeId()), anyLong()); |
| verify(statistics) |
| .incInt(functionExecutionsHasResultRunningId(), -1); |
| verify(statistics, never()) |
| .incLong(eq(functionExecutionsHasResultCompletedProcessingTimeId()), anyLong()); |
| } |
| |
| @Test |
| public void endFunctionExecution_noResult_clockStatsEnabled_incrementsStats() { |
| long endTime = 5; |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, endTime, |
| true); |
| |
| long startTime = 0; |
| functionStats.endFunctionExecution(startTime, false); |
| |
| long elapsedNanos = endTime - startTime; |
| verify(statistics) |
| .incInt(functionExecutionsCompletedId(), 1); |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics) |
| .incLong(functionExecutionsCompletedProcessingTimeId(), elapsedNanos); |
| verify(statistics, never()) |
| .incInt(eq(functionExecutionsHasResultRunningId()), anyInt()); |
| verify(statistics, never()) |
| .incLong(eq(functionExecutionsHasResultCompletedProcessingTimeId()), anyLong()); |
| } |
| |
| @Test |
| public void endFunctionExecution_hasResult_clockStatsEnabled_incrementsStats() { |
| long endTime = 5; |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, endTime, |
| true); |
| |
| long startTime = 0; |
| functionStats.endFunctionExecution(startTime, true); |
| |
| long elapsedNanos = endTime - startTime; |
| verify(statistics) |
| .incInt(functionExecutionsCompletedId(), 1); |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics) |
| .incLong(functionExecutionsCompletedProcessingTimeId(), elapsedNanos); |
| verify(statistics) |
| .incInt(functionExecutionsHasResultRunningId(), -1); |
| verify(statistics) |
| .incLong(functionExecutionsHasResultCompletedProcessingTimeId(), elapsedNanos); |
| } |
| |
| @Test |
| public void endFunctionExecution_incrementsAggregateStats() { |
| long endTime = 5; |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, endTime, |
| false); |
| |
| long startTime = 0; |
| boolean haveResult = true; |
| functionStats.endFunctionExecution(startTime, haveResult); |
| |
| long elapsedNanos = endTime - startTime; |
| verify(functionServiceStats) |
| .endFunctionExecutionWithElapsedTime(elapsedNanos, haveResult); |
| } |
| |
| @Test |
| public void endFunctionExecutionWithException_incrementsFailureTimerCount() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.endFunctionExecutionWithException(0, false); |
| |
| assertThat(failureTimer().count()) |
| .as("Failure timer count") |
| .isEqualTo(1); |
| } |
| |
| @Test |
| public void endFunctionExecutionWithException_incrementsFailureTimerTotalTime() { |
| long endTime = 5; |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats, endTime, |
| false); |
| |
| long startTime = 0; |
| functionStats.endFunctionExecutionWithException(startTime, false); |
| |
| long elapsedNanos = endTime - startTime; |
| assertThat(failureTimer().totalTime(NANOSECONDS)) |
| .as("Failure timer total time") |
| .isEqualTo(elapsedNanos); |
| } |
| |
| @Test |
| public void endFunctionExecutionWithException_noResult_incrementsStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.endFunctionExecutionWithException(0, false); |
| |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics) |
| .incInt(functionExecutionExceptionsId(), 1); |
| verify(statistics, never()) |
| .incInt(eq(functionExecutionsHasResultRunningId()), anyInt()); |
| } |
| |
| @Test |
| public void endFunctionExecutionWithException_hasResult_incrementsStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.endFunctionExecutionWithException(0, true); |
| |
| verify(statistics) |
| .incInt(functionExecutionsRunningId(), -1); |
| verify(statistics) |
| .incInt(functionExecutionExceptionsId(), 1); |
| verify(statistics) |
| .incInt(functionExecutionsHasResultRunningId(), -1); |
| } |
| |
| @Test |
| public void endFunctionExecutionWithException_incrementsAggregateStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| boolean haveResult = true; |
| functionStats.endFunctionExecutionWithException(0, haveResult); |
| |
| verify(functionServiceStats) |
| .endFunctionExecutionWithException(haveResult); |
| } |
| |
| @Test |
| public void close_removesSuccessTimer() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.close(); |
| |
| assertThat(successTimer()) |
| .as("geode.function.executions timer with tags function=%s, succeeded=true", FUNCTION_ID) |
| .isNull(); |
| } |
| |
| @Test |
| public void close_removesFailureTimer() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.close(); |
| |
| assertThat(failureTimer()) |
| .as("geode.function.executions timer with tags function=%s, succeeded=false", FUNCTION_ID) |
| .isNull(); |
| } |
| |
| @Test |
| public void close_closesTimers() { |
| Timer successTimer = mock(Timer.class); |
| Timer failureTimer = mock(Timer.class); |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, mock(MeterRegistry.class), statistics, |
| functionServiceStats, 0L, false, successTimer, failureTimer); |
| |
| functionStats.close(); |
| |
| verify(successTimer).close(); |
| verify(failureTimer).close(); |
| } |
| |
| @Test |
| public void close_closesStats() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.close(); |
| |
| verify(statistics).close(); |
| } |
| |
| @Test |
| public void isClosed_returnsFalse_ifCloseNotCalled() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| assertThat(functionStats.isClosed()) |
| .isFalse(); |
| } |
| |
| @Test |
| public void isClosed_returnsTrue_ifCloseCalled() { |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statistics, functionServiceStats); |
| |
| functionStats.close(); |
| |
| assertThat(functionStats.isClosed()) |
| .isTrue(); |
| } |
| |
| @Test |
| public void getStatistics_returnsGivenStatistics() { |
| Statistics statisticsPassedToConstructor = mock(Statistics.class); |
| |
| FunctionStats functionStats = |
| new FunctionStatsImpl(FUNCTION_ID, meterRegistry, statisticsPassedToConstructor, |
| functionServiceStats); |
| |
| assertThat(functionStats.getStatistics()) |
| .isSameAs(statisticsPassedToConstructor); |
| } |
| |
| private Timer successTimer() { |
| return functionExecutionsTimer(true); |
| } |
| |
| private Timer failureTimer() { |
| return functionExecutionsTimer(false); |
| } |
| |
| private Timer functionExecutionsTimer(boolean succeededTagValue) { |
| return meterRegistry |
| .find("geode.function.executions") |
| .tag("function", FUNCTION_ID) |
| .tag("succeeded", String.valueOf(succeededTagValue)) |
| .timer(); |
| } |
| } |