Skip to content

Commit

Permalink
Optimization for slow AsyncGauge execution (#31)
Browse files Browse the repository at this point in the history
* Optimization for slow AsyncGauge execution

This PR introduces a dynamic way to track slow AsyncGauge metric
execution and tries not to block the caller thread as much as possible.
In the high-level, this PR introduces a `AsyncGaugeExecutor`, which
implements the following strategy:
 1. There are two executors and one for regular metrics and the other one is for slow metrics.
 2. All the metric evaluations are triggered by the caller.
 3. If the actual metric execution time exceeds the configured slow metric threshold, it will be moved to slow metric tracking map,
    which indicates the following behaviors:
    a. The next metric measurement call will return the cached value immediately.
    b. The submitted measurable will be executed asynchronously.
    c. If the actual measurement runtime latency becomes lower than the slow metric threshold, it will be moved out
       of slow metric tracking map.
 4. If the actual metric execution time belows the configured slow metric threshold, the following behaviors will be observed:
    a. After submitting the measurable to the regular executor, it will wait up to configured {@link AsyncGaugeExecutor#initialMetricsMeasurementTimeoutInMs}
       to collect the latest result.
    b. If it can't collect the latest value in step #a, the next call will examine the previous execution to decide
       whether it should be put into the slow metric tracking map or not.

 5. There is an async thread to clean up inactive metrics from slow metric tracking map to avoid the accumulation of garbage because of metric deletion.

There are several config params of `AsyncGaugeExecutor` and the user
can tune it according to the actual load pattern, and the caller
can construct a global `AsyncGaugeExecutor` and pass it to `MetricsRepository`
via `MetricConfig`.

* Addressed comments

* Addressed new comments
  • Loading branch information
gaojieliu authored Mar 14, 2024
1 parent 45e4965 commit a50499f
Show file tree
Hide file tree
Showing 6 changed files with 431 additions and 92 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
group=io.tehuti
archivesBaseName=tehuti
version=0.11.3
version=0.11.4

signing.enabled=false
signing.keyId=
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/io/tehuti/metrics/MetricConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ public class MetricConfig {
private long timeWindowMs;
private TimeUnit unit;
private long expirationAge;
private AsyncGaugeConfig asyncGaugeConfig;
private AsyncGauge.AsyncGaugeExecutor asyncGaugeExecutor;

public MetricConfig() {
this(AsyncGauge.DEFAULT_ASYNC_GAUGE_CONFIG);
this(AsyncGauge.DEFAULT_ASYNC_GAUGE_EXECUTOR);
}

public MetricConfig(AsyncGaugeConfig asyncGaugeConfig) {
public MetricConfig(AsyncGauge.AsyncGaugeExecutor asyncGaugeExecutor) {
super();
this.quota = null;
this.samples = 2;
this.timeWindowMs = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS);
this.unit = TimeUnit.SECONDS;
this.asyncGaugeConfig = asyncGaugeConfig;
this.asyncGaugeExecutor = asyncGaugeExecutor;
updateExpirationAge();
}

Expand Down Expand Up @@ -93,7 +93,7 @@ public long expirationAge() {
return this.expirationAge;
}

public AsyncGaugeConfig getAsyncGaugeConfig() {
return asyncGaugeConfig;
public AsyncGauge.AsyncGaugeExecutor getAsyncGaugeExecutor() {
return this.asyncGaugeExecutor;
}
}
13 changes: 8 additions & 5 deletions src/main/java/io/tehuti/metrics/MetricsRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package io.tehuti.metrics;

import io.tehuti.metrics.stats.AsyncGauge;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -248,8 +249,8 @@ public Metric getMetric(String name) {
return this.metrics.get(name);
}

public AsyncGaugeConfig getAsyncGaugeConfig() {
return this.config.getAsyncGaugeConfig();
public AsyncGauge.AsyncGaugeExecutor getAsyncGaugeExecutor() {
return this.config.getAsyncGaugeExecutor();
}

/**
Expand All @@ -261,9 +262,11 @@ public AsyncGaugeConfig getAsyncGaugeConfig() {
public void close() {
for (MetricsReporter reporter : this.reporters)
reporter.close();
AsyncGaugeConfig asyncGaugeConfig = getAsyncGaugeConfig();
if (asyncGaugeConfig != null) {
asyncGaugeConfig.getMetricsMeasurementExecutor().shutdownNow();
AsyncGauge.AsyncGaugeExecutor asyncGaugeExecutor = getAsyncGaugeExecutor();
try {
asyncGaugeExecutor.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Loading

0 comments on commit a50499f

Please sign in to comment.