Skip to content

Commit 042dd3e

Browse files
authored
feat(bigtable): add per-method session diversion configuration (#14681)
## Summary - Adds a `methodScopeKey` to `DivertingUnaryCallable` so session load can be configured per-RPC-method via `SessionDiversionConfiguration`, in addition to the existing global `session_load`. - `resolveSessionLoad()` checks `global_diversion` or `per_scope_diversion` depending on which strategy the server returns; falls back to flat `session_load` when no diversion config is present. - Fixes `ClientConfigurationManager` to preserve session config when a `SessionDiversionConfiguration` is present even if the global `session_load` is 0 (diversion config encodes its own per-scope session loads). ## Test plan - [ ] `DivertingUnaryCallableTest`: covers global diversion (load=1 routes to experimental, load=0 falls back to classic), per-scope match (routes to experimental), and per-scope non-match (falls back to classic) - [ ] Existing session routing tests continue to pass unchanged
1 parent 86fb459 commit 042dd3e

8 files changed

Lines changed: 329 additions & 46 deletions

File tree

‎java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/compat/ShimImpl.java‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -376,44 +376,50 @@ public <RowT> UnaryCallable<Query, RowT> decorateReadRow(
376376
classic,
377377
new RowBuilderShim<>(readRowShim, rowAdapter, r -> r.hasRow() ? r.getRow() : null),
378378
Util.extractTimeout(settings),
379-
debugTagTracer);
379+
debugTagTracer,
380+
"method:Bigtable.ReadRow",
381+
/* fallbackToGlobalSessionLoad= */ true);
380382
}
381383

382384
@Override
383385
public UnaryCallable<RowMutation, Void> decorateMutateRow(
384386
UnaryCallable<RowMutation, Void> classic, UnaryCallSettings<?, ?> settings) {
385387
return new DivertingUnaryCallable<>(
386-
configManager, classic, mutateRowShim, Util.extractTimeout(settings), debugTagTracer);
388+
configManager,
389+
classic,
390+
mutateRowShim,
391+
Util.extractTimeout(settings),
392+
debugTagTracer,
393+
"method:Bigtable.MutateRow",
394+
/* fallbackToGlobalSessionLoad= */ true);
387395
}
388396

389397
@Override
390398
public UnaryCallable<ConditionalRowMutation, Boolean> decorateCheckAndMutateRow(
391399
UnaryCallable<ConditionalRowMutation, Boolean> classic, UnaryCallSettings<?, ?> settings) {
392-
if (!WipFeatures.CHECK_AND_MUTATE_ROW_ENABLED) {
393-
return classic;
394-
}
395400
return new DivertingUnaryCallable<>(
396401
configManager,
397402
classic,
398403
checkAndMutateRowShim,
399404
Util.extractTimeout(settings),
400-
debugTagTracer);
405+
debugTagTracer,
406+
"method:Bigtable.CheckAndMutateRow",
407+
/* fallbackToGlobalSessionLoad= */ false);
401408
}
402409

403410
@Override
404411
public <RowT> UnaryCallable<ReadModifyWriteRow, RowT> decorateReadModifyWriteRow(
405412
UnaryCallable<ReadModifyWriteRow, RowT> classic,
406413
RowAdapter<RowT> rowAdapter,
407414
UnaryCallSettings<?, ?> settings) {
408-
if (!WipFeatures.READ_MODIFY_WRITE_ROW_ENABLED) {
409-
return classic;
410-
}
411415
return new DivertingUnaryCallable<>(
412416
configManager,
413417
classic,
414418
new RowBuilderShim<>(
415419
readModifyWriteRowShim, rowAdapter, r -> r.hasRow() ? r.getRow() : null),
416420
Util.extractTimeout(settings),
417-
debugTagTracer);
421+
debugTagTracer,
422+
"method:Bigtable.ReadModifyWriteRow",
423+
/* fallbackToGlobalSessionLoad= */ false);
418424
}
419425
}

‎java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/compat/WipFeatures.java‎

Lines changed: 0 additions & 27 deletions
This file was deleted.

