Skip to content

Commit 7c286b9

Browse files
committed
fix(gax): record fractional latency metrics
1 parent 1721e7c commit 7c286b9

2 files changed

Lines changed: 54 additions & 13 deletions

File tree

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.api.gax.rpc.StatusCode;
3939
import com.google.common.annotations.VisibleForTesting;
4040
import com.google.common.base.Stopwatch;
41+
import com.google.common.base.Ticker;
4142
import java.util.HashMap;
4243
import java.util.Map;
4344
import java.util.concurrent.TimeUnit;
@@ -59,17 +60,25 @@ public class MetricsTracer implements ApiTracer {
5960
private static final String OPERATION_FINISHED_STATUS_MESSAGE =
6061
"Operation has already been completed";
6162
private Stopwatch attemptTimer;
62-
private final Stopwatch operationTimer = Stopwatch.createStarted();
63+
private final Ticker ticker;
64+
private final Stopwatch operationTimer;
6365
// These are RPC specific attributes and pertain to a specific API Trace
6466
private final Map<String, String> attributes = new HashMap<>();
6567
private final MetricsRecorder metricsRecorder;
6668
private final AtomicBoolean operationFinished;
6769

6870
public MetricsTracer(MethodName methodName, MetricsRecorder metricsRecorder) {
71+
this(methodName, metricsRecorder, Ticker.systemTicker());
72+
}
73+
74+
@VisibleForTesting
75+
MetricsTracer(MethodName methodName, MetricsRecorder metricsRecorder, Ticker ticker) {
6976
this.attributes.put(METHOD_ATTRIBUTE, methodName.toString());
7077
this.attributes.put(LANGUAGE_ATTRIBUTE, DEFAULT_LANGUAGE);
7178
this.metricsRecorder = metricsRecorder;
7279
this.operationFinished = new AtomicBoolean();
80+
this.ticker = ticker;
81+
this.operationTimer = Stopwatch.createStarted(ticker);
7382
}
7483

7584
/**
@@ -85,8 +94,7 @@ public void operationSucceeded() {
8594
throw new IllegalStateException(OPERATION_FINISHED_STATUS_MESSAGE);
8695
}
8796
attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
88-
metricsRecorder.recordOperationLatency(
89-
operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
97+
metricsRecorder.recordOperationLatency(elapsedMillis(operationTimer), attributes);
9098
metricsRecorder.recordOperationCount(1, attributes);
9199
}
92100

@@ -103,8 +111,7 @@ public void operationCancelled() {
103111
throw new IllegalStateException(OPERATION_FINISHED_STATUS_MESSAGE);
104112
}
105113
attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
106-
metricsRecorder.recordOperationLatency(
107-
operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
114+
metricsRecorder.recordOperationLatency(elapsedMillis(operationTimer), attributes);
108115
metricsRecorder.recordOperationCount(1, attributes);
109116
}
110117

@@ -122,8 +129,7 @@ public void operationFailed(Throwable error) {
122129
}
123130
// Uses the GRPC status code representation.
124131
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
125-
metricsRecorder.recordOperationLatency(
126-
operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
132+
metricsRecorder.recordOperationLatency(elapsedMillis(operationTimer), attributes);
127133
metricsRecorder.recordOperationCount(1, attributes);
128134
}
129135

@@ -138,7 +144,7 @@ public void operationFailed(Throwable error) {
138144
*/
139145
@Override
140146
public void attemptStarted(Object request, int attemptNumber) {
141-
attemptTimer = Stopwatch.createStarted();
147+
attemptTimer = Stopwatch.createStarted(ticker);
142148
}
143149

144150
/**
@@ -148,7 +154,7 @@ public void attemptStarted(Object request, int attemptNumber) {
148154
@Override
149155
public void attemptSucceeded() {
150156
attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
151-
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
157+
metricsRecorder.recordAttemptLatency(elapsedMillis(attemptTimer), attributes);
152158
metricsRecorder.recordAttemptCount(1, attributes);
153159
}
154160

@@ -159,7 +165,7 @@ public void attemptSucceeded() {
159165
@Override
160166
public void attemptCancelled() {
161167
attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
162-
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
168+
metricsRecorder.recordAttemptLatency(elapsedMillis(attemptTimer), attributes);
163169
metricsRecorder.recordAttemptCount(1, attributes);
164170
}
165171

@@ -174,7 +180,7 @@ public void attemptCancelled() {
174180
@Override
175181
public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
176182
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
177-
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
183+
metricsRecorder.recordAttemptLatency(elapsedMillis(attemptTimer), attributes);
178184
metricsRecorder.recordAttemptCount(1, attributes);
179185
}
180186

@@ -198,7 +204,7 @@ public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
198204
@Override
199205
public void attemptFailedRetriesExhausted(Throwable error) {
200206
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
201-
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
207+
metricsRecorder.recordAttemptLatency(elapsedMillis(attemptTimer), attributes);
202208
metricsRecorder.recordAttemptCount(1, attributes);
203209
}
204210

@@ -212,7 +218,7 @@ public void attemptFailedRetriesExhausted(Throwable error) {
212218
@Override
213219
public void attemptPermanentFailure(Throwable error) {
214220
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
215-
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
221+
metricsRecorder.recordAttemptLatency(elapsedMillis(attemptTimer), attributes);
216222
metricsRecorder.recordAttemptCount(1, attributes);
217223
}
218224

@@ -236,6 +242,10 @@ public void addAttributes(Map<String, String> attributes) {
236242
}
237243
;
238244

245+
private static double elapsedMillis(Stopwatch stopwatch) {
246+
return stopwatch.elapsed(TimeUnit.NANOSECONDS) / 1_000_000.0;
247+
}
248+
239249
@VisibleForTesting
240250
Map<String, String> getAttributes() {
241251
return attributes;

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
import com.google.api.gax.rpc.StatusCode.Code;
4444
import com.google.api.gax.rpc.testing.FakeStatusCode;
4545
import com.google.common.collect.ImmutableMap;
46+
import com.google.common.testing.FakeTicker;
4647
import java.util.HashMap;
4748
import java.util.Map;
49+
import java.util.concurrent.TimeUnit;
4850
import org.junit.jupiter.api.BeforeEach;
4951
import org.junit.jupiter.api.Test;
5052
import org.junit.jupiter.api.extension.ExtendWith;
@@ -86,6 +88,35 @@ void testOperationSucceeded_recordsAttributes() {
8688
verifyNoMoreInteractions(metricsRecorder);
8789
}
8890

91+
@Test
92+
void testOperationSucceeded_recordsFractionalMilliseconds() {
93+
FakeTicker ticker = new FakeTicker();
94+
metricsTracer =
95+
new MetricsTracer(MethodName.of("fake_service", "fake_method"), metricsRecorder, ticker);
96+
ticker.advance(3_900_000, TimeUnit.NANOSECONDS);
97+
98+
metricsTracer.operationSucceeded();
99+
100+
verify(metricsRecorder).recordOperationLatency(3.9, getAttributes(Code.OK));
101+
verify(metricsRecorder).recordOperationCount(1, getAttributes(Code.OK));
102+
verifyNoMoreInteractions(metricsRecorder);
103+
}
104+
105+
@Test
106+
void testAttemptSucceeded_recordsFractionalMilliseconds() {
107+
FakeTicker ticker = new FakeTicker();
108+
metricsTracer =
109+
new MetricsTracer(MethodName.of("fake_service", "fake_method"), metricsRecorder, ticker);
110+
metricsTracer.attemptStarted(new Object(), 0);
111+
ticker.advance(4_200_000, TimeUnit.NANOSECONDS);
112+
113+
metricsTracer.attemptSucceeded();
114+
115+
verify(metricsRecorder).recordAttemptLatency(4.2, getAttributes(Code.OK));
116+
verify(metricsRecorder).recordAttemptCount(1, getAttributes(Code.OK));
117+
verifyNoMoreInteractions(metricsRecorder);
118+
}
119+
89120
@Test
90121
void testOperationFailed_recordsAttributes() {
91122
ApiException error0 =

0 commit comments

Comments
 (0)