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

Commit d5565b5

Browse files
feat: add new session based protocol stack (#2862)
The new protocol changes the paradigm of bigtable service from an RPC server where each operation is independent to more of a file like model where a table is opened and allows many read & write operations. This will have a significant impact on lowering latencies. All of the changes are internal to the client and the existing public surface remains the same. By default the new protocol is disabled and will be slowly enabled in the future using the client config api. Notable changes: - When the new protocol is enabled, the BigtableClientFactory doesnt share the underlying ChannelPool. The new ChannelPool is tracks the connected server, so sharing a channel between multiple resource is no longer possible. - The new protocol does not support custom TransportProviders, setting one will force a fallback to the classic protocol - Retries have been revamped: - the client now tracks how far an rpc went which allows the client to retry non-idempotent rpcs safely - attempt timeouts are no longer relevant, they have been replaced with heartbeats - Mutations that are not idempotent (have serverside timestamps or counters) are now correctly identified and will be retried if they left the client - The new transport supports the ambient grpc Context and will consistently respect the ambient deadlines and cancellation - Customers can opt out of the slow rollout by setting the env var `CBT_DISABLE_SESSIONS=true`
1 parent 29db0f2 commit d5565b5

260 files changed

Lines changed: 68269 additions & 751 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

generation_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
gapic_generator_version: 2.68.0
2-
googleapis_commitish: cd090841ab172574e740c214c99df00aef9c0dee
2+
googleapis_commitish: f5cb7afc40b63d52f43bc306cb9b64a87b681aea
33
libraries_bom_version: 26.79.0
44
template_excludes:
55
- .gitignore

google-cloud-bigtable/pom.xml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,9 @@
159159
<artifactId>google-http-client-gson</artifactId>
160160
<scope>runtime</scope>
161161
</dependency>
162-
<!--
163-
grpc-stub is needed directly by our tests and transitively by grpc-alts at runtime.
164-
So it has to be declared as a direct dependency and to avoid overriding grpc-alts'
165-
runtime requirement it has to be promoted to the runtime scope.
166-
-->
167162
<dependency>
168163
<groupId>io.grpc</groupId>
169164
<artifactId>grpc-stub</artifactId>
170-
<scope>runtime</scope>
171165
</dependency>
172166
<dependency>
173167
<groupId>io.grpc</groupId>
@@ -225,6 +219,10 @@
225219
<groupId>io.opentelemetry</groupId>
226220
<artifactId>opentelemetry-api</artifactId>
227221
</dependency>
222+
<dependency>
223+
<groupId>io.opentelemetry</groupId>
224+
<artifactId>opentelemetry-context</artifactId>
225+
</dependency>
228226
<dependency>
229227
<groupId>io.opentelemetry</groupId>
230228
<artifactId>opentelemetry-sdk</artifactId>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ public final class BigtableDataClientFactory implements AutoCloseable {
7474
*/
7575
public static BigtableDataClientFactory create(BigtableDataSettings defaultSettings)
7676
throws IOException {
77+
BigtableDataSettings.Builder builder = defaultSettings.toBuilder();
78+
builder.stubSettings().setSessionsEnabled(false);
79+
defaultSettings = builder.build();
80+
7781
BigtableClientContext sharedClientContext =
7882
BigtableClientContext.create(defaultSettings.getStubSettings());
7983
ClientOperationSettings perOpSettings = defaultSettings.getStubSettings().getPerOpSettings();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public static Builder newBuilderForEmulator(String hostname, int port) {
133133
.setMetricsProvider(
134134
NoopMetricsProvider.INSTANCE) // disable exporting metrics for emulator
135135
.disableInternalMetrics()
136+
.setSessionsEnabled(false)
136137
.setTransportChannelProvider(
137138
InstantiatingGrpcChannelProvider.newBuilder()
138139
.setMaxInboundMessageSize(256 * 1024 * 1024)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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.internal.api;
17+
18+
import com.google.bigtable.v2.FeatureFlags;
19+
import com.google.bigtable.v2.OpenAuthorizedViewRequest;
20+
import com.google.bigtable.v2.OpenAuthorizedViewRequest.Permission;
21+
import com.google.bigtable.v2.SessionMutateRowRequest;
22+
import com.google.bigtable.v2.SessionMutateRowResponse;
23+
import com.google.bigtable.v2.SessionReadRowRequest;
24+
import com.google.bigtable.v2.SessionReadRowResponse;
25+
import com.google.cloud.bigtable.data.v2.internal.channels.ChannelPool;
26+
import com.google.cloud.bigtable.data.v2.internal.csm.Metrics;
27+
import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
28+
import com.google.cloud.bigtable.data.v2.internal.session.SessionPool;
29+
import com.google.cloud.bigtable.data.v2.internal.session.VRpcDescriptor;
30+
import com.google.cloud.bigtable.data.v2.internal.util.ClientConfigurationManager;
31+
import io.grpc.CallOptions;
32+
import io.grpc.Deadline;
33+
import java.io.Closeable;
34+
import java.util.concurrent.CompletableFuture;
35+
import java.util.concurrent.ScheduledExecutorService;
36+
37+
public class AuthorizedViewAsync implements AutoCloseable, Closeable {
38+
39+
private final TableBase base;
40+
41+
static AuthorizedViewAsync createAndStart(
42+
FeatureFlags featureFlags,
43+
ClientInfo clientInfo,
44+
ClientConfigurationManager configManager,
45+
ChannelPool channelPool,
46+
CallOptions callOptions,
47+
String tableId,
48+
String viewId,
49+
Permission permission,
50+
Metrics metrics,
51+
ScheduledExecutorService executorService) {
52+
53+
AuthorizedViewName viewName =
54+
AuthorizedViewName.builder()
55+
.setProjectId(clientInfo.getInstanceName().getProjectId())
56+
.setInstanceId(clientInfo.getInstanceName().getInstanceId())
57+
.setTableId(tableId)
58+
.setAuthorizedViewId(viewId)
59+
.build();
60+
61+
OpenAuthorizedViewRequest openRequest =
62+
OpenAuthorizedViewRequest.newBuilder()
63+
.setAuthorizedViewName(viewName.toString())
64+
.setAppProfileId(clientInfo.getAppProfileId())
65+
.setPermission(permission)
66+
.build();
67+
68+
TableBase base =
69+
TableBase.createAndStart(
70+
openRequest,
71+
VRpcDescriptor.AUTHORIZED_VIEW_SESSION,
72+
VRpcDescriptor.READ_ROW_AUTH_VIEW,
73+
VRpcDescriptor.MUTATE_ROW_AUTH_VIEW,
74+
featureFlags,
75+
clientInfo,
76+
configManager,
77+
channelPool,
78+
callOptions,
79+
viewName.toString(),
80+
metrics,
81+
executorService);
82+
83+
return new AuthorizedViewAsync(base);
84+
}
85+
86+
AuthorizedViewAsync(TableBase viewBase) {
87+
this.base = viewBase;
88+
}
89+
90+
public SessionPool<?> getSessionPool() {
91+
return base.getSessionPool();
92+
}
93+
94+
public CompletableFuture<SessionReadRowResponse> readRow(
95+
SessionReadRowRequest req, Deadline deadline) {
96+
UnaryResponseFuture<SessionReadRowResponse> f = new UnaryResponseFuture<>();
97+
base.readRow(req, f, deadline);
98+
return f;
99+
}
100+
101+
public CompletableFuture<SessionMutateRowResponse> mutateRow(
102+
SessionMutateRowRequest req, Deadline deadline) {
103+
UnaryResponseFuture<SessionMutateRowResponse> f = new UnaryResponseFuture<>();
104+
base.mutateRow(req, f, deadline);
105+
return f;
106+
}
107+
108+
@Override
109+
public void close() {
110+
this.base.close();
111+
}
112+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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.internal.api;
17+
18+
import com.google.auto.value.AutoValue;
19+
import com.google.common.base.Preconditions;
20+
import com.google.common.base.Splitter;
21+
import java.util.List;
22+
23+
@AutoValue
24+
public abstract class AuthorizedViewName {
25+
26+
public abstract String getProjectId();
27+
28+
public abstract String getInstanceId();
29+
30+
public abstract String getTableId();
31+
32+
public abstract String getAuthorizedViewId();
33+
34+
public InstanceName getInstanceName() {
35+
return InstanceName.builder()
36+
.setProjectId(getProjectId())
37+
.setInstanceId(getInstanceId())
38+
.build();
39+
}
40+
41+
public TableName getTableName() {
42+
return TableName.builder()
43+
.setProjectId(getProjectId())
44+
.setInstanceId(getInstanceId())
45+
.setTableId(getTableId())
46+
.build();
47+
}
48+
49+
@Override
50+
public final String toString() {
51+
return String.format("%s/authorizedViews/%s", getTableName(), getAuthorizedViewId());
52+
}
53+
54+
public static AuthorizedViewName of(
55+
String projectId, String instanceId, String tableId, String viewId) {
56+
return builder()
57+
.setProjectId(projectId)
58+
.setInstanceId(instanceId)
59+
.setTableId(tableId)
60+
.setAuthorizedViewId(viewId)
61+
.build();
62+
}
63+
64+
public static Builder builder() {
65+
return new AutoValue_AuthorizedViewName.Builder();
66+
}
67+
68+
public static AuthorizedViewName parse(String name) {
69+
List<String> parts = Splitter.on('/').splitToList(name);
70+
Preconditions.checkArgument(parts.size() == 8, "Invalid authorized view name: %s", name);
71+
Preconditions.checkArgument(
72+
"projects".equals(parts.get(0)),
73+
"Invalid authorized view name: %s, must start with projects/",
74+
name);
75+
Preconditions.checkArgument(
76+
!parts.get(1).isEmpty(), "Invalid authorized view name %s, must have a project id", name);
77+
Preconditions.checkArgument(
78+
"instances".equals(parts.get(2)),
79+
"Invalid authorized view name: %s, must start with projects/$PROJECT_ID/instances/",
80+
name);
81+
Preconditions.checkArgument(
82+
!parts.get(3).isEmpty(), "Invalid authorized view name %s, must have an instance id", name);
83+
Preconditions.checkArgument(
84+
"tables".equals(parts.get(4)),
85+
"Invalid authorized view name: %s, must start with projects/$PROJECT_ID/instances/$INSTANCE_ID/tables",
86+
name);
87+
Preconditions.checkArgument(
88+
!parts.get(5).isEmpty(), "Invalid authorized view name %s, must have table id", name);
89+
Preconditions.checkArgument(
90+
"authorizedViews".equals(parts.get(6)),
91+
"Invalid authorized view name: %s, must start with projects/$PROJECT_ID/instances/$INSTANCE_ID/tables/$TABLE_ID/authorizedViews",
92+
name);
93+
Preconditions.checkArgument(
94+
!parts.get(7).isEmpty(),
95+
"Invalid authorized view name %s, must have authorized view id",
96+
name);
97+
98+
return builder()
99+
.setProjectId(parts.get(1))
100+
.setInstanceId(parts.get(3))
101+
.setTableId(parts.get(5))
102+
.setAuthorizedViewId(parts.get(7))
103+
.build();
104+
}
105+
106+
@AutoValue.Builder
107+
public abstract static class Builder {
108+
public abstract Builder setProjectId(String projectId);
109+
110+
public abstract Builder setInstanceId(String instanceId);
111+
112+
public abstract Builder setTableId(String tableId);
113+
114+
public abstract Builder setAuthorizedViewId(String viewId);
115+
116+
public abstract AuthorizedViewName build();
117+
}
118+
}

0 commit comments

Comments
 (0)