Skip to content

Commit de3b476

Browse files
authored
feat: send attempt and timestamp in headers (#935)
* feat: send attempt and timestamp in headers * refactor * update clirr files * refactor * review updates * remove "x-" * update * Add epoch precision to the header * use microseconds * update micro seconds calculation * fix formatting * Rename headers class * rename local variables * update comment
1 parent a141aa3 commit de3b476

File tree

10 files changed

+474
-44
lines changed

10 files changed

+474
-44
lines changed

google-cloud-bigtable/clirr-ignored-differences.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@
3434
<className>com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub</className>
3535
<method>*</method>
3636
</difference>
37+
<!-- InternalApi that was renamed -->
38+
<difference>
39+
<differenceType>8001</differenceType>
40+
<className>com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerFactory</className>
41+
</difference>
3742
</differences>

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@
7070
import com.google.cloud.bigtable.data.v2.models.RowAdapter;
7171
import com.google.cloud.bigtable.data.v2.models.RowMutation;
7272
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
73-
import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory;
73+
import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerFactory;
7474
import com.google.cloud.bigtable.data.v2.stub.metrics.HeaderTracerStreamingCallable;
7575
import com.google.cloud.bigtable.data.v2.stub.metrics.HeaderTracerUnaryCallable;
7676
import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory;
7777
import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants;
78+
import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersServerStreamingCallable;
79+
import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersUnaryCallable;
7880
import com.google.cloud.bigtable.data.v2.stub.mutaterows.BulkMutateRowsUserFacingCallable;
7981
import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor;
8082
import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsRetryingCallable;
@@ -191,7 +193,7 @@ public static EnhancedBigtableStubSettings finalizeSettings(
191193
.build();
192194
// Inject Opencensus instrumentation
193195
builder.setTracerFactory(
194-
new CompositeTracerFactory(
196+
new BigtableTracerFactory(
195197
ImmutableList.of(
196198
// Add OpenCensus Tracing
197199
new OpencensusTracerFactory(
@@ -397,11 +399,14 @@ public Map<String, String> extract(ReadRowsRequest readRowsRequest) {
397399
.build(),
398400
readRowsSettings.getRetryableCodes());
399401

402+
ServerStreamingCallable<ReadRowsRequest, ReadRowsResponse> withStatsHeaders =
403+
new StatsHeadersServerStreamingCallable<>(base);
404+
400405
// Sometimes ReadRows connections are disconnected via an RST frame. This error is transient and
401406
// should be treated similar to UNAVAILABLE. However, this exception has an INTERNAL error code
402407
// which by default is not retryable. Convert the exception so it can be retried in the client.
403408
ServerStreamingCallable<ReadRowsRequest, ReadRowsResponse> convertException =
404-
new ReadRowsConvertExceptionCallable<>(base);
409+
new ReadRowsConvertExceptionCallable<>(withStatsHeaders);
405410

406411
ServerStreamingCallable<ReadRowsRequest, RowT> merging =
407412
new RowMergingCallable<>(convertException, rowAdapter);
@@ -468,9 +473,12 @@ public Map<String, String> extract(
468473

469474
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> spoolable = base.all();
470475

476+
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> withStatsHeaders =
477+
new StatsHeadersUnaryCallable<>(spoolable);
478+
471479
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> withHeaderTracer =
472480
new HeaderTracerUnaryCallable<>(
473-
spoolable, settings.getHeaderTracer(), getSpanName(methodName).toString());
481+
withStatsHeaders, settings.getHeaderTracer(), getSpanName(methodName).toString());
474482

475483
UnaryCallable<SampleRowKeysRequest, List<SampleRowKeysResponse>> retryable =
476484
Callables.retrying(withHeaderTracer, settings.sampleRowKeysSettings(), clientContext);
@@ -505,9 +513,12 @@ public Map<String, String> extract(MutateRowRequest mutateRowRequest) {
505513
.build(),
506514
settings.mutateRowSettings().getRetryableCodes());
507515

516+
UnaryCallable<MutateRowRequest, MutateRowResponse> withStatsHeaders =
517+
new StatsHeadersUnaryCallable<>(base);
518+
508519
UnaryCallable<MutateRowRequest, MutateRowResponse> withHeaderTracer =
509520
new HeaderTracerUnaryCallable<>(
510-
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
521+
withStatsHeaders, settings.getHeaderTracer(), getSpanName(methodName).toString());
511522

512523
UnaryCallable<MutateRowRequest, MutateRowResponse> retrying =
513524
Callables.retrying(withHeaderTracer, settings.mutateRowSettings(), clientContext);
@@ -646,6 +657,9 @@ public Map<String, String> extract(MutateRowsRequest mutateRowsRequest) {
646657
.build(),
647658
settings.bulkMutateRowsSettings().getRetryableCodes());
648659

660+
ServerStreamingCallable<MutateRowsRequest, MutateRowsResponse> withStatsHeaders =
661+
new StatsHeadersServerStreamingCallable<>(base);
662+
649663
RetryAlgorithm<Void> retryAlgorithm =
650664
new RetryAlgorithm<>(
651665
new ApiResultRetryAlgorithm<Void>(),
@@ -656,7 +670,7 @@ public Map<String, String> extract(MutateRowsRequest mutateRowsRequest) {
656670

657671
return new MutateRowsRetryingCallable(
658672
clientContext.getDefaultCallContext(),
659-
base,
673+
withStatsHeaders,
660674
retryingExecutor,
661675
settings.bulkMutateRowsSettings().getRetryableCodes());
662676
}
@@ -689,9 +703,12 @@ public Map<String, String> extract(
689703
.build(),
690704
settings.checkAndMutateRowSettings().getRetryableCodes());
691705

706+
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> withStatsHeaders =
707+
new StatsHeadersUnaryCallable<>(base);
708+
692709
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> withHeaderTracer =
693710
new HeaderTracerUnaryCallable<>(
694-
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
711+
withStatsHeaders, settings.getHeaderTracer(), getSpanName(methodName).toString());
695712

696713
UnaryCallable<CheckAndMutateRowRequest, CheckAndMutateRowResponse> retrying =
697714
Callables.retrying(withHeaderTracer, settings.checkAndMutateRowSettings(), clientContext);
@@ -726,10 +743,14 @@ public Map<String, String> extract(ReadModifyWriteRowRequest request) {
726743
})
727744
.build(),
728745
settings.readModifyWriteRowSettings().getRetryableCodes());
746+
747+
UnaryCallable<ReadModifyWriteRowRequest, ReadModifyWriteRowResponse> withStatsHeaders =
748+
new StatsHeadersUnaryCallable<>(base);
749+
729750
String methodName = "ReadModifyWriteRow";
730751
UnaryCallable<ReadModifyWriteRowRequest, ReadModifyWriteRowResponse> withHeaderTracer =
731752
new HeaderTracerUnaryCallable<>(
732-
base, settings.getHeaderTracer(), getSpanName(methodName).toString());
753+
withStatsHeaders, settings.getHeaderTracer(), getSpanName(methodName).toString());
733754

734755
UnaryCallable<ReadModifyWriteRowRequest, ReadModifyWriteRowResponse> retrying =
735756
Callables.retrying(withHeaderTracer, settings.readModifyWriteRowSettings(), clientContext);

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java renamed to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@
1515
*/
1616
package com.google.cloud.bigtable.data.v2.stub.metrics;
1717

18+
import com.google.api.gax.rpc.ApiCallContext;
1819
import com.google.api.gax.tracing.ApiTracer;
1920
import com.google.api.gax.tracing.BaseApiTracer;
2021
import com.google.common.collect.ImmutableList;
2122
import java.util.ArrayList;
2223
import java.util.List;
2324
import org.threeten.bp.Duration;
2425

25-
/** Combines multiple {@link ApiTracer}s into a single {@link ApiTracer}. */
26-
class CompositeTracer extends BaseApiTracer {
26+
/**
27+
* A Bigtable specific {@link ApiTracer} that will be used to plumb additional context through the
28+
* call chains as well as combines multiple user defined {@link ApiTracer}s into a single one. This
29+
* will ensure that operation lifecycle events are plumbed through while maintaining user configured
30+
* functionalities.
31+
*/
32+
class BigtableTracer extends BaseApiTracer {
2733
private final List<ApiTracer> children;
34+
private volatile int attempt = 0;
2835

29-
CompositeTracer(List<ApiTracer> children) {
36+
BigtableTracer(List<ApiTracer> children) {
3037
this.children = ImmutableList.copyOf(children);
3138
}
3239

@@ -78,6 +85,7 @@ public void connectionSelected(String id) {
7885

7986
@Override
8087
public void attemptStarted(int attemptNumber) {
88+
this.attempt = attemptNumber;
8189
for (ApiTracer child : children) {
8290
child.attemptStarted(attemptNumber);
8391
}
@@ -152,4 +160,13 @@ public void batchRequestSent(long elementCount, long requestSize) {
152160
child.batchRequestSent(elementCount, requestSize);
153161
}
154162
}
163+
164+
/**
165+
* Get the attempt number of the current call. Attempt number for the current call is passed in
166+
* and recorded in {@link #attemptStarted(int)}. With the getter we can access it from {@link
167+
* ApiCallContext}. Attempt number starts from 0.
168+
*/
169+
public int getAttempt() {
170+
return attempt;
171+
}
155172
}

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerFactory.java renamed to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerFactory.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import java.util.ArrayList;
2525
import java.util.List;
2626

27-
/** Combines multiple {@link ApiTracerFactory} into a single {@link ApiTracerFactory}. */
27+
/**
28+
* A Bigtable specific {@link ApiTracerFactory} that combines multiple {@link ApiTracerFactory} into
29+
* a single one.
30+
*/
2831
@InternalApi("For internal use only")
29-
public class CompositeTracerFactory extends BaseApiTracerFactory {
32+
public class BigtableTracerFactory extends BaseApiTracerFactory {
3033
private final List<ApiTracerFactory> apiTracerFactories;
3134

32-
public CompositeTracerFactory(List<ApiTracerFactory> apiTracerFactories) {
35+
public BigtableTracerFactory(List<ApiTracerFactory> apiTracerFactories) {
3336
this.apiTracerFactories = ImmutableList.copyOf(apiTracerFactories);
3437
}
3538

@@ -40,6 +43,6 @@ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType op
4043
for (ApiTracerFactory factory : apiTracerFactories) {
4144
children.add(factory.newTracer(parent, spanName, operationType));
4245
}
43-
return new CompositeTracer(children);
46+
return new BigtableTracer(children);
4447
}
4548
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.data.v2.stub.metrics;
18+
19+
import com.google.api.core.InternalApi;
20+
import com.google.api.gax.rpc.ApiCallContext;
21+
import com.google.api.gax.rpc.ResponseObserver;
22+
import com.google.api.gax.rpc.ServerStreamingCallable;
23+
24+
/**
25+
* A callable that injects client timestamp and current attempt number to request headers. Attempt
26+
* number starts from 0.
27+
*/
28+
@InternalApi("For internal use only")
29+
public final class StatsHeadersServerStreamingCallable<RequestT, ResponseT>
30+
extends ServerStreamingCallable<RequestT, ResponseT> {
31+
private final ServerStreamingCallable innerCallable;
32+
33+
public StatsHeadersServerStreamingCallable(ServerStreamingCallable innerCallable) {
34+
this.innerCallable = innerCallable;
35+
}
36+
37+
@Override
38+
public void call(
39+
RequestT request,
40+
ResponseObserver<ResponseT> responseObserver,
41+
ApiCallContext apiCallContext) {
42+
ApiCallContext newCallContext =
43+
apiCallContext.withExtraHeaders(Util.createStatsHeaders(apiCallContext));
44+
innerCallable.call(request, responseObserver, newCallContext);
45+
}
46+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.data.v2.stub.metrics;
18+
19+
import com.google.api.core.ApiFuture;
20+
import com.google.api.core.InternalApi;
21+
import com.google.api.gax.rpc.ApiCallContext;
22+
import com.google.api.gax.rpc.UnaryCallable;
23+
24+
/**
25+
* A callable that injects client timestamp and current attempt number to request headers. Attempt
26+
* number starts from 0.
27+
*/
28+
@InternalApi("For internal use only")
29+
public final class StatsHeadersUnaryCallable<RequestT, ResponseT>
30+
extends UnaryCallable<RequestT, ResponseT> {
31+
private final UnaryCallable innerCallable;
32+
33+
public StatsHeadersUnaryCallable(UnaryCallable innerCallable) {
34+
this.innerCallable = innerCallable;
35+
}
36+
37+
@Override
38+
public ApiFuture futureCall(RequestT request, ApiCallContext apiCallContext) {
39+
ApiCallContext newCallContext =
40+
apiCallContext.withExtraHeaders(Util.createStatsHeaders(apiCallContext));
41+
return innerCallable.futureCall(request, newCallContext);
42+
}
43+
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,33 @@
1515
*/
1616
package com.google.cloud.bigtable.data.v2.stub.metrics;
1717

18+
import com.google.api.gax.rpc.ApiCallContext;
1819
import com.google.api.gax.rpc.ApiException;
1920
import com.google.api.gax.rpc.StatusCode;
2021
import com.google.api.gax.rpc.StatusCode.Code;
22+
import com.google.common.collect.ImmutableMap;
23+
import io.grpc.Metadata;
2124
import io.grpc.Status;
2225
import io.grpc.StatusException;
2326
import io.grpc.StatusRuntimeException;
2427
import io.opencensus.tags.TagValue;
28+
import java.time.Instant;
29+
import java.time.temporal.ChronoUnit;
30+
import java.util.Arrays;
31+
import java.util.List;
32+
import java.util.Map;
2533
import java.util.concurrent.CancellationException;
2634
import java.util.concurrent.ExecutionException;
2735
import java.util.concurrent.Future;
2836
import javax.annotation.Nullable;
2937

3038
/** Utilities to help integrating with OpenCensus. */
3139
class Util {
40+
static final Metadata.Key<String> ATTEMPT_HEADER_KEY =
41+
Metadata.Key.of("bigtable-attempt", Metadata.ASCII_STRING_MARSHALLER);
42+
static final Metadata.Key<String> ATTEMPT_EPOCH_KEY =
43+
Metadata.Key.of("bigtable-client-attempt-epoch-usec", Metadata.ASCII_STRING_MARSHALLER);
44+
3245
private static final TagValue OK_STATUS = TagValue.create(StatusCode.Code.OK.toString());
3346

3447
/** Convert an exception into a value that can be used as an OpenCensus tag value. */
@@ -71,4 +84,21 @@ static TagValue extractStatus(Future<?> future) {
7184
}
7285
return extractStatus(error);
7386
}
87+
88+
/**
89+
* Add attempt number and client timestamp from api call context to request headers. Attempt
90+
* number starts from 0.
91+
*/
92+
static Map<String, List<String>> createStatsHeaders(ApiCallContext apiCallContext) {
93+
ImmutableMap.Builder<String, List<String>> headers = ImmutableMap.builder();
94+
headers.put(
95+
ATTEMPT_EPOCH_KEY.name(),
96+
Arrays.asList(String.valueOf(Instant.EPOCH.until(Instant.now(), ChronoUnit.MICROS))));
97+
// This should always be true
98+
if (apiCallContext.getTracer() instanceof BigtableTracer) {
99+
int attemptCount = ((BigtableTracer) apiCallContext.getTracer()).getAttempt();
100+
headers.put(ATTEMPT_HEADER_KEY.name(), Arrays.asList(String.valueOf(attemptCount)));
101+
}
102+
return headers.build();
103+
}
74104
}

0 commit comments

Comments
 (0)