Skip to content

Commit

Permalink
api: add helper methods for Suppliers (#1086)
Browse files Browse the repository at this point in the history
Adds methods to the Timer interface to make it easier to
work with the Supplier types in the JDK. There is already
a method that works with Callable, but since that throws
Exception it can be cumbersome to use in code. Methods have
also been added for the suppliers with primitive types to
avoid boxing.

Overload resolution can be messy with lambdas. To avoid
those issues record methods have been added with a different
name for each interface. The existing record methods for
Callable and Runnable have been deprecated in favor of the
more specifically named method.

Fixes #566.
  • Loading branch information
brharrington committed Oct 25, 2023
1 parent 74f30dd commit 4a6e64d
Show file tree
Hide file tree
Showing 14 changed files with 374 additions and 200 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 Netflix, Inc.
* Copyright 2014-2023 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,10 +15,6 @@
*/
package com.netflix.spectator.api;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


/**
* Base class to simplify implementing a {@link Timer}.
*/
Expand All @@ -32,23 +28,7 @@ public AbstractTimer(Clock clock) {
this.clock = clock;
}

@Override public <T> T record(Callable<T> f) throws Exception {
final long s = clock.monotonicTime();
try {
return f.call();
} finally {
final long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

@Override public void record(Runnable f) {
final long s = clock.monotonicTime();
try {
f.run();
} finally {
final long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
@Override public Clock clock() {
return clock;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 Netflix, Inc.
* Copyright 2014-2023 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@

import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/** Timer implementation for the composite registry. */
Expand All @@ -31,32 +30,16 @@ final class CompositeTimer extends CompositeMeter<Timer> implements Timer {
this.clock = clock;
}

@Override public Clock clock() {
return clock;
}

@Override public void record(long amount, TimeUnit unit) {
for (Timer t : meters) {
t.record(amount, unit);
}
}

@Override public <T> T record(Callable<T> f) throws Exception {
final long s = clock.monotonicTime();
try {
return f.call();
} finally {
final long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

@Override public void record(Runnable f) {
final long s = clock.monotonicTime();
try {
f.run();
} finally {
final long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

@Override public long count() {
Iterator<Timer> it = meters.iterator();
return it.hasNext() ? it.next().count() : 0L;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 Netflix, Inc.
* Copyright 2014-2023 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,7 +16,6 @@
package com.netflix.spectator.api;

import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/** Counter implementation for the no-op registry. */
Expand All @@ -39,14 +38,6 @@ enum NoopTimer implements Timer {
return Collections.emptyList();
}

@Override public <T> T record(Callable<T> f) throws Exception {
return f.call();
}

@Override public void record(Runnable f) {
f.run();
}

@Override public long count() {
return 0L;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2022 Netflix, Inc.
* Copyright 2014-2023 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@

import com.netflix.spectator.impl.SwapMeter;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.LongSupplier;
Expand All @@ -35,28 +34,12 @@ final class SwapTimer extends SwapMeter<Timer> implements Timer {
return registry.timer(id);
}

@Override public void record(long amount, TimeUnit unit) {
get().record(amount, unit);
}

@Override public <T> T record(Callable<T> f) throws Exception {
final long s = registry.clock().monotonicTime();
try {
return f.call();
} finally {
final long e = registry.clock().monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
@Override public Clock clock() {
return registry.clock();
}

@Override public void record(Runnable f) {
final long s = registry.clock().monotonicTime();
try {
f.run();
} finally {
final long e = registry.clock().monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
@Override public void record(long amount, TimeUnit unit) {
get().record(amount, unit);
}

@Override public long count() {
Expand Down
162 changes: 158 additions & 4 deletions spectator-api/src/main/java/com/netflix/spectator/api/Timer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2022 Netflix, Inc.
* Copyright 2014-2023 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,11 @@
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;

/**
* Timer intended to track a large number of short running events. Example would be something like
Expand All @@ -30,6 +35,14 @@
* will always return 0.
*/
public interface Timer extends Meter {

/**
* The clock used for timing events.
*/
default Clock clock() {
return Clock.SYSTEM;
}

/**
* Updates the statistics kept by the timer with the specified amount.
*
Expand Down Expand Up @@ -95,6 +108,34 @@ default void record(Duration[] amounts, int n) {
}
}

/**
* Executes the callable `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*
* @deprecated Use {@link #recordCallable(Callable)} instead.
*/
@Deprecated
default <T> T record(Callable<T> f) throws Exception {
return recordCallable(f);
}

/**
* Executes the runnable `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
*
* @deprecated Use {@link #recordRunnable(Runnable)} instead.
*/
@Deprecated
default void record(Runnable f) {
recordRunnable(f);
}

/**
* Executes the callable `f` and records the time taken.
*
Expand All @@ -103,15 +144,128 @@ default void record(Duration[] amounts, int n) {
* @return
* The return value of `f`.
*/
<T> T record(Callable<T> f) throws Exception;
default <T> T recordCallable(Callable<T> f) throws Exception {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.call();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the runnable `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
*/
void record(Runnable f);
default void recordRunnable(Runnable f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
f.run();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the supplier `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*/
default <T> T recordSupplier(Supplier<T> f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.get();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the supplier `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*/
default boolean recordBooleanSupplier(BooleanSupplier f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.getAsBoolean();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the supplier `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*/
default double recordDoubleSupplier(DoubleSupplier f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.getAsDouble();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the supplier `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*/
default int recordIntSupplier(IntSupplier f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.getAsInt();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* Executes the supplier `f` and records the time taken.
*
* @param f
* Function to execute and measure the execution time.
* @return
* The return value of `f`.
*/
default long recordLongSupplier(LongSupplier f) {
final Clock clock = clock();
long s = clock.monotonicTime();
try {
return f.getAsLong();
} finally {
long e = clock.monotonicTime();
record(e - s, TimeUnit.NANOSECONDS);
}
}

/**
* The number of times that record has been called since this timer was last reset.
Expand All @@ -133,7 +287,7 @@ default void record(Duration[] amounts, int n) {
* cost, but the updates may be delayed a bit in reaching the meter. The updates will only
* be seen after the updater is explicitly flushed.
*
* The caller should ensure that the updater is closed after using to guarantee any resources
* <p>The caller should ensure that the updater is closed after using to guarantee any resources
* associated with it are cleaned up. In some cases failure to close the updater could result
* in a memory leak.
*
Expand Down

0 comments on commit 4a6e64d

Please sign in to comment.