forked from Consensys/teku
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added StubLabelledOperationTimer to StubMetricSystem (Consensys#7625)
- Loading branch information
1 parent
fa84b50
commit 9fad727
Showing
7 changed files
with
400 additions
and
19 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
...rc/test/java/tech/pegasys/teku/infrastructure/metrics/StubLabelledOperationTimerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright Consensys Software Inc., 2023 | ||
* | ||
* Licensed 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 tech.pegasys.teku.infrastructure.metrics; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.function.Supplier; | ||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer; | ||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer.TimingContext; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class StubLabelledOperationTimerTest { | ||
|
||
private StubLabelledOperationTimer labelledOperationTimer; | ||
private Supplier<Long> timeProvider; | ||
|
||
@BeforeEach | ||
@SuppressWarnings("unchecked") | ||
public void setUp() { | ||
timeProvider = mock(Supplier.class); | ||
when(timeProvider.get()).thenReturn(System.currentTimeMillis()); | ||
|
||
labelledOperationTimer = | ||
new StubLabelledOperationTimer( | ||
TekuMetricCategory.VALIDATOR, "test_name", "help msg", timeProvider); | ||
} | ||
|
||
@Test | ||
public void labelledStubOperationTimerMustMarkTimeCorrectly() { | ||
when(timeProvider.get()).thenReturn(1L, 2L); | ||
|
||
final TimingContext timingContext = labelledOperationTimer.labels("foo", "bar").startTimer(); | ||
timingContext.stopTimer(); | ||
|
||
assertThat(labelledOperationTimer.getAverageDuration("foo", "bar")).hasValue(1.0); | ||
} | ||
|
||
@Test | ||
public void shouldSupportMultipleDurationsWithinSameLabels() { | ||
when(timeProvider.get()).thenReturn(1L, 1L, 6L, 11L); | ||
|
||
final OperationTimer timer1 = labelledOperationTimer.labels("foo", "bar"); | ||
final OperationTimer timer2 = labelledOperationTimer.labels("foo", "bar"); | ||
|
||
final TimingContext timingContext1 = timer1.startTimer(); | ||
final TimingContext timingContext2 = timer2.startTimer(); | ||
|
||
timingContext1.stopTimer(); | ||
timingContext2.stopTimer(); | ||
|
||
assertThat(labelledOperationTimer.getDurations("foo", "bar")).contains(5L, 10L); | ||
} | ||
|
||
@Test | ||
public void shouldSupportMultipleTimersWithDifferentLabels() { | ||
when(timeProvider.get()).thenReturn(1L, 1L, 6L, 11L); | ||
|
||
final OperationTimer timer1 = labelledOperationTimer.labels("foo", "bar"); | ||
final OperationTimer timer2 = labelledOperationTimer.labels("blip", "blop"); | ||
|
||
final TimingContext timingContext1 = timer1.startTimer(); | ||
final TimingContext timingContext2 = timer2.startTimer(); | ||
|
||
timingContext1.stopTimer(); | ||
timingContext2.stopTimer(); | ||
|
||
assertThat(labelledOperationTimer.getDurations("foo", "bar")).contains(5L); | ||
assertThat(labelledOperationTimer.getDurations("blip", "blop")).contains(10L); | ||
} | ||
|
||
@Test | ||
public void shouldFailGettingTimeOfNonExistingTimer() { | ||
assertThatThrownBy(() -> labelledOperationTimer.getAverageDuration("nope")) | ||
.isInstanceOf(IllegalArgumentException.class); | ||
} | ||
|
||
@Test | ||
public void shouldReturnExistingTimerForLabels() { | ||
final OperationTimer timer1 = labelledOperationTimer.labels("foo", "bar"); | ||
final OperationTimer timer2 = labelledOperationTimer.labels("foo", "bar"); | ||
|
||
assertThat(timer1).isSameAs(timer2); | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
...etrics/src/test/java/tech/pegasys/teku/infrastructure/metrics/StubOperationTimerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Copyright Consensys Software Inc., 2023 | ||
* | ||
* Licensed 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 tech.pegasys.teku.infrastructure.metrics; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.function.Supplier; | ||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer.TimingContext; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class StubOperationTimerTest { | ||
|
||
private StubOperationTimer operationTimer; | ||
private Supplier<Long> timeProvider; | ||
|
||
@BeforeEach | ||
@SuppressWarnings("unchecked") | ||
public void setUp() { | ||
timeProvider = mock(Supplier.class); | ||
when(timeProvider.get()).thenReturn(System.currentTimeMillis()); | ||
|
||
operationTimer = | ||
new StubOperationTimer( | ||
TekuMetricCategory.VALIDATOR, "test_timer", "help msg", timeProvider); | ||
} | ||
|
||
@Test | ||
public void shouldCalculateCorrectDuration() { | ||
// duration 6 - 1 = 5 | ||
when(timeProvider.get()).thenReturn(1L, 6L); | ||
|
||
TimingContext timingContext = operationTimer.startTimer(); | ||
|
||
assertThat(timingContext.stopTimer()).isEqualTo(5); | ||
} | ||
|
||
@Test | ||
public void shouldCalculateMultipleDurations() { | ||
// 1st duration 6 - 1 = 5 | ||
// 2nd duration 11 - 1 = 10 | ||
when(timeProvider.get()).thenReturn(1L, 1L, 6L, 11L); | ||
|
||
TimingContext timingContext1 = operationTimer.startTimer(); | ||
TimingContext timingContext2 = operationTimer.startTimer(); | ||
|
||
assertThat(timingContext1.stopTimer()).isEqualTo(5); | ||
assertThat(timingContext2.stopTimer()).isEqualTo(10); | ||
} | ||
|
||
@Test | ||
public void shouldCalculateCorrectDurationAverage() { | ||
// 1st duration 6 - 1 = 5 | ||
// 2nd duration 11 - 1 = 10 | ||
when(timeProvider.get()).thenReturn(1L, 1L, 6L, 11L); | ||
|
||
TimingContext timingContext1 = operationTimer.startTimer(); | ||
TimingContext timingContext2 = operationTimer.startTimer(); | ||
|
||
timingContext1.stopTimer(); | ||
timingContext2.stopTimer(); | ||
|
||
assertThat(operationTimer.getAverageDuration()).hasValue(7.5); | ||
} | ||
|
||
@Test | ||
public void shouldNotCalculateDurationIfTimerIsNotStopped() { | ||
// We never stop this timer | ||
operationTimer.startTimer(); | ||
|
||
assertThat(operationTimer.getDurations()).isEmpty(); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
...estFixtures/java/tech/pegasys/teku/infrastructure/metrics/StubLabelledOperationTimer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright Consensys Software Inc., 2023 | ||
* | ||
* Licensed 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 tech.pegasys.teku.infrastructure.metrics; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.OptionalDouble; | ||
import java.util.Set; | ||
import java.util.function.Supplier; | ||
import org.assertj.core.util.VisibleForTesting; | ||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; | ||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory; | ||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer; | ||
|
||
public class StubLabelledOperationTimer extends StubMetric | ||
implements LabelledMetric<OperationTimer> { | ||
|
||
private final Map<List<String>, StubOperationTimer> timers = new HashMap<>(); | ||
private final Supplier<Long> timeProvider; | ||
|
||
protected StubLabelledOperationTimer( | ||
final MetricCategory category, final String name, final String help) { | ||
super(category, name, help); | ||
this.timeProvider = System::currentTimeMillis; | ||
} | ||
|
||
@VisibleForTesting | ||
StubLabelledOperationTimer( | ||
final MetricCategory category, | ||
final String name, | ||
final String help, | ||
Supplier<Long> timeProvider) { | ||
super(category, name, help); | ||
this.timeProvider = timeProvider; | ||
} | ||
|
||
@Override | ||
public OperationTimer labels(final String... labels) { | ||
return timers.computeIfAbsent( | ||
List.of(labels), | ||
__ -> new StubOperationTimer(getCategory(), getName(), getHelp(), timeProvider)); | ||
} | ||
|
||
/** | ||
* Return the average time of operations marked by the timer. | ||
* | ||
* @param labels the labels matching the timer | ||
* @return the average time of operations marked by the timer. | ||
* @throws IllegalArgumentException if the provided labels do not correspond to an existing timer. | ||
*/ | ||
public OptionalDouble getAverageDuration(final String... labels) { | ||
final StubOperationTimer operationTimer = timers.get(List.of(labels)); | ||
if (operationTimer == null) { | ||
throw new IllegalArgumentException("Attempting to get time from a non-existing timer"); | ||
} | ||
return operationTimer.getAverageDuration(); | ||
} | ||
|
||
public Set<Long> getDurations(final String... labels) { | ||
final StubOperationTimer operationTimer = timers.get(List.of(labels)); | ||
if (operationTimer == null) { | ||
throw new IllegalArgumentException("Attempting to get time from a non-existing timer"); | ||
} | ||
return operationTimer.getDurations(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
...cs/src/testFixtures/java/tech/pegasys/teku/infrastructure/metrics/StubOperationTimer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright Consensys Software Inc., 2023 | ||
* | ||
* Licensed 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 tech.pegasys.teku.infrastructure.metrics; | ||
|
||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.OptionalDouble; | ||
import java.util.OptionalLong; | ||
import java.util.Random; | ||
import java.util.Set; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Supplier; | ||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory; | ||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer; | ||
|
||
public class StubOperationTimer extends StubMetric implements OperationTimer { | ||
|
||
private final Random idGenerator = new Random(); | ||
private final Supplier<Long> timeProvider; | ||
private final Map<Long, Long> durations = new ConcurrentHashMap<>(); | ||
|
||
StubOperationTimer( | ||
final MetricCategory category, | ||
final String name, | ||
final String help, | ||
final Supplier<Long> timeProvider) { | ||
super(category, name, help); | ||
this.timeProvider = timeProvider; | ||
} | ||
|
||
@Override | ||
public TimingContext startTimer() { | ||
return new StubTimingContext(timeProvider.get()); | ||
} | ||
|
||
public OptionalDouble getAverageDuration() { | ||
return durations.values().stream().mapToLong(v -> v).average(); | ||
} | ||
|
||
public Set<Long> getDurations() { | ||
return new HashSet<>(durations.values()); | ||
} | ||
|
||
public OptionalLong getDurationsByTimingContextId(final Long id) { | ||
final Long maybeDuration = durations.get(id); | ||
return maybeDuration == null ? OptionalLong.empty() : OptionalLong.of(maybeDuration); | ||
} | ||
|
||
private class StubTimingContext implements TimingContext { | ||
|
||
private final long id = idGenerator.nextLong(); | ||
private final long startTime; | ||
|
||
public StubTimingContext(final long startTime) { | ||
this.startTime = startTime; | ||
} | ||
|
||
@Override | ||
public double stopTimer() { | ||
final long duration = timeProvider.get() - startTime; | ||
durations.put(id, duration); | ||
return duration; | ||
} | ||
} | ||
} |
Oops, something went wrong.