Skip to content

Commit 8240779

Browse files
authored
feat: Surface the server-timing metric (#535)
* Extract server-timing trailer and create metrics for gfe latency * Add more tests and refactor * Refactor comments and imports * reformatting * Clean up comments * Refactor, use GrpcMetadataResponse to get the trailer * Fix based on the code review * clean up HeaderTracerResponseObserver * Add more tests for all the ops * Improve documents, changes for directPath and more tests * Small fixes in the doc * small clean up
1 parent 3f28923 commit 8240779

File tree

15 files changed

+1161
-130
lines changed

15 files changed

+1161
-130
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,22 @@ metrics will be tagged with:
296296
each client RPC, tagged by operation name and the attempt status. Under normal
297297
circumstances, this will be identical to op_latency. However, when the client
298298
receives transient errors, op_latency will be the sum of all attempt_latencies
299-
and the exponential delays
299+
and the exponential delays.
300300

301301
* `cloud.google.com/java/bigtable/attempts_per_op`: A distribution of attempts that
302302
each operation required, tagged by operation name and final operation status.
303303
Under normal circumstances, this will be 1.
304304

305+
### GFE metric views:
306+
307+
* `cloud.google.com/java/bigtable/gfe_latency`: A distribution of the latency
308+
between Google's network receives an RPC and reads back the first byte of
309+
the response.
310+
311+
* `cloud.google.com/java/bigtable/gfe_header_missing_count`: A counter of the
312+
number of RPC responses received without the server-timing header, which
313+
indicates that the request probably never reached Google's network.
314+
305315

306316
By default, the functionality is disabled. For example to enable metrics using
307317
[Google Stackdriver](https://cloud.google.com/monitoring/docs/):
@@ -357,6 +367,8 @@ StackdriverStatsExporter.createAndRegister(
357367
);
358368

359369
BigtableDataSettings.enableOpenCensusStats();
370+
// Enable GFE metric views
371+
BigtableDataSettings.enableGfeOpenCensusStats();
360372
```
361373

362374
## Version Conflicts

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,20 @@ public static void enableOpenCensusStats() {
175175
// io.opencensus.contrib.grpc.metrics.RpcViews.registerClientGrpcBasicViews();
176176
}
177177

178+
/**
179+
* Enables OpenCensus GFE metric aggregations.
180+
*
181+
* <p>This will register views for gfe_latency and gfe_header_missing_count metrics.
182+
*
183+
* <p>gfe_latency measures the latency between Google's network receives an RPC and reads back the
184+
* first byte of the response. gfe_header_missing_count is a counter of the number of RPC
185+
* responses received without the server-timing header.
186+
*/
187+
@BetaApi("OpenCensus stats integration is currently unstable and may change in the future")
188+
public static void enableGfeOpenCensusStats() {
189+
com.google.cloud.bigtable.data.v2.stub.metrics.RpcViews.registerBigtableClientGfeViews();
190+
}
191+
178192
/** Returns the target project id. */
179193
public String getProjectId() {
180194
return stubSettings.getProjectId();

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
import com.google.cloud.bigtable.data.v2.models.RowMutation;
6767
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
6868
import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory;
69+
import com.google.cloud.bigtable.data.v2.stub.metrics.HeaderTracerStreamingCallable;
70+
import com.google.cloud.bigtable.data.v2.stub.metrics.HeaderTracerUnaryCallable;
6971
import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory;
7072
import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants;
7173
import com.google.cloud.bigtable.data.v2.stub.mutaterows.BulkMutateRowsUserFacingCallable;
@@ -162,6 +164,15 @@ public static EnhancedBigtableStubSettings finalizeSettings(
162164
.build());
163165
}
164166

167+
ImmutableMap<TagKey, TagValue> attributes =
168+
ImmutableMap.<TagKey, TagValue>builder()
169+
.put(RpcMeasureConstants.BIGTABLE_PROJECT_ID, TagValue.create(settings.getProjectId()))
170+
.put(
171+
RpcMeasureConstants.BIGTABLE_INSTANCE_ID, TagValue.create(settings.getInstanceId()))
172+
.put(
173+
RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID,
174+
TagValue.create(settings.getAppProfileId()))
175+
.build();
165176
// Inject Opencensus instrumentation
166177
builder.setTracerFactory(
167178
new CompositeTracerFactory(
@@ -187,23 +198,17 @@ public static EnhancedBigtableStubSettings finalizeSettings(
187198
GaxProperties.getLibraryVersion(EnhancedBigtableStubSettings.class))
188199
.build()),
189200
// Add OpenCensus Metrics
190-
MetricsTracerFactory.create(
191-
tagger,
192-
stats,
193-
ImmutableMap.<TagKey, TagValue>builder()
194-
.put(
195-
RpcMeasureConstants.BIGTABLE_PROJECT_ID,
196-
TagValue.create(settings.getProjectId()))
197-
.put(
198-
RpcMeasureConstants.BIGTABLE_INSTANCE_ID,
199-
TagValue.create(settings.getInstanceId()))
200-
.put(
201-
RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID,
202-
TagValue.create(settings.getAppProfileId()))
203-
.build()),
201+
MetricsTracerFactory.create(tagger, stats, attributes),
204202
// Add user configured tracer
205203
settings.getTracerFactory())));
206-
204+
builder.setHeaderTracer(
205+
builder
206+
.getHeaderTracer()
207+
.toBuilder()
208+
.setStats(stats)
209+
.setTagger(tagger)
210+
.setStatsAttributes(attributes)
211+
.build());
207212
return builder.build();
208213
}
209214

@@ -268,11 +273,10 @@ public <RowT> ServerStreamingCallable<Query, RowT> createReadRowsCallable(
268273
ServerStreamingCallable<Query, RowT> readRowsUserCallable =
269274
new ReadRowsUserCallable<>(readRowsCallable, requestContext);
270275

276+
SpanName span = getSpanName("ReadRows");
271277
ServerStreamingCallable<Query, RowT> traced =
272278
new TracedServerStreamingCallable<>(
273-
readRowsUserCallable,
274-
clientContext.getTracerFactory(),
275-
SpanName.of(CLIENT_NAME, "ReadRows"));
279+
readRowsUserCallable, clientContext.getTracerFactory(), span);
276280

277281
return traced.withDefaultCallContext(clientContext.getDefaultCallContext());
278282
}
@@ -315,6 +319,7 @@ public <RowT> UnaryCallable<Query, RowT> createReadRowCallable(RowAdapter<RowT>
315319
* <li>Upon receiving the response stream, it will merge the {@link
316320
* com.google.bigtable.v2.ReadRowsResponse.CellChunk}s in logical rows. The actual row
317321
* implementation can be configured by the {@code rowAdapter} parameter.
322+
* <li>Add header tracer for tracking GFE metrics.
318323
* <li>Retry/resume on failure.
319324
* <li>Filter out marker rows.
320325
* </ul>
@@ -356,10 +361,14 @@ public Map<String, String> extract(ReadRowsRequest readRowsRequest) {
356361
ServerStreamingCallable<ReadRowsRequest, RowT> watched =
357362
Callables.watched(merging, innerSettings, clientContext);
358363

364+
ServerStreamingCallable<ReadRowsRequest, RowT> withHeaderTracer =
365+
new HeaderTracerStreamingCallable<>(
366+
watched, settings.getHeaderTracer(), getSpanName("ReadRows").toString());
367+
359368
// Retry logic is split into 2 parts to workaround a rare edge case described in
360369
// ReadRowsRetryCompletedCallable
361370
ServerStreamingCallable<ReadRowsRequest, RowT> retrying1 =
362-
new ReadRowsRetryCompletedCallable<>(watched);
371+
new ReadRowsRetryCompletedCallable<>(withHeaderTracer);
363372

364373
ServerStreamingCallable<ReadRowsRequest, RowT> retrying2 =
365374
Callables.retrying(retrying1, innerSettings, clientContext);
@@ -380,6 +389,8 @@ public Map<String, String> extract(ReadRowsRequest readRowsRequest) {
380389
* </ul>
381390
*/
382391
private UnaryCallable<String, List<KeyOffset>> createSampleRowKeysCallable() {
392+
String methodName = "SampleRowKeys";
393+
383394
ServerStreamingCallable<SampleRowKeysRequest, SampleRowKeysResponse> base =
384395
GrpcRawCallableFactory.createServerStreamingCallable(
385396
GrpcCallSettings.<SampleRowKeysRequest, SampleRowKeysResponse>newBuilder()
@@ -399,11 +410,15 @@ public Map<String, String> extract(
399410

400411
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> spoolable = base.all();
401412

413+
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> withHeaderTracer =
414+
new HeaderTracerUnaryCallable<>(
415+
spoolable, settings.getHeaderTracer(), getSpanName(methodName).toString());
416+
402417
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> retryable =
403-
Callables.retrying(spoolable, settings.sampleRowKeysSettings(), clientContext);
418+
Callables.retrying(withHeaderTracer, settings.sampleRowKeysSettings(), clientContext);
404419

405420
return createUserFacingUnaryCallable(
406-
"SampleRowKeys", new SampleRowKeysCallable(retryable, requestContext));
421+
methodName, new SampleRowKeysCallable(retryable, requestContext));
407422
}
408423

409424
/**
@@ -415,6 +430,7 @@ public Map<String, String> extract(
415430
* </ul>
416431
*/
417432
private UnaryCallable<RowMutation, Void> createMutateRowCallable() {
433+
String methodName = "MutateRow";
418434
UnaryCallable<MutateRowRequest, MutateRowResponse> base =
419435
GrpcRawCallableFactory.createUnaryCallable(
420436
GrpcCallSettings.<MutateRowRequest, MutateRowResponse>newBuilder()
@@ -431,11 +447,15 @@ public Map<String, String> extract(MutateRowRequest mutateRowRequest) {
431447
.build(),
432448
settings.mutateRowSettings().getRetryableCodes());
433449

450+
UnaryCallable<MutateRowRequest, MutateRowResponse> withHeaderTracer =
451+
new HeaderTracerUnaryCallable<>(
452+
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
453+
434454
UnaryCallable<MutateRowRequest, MutateRowResponse> retrying =
435-
Callables.retrying(base, settings.mutateRowSettings(), clientContext);
455+
Callables.retrying(withHeaderTracer, settings.mutateRowSettings(), clientContext);
436456

437457
return createUserFacingUnaryCallable(
438-
"MutateRow", new MutateRowCallable(retrying, requestContext));
458+
methodName, new MutateRowCallable(retrying, requestContext));
439459
}
440460

441461
/**
@@ -459,11 +479,13 @@ private UnaryCallable<BulkMutation, Void> createBulkMutateRowsCallable() {
459479
UnaryCallable<BulkMutation, Void> userFacing =
460480
new BulkMutateRowsUserFacingCallable(baseCallable, requestContext);
461481

482+
SpanName spanName = getSpanName("MutateRows");
462483
UnaryCallable<BulkMutation, Void> traced =
463-
new TracedUnaryCallable<>(
464-
userFacing, clientContext.getTracerFactory(), SpanName.of(CLIENT_NAME, "MutateRows"));
484+
new TracedUnaryCallable<>(userFacing, clientContext.getTracerFactory(), spanName);
485+
UnaryCallable<BulkMutation, Void> withHeaderTracer =
486+
new HeaderTracerUnaryCallable<>(traced, settings.getHeaderTracer(), spanName.toString());
465487

466-
return traced.withDefaultCallContext(clientContext.getDefaultCallContext());
488+
return withHeaderTracer.withDefaultCallContext(clientContext.getDefaultCallContext());
467489
}
468490

469491
/**
@@ -569,6 +591,7 @@ public Map<String, String> extract(MutateRowsRequest mutateRowsRequest) {
569591
* </ul>
570592
*/
571593
private UnaryCallable<ConditionalRowMutation, Boolean> createCheckAndMutateRowCallable() {
594+
String methodName = "CheckAndMutateRow";
572595
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> base =
573596
GrpcRawCallableFactory.createUnaryCallable(
574597
GrpcCallSettings.<CheckAndMutateRowRequest, CheckAndMutateRowResponse>newBuilder()
@@ -586,11 +609,15 @@ public Map<String, String> extract(
586609
.build(),
587610
settings.checkAndMutateRowSettings().getRetryableCodes());
588611

612+
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> withHeaderTracer =
613+
new HeaderTracerUnaryCallable<>(
614+
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
615+
589616
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> retrying =
590-
Callables.retrying(base, settings.checkAndMutateRowSettings(), clientContext);
617+
Callables.retrying(withHeaderTracer, settings.checkAndMutateRowSettings(), clientContext);
591618

592619
return createUserFacingUnaryCallable(
593-
"CheckAndMutateRow", new CheckAndMutateRowCallable(retrying, requestContext));
620+
methodName, new CheckAndMutateRowCallable(retrying, requestContext));
594621
}
595622

596623
/**
@@ -619,12 +646,16 @@ public Map<String, String> extract(ReadModifyWriteRowRequest request) {
619646
})
620647
.build(),
621648
settings.readModifyWriteRowSettings().getRetryableCodes());
649+
String methodName = "ReadModifyWriteRow";
650+
UnaryCallable<ReadModifyWriteRowRequest, ReadModifyWriteRowResponse> withHeaderTracer =
651+
new HeaderTracerUnaryCallable<>(
652+
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
622653

623654
UnaryCallable<ReadModifyWriteRowRequest, ReadModifyWriteRowResponse> retrying =
624-
Callables.retrying(base, settings.readModifyWriteRowSettings(), clientContext);
655+
Callables.retrying(withHeaderTracer, settings.readModifyWriteRowSettings(), clientContext);
625656

626657
return createUserFacingUnaryCallable(
627-
"ReadModifyWriteRow", new ReadModifyWriteRowCallable(retrying, requestContext));
658+
methodName, new ReadModifyWriteRowCallable(retrying, requestContext));
628659
}
629660

630661
/**
@@ -635,8 +666,7 @@ private <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createUserFacin
635666
String methodName, UnaryCallable<RequestT, ResponseT> inner) {
636667

637668
UnaryCallable<RequestT, ResponseT> traced =
638-
new TracedUnaryCallable<>(
639-
inner, clientContext.getTracerFactory(), SpanName.of(CLIENT_NAME, methodName));
669+
new TracedUnaryCallable<>(inner, clientContext.getTracerFactory(), getSpanName(methodName));
640670

641671
return traced.withDefaultCallContext(clientContext.getDefaultCallContext());
642672
}
@@ -686,6 +716,10 @@ public UnaryCallable<ReadModifyWriteRow, Row> readModifyWriteRowCallable() {
686716
}
687717
// </editor-fold>
688718

719+
private SpanName getSpanName(String methodName) {
720+
return SpanName.of(CLIENT_NAME, methodName);
721+
}
722+
689723
@Override
690724
public void close() {
691725
for (BackgroundResource backgroundResource : clientContext.getBackgroundResources()) {

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
3939
import com.google.cloud.bigtable.data.v2.models.Row;
4040
import com.google.cloud.bigtable.data.v2.models.RowMutation;
41+
import com.google.cloud.bigtable.data.v2.stub.metrics.HeaderTracer;
4142
import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor;
4243
import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor;
4344
import com.google.common.base.MoreObjects;
@@ -154,6 +155,7 @@ public class EnhancedBigtableStubSettings extends StubSettings<EnhancedBigtableS
154155
private final String appProfileId;
155156
private final boolean isRefreshingChannel;
156157
private ImmutableList<String> primedTableIds;
158+
private HeaderTracer headerTracer;
157159

158160
private final ServerStreamingCallSettings<Query, Row> readRowsSettings;
159161
private final UnaryCallSettings<Query, Row> readRowSettings;
@@ -187,6 +189,7 @@ private EnhancedBigtableStubSettings(Builder builder) {
187189
appProfileId = builder.appProfileId;
188190
isRefreshingChannel = builder.isRefreshingChannel;
189191
primedTableIds = builder.primedTableIds;
192+
headerTracer = builder.headerTracer;
190193

191194
// Per method settings.
192195
readRowsSettings = builder.readRowsSettings.build();
@@ -231,6 +234,11 @@ public List<String> getPrimedTableIds() {
231234
return primedTableIds;
232235
}
233236

237+
/** Gets the tracer for capturing metrics in the header. */
238+
HeaderTracer getHeaderTracer() {
239+
return headerTracer;
240+
}
241+
234242
/** Returns a builder for the default ChannelProvider for this service. */
235243
public static InstantiatingGrpcChannelProvider.Builder defaultGrpcTransportProviderBuilder() {
236244
return BigtableStubSettings.defaultGrpcTransportProviderBuilder()
@@ -488,6 +496,7 @@ public static class Builder extends StubSettings.Builder<EnhancedBigtableStubSet
488496
private String appProfileId;
489497
private boolean isRefreshingChannel;
490498
private ImmutableList<String> primedTableIds;
499+
private HeaderTracer headerTracer;
491500

492501
private final ServerStreamingCallSettings.Builder<Query, Row> readRowsSettings;
493502
private final UnaryCallSettings.Builder<Query, Row> readRowSettings;
@@ -511,6 +520,7 @@ private Builder() {
511520
this.appProfileId = SERVER_DEFAULT_APP_PROFILE_ID;
512521
this.isRefreshingChannel = false;
513522
primedTableIds = ImmutableList.of();
523+
headerTracer = HeaderTracer.newBuilder().build();
514524
setCredentialsProvider(defaultCredentialsProviderBuilder().build());
515525

516526
// Defaults provider
@@ -617,6 +627,7 @@ private Builder(EnhancedBigtableStubSettings settings) {
617627
appProfileId = settings.appProfileId;
618628
isRefreshingChannel = settings.isRefreshingChannel;
619629
primedTableIds = settings.primedTableIds;
630+
headerTracer = settings.headerTracer;
620631

621632
// Per method settings.
622633
readRowsSettings = settings.readRowsSettings.toBuilder();
@@ -739,6 +750,17 @@ public List<String> getPrimedTableIds() {
739750
return primedTableIds;
740751
}
741752

753+
/** Configure the header tracer for surfacing metrics in the header. */
754+
Builder setHeaderTracer(HeaderTracer headerTracer) {
755+
this.headerTracer = headerTracer;
756+
return this;
757+
}
758+
759+
/** Gets the header tracer that'll be used to surface metrics in the header. */
760+
HeaderTracer getHeaderTracer() {
761+
return headerTracer;
762+
}
763+
742764
/** Returns the builder for the settings used for calls to readRows. */
743765
public ServerStreamingCallSettings.Builder<Query, Row> readRowsSettings() {
744766
return readRowsSettings;
@@ -818,6 +840,7 @@ public String toString() {
818840
.add("appProfileId", appProfileId)
819841
.add("isRefreshingChannel", isRefreshingChannel)
820842
.add("primedTableIds", primedTableIds)
843+
.add("headerTracer", headerTracer)
821844
.add("readRowsSettings", readRowsSettings)
822845
.add("readRowSettings", readRowSettings)
823846
.add("sampleRowKeysSettings", sampleRowKeysSettings)

0 commit comments

Comments
 (0)