‎java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/compat/ops/DivertingUnaryCallable.java‎

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import com.google.api.gax.rpc.ApiException;
2323
import com.google.api.gax.rpc.ApiExceptionFactory;
2424
import com.google.api.gax.rpc.UnaryCallable;
25+
import com.google.bigtable.v2.SessionClientConfiguration;
26+
import com.google.bigtable.v2.SessionDiversionConfiguration;
27+
import com.google.bigtable.v2.SessionScopeDiversionConfiguration;
2528
import com.google.bigtable.v2.TelemetryConfiguration;
2629
import com.google.cloud.bigtable.data.v2.internal.compat.FutureAdapter;
2730
import com.google.cloud.bigtable.data.v2.internal.compat.Util;
@@ -49,18 +52,28 @@ public class DivertingUnaryCallable<ReqT, RespT> extends UnaryCallable<ReqT, Res
4952

5053
private final Duration defaultTimeout;
5154
private final DebugTagTracer debugTagTracer;
55+
// Scope key for per-method diversion lookup, e.g. "method:Bigtable.ReadRow".
56+
private final String methodScopeKey;
57+
// When true, falls back to the flat SessionClientConfiguration.session_load when no
58+
// SessionDiversionConfiguration is present. Used for ReadRow/MutateRow during the server
59+
// rollout window before the server starts sending SessionDiversionConfiguration.
60+
private final boolean fallbackToGlobalSessionLoad;
5261

5362
public DivertingUnaryCallable(
5463
ClientConfigurationManager configurationManager,
5564
UnaryCallable<ReqT, RespT> classic,
5665
UnaryShim<ReqT, RespT> experimental,
5766
Duration defaultTimeout,
58-
DebugTagTracer debugTagTracer) {
67+
DebugTagTracer debugTagTracer,
68+
String methodScopeKey,
69+
boolean fallbackToGlobalSessionLoad) {
5970
this.configurationManager = configurationManager;
6071
this.classic = classic;
6172
this.experimental = experimental;
6273
this.defaultTimeout = defaultTimeout;
6374
this.debugTagTracer = debugTagTracer;
75+
this.methodScopeKey = methodScopeKey;
76+
this.fallbackToGlobalSessionLoad = fallbackToGlobalSessionLoad;
6477
}
6578

6679
@Override
@@ -109,8 +122,7 @@ public ApiFuture<RespT> futureCall(ReqT request, ApiCallContext context) {
109122
}
110123

