Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit d65feb3

Browse files
chore: merge main into generate-libraries-main
2 parents 13adc2d + bc46174 commit d65feb3

19 files changed

Lines changed: 370 additions & 410 deletions

google-cloud-bigtable/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,13 @@
136136
<groupId>com.google.protobuf</groupId>
137137
<artifactId>protobuf-java-util</artifactId>
138138
</dependency>
139-
<dependency>
140-
<groupId>com.google.code.gson</groupId>
141-
<artifactId>gson</artifactId>
142-
</dependency>
143139
<dependency>
144140
<groupId>io.opencensus</groupId>
145141
<artifactId>opencensus-api</artifactId>
146142
</dependency>
147143
<dependency>
148144
<groupId>io.grpc</groupId>
149145
<artifactId>grpc-alts</artifactId>
150-
<scope>runtime</scope>
151146
</dependency>
152147
<dependency>
153148
<groupId>org.checkerframework</groupId>

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ public CreateInstanceRequest addLabel(@Nonnull String key, @Nonnull String value
118118
return this;
119119
}
120120

121+
/**
122+
* Adds a tag to the instance.
123+
*
124+
* <p>Tags are a way to organize and govern resources across Google Cloud. Unlike labels, Tags are
125+
* standalone resources created and managed through the Resource Manager API.
126+
*
127+
* @see <a href="https://cloud.google.com/bigtable/docs/tags">For more details</a>
128+
*/
129+
@SuppressWarnings("WeakerAccess")
130+
public CreateInstanceRequest addTag(@Nonnull String key, @Nonnull String value) {
131+
Preconditions.checkNotNull(key, "Key can't be null");
132+
Preconditions.checkNotNull(value, "Value can't be null");
133+
builder.getInstanceBuilder().putTags(key, value);
134+
return this;
135+
}
136+
121137
/**
122138
* Adds a cluster to the instance request with manual scaling enabled.
123139
*

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ public String getDisplayName() {
150150
return proto.getDisplayName();
151151
}
152152

153+
/** Gets the instance's tags. */
154+
@SuppressWarnings("WeakerAccess")
155+
public Map<String, String> getTags() {
156+
return proto.getTagsMap();
157+
}
158+
153159
/** Gets the instance's current type. Can be DEVELOPMENT or PRODUCTION. */
154160
@SuppressWarnings("WeakerAccess")
155161
public Type getType() {

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -695,10 +695,10 @@ public ApiFuture<List<KeyOffset>> futureCall(String s, ApiCallContext apiCallCon
695695
withStatsHeaders = new StatsHeadersUnaryCallable<>(spoolable);
696696

697697
UnaryCallable<com.google.bigtable.v2.SampleRowKeysRequest, List<SampleRowKeysResponse>>
698-
withBigtableTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders);
698+
withAttemptTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders);
699699

700700
UnaryCallable<com.google.bigtable.v2.SampleRowKeysRequest, List<SampleRowKeysResponse>>
701-
retryable = withRetries(withBigtableTracer, settings.sampleRowKeysSettings());
701+
retryable = withRetries(withAttemptTracer, settings.sampleRowKeysSettings());
702702

