diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/swift/service/stats/ServerStats.java b/thrift/lib/java/runtime/src/main/java/com/facebook/swift/service/stats/ServerStats.java index cfa490532f0..0161885cbd1 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/swift/service/stats/ServerStats.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/swift/service/stats/ServerStats.java @@ -17,14 +17,16 @@ package com.facebook.swift.service.stats; import com.facebook.nifty.core.NiftyMetrics; -import io.airlift.stats.DecayCounter; -import io.airlift.stats.Distribution; -import io.airlift.stats.ExponentialDecay; +import com.facebook.thrift.metrics.distribution.MultiWindowDistribution; +import com.facebook.thrift.metrics.distribution.Quantile; +import com.facebook.thrift.metrics.rate.ExpMovingAverageRate; +import com.facebook.thrift.metrics.rate.SlidingTimeWindowMovingCounter; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; /** * Implemetation of StatsSource to capture Thrift Server Counters. It tracks following counters: @@ -34,45 +36,13 @@ * counters and queued_requests, we need to pass instance of ThriftServer to this object. */ public class ServerStats { - - // Counter properties - private final AtomicLong receivedRequests; - - /** - * DecayCounters decays exponentially using a formula to calculate counts in a rolling time window - * https://github.com/airlift/airlift/blob/master/stats/src/main/java/io/airlift/stats/DecayCounter.java - * DecayCounters are used for 1 minute and 1 hour count of various counters. - */ - private final DecayCounter receivedRequestsOneMin; - - private final DecayCounter receivedRequestsOneHour; - - private final AtomicLong sentReplies; - private final DecayCounter sentRepliesOneMin; - private final DecayCounter sentRepliesOneHour; - - private final AtomicLong activeRequests; - private final DecayCounter activeRequestsOneMin; - private final DecayCounter activeRequestsOneHour; - - private final Distribution processTime; - private final Distribution processTimeOneMin; - private final Distribution processTimeOneHour; - - private final Distribution readTime; - private final Distribution readTimeOneMin; - private final Distribution readTimeOneHour; - - private final Distribution writeTime; - private final Distribution writeTimeOneMin; - private final Distribution writeTimeOneHour; - - private final AtomicLong outOfDirectMemroyErrors; - - private final ConcurrentHashMap methodCounters = new ConcurrentHashMap<>(); - private final ConcurrentHashMap methodDecayCounters = + private final ConcurrentHashMap movingAverages = new ConcurrentHashMap<>(); - private final ConcurrentHashMap methodDurations = new ConcurrentHashMap<>(); + private final ConcurrentHashMap counters = + new ConcurrentHashMap<>(); + private final ConcurrentHashMap distributions = + new ConcurrentHashMap<>(); + private final ConcurrentHashMap allTimeCounters = new ConcurrentHashMap<>(); // Thrift Server properties private NiftyMetrics niftyMetrics; @@ -97,45 +67,20 @@ public class ServerStats { private static final String METHOD_NUM_CALLS = ".num_calls.sum"; private static final String METHOD_NUM_PROCESSED = ".num_processed.sum"; private static final String METHOD_NUM_EXCEPTIONS = ".num_exceptions.sum"; - private static final String METHOD_PROCESS_TIME = ".time_process_us.avg"; + private static final String METHOD_PROCESS_TIME = ".time_process_us"; // Common Key Prefix/Suffixes private static final String THRIFT = "thrift."; - private static final String P99 = ".p99"; - private static final String P95 = ".p95"; - private static final String AVG = ".avg"; + private static final String ONE_MINUTE = ".60"; private static final String ONE_HOUR = ".3600"; private static final Map ATTRIBUTE_MAP = new HashMap<>(); - public ServerStats() { - // Initializing all counters - this.receivedRequests = new AtomicLong(0); - this.receivedRequestsOneMin = new DecayCounter(ExponentialDecay.oneMinute()); - this.receivedRequestsOneHour = new DecayCounter(ExponentialDecay.seconds(3600)); - - this.sentReplies = new AtomicLong(0); - this.sentRepliesOneMin = new DecayCounter(ExponentialDecay.oneMinute()); - this.sentRepliesOneHour = new DecayCounter(ExponentialDecay.seconds(3600)); - - this.activeRequests = new AtomicLong(0); - this.activeRequestsOneMin = new DecayCounter(ExponentialDecay.oneMinute()); - this.activeRequestsOneHour = new DecayCounter(ExponentialDecay.seconds(3600)); + public ServerStats() {} - this.processTime = new Distribution(); - this.processTimeOneMin = new Distribution(ExponentialDecay.oneMinute()); - this.processTimeOneHour = new Distribution(ExponentialDecay.seconds(3600)); - - this.readTime = new Distribution(); - this.readTimeOneMin = new Distribution(ExponentialDecay.oneMinute()); - this.readTimeOneHour = new Distribution(ExponentialDecay.seconds(3600)); - - this.writeTime = new Distribution(); - this.writeTimeOneMin = new Distribution(ExponentialDecay.oneMinute()); - this.writeTimeOneHour = new Distribution(ExponentialDecay.seconds(3600)); - - this.outOfDirectMemroyErrors = new AtomicLong(0); + public void markDirectOomError() { + incrementCounter(OUT_OF_DIRECT_MEMORY_EXCEPTIONS_KEY, 1); } public void setNiftyMetrics(NiftyMetrics niftyMetrics) { @@ -147,165 +92,108 @@ public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) { } public void requestReceived(long readTimeDuration, String methodName) { - receivedRequests.incrementAndGet(); - receivedRequestsOneMin.add(1); - receivedRequestsOneHour.add(1); + incrementCounter(RECEIVED_REQUESTS_KEY, 1); - activeRequests.incrementAndGet(); - activeRequestsOneMin.add(1); - activeRequestsOneHour.add(1); + incrementAverages(ACTIVE_REQUESTS_KEY, 1); - readTime.add(readTimeDuration); - readTimeOneMin.add(readTimeDuration); - readTimeOneHour.add(readTimeDuration); + updateDistribution(READ_TIME_KEY, readTimeDuration); - incrementCounterValues(methodName + METHOD_NUM_CALLS); + incrementCounter(THRIFT + methodName + METHOD_NUM_CALLS, 1); } public void publishWriteTime(long writeTimeDuration) { - writeTime.add(writeTimeDuration); - writeTimeOneMin.add(writeTimeDuration); - writeTimeOneHour.add(writeTimeDuration); + updateDistribution(WRITE_TIME_KEY, writeTimeDuration); } public void replySent(long processTimeDuration, String methodName) { - sentReplies.incrementAndGet(); - sentRepliesOneMin.add(1); - sentRepliesOneHour.add(1); - - activeRequests.decrementAndGet(); - activeRequestsOneMin.add(-1); - activeRequestsOneHour.add(-1); + incrementCounter(SENT_REPLIES_KEY, 1); + incrementAverages(ACTIVE_REQUESTS_KEY, 1); + incrementCounter(THRIFT + methodName + METHOD_NUM_PROCESSED, 1); - processTime.add(processTimeDuration); - processTimeOneMin.add(processTimeDuration); - processTimeOneHour.add(processTimeDuration); - - incrementCounterValues(methodName + METHOD_NUM_PROCESSED); - addHistogramValue(methodName + METHOD_PROCESS_TIME, processTimeDuration); + updateDistribution(PROCESS_TIME_KEY, processTimeDuration); + updateDistribution(THRIFT + methodName + METHOD_PROCESS_TIME, processTimeDuration); } public void error(String methodName) { - incrementCounterValues(methodName + METHOD_NUM_EXCEPTIONS); + incrementCounter(THRIFT + methodName + METHOD_NUM_EXCEPTIONS, 1); } public Map getCounters() { - Map counters = new HashMap<>(); - counters.put(RECEIVED_REQUESTS_KEY, receivedRequests.get()); - counters.put(RECEIVED_REQUESTS_KEY + ONE_MINUTE, Math.round(receivedRequestsOneMin.getCount())); - counters.put(RECEIVED_REQUESTS_KEY + ONE_HOUR, Math.round(receivedRequestsOneHour.getCount())); - - counters.put(SENT_REPLIES_KEY, sentReplies.get()); - counters.put(SENT_REPLIES_KEY + ONE_MINUTE, Math.round(sentRepliesOneMin.getCount())); - counters.put(SENT_REPLIES_KEY + ONE_HOUR, Math.round(sentRepliesOneHour.getCount())); - - counters.put(ACTIVE_REQUESTS_KEY, activeRequests.get()); - counters.put(ACTIVE_REQUESTS_KEY + ONE_MINUTE, Math.round(activeRequestsOneMin.getCount())); - counters.put(ACTIVE_REQUESTS_KEY + ONE_HOUR, Math.round(activeRequestsOneHour.getCount())); - - counters.put(READ_TIME_KEY + AVG, Math.round(readTime.getAvg())); - counters.put(READ_TIME_KEY + AVG + ONE_MINUTE, Math.round(readTimeOneMin.getAvg())); - counters.put(READ_TIME_KEY + AVG + ONE_HOUR, Math.round(readTimeOneHour.getAvg())); - - counters.put(READ_TIME_KEY + P95, Math.round(readTime.getP95())); - counters.put(READ_TIME_KEY + P95 + ONE_MINUTE, Math.round(readTimeOneMin.getP95())); - counters.put(READ_TIME_KEY + P95 + ONE_HOUR, Math.round(readTimeOneHour.getP95())); + Map resultCounters = new HashMap<>(64); - counters.put(READ_TIME_KEY + P99, Math.round(readTime.getP99())); - counters.put(READ_TIME_KEY + P99 + ONE_MINUTE, Math.round(readTimeOneMin.getP99())); - counters.put(READ_TIME_KEY + P99 + ONE_HOUR, Math.round(readTimeOneHour.getP99())); + allTimeCounters.forEach((key, counter) -> resultCounters.put(key, counter.sum())); - counters.put(PROCESS_TIME_KEY + AVG, Math.round(processTime.getAvg())); - counters.put(PROCESS_TIME_KEY + AVG + ONE_MINUTE, Math.round(processTimeOneMin.getAvg())); - counters.put(PROCESS_TIME_KEY + AVG + ONE_HOUR, Math.round(processTimeOneHour.getAvg())); - - counters.put(PROCESS_TIME_KEY + P95, Math.round(processTime.getP95())); - counters.put(PROCESS_TIME_KEY + P95 + ONE_MINUTE, Math.round(processTimeOneMin.getP95())); - counters.put(PROCESS_TIME_KEY + P95 + ONE_HOUR, Math.round(processTimeOneHour.getP95())); - - counters.put(PROCESS_TIME_KEY + P99, Math.round(processTime.getP99())); - counters.put(PROCESS_TIME_KEY + P99 + ONE_MINUTE, Math.round(processTimeOneMin.getP99())); - counters.put(PROCESS_TIME_KEY + P99 + ONE_HOUR, Math.round(processTimeOneHour.getP99())); - - counters.put(WRITE_TIME_KEY + AVG, Math.round(writeTime.getAvg())); - counters.put(WRITE_TIME_KEY + AVG + ONE_MINUTE, Math.round(writeTimeOneMin.getAvg())); - counters.put(WRITE_TIME_KEY + AVG + ONE_HOUR, Math.round(writeTimeOneHour.getAvg())); - - counters.put(WRITE_TIME_KEY + P95, Math.round(writeTime.getP95())); - counters.put(WRITE_TIME_KEY + P95 + ONE_MINUTE, Math.round(writeTimeOneMin.getP95())); - counters.put(WRITE_TIME_KEY + P95 + ONE_HOUR, Math.round(writeTimeOneHour.getP95())); + counters.forEach( + (key, counter) -> { + resultCounters.put(key + ONE_MINUTE, counter.oneMinuteRate()); + resultCounters.put(key + ONE_HOUR, counter.oneHourRate()); + }); - counters.put(OUT_OF_DIRECT_MEMORY_EXCEPTIONS_KEY, outOfDirectMemroyErrors.get()); + movingAverages.forEach( + (key, counter) -> { + resultCounters.put(key + ONE_MINUTE, counter.oneMinuteRate()); + resultCounters.put(key + ONE_HOUR, counter.oneHourRate()); + }); - counters.put(WRITE_TIME_KEY + P99, Math.round(writeTime.getP99())); - counters.put(WRITE_TIME_KEY + P99 + ONE_MINUTE, Math.round(writeTimeOneMin.getP99())); - counters.put(WRITE_TIME_KEY + P99 + ONE_HOUR, Math.round(writeTimeOneHour.getP99())); + distributions.forEach( + (key, dist) -> { + addQuantilesToCounters(key, ONE_MINUTE, resultCounters, dist.getOneMinuteQuantiles()); + addQuantilesToCounters(key, ONE_HOUR, resultCounters, dist.getOneHourQuantiles()); + addQuantilesToCounters(key, "", resultCounters, dist.getAllTimeQuantiles()); + }); if (threadPoolExecutor != null && threadPoolExecutor.getQueue() != null) { - counters.put(QUEUED_REQUESTS_KEY, (long) threadPoolExecutor.getQueue().size()); + resultCounters.put(QUEUED_REQUESTS_KEY, (long) threadPoolExecutor.getQueue().size()); } if (niftyMetrics != null) { - counters.put(ACCEPTED_CONNS_KEY, niftyMetrics.getAcceptedConnections()); - counters.put(ACCEPTED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getAcceptedConnectionsOneMin()); - counters.put(ACCEPTED_CONNS_KEY + ONE_HOUR, niftyMetrics.getAcceptedConnectionsOneHour()); + resultCounters.put(ACCEPTED_CONNS_KEY, niftyMetrics.getAcceptedConnections()); + resultCounters.put( + ACCEPTED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getAcceptedConnectionsOneMin()); + resultCounters.put( + ACCEPTED_CONNS_KEY + ONE_HOUR, niftyMetrics.getAcceptedConnectionsOneHour()); + + resultCounters.put(DROPPED_CONNS_KEY, niftyMetrics.getDroppedConnections()); + resultCounters.put( + DROPPED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getDroppedConnectionsOneMin()); + resultCounters.put(DROPPED_CONNS_KEY + ONE_HOUR, niftyMetrics.getDroppedConnectionsOneHour()); + + resultCounters.put(REJECTED_CONNS_KEY, niftyMetrics.getRejectedConnections()); + resultCounters.put( + REJECTED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getRejectedConnectionsOneMin()); + resultCounters.put( + REJECTED_CONNS_KEY + ONE_HOUR, niftyMetrics.getRejectedConnectionsOneHour()); + } - counters.put(DROPPED_CONNS_KEY, niftyMetrics.getDroppedConnections()); - counters.put(DROPPED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getDroppedConnectionsOneMin()); - counters.put(DROPPED_CONNS_KEY + ONE_HOUR, niftyMetrics.getDroppedConnectionsOneHour()); + return resultCounters; + } - counters.put(REJECTED_CONNS_KEY, niftyMetrics.getRejectedConnections()); - counters.put(REJECTED_CONNS_KEY + ONE_MINUTE, niftyMetrics.getRejectedConnectionsOneMin()); - counters.put(REJECTED_CONNS_KEY + ONE_HOUR, niftyMetrics.getRejectedConnectionsOneHour()); + private void addQuantilesToCounters( + String baseKey, String windowKey, Map counters, Map quantiles) { + for (Map.Entry entry : quantiles.entrySet()) { + counters.put(baseKey + "." + entry.getKey().getKey() + windowKey, entry.getValue()); } - - methodCounters.forEach( - (key, value) -> { - counters.put(THRIFT + key, value.get()); - }); - methodDecayCounters.forEach( - (key, value) -> { - counters.put(THRIFT + key, Math.round(value.getCount())); - }); - methodDurations.forEach( - (key, value) -> { - counters.put(THRIFT + key, Math.round(value.getAvg())); - }); - return counters; } public Map getAttributes() { return ATTRIBUTE_MAP; } - public void markDirectOomError() { - this.outOfDirectMemroyErrors.incrementAndGet(); + private void incrementAverages(String key, int value) { + movingAverages.computeIfAbsent(key, k -> new ExpMovingAverageRate()).add(value); } - private void incrementCounterValues(String key) { - AtomicLong counter = methodCounters.computeIfAbsent(key, k -> new AtomicLong(0)); - DecayCounter counterOneMin = - methodDecayCounters.computeIfAbsent( - key + ONE_MINUTE, k -> new DecayCounter(ExponentialDecay.oneMinute())); - DecayCounter counterOneHour = - methodDecayCounters.computeIfAbsent( - key + ONE_HOUR, k -> new DecayCounter(ExponentialDecay.oneMinute())); - - counter.incrementAndGet(); - counterOneMin.add(1); - counterOneHour.add(1); + private void incrementCounter(String key, int value) { + allTimeCounters.computeIfAbsent(key, k -> new LongAdder()).add(value); + counters.computeIfAbsent(key, k -> new SlidingTimeWindowMovingCounter()).add(value); } - private void addHistogramValue(String key, long value) { - Distribution duration = methodDurations.computeIfAbsent(key, k -> new Distribution()); - Distribution durationOneMin = - methodDurations.computeIfAbsent( - key + ONE_MINUTE, k -> new Distribution(ExponentialDecay.oneMinute())); - Distribution durationOneHour = - methodDurations.computeIfAbsent( - key + ONE_HOUR, k -> new Distribution(ExponentialDecay.seconds(3600))); - - duration.add(value); - durationOneMin.add(value); - durationOneHour.add(value); + private void updateDistribution(String key, long value) { + distributions + .computeIfAbsent( + key, + k -> + new MultiWindowDistribution( + Arrays.asList(Quantile.AVG, Quantile.P95, Quantile.P99))) + .add(value); } } diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/MultiWindowDistribution.java b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/MultiWindowDistribution.java index 36a893565be..65eb900f77d 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/MultiWindowDistribution.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/MultiWindowDistribution.java @@ -65,6 +65,10 @@ public MultiWindowDistribution() { Arrays.asList(P50, P75, P90, P95, P99, AVG, MIN, MAX, SUM)); } + public MultiWindowDistribution(List quantiles) { + this(Utils.getExecutorService(), Utils.getClock(), quantiles); + } + public MultiWindowDistribution(ScheduledExecutorService executorService, Clock clock) { this(executorService, clock, Arrays.asList(P50, P75, P90, P95, P99, AVG, MIN, MAX, SUM)); } diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/Utils.java b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/Utils.java index 42b47d8bec2..15cd7ccdbef 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/Utils.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/distribution/Utils.java @@ -18,6 +18,7 @@ import com.facebook.thrift.metrics.common.Clock; import com.facebook.thrift.metrics.common.NanoClock; +import com.google.common.annotations.VisibleForTesting; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -43,6 +44,11 @@ public static ScheduledExecutorService getExecutorService() { return executorService; } + @VisibleForTesting + public static void setExecutorService(ScheduledExecutorService executorService) { + Utils.executorService = executorService; + } + /** Allow clean shutdown for testing purposes */ public static void shutdownExecutorService() { if (executorService != null) { diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/ExpMovingAverageRate.java b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/ExpMovingAverageRate.java index 491b1c1098e..f865d68ecad 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/ExpMovingAverageRate.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/ExpMovingAverageRate.java @@ -37,6 +37,10 @@ public class ExpMovingAverageRate implements Rate { private final AtomicLong lastTick; private final Clock clock; + public ExpMovingAverageRate() { + this(NANO_CLOCK); + } + public ExpMovingAverageRate(Clock clock) { this.m1 = EWMA.oneMinuteEWMA(); this.m10 = EWMA.tenMinuteEWMA(); @@ -47,11 +51,7 @@ public ExpMovingAverageRate(Clock clock) { this.lastTick = new AtomicLong(now); } - public ExpMovingAverageRate() { - this(NANO_CLOCK); - } - - public void update(long count) { + public void add(long count) { if (lastTick == null) { return; } diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/Rate.java b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/Rate.java index cbc9fc9b99f..7cc4b80f3ad 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/Rate.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/Rate.java @@ -17,11 +17,11 @@ package com.facebook.thrift.metrics.rate; public interface Rate { - default void update() { - update(1); + default void add() { + add(1); } - void update(long count); + void add(long count); long oneMinuteRate(); diff --git a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingAverages.java b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingCounter.java similarity index 96% rename from thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingAverages.java rename to thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingCounter.java index 3951e9f31a7..c5f60153075 100644 --- a/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingAverages.java +++ b/thrift/lib/java/runtime/src/main/java/com/facebook/thrift/metrics/rate/SlidingTimeWindowMovingCounter.java @@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; -public class SlidingTimeWindowMovingAverages implements Rate { +public class SlidingTimeWindowMovingCounter implements Rate { private static final long TIME_WINDOW_DURATION_MINUTES = 60L; private static final long TICK_INTERVAL = TimeUnit.SECONDS.toNanos(1L); private static final Duration TIME_WINDOW_DURATION = @@ -41,11 +41,11 @@ public class SlidingTimeWindowMovingAverages implements Rate { private final Instant bucketBaseTime; Instant oldestBucketTime; - public SlidingTimeWindowMovingAverages() { + public SlidingTimeWindowMovingCounter() { this(new NanoClock()); } - public SlidingTimeWindowMovingAverages(Clock clock) { + public SlidingTimeWindowMovingCounter(Clock clock) { this.clock = clock; long startTime = clock.tickNanos(); this.lastTick = new AtomicLong(startTime); @@ -61,7 +61,7 @@ public SlidingTimeWindowMovingAverages(Clock clock) { this.currentBucketIndex = 0; } - public void update(long n) { + public void add(long n) { tickIfNecessary(); (buckets.get(this.currentBucketIndex)).add(n); } diff --git a/thrift/lib/java/runtime/src/test/java/com/facebook/swift/service/TestThriftServerStats.java b/thrift/lib/java/runtime/src/test/java/com/facebook/swift/service/TestThriftServerStats.java new file mode 100644 index 00000000000..05da1f50c27 --- /dev/null +++ b/thrift/lib/java/runtime/src/test/java/com/facebook/swift/service/TestThriftServerStats.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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 com.facebook.swift.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.facebook.swift.service.stats.ServerStats; +import com.facebook.thrift.metrics.distribution.Utils; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.*; + +public class TestThriftServerStats { + + private ServerStats thriftServerStats = new ServerStats(); + + @Mock ScheduledExecutorService executorService; + + @Captor ArgumentCaptor runnableCaptor; + + @Before + public void setup() { + initMocks(this); + Utils.setExecutorService(executorService); + } + + // Ensure at least one interval sample is done on histogram so that data is collected + private void performIntervalSampleOnDistributions() { + verify(executorService, atLeastOnce()) + .scheduleAtFixedRate(runnableCaptor.capture(), anyLong(), anyLong(), any(TimeUnit.class)); + runnableCaptor.getAllValues().forEach(Runnable::run); + } + + @Test + public void testRequestReceived() throws Exception { + thriftServerStats.requestReceived(10L, "foo"); + Map actual = thriftServerStats.getCounters(); + Assert.assertEquals(1L, (long) actual.get("thrift.received_requests.count")); + Assert.assertEquals(1L, (long) actual.get("thrift.received_requests.count.60")); + Assert.assertEquals(1L, (long) actual.get("thrift.received_requests.count.3600")); + + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_calls.sum")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_calls.sum.60")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_calls.sum.3600")); + } + + @Test + public void testReplySent() throws Exception { + thriftServerStats.replySent(20L, "foo"); + Map actual = thriftServerStats.getCounters(); + Assert.assertEquals(1L, (long) actual.get("thrift.sent_replies.count")); + Assert.assertEquals(1L, (long) actual.get("thrift.sent_replies.count.60")); + Assert.assertEquals(1L, (long) actual.get("thrift.sent_replies.count.3600")); + } + + @Test + public void testProcessTime() throws Exception { + thriftServerStats.replySent(10L, "foo"); + + performIntervalSampleOnDistributions(); + + Map actual = thriftServerStats.getCounters(); + + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.avg")); + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.avg.60")); + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.avg.3600")); + + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.p99")); + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.p99.60")); + Assert.assertEquals(10L, (long) actual.get("thrift.process_time.p99.3600")); + + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_processed.sum")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_processed.sum.60")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_processed.sum.3600")); + + Assert.assertEquals(10L, (long) actual.get("thrift.foo.time_process_us.avg")); + Assert.assertEquals(10L, (long) actual.get("thrift.foo.time_process_us.avg.60")); + Assert.assertEquals(10L, (long) actual.get("thrift.foo.time_process_us.avg.3600")); + } + + @Test + public void testError() throws Exception { + thriftServerStats.error("foo"); + Map actual = thriftServerStats.getCounters(); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_exceptions.sum")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_exceptions.sum.60")); + Assert.assertEquals(1L, (long) actual.get("thrift.foo.num_exceptions.sum.3600")); + } + + @Test + public void testWriteTime() throws Exception { + thriftServerStats.publishWriteTime(10L); + + performIntervalSampleOnDistributions(); + + Map actual = thriftServerStats.getCounters(); + + Assert.assertEquals(10L, (long) actual.get("thrift.write_time.p99")); + Assert.assertEquals(10L, (long) actual.get("thrift.write_time.p99.60")); + Assert.assertEquals(10L, (long) actual.get("thrift.write_time.p99.3600")); + } +} diff --git a/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/ExpMovingAverageRateTest.java b/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/ExpMovingAverageRateTest.java index 6f424c2a928..25039d5ec5c 100644 --- a/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/ExpMovingAverageRateTest.java +++ b/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/ExpMovingAverageRateTest.java @@ -34,7 +34,7 @@ public void setUp() { private void advanceSeconds(long seconds) { testClock.incrementSec(seconds); - rate.update(0); + rate.add(0); } private void advanceMinutes(long minutes) { @@ -45,7 +45,7 @@ private void advanceMinutes(long minutes) { public void testNormalQuantilesOverOneCycle() { for (int i = 1; i <= 60; i++) { advanceSeconds(1); - rate.update(1); + rate.add(1); } // One Minute, one per second @@ -57,7 +57,7 @@ public void testNormalQuantilesOverOneCycle() { public void testDataDecaysToZeroOver5Minutes() { for (int i = 1; i <= 60; i++) { advanceSeconds(1); - rate.update(1); + rate.add(1); } assertThat(rate.oneMinuteRate()).isEqualTo(60); diff --git a/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/SlidingTimeWindowMovingAverageTest.java b/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/SlidingTimeWindowMovingAverageTest.java index c535dec5695..d62c4988e8f 100644 --- a/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/SlidingTimeWindowMovingAverageTest.java +++ b/thrift/lib/java/runtime/src/test/java/com/facebook/thrift/metrics/distribution/SlidingTimeWindowMovingAverageTest.java @@ -18,23 +18,23 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.facebook.thrift.metrics.rate.SlidingTimeWindowMovingAverages; +import com.facebook.thrift.metrics.rate.SlidingTimeWindowMovingCounter; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; public class SlidingTimeWindowMovingAverageTest { - private SlidingTimeWindowMovingAverages rate; + private SlidingTimeWindowMovingCounter rate; private final TestClock testClock = new TestClock(); @Before public void setUp() { - rate = new SlidingTimeWindowMovingAverages(testClock); + rate = new SlidingTimeWindowMovingCounter(testClock); } private void advanceSeconds(long seconds) { testClock.incrementSec(seconds); - rate.update(0); + rate.add(0); } private void advanceMinutes(long minutes) { @@ -45,7 +45,7 @@ private void advanceMinutes(long minutes) { public void testNormalQuantilesOverOneCycle() { for (int i = 1; i <= 60; i++) { advanceSeconds(1); - rate.update(1); + rate.add(1); } // One Minute, one per second @@ -57,7 +57,7 @@ public void testNormalQuantilesOverOneCycle() { public void testOneMinuteDataDecaysToZeroOver1Minute() { for (int i = 1; i <= 60; i++) { advanceSeconds(1); - rate.update(1); + rate.add(1); } assertThat(rate.oneMinuteRate()).isEqualTo(60); @@ -72,7 +72,7 @@ public void testOneMinuteDataDecaysToZeroOver1Minute() { public void testTenMinuteDataDecaysToZeroOver10Minutes() { for (int i = 1; i <= 600; i++) { advanceSeconds(1); - rate.update(1); + rate.add(1); } assertThat(rate.tenMinuteRate()).isEqualTo(600); @@ -87,7 +87,7 @@ public void testTenMinuteDataDecaysToZeroOver10Minutes() { public void testOneHourDataDecaysToZeroOver10Minutes() { for (int i = 1; i <= 60; i++) { advanceMinutes(1); - rate.update(1); + rate.add(1); } assertThat(rate.oneHourRate()).isEqualTo(60);