111124
private boolean useExperimental(ReqT req) {
112-
float ratio =
113-
configurationManager.getClientConfiguration().getSessionConfiguration().getSessionLoad();
125+
float ratio = resolveSessionLoad();
114126
if (ratio == 0) {
115127
return false;
116128
}
@@ -125,6 +137,28 @@ private boolean useExperimental(ReqT req) {
125137
return true;
126138
}
127139

140+
private float resolveSessionLoad() {
141+
SessionClientConfiguration sessionConfig =
142+
configurationManager.getClientConfiguration().getSessionConfiguration();
143+
144+
if (sessionConfig.hasSessionDiversionConfiguration()) {
145+
SessionDiversionConfiguration diversionConfig =
146+
sessionConfig.getSessionDiversionConfiguration();
147+
switch (diversionConfig.getDiversionStrategyCase()) {
148+
case GLOBAL_DIVERSION:
149+
return diversionConfig.getGlobalDiversion().getSessionLoad();
150+
case PER_SCOPE_DIVERSION:
151+
SessionScopeDiversionConfiguration scopeConfig =
152+
diversionConfig.getPerScopeDiversion().getScopeDiversionsMap().get(methodScopeKey);
153+
return scopeConfig != null ? scopeConfig.getSessionLoad() : 0f;
154+
default:
155+
return 0f;
156+
}
157+
}
158+
159+
return fallbackToGlobalSessionLoad ? sessionConfig.getSessionLoad() : 0f;
160+
}
161+
128162
ApiException translateException(Throwable e) {
129163
Throwable cause = e;
130164
while (cause instanceof CompletionException || cause instanceof ExecutionException) {

‎java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/util/ClientConfigurationManager.java‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import com.google.bigtable.v2.ClientConfiguration.PollingCase;
2222
import com.google.bigtable.v2.FeatureFlags;
2323
import com.google.bigtable.v2.GetClientConfigurationRequest;
24+
import com.google.bigtable.v2.SessionClientConfiguration;
2425
import com.google.bigtable.v2.SessionClientConfiguration.ChannelPoolConfiguration.DirectAccessOnly;
26+
import com.google.bigtable.v2.SessionDiversionConfiguration;
2527
import com.google.bigtable.v2.TelemetryConfiguration;
2628
import com.google.cloud.bigtable.data.v2.internal.api.ChannelProviders.ChannelProvider;
2729
import com.google.cloud.bigtable.data.v2.internal.api.Util;
@@ -447,16 +449,39 @@ private ClientConfiguration normalizeConfig(ClientConfiguration cfg) {
447449
// When sessions are disabled make sure to clear out the config. Read from the builder, not
448450
// cfg, so that a nonzero session_load supplied via the override sys-prop is honoured even when
449451
// the server-returned config has session_load=0.
450-
if (builder.getSessionConfiguration().getSessionLoad() == 0) {
452+
if (!divertsAnyTrafficToSessions(builder.getSessionConfiguration())) {
451453
builder.clearSessionConfiguration();
452454
return builder.build();
453455
}
454456

455457
return builder.build();
456458
}
457459

460+
/**
461+
* Returns true if the configuration routes a nonzero share of traffic to sessions, either via the
462+
* flat {@code session_load} or via a diversion configuration that carries a nonzero load.
463+
*/
464+
private static boolean divertsAnyTrafficToSessions(SessionClientConfiguration session) {
465+
if (session.getSessionLoad() > 0) {
466+
return true;
467+
}
468+
469+
SessionDiversionConfiguration diversion = session.getSessionDiversionConfiguration();
470+
switch (diversion.getDiversionStrategyCase()) {
471+
case GLOBAL_DIVERSION:
472+
return diversion.getGlobalDiversion().getSessionLoad() > 0;
473+
case PER_SCOPE_DIVERSION:
474+
return diversion.getPerScopeDiversion().getScopeDiversionsMap().values().stream()
475+
.anyMatch(scope -> scope.getSessionLoad() > 0);
476+
default:
477+
return false;
478+
}
479+
}
480+
458481
public boolean areSessionsRequired() {
459-
return overrideConfig.map(c -> c.getSessionConfiguration().getSessionLoad() > 0).orElse(false);
482+
return overrideConfig
483+
.map(c -> divertsAnyTrafficToSessions(c.getSessionConfiguration()))
484+
.orElse(false);
460485
}
461486

462487
private long getRetryDelay(int attempt) {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,8 @@ private UnaryCallable<ConditionalRowMutation, Boolean> createCheckAndMutateRowCa
940940

941941
return bigtableClientContext
942942
.getSessionShim()
943-
.decorateCheckAndMutateRow(classic, perOpSettings.checkAndMutateRowSettings);
943+
.decorateCheckAndMutateRow(classic, perOpSettings.checkAndMutateRowSettings)
944+
.withDefaultCallContext(bigtableClientContext.getClientContext().getDefaultCallContext());
944945
}
945946

946947
/**
@@ -968,7 +969,8 @@ private UnaryCallable<ReadModifyWriteRow, Row> createReadModifyWriteRowCallable(
968969

969970
return bigtableClientContext
970971
.getSessionShim()
971-
.decorateReadModifyWriteRow(classic, rowAdapter, perOpSettings.readModifyWriteRowSettings);
972+
.decorateReadModifyWriteRow(classic, rowAdapter, perOpSettings.readModifyWriteRowSettings)
973+
.withDefaultCallContext(bigtableClientContext.getClientContext().getDefaultCallContext());
972974
}
973975

974976
/**

‎java-bigtable/google-cloud-bigtable/src/main/resources/bigtable-default-client-config.textproto‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,22 @@ polling_configuration {
1212
}
1313

1414
session_configuration {
15-
session_load: 0
15+
session_diversion_configuration {
16+
per_scope_diversion {
17+
scope_diversions {
18+
key: "method:Bigtable.ReadRow"
19+
value {
20+
session_load: 0
21+
}
22+
}
23+
scope_diversions {
24+
key: "method:Bigtable.MutateRow"
25+
value {
26+
session_load: 0
27+
}
28+
}
29+
}
30+
}
1631
1732
channel_configuration {
1833
min_server_count: 2

0 commit comments

Comments
 (0)