From dbbc8857eb963fd0eaf84f1586f3ca689e3d6257 Mon Sep 17 00:00:00 2001 From: Brian Harrington Date: Fri, 20 Oct 2023 18:41:14 -0500 Subject: [PATCH] api: add helper methods for Suppliers 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. --- .../netflix/spectator/api/AbstractTimer.java | 26 +-- .../netflix/spectator/api/CompositeTimer.java | 27 +-- .../com/netflix/spectator/api/NoopTimer.java | 11 +- .../com/netflix/spectator/api/SwapTimer.java | 27 +-- .../java/com/netflix/spectator/api/Timer.java | 162 +++++++++++++++++- .../spectator/api/histogram/BucketTimer.java | 29 +--- .../api/histogram/PercentileTimer.java | 31 +--- .../com/netflix/spectator/impl/Scheduler.java | 2 +- .../spectator/sandbox/BucketTimer.java | 16 +- .../netflix/spectator/atlas/AtlasTimer.java | 25 +-- .../spectator/micrometer/MicrometerTimer.java | 11 +- .../spectator/sidecar/SidecarTimer.java | 25 +-- 12 files changed, 201 insertions(+), 191 deletions(-) diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/AbstractTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractTimer.java index c772a24b9..1306d673e 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/AbstractTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractTimer.java @@ -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. @@ -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}. */ @@ -32,23 +28,7 @@ public AbstractTimer(Clock clock) { this.clock = clock; } - @Override public T record(Callable 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; } } diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/CompositeTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/CompositeTimer.java index 9ec77a20a..2001f017d 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/CompositeTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/CompositeTimer.java @@ -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. @@ -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. */ @@ -31,32 +30,16 @@ final class CompositeTimer extends CompositeMeter 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 record(Callable 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 it = meters.iterator(); return it.hasNext() ? it.next().count() : 0L; diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java index ad8738a42..d92641f4d 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java @@ -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. @@ -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. */ @@ -39,14 +38,6 @@ enum NoopTimer implements Timer { return Collections.emptyList(); } - @Override public T record(Callable f) throws Exception { - return f.call(); - } - - @Override public void record(Runnable f) { - f.run(); - } - @Override public long count() { return 0L; } diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/SwapTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/SwapTimer.java index 1e5ff4823..a576f4732 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/SwapTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/SwapTimer.java @@ -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. @@ -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; @@ -35,28 +34,12 @@ final class SwapTimer extends SwapMeter implements Timer { return registry.timer(id); } - @Override public void record(long amount, TimeUnit unit) { - get().record(amount, unit); - } - - @Override public T record(Callable 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() { diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java b/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java index 87bcbaea4..1a5f7fdb8 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java @@ -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. @@ -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 @@ -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. * @@ -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 record(Callable 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. * @@ -103,7 +144,16 @@ default void record(Duration[] amounts, int n) { * @return * The return value of `f`. */ - T record(Callable f) throws Exception; + default T recordCallable(Callable 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. @@ -111,7 +161,111 @@ default void record(Duration[] amounts, int n) { * @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 callable `f` and records the time taken. + * + * @param f + * Function to execute and measure the execution time. + * @return + * The return value of `f`. + */ + default T recordSupplier(Supplier 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 callable `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 callable `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 callable `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 callable `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. @@ -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 + *

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. * diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/histogram/BucketTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/histogram/BucketTimer.java index b96ce699c..11d675862 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/histogram/BucketTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/histogram/BucketTimer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 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. @@ -23,7 +23,6 @@ import com.netflix.spectator.api.Utils; import java.util.Collections; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -76,33 +75,15 @@ public static BucketTimer get(Registry registry, Id id, LongFunction f) return false; } + @Override public Clock clock() { + return registry.clock(); + } + @Override public void record(long amount, TimeUnit unit) { final long nanos = unit.toNanos(amount); timer(f.apply(nanos)).record(amount, unit); } - @Override public T record(Callable rf) throws Exception { - final Clock clock = registry.clock(); - final long s = clock.monotonicTime(); - try { - return rf.call(); - } finally { - final long e = clock.monotonicTime(); - record(e - s, TimeUnit.NANOSECONDS); - } - } - - @Override public void record(Runnable rf) { - final Clock clock = registry.clock(); - final long s = clock.monotonicTime(); - try { - rf.run(); - } finally { - final long e = clock.monotonicTime(); - record(e - s, TimeUnit.NANOSECONDS); - } - } - /** Return the timer for a given bucket. */ Timer timer(String bucket) { return Utils.computeIfAbsent(timers, bucket, timerFactory); diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/histogram/PercentileTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/histogram/PercentileTimer.java index 0960ed65f..960890107 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/api/histogram/PercentileTimer.java +++ b/spectator-api/src/main/java/com/netflix/spectator/api/histogram/PercentileTimer.java @@ -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. @@ -29,7 +29,6 @@ import java.time.Duration; import java.util.Collections; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -153,7 +152,7 @@ public Builder withRange(Duration min, Duration max) { * for the important range of values and reduce the overhead associated with tracking the * data distribution. * - * For example, suppose you are making a client call and timeout after 10 seconds. Setting + *

For example, suppose you are making a client call and timeout after 10 seconds. Setting * the range to 10 seconds will restrict the possible set of buckets used to those * approaching the boundary. So we can still detect if it is nearing failure, but percentiles * that are further away from the range may be inflated compared to the actual value. @@ -246,34 +245,16 @@ private long restrict(long amount) { return Math.max(v, min); } + @Override public Clock clock() { + return registry.clock(); + } + @Override public void record(long amount, TimeUnit unit) { final long nanos = restrict(unit.toNanos(amount)); timer.record(amount, unit); counterFor(PercentileBuckets.indexOf(nanos)).increment(); } - @Override public T record(Callable rf) throws Exception { - final Clock clock = registry.clock(); - final long s = clock.monotonicTime(); - try { - return rf.call(); - } finally { - final long e = clock.monotonicTime(); - record(e - s, TimeUnit.NANOSECONDS); - } - } - - @Override public void record(Runnable rf) { - final Clock clock = registry.clock(); - final long s = clock.monotonicTime(); - try { - rf.run(); - } finally { - final long e = clock.monotonicTime(); - record(e - s, TimeUnit.NANOSECONDS); - } - } - /** * Computes the specified percentile for this timer. The unit will be seconds. * diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/Scheduler.java b/spectator-api/src/main/java/com/netflix/spectator/impl/Scheduler.java index 4716e90df..6affbfba0 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/Scheduler.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/Scheduler.java @@ -486,7 +486,7 @@ private final class Worker implements Runnable { final long delay = clock.wallTime() - task.getNextExecutionTime(); stats.taskExecutionDelay().record(delay, TimeUnit.MILLISECONDS); - stats.taskExecutionTime().record(() -> task.runAndReschedule(queue, stats)); + stats.taskExecutionTime().recordRunnable(() -> task.runAndReschedule(queue, stats)); } catch (InterruptedException e) { LOGGER.debug("task interrupted", e); break; diff --git a/spectator-ext-sandbox/src/main/java/com/netflix/spectator/sandbox/BucketTimer.java b/spectator-ext-sandbox/src/main/java/com/netflix/spectator/sandbox/BucketTimer.java index d496873dd..137f8947b 100644 --- a/spectator-ext-sandbox/src/main/java/com/netflix/spectator/sandbox/BucketTimer.java +++ b/spectator-ext-sandbox/src/main/java/com/netflix/spectator/sandbox/BucketTimer.java @@ -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. @@ -15,13 +15,13 @@ */ package com.netflix.spectator.sandbox; +import com.netflix.spectator.api.Clock; import com.netflix.spectator.api.Id; import com.netflix.spectator.api.Measurement; import com.netflix.spectator.api.Registry; import com.netflix.spectator.api.Spectator; import com.netflix.spectator.api.Timer; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** @@ -85,16 +85,12 @@ public static BucketTimer get(Registry registry, Id id, BucketFunction f) { return t.hasExpired(); } - @Override public void record(long amount, TimeUnit unit) { - t.record(amount, unit); - } - - @Override public T record(Callable rf) throws Exception { - return t.record(rf); + @Override public Clock clock() { + return t.clock(); } - @Override public void record(Runnable rf) { - t.record(rf); + @Override public void record(long amount, TimeUnit unit) { + t.record(amount, unit); } @Override public long count() { diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java index a8eb0c952..9c75bd84c 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java @@ -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. @@ -24,7 +24,6 @@ import com.netflix.spectator.impl.StepValue; import java.time.Duration; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -95,6 +94,10 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid consumer.accept(mid, timestamp, maxValue); } + @Override public Clock clock() { + return clock; + } + @Override public void record(long amount, TimeUnit unit) { long now = clock.wallTime(); count.getCurrent(now).incrementAndGet(); @@ -166,24 +169,6 @@ private void updateMax(AtomicLong maxValue, long v) { } } - @Override public T record(Callable f) throws Exception { - final long start = clock.monotonicTime(); - try { - return f.call(); - } finally { - record(clock.monotonicTime() - start, TimeUnit.NANOSECONDS); - } - } - - @Override public void record(Runnable f) { - final long start = clock.monotonicTime(); - try { - f.run(); - } finally { - record(clock.monotonicTime() - start, TimeUnit.NANOSECONDS); - } - } - @Override public long count() { return count.poll(); } diff --git a/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerTimer.java b/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerTimer.java index 6a8e8a675..ceb1e8c24 100644 --- a/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerTimer.java +++ b/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerTimer.java @@ -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. @@ -19,7 +19,6 @@ import com.netflix.spectator.api.Measurement; import com.netflix.spectator.api.Timer; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** @@ -39,14 +38,6 @@ class MicrometerTimer extends MicrometerMeter implements Timer { impl.record(amount, unit); } - @Override public T record(Callable f) throws Exception { - return impl.recordCallable(f); - } - - @Override public void record(Runnable f) { - impl.record(f); - } - @Override public long count() { return impl.count(); } diff --git a/spectator-reg-sidecar/src/main/java/com/netflix/spectator/sidecar/SidecarTimer.java b/spectator-reg-sidecar/src/main/java/com/netflix/spectator/sidecar/SidecarTimer.java index 930f170c4..60718f29e 100644 --- a/spectator-reg-sidecar/src/main/java/com/netflix/spectator/sidecar/SidecarTimer.java +++ b/spectator-reg-sidecar/src/main/java/com/netflix/spectator/sidecar/SidecarTimer.java @@ -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. @@ -19,7 +19,6 @@ import com.netflix.spectator.api.Id; import com.netflix.spectator.api.Timer; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** @@ -37,6 +36,10 @@ class SidecarTimer extends SidecarMeter implements Timer { this.writer = writer; } + @Override public Clock clock() { + return clock; + } + @Override public void record(long amount, TimeUnit unit) { final double seconds = unit.toNanos(amount) / 1e9; if (seconds >= 0.0) { @@ -44,24 +47,6 @@ class SidecarTimer extends SidecarMeter implements Timer { } } - @Override public T record(Callable f) throws Exception { - final long start = clock.monotonicTime(); - try { - return f.call(); - } finally { - record(clock.monotonicTime() - start, TimeUnit.NANOSECONDS); - } - } - - @Override public void record(Runnable f) { - final long start = clock.monotonicTime(); - try { - f.run(); - } finally { - record(clock.monotonicTime() - start, TimeUnit.NANOSECONDS); - } - } - @Override public long count() { return 0L; }