703703
return createUserFacingUnaryCallable(
704704
methodName,
@@ -774,7 +774,7 @@ private UnaryCallable<BulkMutation, MutateRowsAttemptResult> createMutateRowsBas
774774
ServerStreamingCallable<MutateRowsRequest, MutateRowsResponse> convertException =
775775
new ConvertExceptionCallable<>(callable);
776776

777-
ServerStreamingCallable<MutateRowsRequest, MutateRowsResponse> withBigtableTracer =
777+
ServerStreamingCallable<MutateRowsRequest, MutateRowsResponse> withAttemptTracer =
778778
new BigtableTracerStreamingCallable<>(convertException);
779779

780780
BasicResultRetryAlgorithm<MutateRowsAttemptResult> resultRetryAlgorithm;
@@ -797,7 +797,7 @@ private UnaryCallable<BulkMutation, MutateRowsAttemptResult> createMutateRowsBas
797797
UnaryCallable<MutateRowsRequest, MutateRowsAttemptResult> baseCallable =
798798
new MutateRowsRetryingCallable(
799799
clientContext.getDefaultCallContext(),
800-
withBigtableTracer,
800+
withAttemptTracer,
801801
retryingExecutor,
802802
settings.bulkMutateRowsSettings().getRetryableCodes(),
803803
retryAlgorithm);
@@ -1033,11 +1033,11 @@ private UnaryCallable<ReadModifyWriteRow, Row> createReadModifyWriteRowCallable(
10331033
ServerStreamingCallable<String, ByteStringRange> watched =
10341034
Callables.watched(convertException, innerSettings, clientContext);
10351035

1036-
ServerStreamingCallable<String, ByteStringRange> withBigtableTracer =
1036+
ServerStreamingCallable<String, ByteStringRange> withAttemptTracer =
10371037
new BigtableTracerStreamingCallable<>(watched);
10381038

10391039
ServerStreamingCallable<String, ByteStringRange> retrying =
1040-
withRetries(withBigtableTracer, innerSettings);
1040+
withRetries(withAttemptTracer, innerSettings);
10411041

10421042
SpanName span = getSpanName("GenerateInitialChangeStreamPartitions");
10431043
ServerStreamingCallable<String, ByteStringRange> traced =
@@ -1105,11 +1105,11 @@ private UnaryCallable<ReadModifyWriteRow, Row> createReadModifyWriteRowCallable(
11051105
ServerStreamingCallable<ReadChangeStreamRequest, ChangeStreamRecordT> watched =
11061106
Callables.watched(merging, innerSettings, clientContext);
11071107

1108-
ServerStreamingCallable<ReadChangeStreamRequest, ChangeStreamRecordT> withBigtableTracer =
1108+
ServerStreamingCallable<ReadChangeStreamRequest, ChangeStreamRecordT> withAttemptTracer =
11091109
new BigtableTracerStreamingCallable<>(watched);
11101110

11111111
ServerStreamingCallable<ReadChangeStreamRequest, ChangeStreamRecordT> readChangeStreamCallable =
1112-
withRetries(withBigtableTracer, innerSettings);
1112+
withRetries(withAttemptTracer, innerSettings);
11131113

11141114
ServerStreamingCallable<ReadChangeStreamQuery, ChangeStreamRecordT>
11151115
readChangeStreamUserCallable =
@@ -1175,6 +1175,9 @@ public Map<String, String> extract(ExecuteQueryRequest executeQueryRequest) {
11751175
ServerStreamingCallable<ExecuteQueryCallContext, ExecuteQueryResponse> convertException =
11761176
new ConvertExceptionCallable<>(withPlanRefresh);
11771177

1178+
ServerStreamingCallable<ExecuteQueryCallContext, ExecuteQueryResponse> withAttemptTracer =
1179+
new BigtableTracerStreamingCallable<>(convertException);
1180+
11781181
ServerStreamingCallSettings<ExecuteQueryCallContext, ExecuteQueryResponse> retrySettings =
11791182
ServerStreamingCallSettings.<ExecuteQueryCallContext, ExecuteQueryResponse>newBuilder()
11801183
.setResumptionStrategy(new ExecuteQueryResumptionStrategy())
@@ -1189,7 +1192,7 @@ public Map<String, String> extract(ExecuteQueryRequest executeQueryRequest) {
11891192
// attempt stream will have reset set to true, so any unyielded data from the previous
11901193
// attempt will be reset properly
11911194
ServerStreamingCallable<ExecuteQueryCallContext, ExecuteQueryResponse> retries =
1192-
withRetries(convertException, retrySettings);
1195+
withRetries(withAttemptTracer, retrySettings);
11931196

11941197
ServerStreamingCallable<ExecuteQueryCallContext, SqlRow> merging =
11951198
new SqlRowMergingCallable(retries);
@@ -1208,13 +1211,10 @@ public Map<String, String> extract(ExecuteQueryRequest executeQueryRequest) {
12081211
ServerStreamingCallable<ExecuteQueryCallContext, SqlRow> passingThroughErrorsToMetadata =
12091212
new MetadataErrorHandlingCallable(watched);
12101213

1211-
ServerStreamingCallable<ExecuteQueryCallContext, SqlRow> withBigtableTracer =
1212-
new BigtableTracerStreamingCallable<>(passingThroughErrorsToMetadata);
1213-
12141214
SpanName span = getSpanName("ExecuteQuery");
12151215
ServerStreamingCallable<ExecuteQueryCallContext, SqlRow> traced =
12161216
new TracedServerStreamingCallable<>(
1217-
withBigtableTracer, clientContext.getTracerFactory(), span);
1217+
passingThroughErrorsToMetadata, clientContext.getTracerFactory(), span);
12181218

12191219
return new ExecuteQueryCallable(
12201220
traced.withDefaultCallContext(

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,8 @@ private Builder() {
922922
.setReverseScans(true)
923923
.setLastScannedRowResponses(true)
924924
.setDirectAccessRequested(DIRECT_PATH_ENABLED)
925-
.setTrafficDirectorEnabled(DIRECT_PATH_ENABLED);
925+
.setTrafficDirectorEnabled(DIRECT_PATH_ENABLED)
926+
.setPeerInfo(true);
926927
}
927928

928929
private Builder(EnhancedBigtableStubSettings settings) {
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Copyright 2026 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.stub;
17+
18+
import com.google.api.core.InternalApi;
19+
import com.google.api.gax.grpc.GrpcCallContext;
20+
import com.google.bigtable.v2.PeerInfo;
21+
import com.google.bigtable.v2.ResponseParams;
22+
import com.google.common.base.Strings;
23+
import com.google.protobuf.InvalidProtocolBufferException;
24+
import io.grpc.Attributes;
25+
import io.grpc.CallOptions;
26+
import io.grpc.Channel;
27+
import io.grpc.ClientCall;
28+
import io.grpc.ClientInterceptor;
29+
import io.grpc.ClientInterceptors;
30+
import io.grpc.ForwardingClientCall;
31+
import io.grpc.ForwardingClientCallListener;
32+
import io.grpc.Metadata;
33+
import io.grpc.MethodDescriptor;
34+
import io.grpc.Status;
35+
import io.grpc.alts.AltsContextUtil;
36+
import java.util.Base64;
37+
import java.util.regex.Matcher;
38+
import java.util.regex.Pattern;
39+
import javax.annotation.Nullable;
40+
41+
@InternalApi
42+
public class MetadataExtractorInterceptor implements ClientInterceptor {
43+
private final SidebandData sidebandData = new SidebandData();
44+
45+
public GrpcCallContext injectInto(GrpcCallContext ctx) {
46+
// TODO: migrate to using .withTransportChannel
47+
// This will require a change on gax's side to expose the underlying ManagedChannel in
48+
// GrpcTransportChannel (its currently package private).
49+
return ctx.withChannel(ClientInterceptors.intercept(ctx.getChannel(), this))
50+
.withCallOptions(ctx.getCallOptions().withOption(SidebandData.KEY, sidebandData));
51+
}
52+
53+
@Override
54+
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
55+
MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel channel) {
56+
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
57+
channel.newCall(methodDescriptor, callOptions)) {
58+
@Override
59+
public void start(Listener<RespT> responseListener, Metadata headers) {
60+
sidebandData.reset();
61+
62+
super.start(
63+
new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(
64+
responseListener) {
65+
@Override
66+
public void onHeaders(Metadata headers) {
67+
sidebandData.onResponseHeaders(headers, getAttributes());
68+
super.onHeaders(headers);
69+
}
70+
71+
@Override
72+
public void onClose(Status status, Metadata trailers) {
73+
sidebandData.onClose(status, trailers);
74+
super.onClose(status, trailers);
75+
}
76+
},
77+
headers);
78+
}
79+
};
80+
}
81+
82+
public SidebandData getSidebandData() {
83+
return sidebandData;
84+
}
85+
86+
public static class SidebandData {
87+
private static final CallOptions.Key<SidebandData> KEY =
88+
CallOptions.Key.create("bigtable-sideband");
89+
90+
private static final Metadata.Key<String> SERVER_TIMING_HEADER_KEY =
91+
Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER);
92+
private static final Pattern SERVER_TIMING_HEADER_PATTERN =
93+
Pattern.compile(".*dur=(?<dur>\\d+)");
94+
private static final Metadata.Key<byte[]> LOCATION_METADATA_KEY =
95+
Metadata.Key.of("x-goog-ext-425905942-bin", Metadata.BINARY_BYTE_MARSHALLER);
96+
private static final Metadata.Key<String> PEER_INFO_KEY =
97+
Metadata.Key.of("bigtable-peer-info", Metadata.ASCII_STRING_MARSHALLER);
98+
99+
@Nullable private volatile ResponseParams responseParams;
100+
@Nullable private volatile PeerInfo peerInfo;
101+
@Nullable private volatile Long gfeTiming;
102+
103+
@Nullable
104+
public ResponseParams getResponseParams() {
105+
return responseParams;
106+
}
107+
108+
@Nullable
109+
public PeerInfo getPeerInfo() {
110+
return peerInfo;
111+
}
112+
113+
@Nullable
114+
public Long getGfeTiming() {
115+
return gfeTiming;
116+
}
117+
118+
private void reset() {
119+
responseParams = null;
120+
peerInfo = null;
121+
gfeTiming = null;
122+
}
123+
124+
void onResponseHeaders(Metadata md, Attributes attributes) {
125+
responseParams = extractResponseParams(md);
126+
gfeTiming = extractGfeLatency(md);
127+
peerInfo = extractPeerInfo(md, gfeTiming, attributes);
128+
}
129+
130+
void onClose(Status status, Metadata trailers) {
131+
if (responseParams == null) {
132+
responseParams = extractResponseParams(trailers);
133+
}
134+
}
135+
136+
@Nullable
137+
private static Long extractGfeLatency(Metadata metadata) {
138+
String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY);
139+
if (serverTiming == null) {
140+
return null;
141+
}
142+
Matcher matcher = SERVER_TIMING_HEADER_PATTERN.matcher(serverTiming);
143+
// this should always be true
144+
if (matcher.find()) {
145+
return Long.parseLong(matcher.group("dur"));
146+
}
147+
return null;
148+
}
149+
150+
@Nullable
151+
private static PeerInfo extractPeerInfo(
152+
Metadata metadata, Long gfeTiming, Attributes attributes) {
153+
String encodedStr = metadata.get(PEER_INFO_KEY);
154+
if (Strings.isNullOrEmpty(encodedStr)) {
155+
return null;
156+
}
157+
158+
try {
159+
byte[] decoded = Base64.getUrlDecoder().decode(encodedStr);
160+
PeerInfo peerInfo = PeerInfo.parseFrom(decoded);
161+
PeerInfo.TransportType effectiveTransport = peerInfo.getTransportType();
162+
163+
// TODO: remove this once transport_type is being sent by the server
164+
// This is a temporary workaround to detect directpath until its available from the server
165+
if (effectiveTransport == PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) {
166+
boolean isAlts = AltsContextUtil.check(attributes);
167+
if (isAlts) {
168+
effectiveTransport = PeerInfo.TransportType.TRANSPORT_TYPE_DIRECT_ACCESS;
169+
} else if (gfeTiming != null) {
170+
effectiveTransport = PeerInfo.TransportType.TRANSPORT_TYPE_CLOUD_PATH;
171+
}
172+
}
173+
if (effectiveTransport != PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) {
174+
peerInfo = peerInfo.toBuilder().setTransportType(effectiveTransport).build();
175+
}
176+
return peerInfo;
177+
} catch (Exception e) {
178+
throw new IllegalArgumentException(
179+
"Failed to parse "
180+
+ PEER_INFO_KEY.name()
181+
+ " from the response header value: "
182+
+ encodedStr);
183+
}
184+
}
185+
186+
@Nullable
187+
private static ResponseParams extractResponseParams(Metadata metadata) {
188+
byte[] responseParams = metadata.get(LOCATION_METADATA_KEY);
189+
if (responseParams != null) {
190+
try {
191+
return ResponseParams.parseFrom(responseParams);
192+
} catch (InvalidProtocolBufferException e) {
193+
}
194+
}
195+
return null;
196+
}
197+
}
198+
}

0 commit comments

Comments
 (0)