Skip to content

Commit 52d59ce

Browse files
authored
feat: add response protos (#1246)
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/java-bigtable/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) Fixes #<issue_number_goes_here> ☕️ If you write sample code, please follow the [samples format]( https://github.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
1 parent 93edfe1 commit 52d59ce

File tree

11 files changed

+285
-29
lines changed

11 files changed

+285
-29
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ If you are using Maven without BOM, add this to your dependencies:
4949
If you are using Gradle 5.x or later, add this to your dependencies
5050

5151
```Groovy
52-
implementation platform('com.google.cloud:libraries-bom:25.4.0')
52+
implementation platform('com.google.cloud:libraries-bom:26.0.0')
5353
5454
implementation 'com.google.cloud:google-cloud-bigtable'
5555
```

google-cloud-bigtable-stats/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
<dependency>
5151
<groupId>io.opencensus</groupId>
5252
<artifactId>opencensus-impl</artifactId>
53-
<scope>test</scope>
53+
<scope>runtime</scope>
5454
</dependency>
5555
<dependency>
5656
<groupId>com.google.truth</groupId>

google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/BuiltinViews.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
/** For registering built-in metric views */
2626
@InternalApi("For internal use only")
2727
public class BuiltinViews {
28+
2829
@VisibleForTesting
2930
static final ImmutableSet<View> BIGTABLE_BUILTIN_VIEWS =
3031
ImmutableSet.of(
@@ -44,7 +45,7 @@ void registerPrivateViews(ViewManager viewManager) {
4445
}
4546
}
4647

47-
public void registerBigtableBuiltinViews() {
48+
public static void registerBigtableBuiltinViews() {
4849
ViewManager viewManager = Stats.getViewManager();
4950
for (View view : BIGTABLE_BUILTIN_VIEWS) {
5051
viewManager.registerView(view);

google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
import com.google.api.core.InternalApi;
2121
import com.google.api.gax.tracing.SpanName;
2222
import io.opencensus.stats.Stats;
23+
import java.util.ArrayList;
24+
import java.util.List;
2325
import java.util.Map;
26+
import java.util.stream.Collectors;
2427

2528
/**
2629
* Wrapper class for accessing opencensus. We use a shaded version of opencensus to avoid polluting
@@ -34,4 +37,16 @@ public static StatsRecorderWrapper createRecorder(
3437
return new StatsRecorderWrapper(
3538
operationType, spanName, statsAttributes, Stats.getStatsRecorder());
3639
}
40+
41+
// This is used in integration tests to get the tag value strings from view manager because Stats
42+
// is relocated to com.google.bigtable.veneer.repackaged.io.opencensus.
43+
@InternalApi("Visible for testing")
44+
public static List<String> getOperationLatencyViewTagValueStrings() {
45+
return Stats.getViewManager().getView(BuiltinViewConstants.OPERATION_LATENCIES_VIEW.getName())
46+
.getAggregationMap().entrySet().stream()
47+
.map(Map.Entry::getKey)
48+
.flatMap(x -> x.stream())
49+
.map(x -> x.asString())
50+
.collect(Collectors.toCollection(ArrayList::new));
51+
}
3752
}

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,26 @@
2121
import com.google.api.gax.rpc.ResponseObserver;
2222
import com.google.api.gax.rpc.ServerStreamingCallable;
2323
import com.google.api.gax.rpc.StreamController;
24+
import com.google.bigtable.v2.ResponseParams;
2425
import com.google.common.base.Preconditions;
2526
import com.google.common.base.Stopwatch;
27+
import com.google.protobuf.InvalidProtocolBufferException;
2628
import io.grpc.Metadata;
2729
import java.util.concurrent.TimeUnit;
2830
import javax.annotation.Nonnull;
2931

3032
/**
3133
* This callable will
32-
*
33-
* <p>-inject a {@link GrpcResponseMetadata} to access the headers and trailers returned by gRPC
34-
* methods upon completion. The {@link BigtableTracer} will process metrics that were injected in
35-
* the header/trailer and publish them to OpenCensus. If {@link GrpcResponseMetadata#getMetadata()}
36-
* returned null, it probably means that the request has never reached GFE, and it'll increment the
37-
* gfe_header_missing_counter in this case.
38-
*
39-
* <p>-Call {@link BigtableTracer#onRequest()} to record the request events in a stream.
40-
*
41-
* <p>This class is considered an internal implementation detail and not meant to be used by
42-
* applications.
34+
* <li>-Inject a {@link GrpcResponseMetadata} to access the headers returned by gRPC methods upon
35+
* completion. The {@link BigtableTracer} will process metrics that were injected in the
36+
* header/trailer and publish them to OpenCensus. If {@link GrpcResponseMetadata#getMetadata()}
37+
* returned null, it probably means that the request has never reached GFE, and it'll increment
38+
* the gfe_header_missing_counter in this case.
39+
* <li>-This class will also access trailers from {@link GrpcResponseMetadata} to record zone and
40+
* cluster ids.
41+
* <li>-Call {@link BigtableTracer#onRequest(int)} to record the request events in a stream.
42+
* <li>This class is considered an internal implementation detail and not meant to be used by
43+
* applications.
4344
*/
4445
@InternalApi
4546
public class BigtableTracerStreamingCallable<RequestT, ResponseT>
@@ -102,6 +103,14 @@ public void onError(Throwable t) {
102103
Metadata metadata = responseMetadata.getMetadata();
103104
Long latency = Util.getGfeLatency(metadata);
104105
tracer.recordGfeMetadata(latency, t);
106+
try {
107+
byte[] trailers =
108+
metadata.get(Metadata.Key.of(Util.RESPONSE_PRAMS_KEY, Metadata.BINARY_BYTE_MARSHALLER));
109+
ResponseParams decodedTrailers = ResponseParams.parseFrom(trailers);
110+
tracer.setLocations(decodedTrailers.getZoneId(), decodedTrailers.getClusterId());
111+
} catch (NullPointerException | InvalidProtocolBufferException e) {
112+
}
113+
105114
outerObserver.onError(t);
106115
}
107116

@@ -110,6 +119,14 @@ public void onComplete() {
110119
Metadata metadata = responseMetadata.getMetadata();
111120
Long latency = Util.getGfeLatency(metadata);
112121
tracer.recordGfeMetadata(latency, null);
122+
try {
123+
byte[] trailers =
124+
metadata.get(Metadata.Key.of(Util.RESPONSE_PRAMS_KEY, Metadata.BINARY_BYTE_MARSHALLER));
125+
ResponseParams decodedTrailers = ResponseParams.parseFrom(trailers);
126+
tracer.setLocations(decodedTrailers.getZoneId(), decodedTrailers.getClusterId());
127+
} catch (NullPointerException | InvalidProtocolBufferException e) {
128+
}
129+
113130
outerObserver.onComplete();
114131
}
115132
}

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,24 @@
2222
import com.google.api.gax.grpc.GrpcResponseMetadata;
2323
import com.google.api.gax.rpc.ApiCallContext;
2424
import com.google.api.gax.rpc.UnaryCallable;
25+
import com.google.bigtable.v2.ResponseParams;
2526
import com.google.common.base.Preconditions;
2627
import com.google.common.util.concurrent.MoreExecutors;
28+
import com.google.protobuf.InvalidProtocolBufferException;
2729
import io.grpc.Metadata;
2830
import javax.annotation.Nonnull;
2931

3032
/**
31-
* This callable will inject a {@link GrpcResponseMetadata} to access the headers and trailers
32-
* returned by gRPC methods upon completion. The {@link BigtableTracer} will process metrics that
33-
* were injected in the header/trailer and publish them to OpenCensus. If {@link
34-
* GrpcResponseMetadata#getMetadata()} returned null, it probably means that the request has never
35-
* reached GFE, and it'll increment the gfe_header_missing_counter in this case.
36-
*
37-
* <p>This class is considered an internal implementation detail and not meant to be used by
38-
* applications.
33+
* This callable will:
34+
* <li>- Inject a {@link GrpcResponseMetadata} to access the headers returned by gRPC methods upon
35+
* completion. The {@link BigtableTracer} will process metrics that were injected in the
36+
* header/trailer and publish them to OpenCensus. If {@link GrpcResponseMetadata#getMetadata()}
37+
* returned null, it probably means that the request has never reached GFE, and it'll increment
38+
* the gfe_header_missing_counter in this case.
39+
* <li>-This class will also access trailers from {@link GrpcResponseMetadata} to record zone and
40+
* cluster ids.
41+
* <li>This class is considered an internal implementation detail and not meant to be used by
42+
* applications.
3943
*/
4044
@InternalApi
4145
public class BigtableTracerUnaryCallable<RequestT, ResponseT>
@@ -78,13 +82,27 @@ public void onFailure(Throwable throwable) {
7882
Metadata metadata = responseMetadata.getMetadata();
7983
Long latency = Util.getGfeLatency(metadata);
8084
tracer.recordGfeMetadata(latency, throwable);
85+
try {
86+
byte[] trailers =
87+
metadata.get(Metadata.Key.of(Util.RESPONSE_PRAMS_KEY, Metadata.BINARY_BYTE_MARSHALLER));
88+
ResponseParams decodedTrailers = ResponseParams.parseFrom(trailers);
89+
tracer.setLocations(decodedTrailers.getZoneId(), decodedTrailers.getClusterId());
90+
} catch (NullPointerException | InvalidProtocolBufferException e) {
91+
}
8192
}
8293

8394
@Override
8495
public void onSuccess(ResponseT response) {
8596
Metadata metadata = responseMetadata.getMetadata();
8697
Long latency = Util.getGfeLatency(metadata);
8798
tracer.recordGfeMetadata(latency, null);
99+
try {
100+
byte[] trailers =
101+
metadata.get(Metadata.Key.of(Util.RESPONSE_PRAMS_KEY, Metadata.BINARY_BYTE_MARSHALLER));
102+
ResponseParams decodedTrailers = ResponseParams.parseFrom(trailers);
103+
tracer.setLocations(decodedTrailers.getZoneId(), decodedTrailers.getClusterId());
104+
} catch (NullPointerException | InvalidProtocolBufferException e) {
105+
}
88106
}
89107
}
90108
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class Util {
5858
Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER);
5959
private static final Pattern SERVER_TIMING_HEADER_PATTERN = Pattern.compile(".*dur=(?<dur>\\d+)");
6060

61-
static final String TRAILER_KEY = "x-goog-ext-425905942-bin";
61+
static final String RESPONSE_PRAMS_KEY = "x-goog-ext-425905942-bin";
6262

6363
/** Convert an exception into a value that can be used to create an OpenCensus tag value. */
6464
static String extractStatus(@Nullable Throwable error) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2022 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+
package com.google.cloud.bigtable.data.v2.it;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
import static com.google.common.truth.TruthJUnit.assume;
20+
21+
import com.google.api.core.ApiFuture;
22+
import com.google.api.gax.rpc.NotFoundException;
23+
import com.google.cloud.bigtable.admin.v2.models.Cluster;
24+
import com.google.cloud.bigtable.data.v2.models.Query;
25+
import com.google.cloud.bigtable.data.v2.models.Row;
26+
import com.google.cloud.bigtable.stats.BuiltinViews;
27+
import com.google.cloud.bigtable.stats.StatsWrapper;
28+
import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
29+
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
30+
import com.google.common.collect.Lists;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.UUID;
34+
import java.util.concurrent.TimeUnit;
35+
import org.junit.BeforeClass;
36+
import org.junit.ClassRule;
37+
import org.junit.Test;
38+
39+
public class StreamingMetricsMetadataIT {
40+
@ClassRule public static TestEnvRule testEnvRule = new TestEnvRule();
41+
42+
@BeforeClass
43+
public static void setUpClass() {
44+
assume()
45+
.withMessage("StreamingMetricsMetadataIT is not supported on Emulator")
46+
.that(testEnvRule.env())
47+
.isNotInstanceOf(EmulatorEnv.class);
48+
BuiltinViews.registerBigtableBuiltinViews();
49+
}
50+
51+
@Test
52+
public void testSuccess() throws Exception {
53+
String prefix = UUID.randomUUID().toString();
54+
String uniqueKey = prefix + "-read";
55+
56+
Query query = Query.create(testEnvRule.env().getTableId()).rowKey(uniqueKey);
57+
ArrayList<Row> rows = Lists.newArrayList(testEnvRule.env().getDataClient().readRows(query));
58+
59+
ApiFuture<List<Cluster>> clustersFuture =
60+
testEnvRule
61+
.env()
62+
.getInstanceAdminClient()
63+
.listClustersAsync(testEnvRule.env().getInstanceId());
64+
65+
List<Cluster> clusters = clustersFuture.get(1, TimeUnit.MINUTES);
66+
67+
// give opencensus some time to populate view data
68+
Thread.sleep(100);
69+
70+
List<String> tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings();
71+
assertThat(tagValueStrings).contains(clusters.get(0).getZone());
72+
assertThat(tagValueStrings).contains(clusters.get(0).getId());
73+
}
74+
75+
@Test
76+
public void testFailure() throws InterruptedException {
77+
Query query = Query.create("non-exist-table");
78+
try {
79+
Lists.newArrayList(testEnvRule.env().getDataClient().readRows(query));
80+
} catch (NotFoundException e) {
81+
}
82+
83+
// give opencensus some time to populate view data
84+
Thread.sleep(100);
85+
86+
List<String> tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings();
87+
assertThat(tagValueStrings).contains("undefined");
88+
assertThat(tagValueStrings).contains("undefined");
89+
}
90+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2022 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+
package com.google.cloud.bigtable.data.v2.it;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
import static com.google.common.truth.TruthJUnit.assume;
20+
21+
import com.google.api.core.ApiFuture;
22+
import com.google.api.gax.rpc.NotFoundException;
23+
import com.google.cloud.bigtable.admin.v2.models.Cluster;
24+
import com.google.cloud.bigtable.data.v2.models.RowMutation;
25+
import com.google.cloud.bigtable.stats.BuiltinViews;
26+
import com.google.cloud.bigtable.stats.StatsWrapper;
27+
import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
28+
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
29+
import java.util.List;
30+
import java.util.UUID;
31+
import java.util.concurrent.TimeUnit;
32+
import org.junit.BeforeClass;
33+
import org.junit.ClassRule;
34+
import org.junit.Test;
35+
36+
public class UnaryMetricsMetadataIT {
37+
@ClassRule public static TestEnvRule testEnvRule = new TestEnvRule();
38+
39+
@BeforeClass
40+
public static void setUpClass() {
41+
assume()
42+
.withMessage("UnaryMetricsMetadataIT is not supported on Emulator")
43+
.that(testEnvRule.env())
44+
.isNotInstanceOf(EmulatorEnv.class);
45+
BuiltinViews.registerBigtableBuiltinViews();
46+
}
47+
48+
@Test
49+
public void testSuccess() throws Exception {
50+
String rowKey = UUID.randomUUID().toString();
51+
String familyId = testEnvRule.env().getFamilyId();
52+
53+
ApiFuture<Void> future =
54+
testEnvRule
55+
.env()
56+
.getDataClient()
57+
.mutateRowCallable()
58+
.futureCall(
59+
RowMutation.create(testEnvRule.env().getTableId(), rowKey)
60+
.setCell(familyId, "q", "myVal"));
61+
62+
future.get(1, TimeUnit.MINUTES);
63+
64+
ApiFuture<List<Cluster>> clustersFuture =
65+
testEnvRule
66+
.env()
67+
.getInstanceAdminClient()
68+
.listClustersAsync(testEnvRule.env().getInstanceId());
69+
List<Cluster> clusters = clustersFuture.get(1, TimeUnit.MINUTES);
70+
71+
// give opencensus some time to populate view data
72+
Thread.sleep(100);
73+
74+
List<String> tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings();
75+
assertThat(tagValueStrings).contains(clusters.get(0).getZone());
76+
assertThat(tagValueStrings).contains(clusters.get(0).getId());
77+
}
78+
79+
@Test
80+
public void testFailure() throws InterruptedException {
81+
String rowKey = UUID.randomUUID().toString();
82+
String familyId = testEnvRule.env().getFamilyId();
83+
84+
try {
85+
testEnvRule
86+
.env()
87+
.getDataClient()
88+
.mutateRowCallable()
89+
.call(RowMutation.create("non-exist-table", rowKey).setCell(familyId, "q", "myVal"));
90+
} catch (NotFoundException e) {
91+
}
92+
93+
// give opencensus some time to populate view data
94+
Thread.sleep(100);
95+
96+
List<String> tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings();
97+
assertThat(tagValueStrings).contains("undefined");
98+
assertThat(tagValueStrings).contains("undefined");
99+
}
100+
}

0 commit comments

Comments
 (0)