Sitelet https://github.com/googleapis/google-cloud-java/pull/14997
Skip to content

feat(bigtable): plumb per-operation timeouts through CbtTestProxy - #14997

Open
alexoneill wants to merge 1 commit into
googleapis:mainfrom
alexoneill:feat-bigtable-test-proxy-per-op-timeout
Open

alexoneill wants to merge 1 commit into
googleapis:mainfrom
alexoneill:feat-bigtable-test-proxy-per-op-timeout

Conversation

@alexoneill

Copy link
Copy Markdown
Contributor

Add an optional google.protobuf.Duration per_operation_timeout field to each operation request message in test_proxy.proto (ReadRowRequest, ReadRowsRequest, MutateRowRequest, MutateRowsRequest, CheckAndMutateRowRequest, SampleRowKeysRequest, ReadModifyWriteRowRequest, and ExecuteQueryRequest) so callers can override CreateClientRequest.per_operation_timeout (in both directions) and the client library default timeout on a per-call basis.

  • Pass callContext into retryAlgorithm.createFirstAttempt(callContext) in MutateRowsAttemptCallable so per-call RetrySettings overrides on bulkMutationCallable().call(batch, callContext) apply to entry-level retry decisions as well as the parent RetryingFuture.
  • Add hasPositiveTimeout and createCallContextWithTimeout in CbtTestProxy, updating updateTimeout to also raise initialRpcTimeoutDuration and maxRpcTimeoutDuration when they were previously clamped equal to oldTotalTimeout.
  • Plumb per-operation timeout overrides through mutateRow, bulkMutateRows, readRow, readRows, sampleRowKeys, checkAndMutateRow, readModifyWriteRow, and executeQuery.
  • Add unit tests in CbtTestProxyTest and MutateRowsAttemptCallableTest covering shorter and longer per-operation timeout overrides.

Add an optional `google.protobuf.Duration per_operation_timeout` field to each operation request message in `test_proxy.proto` (`ReadRowRequest`, `ReadRowsRequest`, `MutateRowRequest`, `MutateRowsRequest`, `CheckAndMutateRowRequest`, `SampleRowKeysRequest`, `ReadModifyWriteRowRequest`, and `ExecuteQueryRequest`) so callers can override `CreateClientRequest.per_operation_timeout` (in both directions) and the client library default timeout on a per-call basis.

- Pass `callContext` into `retryAlgorithm.createFirstAttempt(callContext)` in `MutateRowsAttemptCallable` so per-call `RetrySettings` overrides on `bulkMutationCallable().call(batch, callContext)` apply to entry-level retry decisions as well as the parent `RetryingFuture`.
- Add `hasPositiveTimeout` and `createCallContextWithTimeout` in `CbtTestProxy`, updating `updateTimeout` to also raise `initialRpcTimeoutDuration` and `maxRpcTimeoutDuration` when they were previously clamped equal to `oldTotalTimeout`.
- Plumb per-operation timeout overrides through `mutateRow`, `bulkMutateRows`, `readRow`, `readRows`, `sampleRowKeys`, `checkAndMutateRow`, `readModifyWriteRow`, and `executeQuery`.
- Add unit tests in `CbtTestProxyTest` and `MutateRowsAttemptCallableTest` covering shorter and longer per-operation timeout overrides.
@alexoneill
alexoneill requested review from a team as code owners November 13, 2026 02:34

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces per-operation timeout overrides for various Bigtable operations in the test proxy, updating CbtTestProxy to apply these timeouts and extending test_proxy.proto with per_operation_timeout fields. It also includes comprehensive tests verifying that these overrides correctly supersede client-level timeouts, and updates MutateRowsAttemptCallable to pass the call context on the first attempt. Feedback on the changes highlights a potential NullPointerException in CbtTestProxy.java if previousContext is null when calling detach, which should be guarded to prevent masking other exceptions.

Comment on lines +846 to +849
if (cancellableContext != null) {
cancellableContext.detach(previousContext);
cancellableContext.close();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If cancellableContext is initialized but previousContext remains null (for example, if an exception occurs or the thread is interrupted before attach() completes), calling cancellableContext.detach(previousContext) will throw a NullPointerException because Context.detach does not accept null. This NPE would mask the original exception.

To prevent this, guard the detach call by checking if previousContext is not null.

      if (previousContext != null) {
        cancellableContext.detach(previousContext);
      }
      if (cancellableContext != null) {
        cancellableContext.close();
      }

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant