From 2498287673c0088cbf0d48270307559070fb72ad Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 03:25:47 -0500 Subject: [PATCH 01/33] chore(main): release 2.73.2-SNAPSHOT (#2787) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- google-cloud-bigtable-bom/pom.xml | 16 ++++++++-------- google-cloud-bigtable-deps-bom/pom.xml | 2 +- google-cloud-bigtable-emulator-core/pom.xml | 4 ++-- google-cloud-bigtable-emulator/pom.xml | 10 +++++----- google-cloud-bigtable/pom.xml | 10 +++++----- .../java/com/google/cloud/bigtable/Version.java | 2 +- grpc-google-cloud-bigtable-admin-v2/pom.xml | 8 ++++---- grpc-google-cloud-bigtable-v2/pom.xml | 8 ++++---- pom.xml | 12 ++++++------ proto-google-cloud-bigtable-admin-v2/pom.xml | 8 ++++---- proto-google-cloud-bigtable-v2/pom.xml | 8 ++++---- samples/snapshot/pom.xml | 2 +- test-proxy/pom.xml | 4 ++-- versions.txt | 14 +++++++------- 14 files changed, 54 insertions(+), 54 deletions(-) diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml index cec378cb4d..58187f9673 100644 --- a/google-cloud-bigtable-bom/pom.xml +++ b/google-cloud-bigtable-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom com.google.cloud @@ -63,37 +63,37 @@ com.google.cloud google-cloud-bigtable - 2.73.1 + 2.73.2-SNAPSHOT com.google.cloud google-cloud-bigtable-emulator - 0.210.1 + 0.210.2-SNAPSHOT com.google.cloud google-cloud-bigtable-emulator-core - 0.210.1 + 0.210.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml index 99be3a9bc7..744079ceee 100644 --- a/google-cloud-bigtable-deps-bom/pom.xml +++ b/google-cloud-bigtable-deps-bom/pom.xml @@ -13,7 +13,7 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom Google Cloud Bigtable Dependency BOM diff --git a/google-cloud-bigtable-emulator-core/pom.xml b/google-cloud-bigtable-emulator-core/pom.xml index 4e7f599e89..38841ddc1b 100644 --- a/google-cloud-bigtable-emulator-core/pom.xml +++ b/google-cloud-bigtable-emulator-core/pom.xml @@ -7,12 +7,12 @@ google-cloud-bigtable-parent com.google.cloud - 2.73.1 + 2.73.2-SNAPSHOT Google Cloud Java - Bigtable Emulator Core google-cloud-bigtable-emulator-core - 0.210.1 + 0.210.2-SNAPSHOT A Java wrapper for the Cloud Bigtable emulator. diff --git a/google-cloud-bigtable-emulator/pom.xml b/google-cloud-bigtable-emulator/pom.xml index 2601f88fa2..449288a782 100644 --- a/google-cloud-bigtable-emulator/pom.xml +++ b/google-cloud-bigtable-emulator/pom.xml @@ -5,7 +5,7 @@ 4.0.0 google-cloud-bigtable-emulator - 0.210.1 + 0.210.2-SNAPSHOT Google Cloud Java - Bigtable Emulator https://github.com/googleapis/java-bigtable @@ -14,7 +14,7 @@ com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT scm:git:git@github.com:googleapis/java-bigtable.git @@ -81,14 +81,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import @@ -99,7 +99,7 @@ com.google.cloud google-cloud-bigtable-emulator-core - 0.210.1 + 0.210.2-SNAPSHOT diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 0f682ff143..30d61cdc6a 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-bigtable - 2.73.1 + 2.73.2-SNAPSHOT jar Google Cloud Bigtable https://github.com/googleapis/java-bigtable @@ -12,11 +12,11 @@ com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT - 2.73.1 + 2.73.2-SNAPSHOT google-cloud-bigtable @@ -54,14 +54,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java index 0e9f25aaaa..f0d8f073b8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java @@ -20,6 +20,6 @@ @InternalApi("For internal use only") public final class Version { // {x-version-update-start:google-cloud-bigtable:current} - public static String VERSION = "2.73.1"; + public static String VERSION = "2.73.2-SNAPSHOT"; // {x-version-update-end} } diff --git a/grpc-google-cloud-bigtable-admin-v2/pom.xml b/grpc-google-cloud-bigtable-admin-v2/pom.xml index e6528b9d17..44cf46c227 100644 --- a/grpc-google-cloud-bigtable-admin-v2/pom.xml +++ b/grpc-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT grpc-google-cloud-bigtable-admin-v2 GRPC library for grpc-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import diff --git a/grpc-google-cloud-bigtable-v2/pom.xml b/grpc-google-cloud-bigtable-v2/pom.xml index 319769faa2..fc3f6e1e3a 100644 --- a/grpc-google-cloud-bigtable-v2/pom.xml +++ b/grpc-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT grpc-google-cloud-bigtable-v2 GRPC library for grpc-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import diff --git a/pom.xml b/pom.xml index 1b7a650e7e..42c2741202 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ google-cloud-bigtable-parent pom - 2.73.1 + 2.73.2-SNAPSHOT Google Cloud Bigtable Parent https://github.com/googleapis/java-bigtable @@ -156,27 +156,27 @@ com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT com.google.cloud google-cloud-bigtable - 2.73.1 + 2.73.2-SNAPSHOT diff --git a/proto-google-cloud-bigtable-admin-v2/pom.xml b/proto-google-cloud-bigtable-admin-v2/pom.xml index 4b160499ae..e35a712daf 100644 --- a/proto-google-cloud-bigtable-admin-v2/pom.xml +++ b/proto-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.1 + 2.73.2-SNAPSHOT proto-google-cloud-bigtable-admin-v2 PROTO library for proto-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import diff --git a/proto-google-cloud-bigtable-v2/pom.xml b/proto-google-cloud-bigtable-v2/pom.xml index 0d1da46f9b..7cabe4537d 100644 --- a/proto-google-cloud-bigtable-v2/pom.xml +++ b/proto-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.1 + 2.73.2-SNAPSHOT proto-google-cloud-bigtable-v2 PROTO library for proto-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.1 + 2.73.2-SNAPSHOT @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import com.google.cloud google-cloud-bigtable-bom - 2.73.1 + 2.73.2-SNAPSHOT pom import diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 6e1e0caad6..bff7331e66 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-bigtable - 2.73.1 + 2.73.2-SNAPSHOT diff --git a/test-proxy/pom.xml b/test-proxy/pom.xml index d5110580fc..0fe5793b86 100644 --- a/test-proxy/pom.xml +++ b/test-proxy/pom.xml @@ -12,11 +12,11 @@ google-cloud-bigtable-parent com.google.cloud - 2.73.1 + 2.73.2-SNAPSHOT - 2.73.1 + 2.73.2-SNAPSHOT diff --git a/versions.txt b/versions.txt index 40fcb9fecd..194138774b 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -google-cloud-bigtable:2.73.1:2.73.1 -grpc-google-cloud-bigtable-admin-v2:2.73.1:2.73.1 -grpc-google-cloud-bigtable-v2:2.73.1:2.73.1 -proto-google-cloud-bigtable-admin-v2:2.73.1:2.73.1 -proto-google-cloud-bigtable-v2:2.73.1:2.73.1 -google-cloud-bigtable-emulator:0.210.1:0.210.1 -google-cloud-bigtable-emulator-core:0.210.1:0.210.1 +google-cloud-bigtable:2.73.1:2.73.2-SNAPSHOT +grpc-google-cloud-bigtable-admin-v2:2.73.1:2.73.2-SNAPSHOT +grpc-google-cloud-bigtable-v2:2.73.1:2.73.2-SNAPSHOT +proto-google-cloud-bigtable-admin-v2:2.73.1:2.73.2-SNAPSHOT +proto-google-cloud-bigtable-v2:2.73.1:2.73.2-SNAPSHOT +google-cloud-bigtable-emulator:0.210.1:0.210.2-SNAPSHOT +google-cloud-bigtable-emulator-core:0.210.1:0.210.2-SNAPSHOT From cf15d45a8f4c0ee385d3e53a0bae153ee1064999 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Wed, 18 Feb 2026 14:06:35 -0500 Subject: [PATCH 02/33] feat: Add awaitOptimizeRestoredTable helper for Bigtable Admin (#2781) * feat: Add awaitOptimizeRestoredTable helper for Bigtable Admin Adds `awaitOptimizeRestoredTable` to simplify waiting for the secondary "Optimize" operation after a table restore. This method automatically extracts the operation token from the restore metadata and resumes the optimization LRO. This addresses the Long Running Sub-operations CUJ. Tracking Bug: b/475820271 * chore: generate libraries at Fri Feb 13 21:52:28 UTC 2026 --------- Co-authored-by: cloud-java-bot --- .../admin/v2/BigtableTableAdminClient.java | 32 ++++++++++ .../v2/BigtableTableAdminClientTests.java | 59 +++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java index 0e5a4c9433..b5ee9d90ea 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java @@ -72,6 +72,7 @@ import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub; import com.google.cloud.bigtable.data.v2.internal.TableAdminRequestContext; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; @@ -1296,6 +1297,37 @@ public ApiFuture apply(com.google.bigtable.admin.v2.Table t MoreExecutors.directExecutor()); } + /** + * Awaits the completion of the "Optimize Restored Table" operation. + * + *

This method blocks until the restore operation is complete, extracts the optimization token, + * and returns an ApiFuture for the optimization phase. + * + * @param restoreFuture The future returned by restoreTableAsync(). + * @return An ApiFuture that tracks the optimization progress. + */ + public ApiFuture awaitOptimizeRestoredTable(ApiFuture restoreFuture) { + // 1. Block and wait for the restore operation to complete + RestoredTableResult result; + try { + result = restoreFuture.get(); + } catch (Exception e) { + throw new RuntimeException("Restore operation failed", e); + } + + // 2. Extract the operation token from the result + // (RestoredTableResult already wraps the OptimizeRestoredTableOperationToken) + OptimizeRestoredTableOperationToken token = result.getOptimizeRestoredTableOperationToken(); + + if (token == null || Strings.isNullOrEmpty(token.getOperationName())) { + // If there is no optimization operation, return immediate success. + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + + // 3. Return the future for the optimization operation + return stub.awaitOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName()); + } + /** * Awaits a restored table is fully optimized. * diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java index e89bd8fbb5..c1d5da6592 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java @@ -45,6 +45,7 @@ import com.google.bigtable.admin.v2.ListBackupsRequest; import com.google.bigtable.admin.v2.ListTablesRequest; import com.google.bigtable.admin.v2.ModifyColumnFamiliesRequest.Modification; +import com.google.bigtable.admin.v2.OptimizeRestoredTableMetadata; import com.google.bigtable.admin.v2.RestoreSourceType; import com.google.bigtable.admin.v2.RestoreTableMetadata; import com.google.bigtable.admin.v2.SchemaBundleName; @@ -76,6 +77,7 @@ import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; import com.google.cloud.bigtable.admin.v2.models.EncryptionInfo; import com.google.cloud.bigtable.admin.v2.models.ModifyColumnFamiliesRequest; +import com.google.cloud.bigtable.admin.v2.models.OptimizeRestoredTableOperationToken; import com.google.cloud.bigtable.admin.v2.models.RestoreTableRequest; import com.google.cloud.bigtable.admin.v2.models.RestoredTableResult; import com.google.cloud.bigtable.admin.v2.models.SchemaBundle; @@ -285,6 +287,10 @@ public class BigtableTableAdminClientTests { com.google.iam.v1.TestIamPermissionsRequest, com.google.iam.v1.TestIamPermissionsResponse> mockTestIamPermissionsCallable; + @Mock + private OperationCallable + mockOptimizeRestoredTableCallable; + @Before public void setUp() { adminClient = BigtableTableAdminClient.create(PROJECT_ID, INSTANCE_ID, mockStub); @@ -1682,6 +1688,59 @@ public void testWaitForConsistencyWithToken() { assertThat(wasCalled.get()).isTrue(); } + @Test + public void testAwaitOptimizeRestoredTable() throws Exception { + // Setup + Mockito.when(mockStub.awaitOptimizeRestoredTableCallable()) + .thenReturn(mockOptimizeRestoredTableCallable); + + String optimizeToken = "my-optimization-token"; + + // 1. Mock the Token + OptimizeRestoredTableOperationToken mockToken = + Mockito.mock(OptimizeRestoredTableOperationToken.class); + Mockito.when(mockToken.getOperationName()).thenReturn(optimizeToken); + + // 2. Mock the Result (wrapping the token) + RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class); + Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(mockToken); + + // 3. Mock the Input Future (returning the result) + ApiFuture mockRestoreFuture = Mockito.mock(ApiFuture.class); + Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult); + + // 4. Mock the Stub's behavior (resuming the Optimize Op) + OperationFuture mockOptimizeOp = + Mockito.mock(OperationFuture.class); + Mockito.when(mockOptimizeRestoredTableCallable.resumeFutureCall(optimizeToken)) + .thenReturn(mockOptimizeOp); + + // Execute + ApiFuture result = adminClient.awaitOptimizeRestoredTable(mockRestoreFuture); + + // Verify + assertThat(result).isEqualTo(mockOptimizeOp); + Mockito.verify(mockOptimizeRestoredTableCallable).resumeFutureCall(optimizeToken); + } + + @Test + public void testAwaitOptimizeRestoredTable_NoOp() throws Exception { + // Setup: Result with NO optimization token (null or empty) + RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class); + Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(null); + + // Mock the Input Future + ApiFuture mockRestoreFuture = Mockito.mock(ApiFuture.class); + Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult); + + // Execute + ApiFuture result = adminClient.awaitOptimizeRestoredTable(mockRestoreFuture); + + // Verify: Returns immediate success (Empty) without calling the stub + assertThat(result.get()).isEqualTo(Empty.getDefaultInstance()); + Mockito.verifyNoInteractions(mockStub); + } + private void mockOperationResult( OperationCallable callable, ReqT request, From 054279404da3754a695bf2dcf0775b904cc2eaeb Mon Sep 17 00:00:00 2001 From: Tomo Suzuki Date: Wed, 18 Feb 2026 16:25:19 -0500 Subject: [PATCH 03/33] chore: replace old Bigtable and Java teams with updated names (#2790) b/478003109 --- .github/CODEOWNERS | 10 +++++----- .github/sync-repo-settings.yaml | 2 +- .repo-metadata.json | 2 +- generation_config.yaml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 627419201d..e1f4bd0e65 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,11 +4,11 @@ # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -# The @googleapis/api-bigtable is the default owner for changes in this repo -* @googleapis/cloud-sdk-java-team @googleapis/api-bigtable +# The @googleapis/bigtable-team is the default owner for changes in this repo +* @googleapis/cloud-sdk-java-team @googleapis/bigtable-team # for handwritten libraries, keep codeowner_team in .repo-metadata.json as owner -**/*.java @googleapis/api-bigtable @googleapis/cloud-sdk-java-team +**/*.java @googleapis/bigtable-team @googleapis/cloud-sdk-java-team # The java-samples-reviewers team is the default owner for samples changes @@ -18,5 +18,5 @@ samples/**/*.java @googleapis/java-samples-reviewers samples/snippets/generated/ @googleapis/cloud-sdk-java-team # Admin Module (Cloud Java Team ownership) -**/com/google/cloud/bigtable/admin/** @googleapis/api-bigtable @googleapis/cloud-sdk-java-team -**/com/google/bigtable/admin/** @googleapis/api-bigtable @googleapis/cloud-sdk-java-team +**/com/google/cloud/bigtable/admin/** @googleapis/bigtable-team @googleapis/cloud-sdk-java-team +**/com/google/bigtable/admin/** @googleapis/bigtable-team @googleapis/cloud-sdk-java-team diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 7be7e5e5f1..1005971cae 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -224,5 +224,5 @@ permissionRules: permission: admin - team: yoshi-java-admins permission: admin - - team: yoshi-java + - team: cloud-sdk-java-team permission: push diff --git a/.repo-metadata.json b/.repo-metadata.json index d40cb5f9c0..8ac2726bf0 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -13,7 +13,7 @@ "api_id": "bigtable.googleapis.com", "library_type": "GAPIC_COMBO", "requires_billing": true, - "codeowner_team": "@googleapis/api-bigtable", + "codeowner_team": "@googleapis/bigtable-team", "excluded_poms": "google-cloud-bigtable-bom", "issue_tracker": "https://issuetracker.google.com/savedsearches/559777", "extra_versioned_modules": "google-cloud-bigtable-emulator,google-cloud-bigtable-emulator-core", diff --git a/generation_config.yaml b/generation_config.yaml index 206787fb31..0f2a822e5c 100644 --- a/generation_config.yaml +++ b/generation_config.yaml @@ -27,7 +27,7 @@ libraries: issue_tracker: https://issuetracker.google.com/savedsearches/559777 release_level: stable distribution_name: com.google.cloud:google-cloud-bigtable - codeowner_team: '@googleapis/api-bigtable' + codeowner_team: '@googleapis/bigtable-team' api_id: bigtable.googleapis.com library_type: GAPIC_COMBO extra_versioned_modules: google-cloud-bigtable-emulator,google-cloud-bigtable-emulator-core From 54fee08e1e13e123ab5e6ac746ef620251755778 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 19 Feb 2026 13:14:19 -0500 Subject: [PATCH 04/33] chore: introduce PeerInfo & MetadataExtractor (#2788) * chore: introduce PeerInfo & MetadataExtractor Centralize sideband metadata collection using a new interceptor. Which gets injected into the GrpcCallContext channel. This provides the following benefits: - it works even if the end user sets their own channel provider - centralizes fetching of sideband metadata - removes the need for fetching directpath signals from grpc internals Change-Id: I42917074d65ccd7b8680f4a2a10c904b7646e4b6 * format Change-Id: Id9dfd28ca4a3f9e98474b33c98895e05b830b410 * oops Change-Id: I87f110743cd6261e07b59bdcf2bc005af0916d35 * format Change-Id: Ide93ae0406012e8e5779d65cb9770dbda5d0562e * remove replaced location & gfe methods Change-Id: I2aff3f13f2f07b400d6f2099b75c9f1462077e44 * add todo Change-Id: If07939e62e04e9c7a27862bf87ca5b8731711b75 * fix null handling of sideband data formating and remove stale code Change-Id: Icf1b8ed5d020c9bf5386173b817a89f1679369b4 * todo Change-Id: I99e5dd3b4b2397d32fbd41ac7c4e697f6788cd4f * remove stale dep Change-Id: I3c98eef573aea3364f2788135407acbba991d7c7 * Eagerly set sideband data instead of deferring until onClose Also defensively add null checks for it and a todo to remove them Change-Id: Ie8237bfcc8c5a0886735ca5b93c0f03f5373e24b --- google-cloud-bigtable/pom.xml | 5 - .../v2/stub/EnhancedBigtableStubSettings.java | 3 +- .../v2/stub/MetadataExtractorInterceptor.java | 198 ++++++++++++++++++ .../metrics/BigtableGrpcStreamTracer.java | 33 +-- .../data/v2/stub/metrics/BigtableTracer.java | 27 +-- .../BigtableTracerStreamingCallable.java | 51 ++--- .../metrics/BigtableTracerUnaryCallable.java | 66 ++---- .../v2/stub/metrics/BuiltinMetricsTracer.java | 94 +++------ .../data/v2/stub/metrics/CompositeTracer.java | 28 +-- .../data/v2/stub/metrics/MetricsTracer.java | 26 ++- .../bigtable/data/v2/stub/metrics/Util.java | 146 +++---------- .../metrics/BuiltinMetricsTracerTest.java | 5 +- .../v2/stub/metrics/CompositeTracerTest.java | 21 +- .../data/v2/stub/metrics/UtilTest.java | 22 -- 14 files changed, 328 insertions(+), 397 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 30d61cdc6a..bd4c6f0b63 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -136,10 +136,6 @@ com.google.protobuf protobuf-java-util - - com.google.code.gson - gson - io.opencensus opencensus-api @@ -147,7 +143,6 @@ io.grpc grpc-alts - runtime org.checkerframework diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index d1fe259ea1..6a9dcdfbec 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -922,7 +922,8 @@ private Builder() { .setReverseScans(true) .setLastScannedRowResponses(true) .setDirectAccessRequested(DIRECT_PATH_ENABLED) - .setTrafficDirectorEnabled(DIRECT_PATH_ENABLED); + .setTrafficDirectorEnabled(DIRECT_PATH_ENABLED) + .setPeerInfo(true); } private Builder(EnhancedBigtableStubSettings settings) { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java new file mode 100644 index 0000000000..5b43f57527 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java @@ -0,0 +1,198 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub; + +import com.google.api.core.InternalApi; +import com.google.api.gax.grpc.GrpcCallContext; +import com.google.bigtable.v2.PeerInfo; +import com.google.bigtable.v2.ResponseParams; +import com.google.common.base.Strings; +import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.Attributes; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ClientInterceptors; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.Status; +import io.grpc.alts.AltsContextUtil; +import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +@InternalApi +public class MetadataExtractorInterceptor implements ClientInterceptor { + private final SidebandData sidebandData = new SidebandData(); + + public GrpcCallContext injectInto(GrpcCallContext ctx) { + // TODO: migrate to using .withTransportChannel + // This will require a change on gax's side to expose the underlying ManagedChannel in + // GrpcTransportChannel (its currently package private). + return ctx.withChannel(ClientInterceptors.intercept(ctx.getChannel(), this)) + .withCallOptions(ctx.getCallOptions().withOption(SidebandData.KEY, sidebandData)); + } + + @Override + public ClientCall interceptCall( + MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { + return new ForwardingClientCall.SimpleForwardingClientCall( + channel.newCall(methodDescriptor, callOptions)) { + @Override + public void start(Listener responseListener, Metadata headers) { + sidebandData.reset(); + + super.start( + new ForwardingClientCallListener.SimpleForwardingClientCallListener( + responseListener) { + @Override + public void onHeaders(Metadata headers) { + sidebandData.onResponseHeaders(headers, getAttributes()); + super.onHeaders(headers); + } + + @Override + public void onClose(Status status, Metadata trailers) { + sidebandData.onClose(status, trailers); + super.onClose(status, trailers); + } + }, + headers); + } + }; + } + + public SidebandData getSidebandData() { + return sidebandData; + } + + public static class SidebandData { + private static final CallOptions.Key KEY = + CallOptions.Key.create("bigtable-sideband"); + + private static final Metadata.Key SERVER_TIMING_HEADER_KEY = + Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER); + private static final Pattern SERVER_TIMING_HEADER_PATTERN = + Pattern.compile(".*dur=(?\\d+)"); + private static final Metadata.Key LOCATION_METADATA_KEY = + Metadata.Key.of("x-goog-ext-425905942-bin", Metadata.BINARY_BYTE_MARSHALLER); + private static final Metadata.Key PEER_INFO_KEY = + Metadata.Key.of("bigtable-peer-info", Metadata.ASCII_STRING_MARSHALLER); + + @Nullable private volatile ResponseParams responseParams; + @Nullable private volatile PeerInfo peerInfo; + @Nullable private volatile Long gfeTiming; + + @Nullable + public ResponseParams getResponseParams() { + return responseParams; + } + + @Nullable + public PeerInfo getPeerInfo() { + return peerInfo; + } + + @Nullable + public Long getGfeTiming() { + return gfeTiming; + } + + private void reset() { + responseParams = null; + peerInfo = null; + gfeTiming = null; + } + + void onResponseHeaders(Metadata md, Attributes attributes) { + responseParams = extractResponseParams(md); + gfeTiming = extractGfeLatency(md); + peerInfo = extractPeerInfo(md, gfeTiming, attributes); + } + + void onClose(Status status, Metadata trailers) { + if (responseParams == null) { + responseParams = extractResponseParams(trailers); + } + } + + @Nullable + private static Long extractGfeLatency(Metadata metadata) { + String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY); + if (serverTiming == null) { + return null; + } + Matcher matcher = SERVER_TIMING_HEADER_PATTERN.matcher(serverTiming); + // this should always be true + if (matcher.find()) { + return Long.parseLong(matcher.group("dur")); + } + return null; + } + + @Nullable + private static PeerInfo extractPeerInfo( + Metadata metadata, Long gfeTiming, Attributes attributes) { + String encodedStr = metadata.get(PEER_INFO_KEY); + if (Strings.isNullOrEmpty(encodedStr)) { + return null; + } + + try { + byte[] decoded = Base64.getUrlDecoder().decode(encodedStr); + PeerInfo peerInfo = PeerInfo.parseFrom(decoded); + PeerInfo.TransportType effectiveTransport = peerInfo.getTransportType(); + + // TODO: remove this once transport_type is being sent by the server + // This is a temporary workaround to detect directpath until its available from the server + if (effectiveTransport == PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) { + boolean isAlts = AltsContextUtil.check(attributes); + if (isAlts) { + effectiveTransport = PeerInfo.TransportType.TRANSPORT_TYPE_DIRECT_ACCESS; + } else if (gfeTiming != null) { + effectiveTransport = PeerInfo.TransportType.TRANSPORT_TYPE_CLOUD_PATH; + } + } + if (effectiveTransport != PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) { + peerInfo = peerInfo.toBuilder().setTransportType(effectiveTransport).build(); + } + return peerInfo; + } catch (Exception e) { + throw new IllegalArgumentException( + "Failed to parse " + + PEER_INFO_KEY.name() + + " from the response header value: " + + encodedStr); + } + } + + @Nullable + private static ResponseParams extractResponseParams(Metadata metadata) { + byte[] responseParams = metadata.get(LOCATION_METADATA_KEY); + if (responseParams != null) { + try { + return ResponseParams.parseFrom(responseParams); + } catch (InvalidProtocolBufferException e) { + } + } + return null; + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java index a364adbc46..9b220c1de3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java @@ -15,10 +15,8 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracer.TransportAttrs; import io.grpc.ClientStreamTracer; import io.grpc.Metadata; -import io.grpc.Status; /** * Records the time a request is enqueued in a grpc channel queue. This a bridge between gRPC stream @@ -26,16 +24,9 @@ * asking gRPC to start an RPC and gRPC actually serializing that RPC. */ class BigtableGrpcStreamTracer extends ClientStreamTracer { - private static final String GRPC_LB_LOCALITY_KEY = "grpc.lb.locality"; - private static final String GRPC_LB_BACKEND_SERVICE_KEY = "grpc.lb.backend_service"; - - private final StreamInfo info; private final BigtableTracer tracer; - private volatile String backendService = null; - private volatile String locality = null; - public BigtableGrpcStreamTracer(StreamInfo info, BigtableTracer tracer) { - this.info = info; + public BigtableGrpcStreamTracer(BigtableTracer tracer) { this.tracer = tracer; } @@ -44,26 +35,6 @@ public void outboundMessageSent(int seqNo, long optionalWireSize, long optionalU tracer.grpcMessageSent(); } - @Override - public void addOptionalLabel(String key, String value) { - switch (key) { - case GRPC_LB_LOCALITY_KEY: - this.locality = value; - break; - case GRPC_LB_BACKEND_SERVICE_KEY: - this.backendService = value; - break; - } - - super.addOptionalLabel(key, value); - } - - @Override - public void streamClosed(Status status) { - tracer.setTransportAttrs(TransportAttrs.create(locality, backendService)); - super.streamClosed(status); - } - static class Factory extends ClientStreamTracer.Factory { private final BigtableTracer tracer; @@ -75,7 +46,7 @@ static class Factory extends ClientStreamTracer.Factory { @Override public ClientStreamTracer newClientStreamTracer( ClientStreamTracer.StreamInfo info, Metadata headers) { - return new BigtableGrpcStreamTracer(info, tracer); + return new BigtableGrpcStreamTracer(tracer); } } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java index 898d743cd9..a1a53b6089 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java @@ -20,6 +20,7 @@ import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.BaseApiTracer; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import java.time.Duration; import javax.annotation.Nullable; @@ -70,36 +71,12 @@ public int getAttempt() { return attempt; } - /** - * Record the latency between Google's network receives the RPC and reads back the first byte of - * the response from server-timing header. If server-timing header is missing, increment the - * missing header count. - */ - public void recordGfeMetadata(@Nullable Long latency, @Nullable Throwable throwable) { - // noop - } - /** Adds an annotation of the total throttled time of a batch. */ public void batchRequestThrottled(long throttledTimeMs) { // noop } - /** - * Set the Bigtable zone and cluster so metrics can be tagged with location information. This will - * be called in BuiltinMetricsTracer. - */ - public void setLocations(String zone, String cluster) { - // noop - } - - /** Set the underlying transport used to process the attempt */ - public void setTransportAttrs(BuiltinMetricsTracer.TransportAttrs attrs) {} - - @Deprecated - /** - * @deprecated {@link #grpcMessageSent()} is called instead. - */ - public void grpcChannelQueuedLatencies(long queuedTimeMs) { + public void setSidebandData(MetadataExtractorInterceptor.SidebandData sidebandData) { // noop } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java index 13b832b8b1..3cdcdc374e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java @@ -16,11 +16,12 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.core.InternalApi; -import com.google.api.gax.grpc.GrpcResponseMetadata; +import com.google.api.gax.grpc.GrpcCallContext; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.StreamController; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.cloud.bigtable.data.v2.stub.SafeResponseObserver; import com.google.common.base.Preconditions; import com.google.common.base.Stopwatch; @@ -28,19 +29,8 @@ import javax.annotation.Nonnull; /** - * This callable will - *

  • -Inject a {@link GrpcResponseMetadata} to access the headers returned by gRPC methods upon - * completion. The {@link BigtableTracer} will process metrics that were injected in the - * header/trailer and publish them to OpenCensus. If {@link GrpcResponseMetadata#getMetadata()} - * returned null, it probably means that the request has never reached GFE, and it'll increment - * the gfe_header_missing_counter in this case. - *
  • -This class will also access trailers from {@link GrpcResponseMetadata} to record zone and - * cluster ids. - *
  • -Call {@link BigtableTracer#onRequest(int)} to record the request events in a stream. - *
  • -This class will also inject a {@link BigtableGrpcStreamTracer} that'll record the time an - * RPC spent in a grpc channel queue. - *
  • This class is considered an internal implementation detail and not meant to be used by - * applications. + * This class is considered an internal implementation detail and not meant to be used by + * applications. */ @InternalApi public class BigtableTracerStreamingCallable @@ -56,40 +46,41 @@ public BigtableTracerStreamingCallable( @Override public void call( RequestT request, ResponseObserver responseObserver, ApiCallContext context) { - final GrpcResponseMetadata responseMetadata = new GrpcResponseMetadata(); + GrpcCallContext grpcCtx = (GrpcCallContext) context; + + MetadataExtractorInterceptor metadataExtractor = new MetadataExtractorInterceptor(); + grpcCtx = metadataExtractor.injectInto(grpcCtx); + // tracer should always be an instance of bigtable tracer if (context.getTracer() instanceof BigtableTracer) { BigtableTracer tracer = (BigtableTracer) context.getTracer(); + tracer.setSidebandData(metadataExtractor.getSidebandData()); + grpcCtx = + grpcCtx.withCallOptions( + grpcCtx + .getCallOptions() + .withStreamTracerFactory(new BigtableGrpcStreamTracer.Factory(tracer))); + BigtableTracerResponseObserver innerObserver = - new BigtableTracerResponseObserver<>(responseObserver, tracer, responseMetadata); + new BigtableTracerResponseObserver<>(responseObserver, tracer); if (context.getRetrySettings() != null) { tracer.setTotalTimeoutDuration(context.getRetrySettings().getTotalTimeoutDuration()); } - innerCallable.call( - request, - innerObserver, - Util.injectBigtableStreamTracer( - context, responseMetadata, (BigtableTracer) context.getTracer())); + innerCallable.call(request, innerObserver, grpcCtx); } else { - innerCallable.call(request, responseObserver, context); + innerCallable.call(request, responseObserver, grpcCtx); } } private class BigtableTracerResponseObserver extends SafeResponseObserver { - private final BigtableTracer tracer; private final ResponseObserver outerObserver; - private final GrpcResponseMetadata responseMetadata; - BigtableTracerResponseObserver( - ResponseObserver observer, - BigtableTracer tracer, - GrpcResponseMetadata metadata) { + BigtableTracerResponseObserver(ResponseObserver observer, BigtableTracer tracer) { super(observer); this.tracer = tracer; this.outerObserver = observer; - this.responseMetadata = metadata; } @Override @@ -107,13 +98,11 @@ protected void onResponseImpl(ResponseT response) { @Override protected void onErrorImpl(Throwable t) { - Util.recordMetricsFromMetadata(responseMetadata, tracer, t); outerObserver.onError(t); } @Override protected void onCompleteImpl() { - Util.recordMetricsFromMetadata(responseMetadata, tracer, null); outerObserver.onComplete(); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java index 37ba74bfdb..363a69af3d 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java @@ -16,29 +16,17 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutureCallback; -import com.google.api.core.ApiFutures; import com.google.api.core.InternalApi; -import com.google.api.gax.grpc.GrpcResponseMetadata; +import com.google.api.gax.grpc.GrpcCallContext; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.UnaryCallable; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.MoreExecutors; import javax.annotation.Nonnull; /** - * This callable will: - *
  • - Inject a {@link GrpcResponseMetadata} to access the headers returned by gRPC methods upon - * completion. The {@link BigtableTracer} will process metrics that were injected in the - * header/trailer and publish them to OpenCensus. If {@link GrpcResponseMetadata#getMetadata()} - * returned null, it probably means that the request has never reached GFE, and it'll increment - * the gfe_header_missing_counter in this case. - *
  • -This class will also access trailers from {@link GrpcResponseMetadata} to record zone and - * cluster ids. - *
  • -This class will also inject a {@link BigtableGrpcStreamTracer} that'll record the time an - * RPC spent in a grpc channel queue. - *
  • This class is considered an internal implementation detail and not meant to be used by - * applications. + * This class is considered an internal implementation detail and not meant to be used by + * applications. */ @InternalApi public class BigtableTracerUnaryCallable @@ -52,46 +40,24 @@ public BigtableTracerUnaryCallable(@Nonnull UnaryCallable i @Override public ApiFuture futureCall(RequestT request, ApiCallContext context) { + MetadataExtractorInterceptor interceptor = new MetadataExtractorInterceptor(); + GrpcCallContext grpcCtx = interceptor.injectInto((GrpcCallContext) context); + // tracer should always be an instance of BigtableTracer if (context.getTracer() instanceof BigtableTracer) { BigtableTracer tracer = (BigtableTracer) context.getTracer(); - final GrpcResponseMetadata responseMetadata = new GrpcResponseMetadata(); - BigtableTracerUnaryCallback callback = - new BigtableTracerUnaryCallback( - (BigtableTracer) context.getTracer(), responseMetadata); + tracer.setSidebandData(interceptor.getSidebandData()); + + grpcCtx = + grpcCtx.withCallOptions( + grpcCtx + .getCallOptions() + .withStreamTracerFactory(new BigtableGrpcStreamTracer.Factory(tracer))); + if (context.getRetrySettings() != null) { tracer.setTotalTimeoutDuration(context.getRetrySettings().getTotalTimeoutDuration()); } - ApiFuture future = - innerCallable.futureCall( - request, - Util.injectBigtableStreamTracer( - context, responseMetadata, (BigtableTracer) context.getTracer())); - ApiFutures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; - } else { - return innerCallable.futureCall(request, context); - } - } - - private class BigtableTracerUnaryCallback implements ApiFutureCallback { - - private final BigtableTracer tracer; - private final GrpcResponseMetadata responseMetadata; - - BigtableTracerUnaryCallback(BigtableTracer tracer, GrpcResponseMetadata responseMetadata) { - this.tracer = tracer; - this.responseMetadata = responseMetadata; - } - - @Override - public void onFailure(Throwable throwable) { - Util.recordMetricsFromMetadata(responseMetadata, tracer, throwable); - } - - @Override - public void onSuccess(ResponseT response) { - Util.recordMetricsFromMetadata(responseMetadata, tracer, null); } + return innerCallable.futureCall(request, grpcCtx); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index e6ebad367a..546ea41c9f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -34,27 +34,23 @@ import com.google.api.core.ObsoleteApi; import com.google.api.gax.retrying.ServerStreamingAttemptException; import com.google.api.gax.tracing.SpanName; -import com.google.auto.value.AutoValue; +import com.google.bigtable.v2.PeerInfo; import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Stopwatch; -import com.google.common.base.Strings; import com.google.common.math.IntMath; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; import io.grpc.Deadline; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleGauge; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.LongCounter; import java.time.Duration; -import java.util.Map; +import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.Nullable; /** @@ -62,24 +58,6 @@ * bigtable.googleapis.com/client namespace */ class BuiltinMetricsTracer extends BigtableTracer { - @AutoValue - abstract static class TransportAttrs { - @Nullable - abstract String getLocality(); - - @Nullable - abstract String getBackendService(); - - static TransportAttrs create(@Nullable String locality, @Nullable String backendService) { - return new AutoValue_BuiltinMetricsTracer_TransportAttrs(locality, backendService); - } - } - - private static final Logger logger = Logger.getLogger(BuiltinMetricsTracer.class.getName()); - private static final Gson GSON = new Gson(); - private static final TypeToken> LOCALITY_TYPE = - new TypeToken>() {}; - private static final String NAME = "java-bigtable/" + Version.VERSION; private final OperationType operationType; private final SpanName spanName; @@ -108,22 +86,21 @@ static TransportAttrs create(@Nullable String locality, @Nullable String backend private final AtomicInteger requestLeft = new AtomicInteger(0); - // Monitored resource labels private String tableId = ""; - private String zone = "global"; - private String cluster = ""; private final AtomicLong totalClientBlockingTime = new AtomicLong(0); private final Attributes baseAttributes; - private Long serverLatencies = null; private final AtomicLong grpcMessageSentDelay = new AtomicLong(0); private Deadline operationDeadline = null; private volatile long remainingDeadlineAtAttemptStart = 0; - private TransportAttrs transportAttrs = null; + // TODO: ensure that this is never null and remove all of the checks + // Sideband data wrapper itself should never be null unless a callable chain forgets to + // add BigtableTracer{Streaming,Unary}Callable. Which would be considered a bug. + @Nullable private volatile MetadataExtractorInterceptor.SidebandData sidebandData = null; // OpenCensus (and server) histogram buckets use [start, end), however OpenTelemetry uses (start, // end]. To work around this, we measure all the latencies in nanoseconds and convert them @@ -328,21 +305,8 @@ public int getAttempt() { } @Override - public void recordGfeMetadata(@Nullable Long latency, @Nullable Throwable throwable) { - if (latency != null) { - serverLatencies = latency; - } - } - - @Override - public void setLocations(String zone, String cluster) { - this.zone = zone; - this.cluster = cluster; - } - - @Override - public void setTransportAttrs(TransportAttrs attrs) { - this.transportAttrs = attrs; + public void setSidebandData(MetadataExtractorInterceptor.SidebandData sidebandData) { + this.sidebandData = sidebandData; } @Override @@ -390,8 +354,8 @@ private void recordOperationCompletion(@Nullable Throwable status) { Attributes attributes = baseAttributes.toBuilder() .put(TABLE_ID_KEY, tableId) - .put(CLUSTER_ID_KEY, cluster) - .put(ZONE_ID_KEY, zone) + .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(sidebandData)) + .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(sidebandData)) .put(METHOD_KEY, spanName.toString()) .put(CLIENT_NAME_KEY, NAME) .put(STREAMING_KEY, isStreaming) @@ -445,8 +409,8 @@ private void recordAttemptCompletion(@Nullable Throwable status) { Attributes attributes = baseAttributes.toBuilder() .put(TABLE_ID_KEY, tableId) - .put(CLUSTER_ID_KEY, cluster) - .put(ZONE_ID_KEY, zone) + .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(sidebandData)) + .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(sidebandData)) .put(METHOD_KEY, spanName.toString()) .put(CLIENT_NAME_KEY, NAME) .put(STREAMING_KEY, isStreaming) @@ -459,29 +423,27 @@ private void recordAttemptCompletion(@Nullable Throwable status) { attemptLatenciesHistogram.record( convertToMs(attemptTimer.elapsed(TimeUnit.NANOSECONDS)), attributes); - String transportType = "cloudpath"; + String transportTypeStr = "cloudpath"; String transportRegion = ""; String transportZone = ""; String transportSubzone = ""; - try { - if (transportAttrs != null && !Strings.isNullOrEmpty(transportAttrs.getLocality())) { - // only directpath has locality - transportType = "directpath"; - Map localityMap = - GSON.fromJson(transportAttrs.getLocality(), LOCALITY_TYPE); - transportRegion = localityMap.getOrDefault("region", ""); - transportZone = localityMap.getOrDefault("zone", ""); - transportSubzone = localityMap.getOrDefault("sub_zone", ""); - } - } catch (RuntimeException e) { - logger.log( - Level.WARNING, "Failed to parse transport locality: " + transportAttrs.getLocality(), e); + if (sidebandData != null) { + transportTypeStr = Util.formatTransportTypeMetricLabel(sidebandData); + transportZone = + Optional.ofNullable(sidebandData.getPeerInfo()) + .map(PeerInfo::getApplicationFrontendZone) + .orElse(""); + transportSubzone = + Optional.ofNullable(sidebandData.getPeerInfo()) + .map(PeerInfo::getApplicationFrontendSubzone) + .orElse(""); } + attemptLatencies2Histogram.record( convertToMs(attemptTimer.elapsed(TimeUnit.NANOSECONDS)), attributes.toBuilder() - .put(TRANSPORT_TYPE, transportType) + .put(TRANSPORT_TYPE, transportTypeStr) .put(TRANSPORT_REGION, transportRegion) .put(TRANSPORT_ZONE, transportZone) .put(TRANSPORT_SUBZONE, transportSubzone) @@ -493,8 +455,8 @@ private void recordAttemptCompletion(@Nullable Throwable status) { remainingDeadlineHistogram.record(Math.max(0, remainingDeadlineAtAttemptStart), attributes); } - if (serverLatencies != null) { - serverLatenciesHistogram.record(serverLatencies, attributes); + if (sidebandData != null && sidebandData.getGfeTiming() != null) { + serverLatenciesHistogram.record(sidebandData.getGfeTiming(), attributes); connectivityErrorCounter.add(0, attributes); } else { connectivityErrorCounter.add(1, attributes); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java index f6d0858459..fad00a6d91 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java @@ -19,6 +19,7 @@ import com.google.api.core.ObsoleteApi; import com.google.api.gax.tracing.ApiTracer; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; @@ -197,13 +198,6 @@ public int getAttempt() { return attempt; } - @Override - public void recordGfeMetadata(@Nullable Long latency, @Nullable Throwable throwable) { - for (BigtableTracer tracer : bigtableTracers) { - tracer.recordGfeMetadata(latency, throwable); - } - } - @Override public void batchRequestThrottled(long throttledTimeMs) { for (BigtableTracer tracer : bigtableTracers) { @@ -212,16 +206,9 @@ public void batchRequestThrottled(long throttledTimeMs) { } @Override - public void setLocations(String zone, String cluster) { - for (BigtableTracer tracer : bigtableTracers) { - tracer.setLocations(zone, cluster); - } - } - - @Override - public void setTransportAttrs(BuiltinMetricsTracer.TransportAttrs attrs) { - for (BigtableTracer tracer : bigtableTracers) { - tracer.setTransportAttrs(attrs); + public void setSidebandData(MetadataExtractorInterceptor.SidebandData sidebandData) { + for (BigtableTracer bigtableTracer : bigtableTracers) { + bigtableTracer.setSidebandData(sidebandData); } } @@ -246,13 +233,6 @@ public void afterResponse(long applicationLatency) { } } - @Override - public void grpcChannelQueuedLatencies(long queuedTimeMs) { - for (BigtableTracer tracer : bigtableTracers) { - tracer.grpcChannelQueuedLatencies(queuedTimeMs); - } - } - @Override public void grpcMessageSent() { for (BigtableTracer tracer : bigtableTracers) { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java index c322b75df8..53b4ca87a8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java @@ -21,6 +21,7 @@ import com.google.api.gax.retrying.ServerStreamingAttemptException; import com.google.api.gax.tracing.ApiTracerFactory.OperationType; import com.google.api.gax.tracing.SpanName; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Stopwatch; import io.opencensus.stats.MeasureMap; import io.opencensus.stats.StatsRecorder; @@ -63,6 +64,7 @@ class MetricsTracer extends BigtableTracer { private volatile boolean reportBatchingLatency = false; private volatile long batchThrottledLatency = 0; + private MetadataExtractorInterceptor.SidebandData sidebandData; MetricsTracer( OperationType operationType, @@ -187,6 +189,14 @@ private void recordAttemptCompletion(@Nullable Throwable throwable) { RpcMeasureConstants.BIGTABLE_ATTEMPT_LATENCY, attemptTimer.elapsed(TimeUnit.MILLISECONDS)); + if (sidebandData != null && sidebandData.getGfeTiming() != null) { + measures + .put(RpcMeasureConstants.BIGTABLE_GFE_LATENCY, sidebandData.getGfeTiming()) + .put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 0L); + } else { + measures.put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 1L); + } + if (reportBatchingLatency) { measures.put(RpcMeasureConstants.BIGTABLE_BATCH_THROTTLED_TIME, batchThrottledLatency); @@ -226,20 +236,8 @@ public int getAttempt() { } @Override - public void recordGfeMetadata(@Nullable Long latency, @Nullable Throwable throwable) { - MeasureMap measures = stats.newMeasureMap(); - if (latency != null) { - measures - .put(RpcMeasureConstants.BIGTABLE_GFE_LATENCY, latency) - .put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 0L); - } else { - measures.put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 1L); - } - measures.record( - newTagCtxBuilder() - .putLocal( - RpcMeasureConstants.BIGTABLE_STATUS, TagValue.create(Util.extractStatus(throwable))) - .build()); + public void setSidebandData(MetadataExtractorInterceptor.SidebandData sidebandData) { + this.sidebandData = sidebandData; } @Override diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index 9ba2d39c49..da7de371c3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -16,8 +16,6 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.core.InternalApi; -import com.google.api.gax.grpc.GrpcCallContext; -import com.google.api.gax.grpc.GrpcResponseMetadata; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode; @@ -29,6 +27,7 @@ import com.google.bigtable.v2.MaterializedViewName; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowsRequest; +import com.google.bigtable.v2.PeerInfo; import com.google.bigtable.v2.ReadChangeStreamRequest; import com.google.bigtable.v2.ReadModifyWriteRowRequest; import com.google.bigtable.v2.ReadRowsRequest; @@ -36,15 +35,13 @@ import com.google.bigtable.v2.SampleRowKeysRequest; import com.google.bigtable.v2.TableName; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; -import com.google.protobuf.InvalidProtocolBufferException; -import io.grpc.CallOptions; import io.grpc.Metadata; import io.grpc.Status; import io.grpc.StatusException; import io.grpc.StatusRuntimeException; -import io.opencensus.tags.TagValue; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -57,13 +54,11 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.annotation.Nullable; /** Utilities to help integrating with OpenCensus. */ @@ -74,12 +69,6 @@ public class Util { static final Metadata.Key ATTEMPT_EPOCH_KEY = Metadata.Key.of("bigtable-client-attempt-epoch-usec", Metadata.ASCII_STRING_MARSHALLER); - private static final Metadata.Key SERVER_TIMING_HEADER_KEY = - Metadata.Key.of("server-timing", Metadata.ASCII_STRING_MARSHALLER); - private static final Pattern SERVER_TIMING_HEADER_PATTERN = Pattern.compile(".*dur=(?\\d+)"); - static final Metadata.Key LOCATION_METADATA_KEY = - Metadata.Key.of("x-goog-ext-425905942-bin", Metadata.BINARY_BYTE_MARSHALLER); - /** Convert an exception into a value that can be used to create an OpenCensus tag value. */ public static String extractStatus(@Nullable Throwable error) { final String statusString; @@ -101,26 +90,6 @@ public static String extractStatus(@Nullable Throwable error) { return statusString; } - /** - * Await the result of the future and convert it into a value that can be used as an OpenCensus - * tag value. - */ - static TagValue extractStatusFromFuture(Future future) { - Throwable error = null; - - try { - future.get(); - } catch (InterruptedException e) { - error = e; - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - error = e.getCause(); - } catch (RuntimeException e) { - error = e; - } - return TagValue.create(extractStatus(error)); - } - static String extractTableId(Object request) { String tableName = null; String authorizedViewName = null; @@ -179,84 +148,6 @@ static Map> createStatsHeaders(ApiCallContext apiCallContex return headers.build(); } - private static Long getGfeLatency(@Nullable Metadata metadata) { - if (metadata == null) { - return null; - } - String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY); - if (serverTiming == null) { - return null; - } - Matcher matcher = SERVER_TIMING_HEADER_PATTERN.matcher(serverTiming); - // this should always be true - if (matcher.find()) { - long latency = Long.valueOf(matcher.group("dur")); - return latency; - } - return null; - } - - private static ResponseParams getResponseParams(@Nullable Metadata metadata) { - if (metadata == null) { - return null; - } - byte[] responseParams = metadata.get(Util.LOCATION_METADATA_KEY); - if (responseParams != null) { - try { - return ResponseParams.parseFrom(responseParams); - } catch (InvalidProtocolBufferException e) { - } - } - return null; - } - - static void recordMetricsFromMetadata( - GrpcResponseMetadata responseMetadata, BigtableTracer tracer, Throwable throwable) { - Metadata metadata = responseMetadata.getMetadata(); - - // Get the response params from the metadata. Check both headers and trailers - // because in different environments the metadata could be returned in headers or trailers - @Nullable ResponseParams responseParams = getResponseParams(responseMetadata.getMetadata()); - if (responseParams == null) { - responseParams = getResponseParams(responseMetadata.getTrailingMetadata()); - } - // Set tracer locations if response params is not null - if (responseParams != null) { - tracer.setLocations(responseParams.getZoneId(), responseParams.getClusterId()); - } - - // server-timing metric will be added through GrpcResponseMetadata#onHeaders(Metadata), - // so it's not checking trailing metadata here. - @Nullable Long latency = getGfeLatency(metadata); - // For direct path, we won't see GFE server-timing header. However, if we received the - // location info, we know that there isn't a connectivity issue. Set the latency to - // 0 so gfe missing header won't get incremented. - if (responseParams != null && latency == null) { - latency = 0L; - } - // Record gfe metrics - tracer.recordGfeMetadata(latency, throwable); - } - - /** - * This method bridges gRPC stream tracing to bigtable tracing by adding a {@link - * io.grpc.ClientStreamTracer} to the callContext. - */ - static GrpcCallContext injectBigtableStreamTracer( - ApiCallContext context, GrpcResponseMetadata responseMetadata, BigtableTracer tracer) { - if (context instanceof GrpcCallContext) { - GrpcCallContext callContext = (GrpcCallContext) context; - CallOptions callOptions = callContext.getCallOptions(); - return responseMetadata.addHandlers( - callContext.withCallOptions( - callOptions.withStreamTracerFactory(new BigtableGrpcStreamTracer.Factory(tracer)))); - } else { - // context should always be an instance of GrpcCallContext. If not throw an exception - // so we can see what class context is. - throw new RuntimeException("Unexpected context class: " + context.getClass().getName()); - } - } - public static OpenTelemetrySdk newInternalOpentelemetry( EnhancedBigtableStubSettings settings, Credentials credentials, @@ -285,4 +176,33 @@ public static OpenTelemetrySdk newInternalOpentelemetry( .build()); return OpenTelemetrySdk.builder().setMeterProvider(meterProviderBuilder.build()).build(); } + + public static String formatTransportTypeMetricLabel( + MetadataExtractorInterceptor.SidebandData sidebandData) { + return Optional.ofNullable(sidebandData) + .flatMap(s -> Optional.ofNullable(s.getPeerInfo())) + .map(PeerInfo::getTransportType) + .orElse(PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) + .name() + .replace("TRANSPORT_TYPE_", "") + .toLowerCase(Locale.ENGLISH); + } + + public static String formatClusterIdMetricLabel( + @Nullable MetadataExtractorInterceptor.SidebandData sidebandData) { + return Optional.ofNullable(sidebandData) + .flatMap(d -> Optional.ofNullable(d.getResponseParams())) + .map(ResponseParams::getClusterId) + .filter(s -> !s.isEmpty()) + .orElse(""); + } + + public static String formatZoneIdMetricLabel( + @Nullable MetadataExtractorInterceptor.SidebandData sidebandData) { + return Optional.ofNullable(sidebandData) + .flatMap(d -> Optional.ofNullable(d.getResponseParams())) + .map(ResponseParams::getZoneId) + .filter(s -> !s.isEmpty()) + .orElse("global"); + } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index df63ff8019..3d0e6425d9 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -136,6 +136,9 @@ @RunWith(JUnit4.class) public class BuiltinMetricsTracerTest { + private static final Metadata.Key LOCATION_METADATA_KEY = + Metadata.Key.of("x-goog-ext-425905942-bin", Metadata.BINARY_BYTE_MARSHALLER); + private static final String PROJECT_ID = "fake-project"; private static final String INSTANCE_ID = "fake-instance"; private static final String APP_PROFILE_ID = "default"; @@ -211,7 +214,7 @@ public void sendHeaders(Metadata headers) { ResponseParams params = ResponseParams.newBuilder().setZoneId(ZONE).setClusterId(CLUSTER).build(); byte[] byteArray = params.toByteArray(); - headers.put(Util.LOCATION_METADATA_KEY, byteArray); + headers.put(LOCATION_METADATA_KEY, byteArray); super.sendHeaders(headers); } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java index 71a4728f9f..62c343f16c 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java @@ -25,10 +25,9 @@ import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.ApiTracer.Scope; import com.google.bigtable.v2.ReadRowsRequest; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.cloud.bigtable.misc_utilities.MethodComparator; import com.google.common.collect.ImmutableList; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; import java.lang.reflect.Method; import java.util.Arrays; import org.junit.Assert; @@ -241,11 +240,12 @@ public void testGetAttempt() { } @Test - public void testRecordGfeLatency() { - Throwable t = new StatusRuntimeException(Status.UNAVAILABLE); - compositeTracer.recordGfeMetadata(20L, t); - verify(child3, times(1)).recordGfeMetadata(20L, t); - verify(child4, times(1)).recordGfeMetadata(20L, t); + public void testSidebandData() { + MetadataExtractorInterceptor.SidebandData sidebandData = + new MetadataExtractorInterceptor.SidebandData(); + compositeTracer.setSidebandData(sidebandData); + verify(child3, times(1)).setSidebandData(sidebandData); + verify(child4, times(1)).setSidebandData(sidebandData); } @Test @@ -264,13 +264,6 @@ public void testMethodsOverride() { .containsAtLeastElementsIn(baseMethods); } - @Test - public void testRequestBlockedOnChannel() { - compositeTracer.grpcChannelQueuedLatencies(5L); - verify(child3, times(1)).grpcChannelQueuedLatencies(5L); - verify(child4, times(1)).grpcChannelQueuedLatencies(5L); - } - @Test public void testGrpcMessageSent() { compositeTracer.grpcMessageSent(); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java index 3c0fb4e617..824d8be307 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java @@ -19,7 +19,6 @@ import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.DeadlineExceededException; -import com.google.common.util.concurrent.Futures; import io.grpc.Status; import io.opencensus.tags.TagValue; import org.junit.Test; @@ -34,12 +33,6 @@ public void testOk() { assertThat(tagValue.asString()).isEqualTo("OK"); } - @Test - public void testOkFuture() { - TagValue tagValue = Util.extractStatusFromFuture(Futures.immediateFuture(null)); - assertThat(tagValue.asString()).isEqualTo("OK"); - } - @Test public void testError() { DeadlineExceededException error = @@ -48,19 +41,4 @@ public void testError() { TagValue tagValue = TagValue.create(Util.extractStatus(error)); assertThat(tagValue.asString()).isEqualTo("DEADLINE_EXCEEDED"); } - - @Test - public void testErrorFuture() { - DeadlineExceededException error = - new DeadlineExceededException( - "Deadline exceeded", null, GrpcStatusCode.of(Status.Code.DEADLINE_EXCEEDED), true); - TagValue tagValue = Util.extractStatusFromFuture(Futures.immediateFailedFuture(error)); - assertThat(tagValue.asString()).isEqualTo("DEADLINE_EXCEEDED"); - } - - @Test - public void testCancelledFuture() { - TagValue tagValue = Util.extractStatusFromFuture(Futures.immediateCancelledFuture()); - assertThat(tagValue.asString()).isEqualTo("CANCELLED"); - } } From 1f390328b23855ee39e2c3dacf8a0eed8d962b08 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 19 Feb 2026 13:18:54 -0500 Subject: [PATCH 05/33] fix: ensure that per attempt metrics tracer is below the retries (#2793) Change-Id: Idc58fa55bdb34a1e85fff6685c043bf559655e84 --- .../data/v2/stub/EnhancedBigtableStub.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index cf8b65684e..e756e41f02 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -695,10 +695,10 @@ public ApiFuture> futureCall(String s, ApiCallContext apiCallCon withStatsHeaders = new StatsHeadersUnaryCallable<>(spoolable); UnaryCallable> - withBigtableTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders); + withAttemptTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders); UnaryCallable> - retryable = withRetries(withBigtableTracer, settings.sampleRowKeysSettings()); + retryable = withRetries(withAttemptTracer, settings.sampleRowKeysSettings()); return createUserFacingUnaryCallable( methodName, @@ -774,7 +774,7 @@ private UnaryCallable createMutateRowsBas ServerStreamingCallable convertException = new ConvertExceptionCallable<>(callable); - ServerStreamingCallable withBigtableTracer = + ServerStreamingCallable withAttemptTracer = new BigtableTracerStreamingCallable<>(convertException); BasicResultRetryAlgorithm resultRetryAlgorithm; @@ -797,7 +797,7 @@ private UnaryCallable createMutateRowsBas UnaryCallable baseCallable = new MutateRowsRetryingCallable( clientContext.getDefaultCallContext(), - withBigtableTracer, + withAttemptTracer, retryingExecutor, settings.bulkMutateRowsSettings().getRetryableCodes(), retryAlgorithm); @@ -1033,11 +1033,11 @@ private UnaryCallable createReadModifyWriteRowCallable( ServerStreamingCallable watched = Callables.watched(convertException, innerSettings, clientContext); - ServerStreamingCallable withBigtableTracer = + ServerStreamingCallable withAttemptTracer = new BigtableTracerStreamingCallable<>(watched); ServerStreamingCallable retrying = - withRetries(withBigtableTracer, innerSettings); + withRetries(withAttemptTracer, innerSettings); SpanName span = getSpanName("GenerateInitialChangeStreamPartitions"); ServerStreamingCallable traced = @@ -1105,11 +1105,11 @@ private UnaryCallable createReadModifyWriteRowCallable( ServerStreamingCallable watched = Callables.watched(merging, innerSettings, clientContext); - ServerStreamingCallable withBigtableTracer = + ServerStreamingCallable withAttemptTracer = new BigtableTracerStreamingCallable<>(watched); ServerStreamingCallable readChangeStreamCallable = - withRetries(withBigtableTracer, innerSettings); + withRetries(withAttemptTracer, innerSettings); ServerStreamingCallable readChangeStreamUserCallable = @@ -1175,6 +1175,9 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { ServerStreamingCallable convertException = new ConvertExceptionCallable<>(withPlanRefresh); + ServerStreamingCallable withAttemptTracer = + new BigtableTracerStreamingCallable<>(convertException); + ServerStreamingCallSettings retrySettings = ServerStreamingCallSettings.newBuilder() .setResumptionStrategy(new ExecuteQueryResumptionStrategy()) @@ -1189,7 +1192,7 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { // attempt stream will have reset set to true, so any unyielded data from the previous // attempt will be reset properly ServerStreamingCallable retries = - withRetries(convertException, retrySettings); + withRetries(withAttemptTracer, retrySettings); ServerStreamingCallable merging = new SqlRowMergingCallable(retries); @@ -1208,13 +1211,10 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { ServerStreamingCallable passingThroughErrorsToMetadata = new MetadataErrorHandlingCallable(watched); - ServerStreamingCallable withBigtableTracer = - new BigtableTracerStreamingCallable<>(passingThroughErrorsToMetadata); - SpanName span = getSpanName("ExecuteQuery"); ServerStreamingCallable traced = new TracedServerStreamingCallable<>( - withBigtableTracer, clientContext.getTracerFactory(), span); + passingThroughErrorsToMetadata, clientContext.getTracerFactory(), span); return new ExecuteQueryCallable( traced.withDefaultCallContext( From bc461749a0aa702f65c26774dd4696d47ef88eae Mon Sep 17 00:00:00 2001 From: Dohun Kim Date: Thu, 19 Feb 2026 14:48:11 -0500 Subject: [PATCH 06/33] feat(Bigtable): Add support for creating instances with tags (#2733) --- .../admin/v2/models/CreateInstanceRequest.java | 16 ++++++++++++++++ .../cloud/bigtable/admin/v2/models/Instance.java | 6 ++++++ .../v2/models/CreateClusterRequestTest.java | 2 ++ .../bigtable/admin/v2/models/InstanceTest.java | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java index 685e52d555..69c75f9011 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java @@ -118,6 +118,22 @@ public CreateInstanceRequest addLabel(@Nonnull String key, @Nonnull String value return this; } + /** + * Adds a tag to the instance. + * + *

    Tags are a way to organize and govern resources across Google Cloud. Unlike labels, Tags are + * standalone resources created and managed through the Resource Manager API. + * + * @see For more details + */ + @SuppressWarnings("WeakerAccess") + public CreateInstanceRequest addTag(@Nonnull String key, @Nonnull String value) { + Preconditions.checkNotNull(key, "Key can't be null"); + Preconditions.checkNotNull(value, "Value can't be null"); + builder.getInstanceBuilder().putTags(key, value); + return this; + } + /** * Adds a cluster to the instance request with manual scaling enabled. * diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java index c3a0c43bca..df163b0e0d 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java @@ -150,6 +150,12 @@ public String getDisplayName() { return proto.getDisplayName(); } + /** Gets the instance's tags. */ + @SuppressWarnings("WeakerAccess") + public Map getTags() { + return proto.getTagsMap(); + } + /** Gets the instance's current type. Can be DEVELOPMENT or PRODUCTION. */ @SuppressWarnings("WeakerAccess") public Type getType() { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java index 566641039a..fe28948347 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java @@ -128,6 +128,7 @@ public void testOptionalFields() { .setDisplayName("custom display name") .addLabel("my label", "with some value") .addLabel("my other label", "with some value") + .addTag("tagKeys/123", "tagValues/456") .setType(Instance.Type.DEVELOPMENT) .addCluster("cluster1", "us-east1-c", 1, StorageType.SSD); @@ -142,6 +143,7 @@ public void testOptionalFields() { .setDisplayName("custom display name") .putLabels("my label", "with some value") .putLabels("my other label", "with some value") + .putTags("tagKeys/123", "tagValues/456") .setType(com.google.bigtable.admin.v2.Instance.Type.DEVELOPMENT)) .putClusters( "cluster1", diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java index 78fdf15b03..35b776fbe4 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java @@ -36,6 +36,8 @@ public void testFromProto() { .setState(com.google.bigtable.admin.v2.Instance.State.READY) .putLabels("label1", "value1") .putLabels("label2", "value2") + .putTags("tagKeys/123", "tagValues/456") + .putTags("tagKeys/234", "tagValues/567") .build(); Instance result = Instance.fromProto(proto); @@ -48,6 +50,8 @@ public void testFromProto() { .containsExactly( "label1", "value1", "label2", "value2"); + assertThat(result.getTags()) + .containsExactly("tagKeys/123", "tagValues/456", "tagKeys/234", "tagValues/567"); } @Test @@ -59,6 +63,7 @@ public void testRequiresName() { .setState(com.google.bigtable.admin.v2.Instance.State.READY) .putLabels("label1", "value1") .putLabels("label2", "value2") + .putTags("tagKeys/123", "tagValues/456") .build(); Exception actualException = null; From ff8d7755f420f67b59e65cf86a6fac05b2276bd4 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Sun, 22 Feb 2026 11:05:10 -0500 Subject: [PATCH 07/33] chore: refactor otel integration (#2794) * chore: refactor otel exporter Previously bigtable client would construct 2 otel instances: - one for internal metrics not relevant for customers - another for user visible metrics The 2nd otel instance would be used for both exporting to cloud monitoring and to end user provided exporters. This was done by having the enduser provide a SdkMeterProviderBuilder and use a utility to inject the bigtable builtin exporter. This created a number of problems and overheads. This PR changes the split, now there is a builtin otel instance with a bigtable exporter preconfigured. This receives both internal and user visible cloud monitoring metrics. This exporter can be disabled via EnhancedStubSettings#disableInternalMetrics() In addition, if an enduser provides their own otel instance via CustomOpenTelemetryMetricsProvider, this will be dual written in parallel to the builtin one. Now the default case is that we have 1 otel instance and we dont have to inject our internal exporter into the use otel instance. Change-Id: Ic0b8efdcdf342aef8f589c2c9e05b7672a0e5d08 * combine the exporters Change-Id: Id8a2052a4d49a9623a2b63debe1f8ef10210f84c --- .../data/v2/BigtableDataClientFactory.java | 16 ++- .../data/v2/stub/BigtableClientContext.java | 115 +++++++----------- .../data/v2/stub/EnhancedBigtableStub.java | 30 +++-- .../v2/stub/EnhancedBigtableStubSettings.java | 48 ++------ .../BigtableCloudMonitoringExporter.java | 70 +++++++---- .../stub/metrics/BigtableExporterUtils.java | 15 +-- .../v2/stub/metrics/BuiltinMetricsView.java | 78 ++---------- .../CustomOpenTelemetryMetricsProvider.java | 35 ++++-- .../stub/metrics/DefaultMetricsProvider.java | 25 +--- .../bigtable/data/v2/stub/metrics/Util.java | 64 ++++++---- .../BigtableCloudMonitoringExporterTest.java | 36 +++--- .../metrics/BigtableTracerCallableTest.java | 2 + .../v2/stub/metrics/MetricsTracerTest.java | 1 + 13 files changed, 235 insertions(+), 300 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java index 599dce9f31..47cf7b15ed 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java @@ -111,7 +111,9 @@ public BigtableDataClient createDefault() { sharedClientContext.getClientContext().toBuilder() .setTracerFactory( EnhancedBigtableStub.createBigtableTracerFactory( - defaultSettings.getStubSettings(), sharedClientContext.getOpenTelemetry())) + defaultSettings.getStubSettings(), + sharedClientContext.getBuiltinOpenTelemetry(), + sharedClientContext.getUserOpenTelemetry())) .build(); return BigtableDataClient.createWithClientContext( @@ -140,7 +142,9 @@ public BigtableDataClient createForAppProfile(@Nonnull String appProfileId) thro sharedClientContext.getClientContext().toBuilder() .setTracerFactory( EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), sharedClientContext.getOpenTelemetry())) + settings.getStubSettings(), + sharedClientContext.getBuiltinOpenTelemetry(), + sharedClientContext.getUserOpenTelemetry())) .build(); return BigtableDataClient.createWithClientContext( settings, sharedClientContext.withClientContext(clientContext)); @@ -168,7 +172,9 @@ public BigtableDataClient createForInstance(@Nonnull String projectId, @Nonnull sharedClientContext.getClientContext().toBuilder() .setTracerFactory( EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), sharedClientContext.getOpenTelemetry())) + settings.getStubSettings(), + sharedClientContext.getBuiltinOpenTelemetry(), + sharedClientContext.getUserOpenTelemetry())) .build(); return BigtableDataClient.createWithClientContext( @@ -197,7 +203,9 @@ public BigtableDataClient createForInstance( sharedClientContext.getClientContext().toBuilder() .setTracerFactory( EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), sharedClientContext.getOpenTelemetry())) + settings.getStubSettings(), + sharedClientContext.getBuiltinOpenTelemetry(), + sharedClientContext.getUserOpenTelemetry())) .build(); return BigtableDataClient.createWithClientContext( settings, sharedClientContext.withClientContext(clientContext)); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index c7634bdc70..97f4aad495 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -26,14 +26,12 @@ import com.google.api.gax.rpc.ClientContext; import com.google.auth.Credentials; import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; -import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider; +import com.google.cloud.bigtable.data.v2.stub.metrics.Util; import com.google.cloud.bigtable.gaxx.grpc.BigtableTransportChannelProvider; import com.google.cloud.bigtable.gaxx.grpc.ChannelPrimer; import io.grpc.ManagedChannelBuilder; @@ -57,9 +55,8 @@ public class BigtableClientContext { private static final Logger logger = Logger.getLogger(BigtableClientContext.class.getName()); - @Nullable private final OpenTelemetry openTelemetry; - @Nullable private final OpenTelemetrySdk internalOpenTelemetry; - private final MetricsProvider metricsProvider; + @Nullable private final OpenTelemetrySdk builtinOpenTelemetry; + @Nullable private final OpenTelemetry userOpenTelemetry; private final ClientContext clientContext; // the background executor shared for OTEL instances and monitoring client and all other // background tasks @@ -89,17 +86,24 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings builder.setBackgroundExecutorProvider(executorProvider); // Set up OpenTelemetry - OpenTelemetry openTelemetry = null; + @Nullable OpenTelemetry userOtel = null; + if (settings.getMetricsProvider() instanceof CustomOpenTelemetryMetricsProvider) { + userOtel = + ((CustomOpenTelemetryMetricsProvider) settings.getMetricsProvider()).getOpenTelemetry(); + } + + @Nullable OpenTelemetrySdk builtinOtel = null; try { - // We don't want client side metrics to crash the client, so catch any exception when getting - // the OTEL instance and log the exception instead. - openTelemetry = - getOpenTelemetryFromMetricsProvider( - settings.getMetricsProvider(), - credentials, - settings.getMetricsEndpoint(), - universeDomain, - backgroundExecutor); + if (settings.areInternalMetricsEnabled()) { + builtinOtel = + Util.createBuiltinOtel( + InstanceName.of(settings.getProjectId(), settings.getInstanceId()), + settings.getAppProfileId(), + credentials, + settings.getMetricsEndpoint(), + universeDomain, + backgroundExecutor); + } } catch (Throwable t) { logger.log(Level.WARNING, "Failed to get OTEL, will skip exporting client side metrics", t); } @@ -110,21 +114,16 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings ? ((InstantiatingGrpcChannelProvider) builder.getTransportChannelProvider()).toBuilder() : null; - @Nullable OpenTelemetrySdk internalOtel = null; @Nullable ChannelPoolMetricsTracer channelPoolMetricsTracer = null; // Internal metrics are scoped to the connections, so we need a mutable transportProvider, // otherwise there is // no reason to build the internal OtelProvider if (transportProvider != null) { - internalOtel = - settings - .getInternalMetricsProvider() - .createOtelProvider(settings, credentials, backgroundExecutor); - if (internalOtel != null) { - channelPoolMetricsTracer = new ChannelPoolMetricsTracer(internalOtel); + if (builtinOtel != null) { + channelPoolMetricsTracer = new ChannelPoolMetricsTracer(builtinOtel); // Configure grpc metrics - configureGrpcOtel(transportProvider, internalOtel); + configureGrpcOtel(transportProvider, builtinOtel); } } @@ -149,7 +148,7 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings BigtableTransportChannelProvider btTransportProvider = BigtableTransportChannelProvider.create( - (InstantiatingGrpcChannelProvider) transportProvider.build(), + transportProvider.build(), channelPrimer, channelPoolMetricsTracer, backgroundExecutor); @@ -162,12 +161,7 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings channelPoolMetricsTracer.start(clientContext.getExecutor()); } - return new BigtableClientContext( - clientContext, - openTelemetry, - internalOtel, - settings.getMetricsProvider(), - executorProvider); + return new BigtableClientContext(clientContext, builtinOtel, userOtel, executorProvider); } private static void configureGrpcOtel( @@ -199,19 +193,23 @@ private static void configureGrpcOtel( private BigtableClientContext( ClientContext clientContext, - @Nullable OpenTelemetry openTelemetry, @Nullable OpenTelemetrySdk internalOtel, - MetricsProvider metricsProvider, + @Nullable OpenTelemetry userOpenTelemetry, ExecutorProvider backgroundExecutorProvider) { this.clientContext = clientContext; - this.openTelemetry = openTelemetry; - this.internalOpenTelemetry = internalOtel; - this.metricsProvider = metricsProvider; + this.userOpenTelemetry = userOpenTelemetry; + this.builtinOpenTelemetry = internalOtel; this.backgroundExecutorProvider = backgroundExecutorProvider; } - public OpenTelemetry getOpenTelemetry() { - return this.openTelemetry; + @Nullable + public OpenTelemetrySdk getBuiltinOpenTelemetry() { + return builtinOpenTelemetry; + } + + @Nullable + public OpenTelemetry getUserOpenTelemetry() { + return this.userOpenTelemetry; } public ClientContext getClientContext() { @@ -220,53 +218,24 @@ public ClientContext getClientContext() { public BigtableClientContext withClientContext(ClientContext clientContext) { return new BigtableClientContext( - clientContext, - openTelemetry, - internalOpenTelemetry, - metricsProvider, - backgroundExecutorProvider); + clientContext, builtinOpenTelemetry, userOpenTelemetry, backgroundExecutorProvider); } public void close() throws Exception { for (BackgroundResource resource : clientContext.getBackgroundResources()) { resource.close(); } - if (internalOpenTelemetry != null) { - internalOpenTelemetry.close(); + if (builtinOpenTelemetry != null) { + builtinOpenTelemetry.close(); } - if (metricsProvider instanceof DefaultMetricsProvider && openTelemetry != null) { - ((OpenTelemetrySdk) openTelemetry).close(); + if (builtinOpenTelemetry != null) { + builtinOpenTelemetry.close(); } if (backgroundExecutorProvider.shouldAutoClose()) { backgroundExecutorProvider.getExecutor().shutdown(); } } - private static OpenTelemetry getOpenTelemetryFromMetricsProvider( - MetricsProvider metricsProvider, - @Nullable Credentials defaultCredentials, - @Nullable String metricsEndpoint, - String universeDomain, - ScheduledExecutorService executor) - throws IOException { - if (metricsProvider instanceof CustomOpenTelemetryMetricsProvider) { - CustomOpenTelemetryMetricsProvider customMetricsProvider = - (CustomOpenTelemetryMetricsProvider) metricsProvider; - return customMetricsProvider.getOpenTelemetry(); - } else if (metricsProvider instanceof DefaultMetricsProvider) { - Credentials credentials = - BigtableDataSettings.getMetricsCredentials() != null - ? BigtableDataSettings.getMetricsCredentials() - : defaultCredentials; - DefaultMetricsProvider defaultMetricsProvider = (DefaultMetricsProvider) metricsProvider; - return defaultMetricsProvider.getOpenTelemetry( - metricsEndpoint, universeDomain, credentials, executor); - } else if (metricsProvider instanceof NoopMetricsProvider) { - return null; - } - throw new IOException("Invalid MetricsProvider type " + metricsProvider); - } - private static void patchCredentials(EnhancedBigtableStubSettings.Builder settings) throws IOException { String audience = settings.getJwtAudience(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index e756e41f02..18361f1568 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -200,10 +200,13 @@ public class EnhancedBigtableStub implements AutoCloseable { public static EnhancedBigtableStub create(EnhancedBigtableStubSettings settings) throws IOException { BigtableClientContext bigtableClientContext = createBigtableClientContext(settings); - OpenTelemetry openTelemetry = bigtableClientContext.getOpenTelemetry(); ClientContext contextWithTracer = bigtableClientContext.getClientContext().toBuilder() - .setTracerFactory(createBigtableTracerFactory(settings, openTelemetry)) + .setTracerFactory( + createBigtableTracerFactory( + settings, + bigtableClientContext.getBuiltinOpenTelemetry(), + bigtableClientContext.getUserOpenTelemetry())) .build(); bigtableClientContext = bigtableClientContext.withClientContext(contextWithTracer); return new EnhancedBigtableStub(settings, bigtableClientContext); @@ -222,10 +225,12 @@ public static BigtableClientContext createBigtableClientContext( } public static ApiTracerFactory createBigtableTracerFactory( - EnhancedBigtableStubSettings settings, @Nullable OpenTelemetry openTelemetry) + EnhancedBigtableStubSettings settings, + @Nullable OpenTelemetry builtinOtel, + @Nullable OpenTelemetry userOtel) throws IOException { return createBigtableTracerFactory( - settings, Tags.getTagger(), Stats.getStatsRecorder(), openTelemetry); + settings, Tags.getTagger(), Stats.getStatsRecorder(), builtinOtel, userOtel); } @VisibleForTesting @@ -233,7 +238,8 @@ public static ApiTracerFactory createBigtableTracerFactory( EnhancedBigtableStubSettings settings, Tagger tagger, StatsRecorder stats, - @Nullable OpenTelemetry openTelemetry) + @Nullable OpenTelemetry builtinOtel, + @Nullable OpenTelemetry userOtel) throws IOException { String projectId = settings.getProjectId(); String instanceId = settings.getInstanceId(); @@ -265,12 +271,14 @@ public static ApiTracerFactory createBigtableTracerFactory( .add(MetricsTracerFactory.create(tagger, stats, attributes)) // Add user configured tracer .add(settings.getTracerFactory()); - BuiltinMetricsTracerFactory builtinMetricsTracerFactory = - openTelemetry != null - ? BuiltinMetricsTracerFactory.create(openTelemetry, createBuiltinAttributes(settings)) - : null; - if (builtinMetricsTracerFactory != null) { - tracerFactories.add(builtinMetricsTracerFactory); + + if (builtinOtel != null) { + tracerFactories.add( + BuiltinMetricsTracerFactory.create(builtinOtel, createBuiltinAttributes(settings))); + } + if (userOtel != null) { + tracerFactories.add( + BuiltinMetricsTracerFactory.create(userOtel, createBuiltinAttributes(settings))); } return new CompositeTracerFactory(tracerFactories.build()); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 6a9dcdfbec..f0c959cc67 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -32,7 +32,6 @@ import com.google.api.gax.rpc.StubSettings; import com.google.api.gax.rpc.TransportChannelProvider; import com.google.api.gax.rpc.UnaryCallSettings; -import com.google.auth.Credentials; import com.google.bigtable.v2.FeatureFlags; import com.google.bigtable.v2.PingAndWarmRequest; import com.google.cloud.bigtable.Version; @@ -51,7 +50,6 @@ import com.google.cloud.bigtable.data.v2.models.sql.BoundStatement; import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.metrics.Util; import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor; import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor; import com.google.common.base.MoreObjects; @@ -59,7 +57,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -68,7 +65,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -283,7 +279,7 @@ public class EnhancedBigtableStubSettings extends StubSettings null; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 375ab17142..9043a351ab 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -100,11 +101,9 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { // https://cloud.google.com/monitoring/quotas#custom_metrics_quotas. private static final int EXPORT_BATCH_SIZE_LIMIT = 200; - private final String exporterName; - private final MetricServiceClient client; - private final TimeSeriesConverter timeSeriesConverter; + private final List timeSeriesConverters; private final AtomicBoolean isShutdown = new AtomicBoolean(false); @@ -113,11 +112,10 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { private final AtomicBoolean exportFailureLogged = new AtomicBoolean(false); static BigtableCloudMonitoringExporter create( - String exporterName, @Nullable Credentials credentials, @Nullable String endpoint, String universeDomain, - TimeSeriesConverter converter, + List converters, @Nullable ScheduledExecutorService executorService) throws IOException { Preconditions.checkNotNull(universeDomain); @@ -155,15 +153,14 @@ static BigtableCloudMonitoringExporter create( settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout); return new BigtableCloudMonitoringExporter( - exporterName, MetricServiceClient.create(settingsBuilder.build()), converter); + MetricServiceClient.create(settingsBuilder.build()), converters); } @VisibleForTesting BigtableCloudMonitoringExporter( - String exporterName, MetricServiceClient client, TimeSeriesConverter converter) { - this.exporterName = exporterName; + MetricServiceClient client, List converters) { this.client = client; - this.timeSeriesConverter = converter; + this.timeSeriesConverters = ImmutableList.copyOf(converters); } @Override @@ -176,17 +173,30 @@ public CompletableResultCode export(Collection metricData) { /** Export metrics associated with a BigtableTable resource. */ private CompletableResultCode doExport(Collection metricData) { - Map> bigtableTimeSeries; - - try { - bigtableTimeSeries = timeSeriesConverter.convert(metricData); - } catch (Throwable t) { - logger.log( - Level.WARNING, - String.format( - "Failed to convert %s metric data to cloud monitoring timeseries.", exporterName), - t); - return CompletableResultCode.ofFailure(); + Map> bigtableTimeSeries = new HashMap<>(); + + List results = new ArrayList<>(); + + for (TimeSeriesConverter c : timeSeriesConverters) { + try { + for (Map.Entry> e : c.convert(metricData).entrySet()) { + bigtableTimeSeries + .computeIfAbsent(e.getKey(), (k) -> new ArrayList<>()) + .addAll(e.getValue()); + } + results.add(CompletableResultCode.ofSuccess()); + } catch (Throwable t) { + logger.log( + Level.WARNING, + String.format( + "Failed to convert %s metric data to cloud monitoring timeseries.", c.name), + t); + results.add(CompletableResultCode.ofExceptionalFailure(t)); + } + } + CompletableResultCode overall = CompletableResultCode.ofAll(results); + if (!overall.isSuccess()) { + return overall; } // Skips exporting if there's none @@ -204,9 +214,7 @@ private CompletableResultCode doExport(Collection metricData) { @Override public void onFailure(Throwable throwable) { if (exportFailureLogged.compareAndSet(false, true)) { - String msg = - String.format( - "createServiceTimeSeries request failed for %s.", exporterName); + String msg = "createServiceTimeSeries request failed"; if (throwable instanceof PermissionDeniedException) { msg += String.format( @@ -294,11 +302,17 @@ public AggregationTemporality getAggregationTemporality(InstrumentType instrumen return AggregationTemporality.CUMULATIVE; } - interface TimeSeriesConverter { - Map> convert(Collection metricData); + abstract static class TimeSeriesConverter { + private final String name; + + TimeSeriesConverter(String name) { + this.name = name; + } + + abstract Map> convert(Collection metricData); } - static class PublicTimeSeriesConverter implements TimeSeriesConverter { + static class PublicTimeSeriesConverter extends TimeSeriesConverter { private static final ImmutableList BIGTABLE_TABLE_METRICS = ImmutableSet.of( OPERATION_LATENCIES_NAME, @@ -326,6 +340,7 @@ static class PublicTimeSeriesConverter implements TimeSeriesConverter { } PublicTimeSeriesConverter(String taskId) { + super("table metrics"); this.taskId = taskId; } @@ -342,7 +357,7 @@ public Map> convert(Collection metricD } } - static class InternalTimeSeriesConverter implements TimeSeriesConverter { + static class InternalTimeSeriesConverter extends TimeSeriesConverter { private static final ImmutableList APPLICATION_METRICS = ImmutableSet.of(PER_CONNECTION_ERROR_COUNT_NAME).stream() .map(m -> METER_NAME + m) @@ -351,6 +366,7 @@ static class InternalTimeSeriesConverter implements TimeSeriesConverter { private final Supplier monitoredResource; InternalTimeSeriesConverter(Supplier monitoredResource) { + super("client metrics"); this.monitoredResource = monitoredResource; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 882365c6b4..3b95ed1819 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -38,8 +38,8 @@ import com.google.api.Distribution; import com.google.api.Metric; import com.google.api.MonitoredResource; +import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.Version; -import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import com.google.cloud.opentelemetry.detection.AttributeKeys; import com.google.cloud.opentelemetry.detection.DetectedPlatform; import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; @@ -182,9 +182,10 @@ static List convertToApplicationResourceTimeSeries( } @Nullable - static MonitoredResource createInternalMonitoredResource(EnhancedBigtableStubSettings settings) { + static MonitoredResource createInternalMonitoredResource( + InstanceName instanceName, String appProfileId) { try { - MonitoredResource monitoredResource = detectResource(settings); + MonitoredResource monitoredResource = detectResource(instanceName, appProfileId); logger.log(Level.FINE, "Internal metrics monitored resource: %s", monitoredResource); return monitoredResource; } catch (Exception e) { @@ -197,7 +198,7 @@ static MonitoredResource createInternalMonitoredResource(EnhancedBigtableStubSet } @Nullable - private static MonitoredResource detectResource(EnhancedBigtableStubSettings settings) { + private static MonitoredResource detectResource(InstanceName instanceName, String appProfileId) { GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE; DetectedPlatform detectedPlatform = detector.detectPlatform(); @@ -244,9 +245,9 @@ private static MonitoredResource detectResource(EnhancedBigtableStubSettings set return MonitoredResource.newBuilder() .setType("bigtable_client") - .putLabels("project_id", settings.getProjectId()) - .putLabels("instance", settings.getInstanceId()) - .putLabels("app_profile", settings.getAppProfileId()) + .putLabels("project_id", instanceName.getProject()) + .putLabels("instance", instanceName.getInstance()) + .putLabels("app_profile", appProfileId) .putLabels("client_project", detectedPlatform.getProjectId()) .putLabels("region", region) .putLabels("cloud_platform", cloud_platform) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index 24e38c3a2c..edca9bd53f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -16,13 +16,9 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.auth.Credentials; -import com.google.auth.oauth2.GoogleCredentials; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; import java.io.IOException; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; @@ -37,53 +33,29 @@ */ @Deprecated public class BuiltinMetricsView { - private BuiltinMetricsView() {} - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with application default - * credentials and default endpoint. - * - * @deprecated projectId is no longer used. Call {@link - * #registerBuiltinMetrics(SdkMeterProviderBuilder)} instead. - */ @Deprecated public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) throws IOException { - BuiltinMetricsView.registerBuiltinMetrics( - GoogleCredentials.getApplicationDefault(), builder, null); + registerBuiltinMetrics(builder); } - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with application default - * credentials and default endpoint. - */ + @Deprecated public static void registerBuiltinMetrics(SdkMeterProviderBuilder builder) throws IOException { - BuiltinMetricsView.registerBuiltinMetrics( - GoogleCredentials.getApplicationDefault(), builder, null); + for (Map.Entry entry : + BuiltinMetricsConstants.getAllViews().entrySet()) { + builder.registerView(entry.getKey(), entry.getValue()); + } } - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with custom credentials and - * default endpoint. - * - * @deprecated projectId is no longer used. Call {@link #registerBuiltinMetrics(Credentials, - * SdkMeterProviderBuilder, String)} instead. - */ @Deprecated public static void registerBuiltinMetrics( String projectId, @Nullable Credentials credentials, SdkMeterProviderBuilder builder) throws IOException { - BuiltinMetricsView.registerBuiltinMetrics(credentials, builder, null); + registerBuiltinMetrics(builder); } - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with custom credentials and - * endpoint. - * - * @deprecated projectId is no longer used. Call {@link #registerBuiltinMetrics(Credentials, - * SdkMeterProviderBuilder, String)} instead. - */ @Deprecated public static void registerBuiltinMetrics( String projectId, @@ -94,31 +66,24 @@ public static void registerBuiltinMetrics( registerBuiltinMetrics(credentials, builder, endpoint); } - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with custom credentials and - * endpoint. - */ + @Deprecated public static void registerBuiltinMetrics( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @Nullable String endpoint) throws IOException { - registerBuiltinMetricsWithUniverseDomain( - credentials, builder, endpoint, Credentials.GOOGLE_DEFAULT_UNIVERSE, null); + registerBuiltinMetrics(builder); } - /** - * Register built-in metrics on the {@link SdkMeterProviderBuilder} with custom credentials, - * endpoint and executor service. - */ + @Deprecated public static void registerBuiltinMetrics( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @Nullable String endpoint, @Nullable ScheduledExecutorService executorService) throws IOException { - registerBuiltinMetricsWithUniverseDomain( - credentials, builder, endpoint, Credentials.GOOGLE_DEFAULT_UNIVERSE, executorService); + registerBuiltinMetrics(builder); } + @Deprecated static void registerBuiltinMetricsWithUniverseDomain( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @@ -126,23 +91,6 @@ static void registerBuiltinMetricsWithUniverseDomain( String universeDomain, @Nullable ScheduledExecutorService executorService) throws IOException { - MetricExporter publicExporter = - BigtableCloudMonitoringExporter.create( - "bigtable metrics", - credentials, - endpoint, - universeDomain, - new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(), - executorService); - - for (Map.Entry entry : - BuiltinMetricsConstants.getAllViews().entrySet()) { - builder.registerView(entry.getKey(), entry.getValue()); - } - PeriodicMetricReaderBuilder readerBuilder = PeriodicMetricReader.builder(publicExporter); - if (executorService != null) { - readerBuilder.setExecutor(executorService); - } - builder.registerMetricReader(readerBuilder.build()); + registerBuiltinMetrics(builder); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java index c0a8ed7f36..66f4e25a17 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java @@ -18,8 +18,11 @@ import com.google.auth.Credentials; import com.google.common.base.MoreObjects; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; import java.io.IOException; +import java.util.Map; import java.util.concurrent.ScheduledExecutorService; /** @@ -71,39 +74,51 @@ public OpenTelemetry getOpenTelemetry() { * Convenient method to set up SdkMeterProviderBuilder with the default credential and endpoint. */ public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder) throws IOException { - setupSdkMeterProvider(builder, null, null, null); + for (Map.Entry entry : + BuiltinMetricsConstants.getAllViews().entrySet()) { + builder.registerView(entry.getKey(), entry.getValue()); + } } - /** Convenient method to set up SdkMeterProviderBuilder with a custom credential. */ + /** + * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)} + */ + @Deprecated public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, Credentials credentials) throws IOException { - setupSdkMeterProvider(builder, credentials, null, null); + setupSdkMeterProvider(builder); } - /** Convenient method to set up SdkMeterProviderBuilder with a custom endpoint. */ + /** + * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)} + */ + @Deprecated public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, String endpoint) throws IOException { - setupSdkMeterProvider(builder, null, endpoint, null); + setupSdkMeterProvider(builder); } - /** Convenient method to set up SdkMeterProviderBuilder with custom credentials and endpoint. */ + /** + * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)} + */ + @Deprecated public static void setupSdkMeterProvider( SdkMeterProviderBuilder builder, Credentials credentials, String endpoint) throws IOException { - setupSdkMeterProvider(builder, credentials, endpoint, null); + setupSdkMeterProvider(builder); } /** - * Convenient method to set up SdkMeterProviderBuilder with custom credentials, endpoint and a - * shared executor service. + * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)} */ + @Deprecated public static void setupSdkMeterProvider( SdkMeterProviderBuilder builder, Credentials credentials, String endpoint, ScheduledExecutorService executor) throws IOException { - BuiltinMetricsView.registerBuiltinMetrics(credentials, builder, endpoint, executor); + setupSdkMeterProvider(builder); } @Override diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java index 4a226d25d9..02cdf7c257 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java @@ -15,20 +15,10 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import com.google.api.core.InternalApi; -import com.google.auth.Credentials; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import java.io.IOException; -import java.util.concurrent.ScheduledExecutorService; -import javax.annotation.Nullable; - /** * Set {@link * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)}, - * to {@link this#INSTANCE} to enable collecting and export client side metrics + * to {@link DefaultMetricsProvider#INSTANCE} to enable collecting and export client side metrics * https://cloud.google.com/bigtable/docs/client-side-metrics. This is the default setting in {@link * com.google.cloud.bigtable.data.v2.BigtableDataSettings}. */ @@ -38,19 +28,6 @@ public final class DefaultMetricsProvider implements MetricsProvider { private DefaultMetricsProvider() {} - @InternalApi - public OpenTelemetry getOpenTelemetry( - @Nullable String metricsEndpoint, - String universeDomain, - @Nullable Credentials credentials, - ScheduledExecutorService executor) - throws IOException { - SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); - BuiltinMetricsView.registerBuiltinMetricsWithUniverseDomain( - credentials, meterProvider, metricsEndpoint, universeDomain, executor); - return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - } - @Override public String toString() { return "DefaultMetricsProvider"; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index da7de371c3..f5ba5f52fc 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -24,6 +24,7 @@ import com.google.bigtable.v2.AuthorizedViewName; import com.google.bigtable.v2.CheckAndMutateRowRequest; import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest; +import com.google.bigtable.v2.InstanceName; import com.google.bigtable.v2.MaterializedViewName; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowsRequest; @@ -34,9 +35,10 @@ import com.google.bigtable.v2.ResponseParams; import com.google.bigtable.v2.SampleRowKeysRequest; import com.google.bigtable.v2.TableName; -import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; import io.grpc.Status; @@ -47,9 +49,10 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; import java.io.IOException; -import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -148,33 +151,50 @@ static Map> createStatsHeaders(ApiCallContext apiCallContex return headers.build(); } - public static OpenTelemetrySdk newInternalOpentelemetry( - EnhancedBigtableStubSettings settings, - Credentials credentials, + public static OpenTelemetrySdk createBuiltinOtel( + InstanceName instanceName, + String appProfileId, + @Nullable Credentials defaultCredentials, + @Nullable String metricsEndpoint, + String universeDomain, ScheduledExecutorService executor) throws IOException { - SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder(); + + Credentials credentials = + BigtableDataSettings.getMetricsCredentials() != null + ? BigtableDataSettings.getMetricsCredentials() + : defaultCredentials; + + SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); + + for (Map.Entry entry : + BuiltinMetricsConstants.getAllViews().entrySet()) { + meterProvider.registerView(entry.getKey(), entry.getValue()); + } for (Map.Entry e : BuiltinMetricsConstants.getInternalViews().entrySet()) { - meterProviderBuilder.registerView(e.getKey(), e.getValue()); + meterProvider.registerView(e.getKey(), e.getValue()); } - meterProviderBuilder.registerMetricReader( - PeriodicMetricReader.builder( - BigtableCloudMonitoringExporter.create( - "application metrics", - credentials, - settings.getMetricsEndpoint(), - settings.getUniverseDomain(), - new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( - Suppliers.memoize( - () -> BigtableExporterUtils.createInternalMonitoredResource(settings))), - executor)) - .setExecutor(settings.getBackgroundExecutorProvider().getExecutor()) - .setInterval(Duration.ofMinutes(1)) - .build()); - return OpenTelemetrySdk.builder().setMeterProvider(meterProviderBuilder.build()).build(); + MetricExporter publicExporter = + BigtableCloudMonitoringExporter.create( + credentials, + metricsEndpoint, + universeDomain, + ImmutableList.of( + new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(), + new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( + Suppliers.memoize( + () -> + BigtableExporterUtils.createInternalMonitoredResource( + instanceName, appProfileId)))), + executor); + PeriodicMetricReaderBuilder readerBuilder = + PeriodicMetricReader.builder(publicExporter).setExecutor(executor); + meterProvider.registerMetricReader(readerBuilder.build()); + + return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); } public static String formatTransportTypeMetricLabel( diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 0a8ad0afbd..285206e949 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -96,9 +96,9 @@ public void setUp() { exporter = new BigtableCloudMonitoringExporter( - "bigtable metrics", fakeMetricServiceClient, - new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(taskId)); + ImmutableList.of( + new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(taskId))); attributes = Attributes.builder() @@ -310,23 +310,23 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() { String gceProjectId = "fake-gce-project"; BigtableCloudMonitoringExporter exporter = new BigtableCloudMonitoringExporter( - "application metrics", fakeMetricServiceClient, - new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( - Suppliers.ofInstance( - MonitoredResource.newBuilder() - .setType("bigtable_client") - .putLabels("project_id", gceProjectId) - .putLabels("instance", "resource-instance") - .putLabels("app_profile", "resource-app-profile") - .putLabels("client_project", "client-project") - .putLabels("region", "cleint-region") - .putLabels("cloud_platform", "gce_instance") - .putLabels("host_id", "1234567890") - .putLabels("host_name", "harold") - .putLabels("client_name", "java/1234") - .putLabels("uuid", "something") - .build()))); + ImmutableList.of( + new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( + Suppliers.ofInstance( + MonitoredResource.newBuilder() + .setType("bigtable_client") + .putLabels("project_id", gceProjectId) + .putLabels("instance", "resource-instance") + .putLabels("app_profile", "resource-app-profile") + .putLabels("client_project", "client-project") + .putLabels("region", "cleint-region") + .putLabels("cloud_platform", "gce_instance") + .putLabels("host_id", "1234567890") + .putLabels("host_name", "harold") + .putLabels("client_name", "java/1234") + .putLabels("uuid", "something") + .build())))); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CreateTimeSeriesRequest.class); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java index 8c3746144f..639228b8e3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java @@ -136,6 +136,7 @@ public void sendHeaders(Metadata headers) { settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder(), + null, null)) .build(); attempts = settings.getStubSettings().readRowsSettings().getRetrySettings().getMaxAttempts(); @@ -163,6 +164,7 @@ public void sendHeaders(Metadata headers) { noHeaderSettings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder(), + null, null)) .build(); noHeaderStub = diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java index 5c4161d0e3..47b64fba14 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java @@ -131,6 +131,7 @@ public void setUp() throws Exception { settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder(), + null, null)) .build(); stub = From c8ab419e9ce63db4fe0248fd777786379b3df823 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Mon, 23 Feb 2026 13:27:32 -0500 Subject: [PATCH 08/33] chore: remove ability to disable routing cookies & RetryInfo handling + cleanups (#2795) * chore: remove ability to disable routing cookies & RetryInfo handling. These are now mandatory. This simplifies the upcoming refactor Change-Id: I8c9d76d36419f0d06ce86ed658603e321bb9e41a * a bit more clean up - remove prime table ids - remove unused logger - fix random warnings in stub settings Change-Id: I92be83ae85710c3c2c3502d572db18b48cc1f483 * upgrade protoc for test protos Change-Id: I53c2d54fd4680993ea130c8df65f2a97181a5e16 * match test protoc & grpc plugin versions to gapic Change-Id: I8597dddd20d836afdfec6a9eee94310073ef5edf --- google-cloud-bigtable/pom.xml | 4 +- .../data/v2/BigtableDataSettings.java | 1 - .../data/v2/stub/BigtableClientContext.java | 5 +- .../data/v2/stub/EnhancedBigtableStub.java | 64 ++--- .../v2/stub/EnhancedBigtableStubSettings.java | 78 ++---- .../data/v2/stub/CookiesHolderTest.java | 53 ---- .../EnhancedBigtableStubSettingsTest.java | 83 +----- .../v2/stub/EnhancedBigtableStubTest.java | 10 +- .../bigtable/data/v2/stub/RetryInfoTest.java | 259 ------------------ 9 files changed, 44 insertions(+), 513 deletions(-) diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index bd4c6f0b63..102de75784 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -45,8 +45,8 @@ batch-bigtable.googleapis.com:443 - 1.65.0 - 3.25.5 + 1.76.3 + 4.33.2 diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index b8a514433f..4329e98f63 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -450,7 +450,6 @@ public boolean isRefreshingChannel() { */ @Deprecated public Builder setPrimingTableIds(String... tableIds) { - stubSettings.setPrimedTableIds(tableIds); return this; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index 97f4aad495..d71355d6cd 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -128,10 +128,7 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings } if (transportProvider != null) { - // Set up cookie holder if routing cookie is enabled - if (builder.getEnableRoutingCookie()) { - setupCookieHolder(transportProvider); - } + setupCookieHolder(transportProvider); ChannelPrimer channelPrimer = NoOpChannelPrimer.create(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 18361f1568..b3cc8d3655 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -121,7 +121,6 @@ import com.google.cloud.bigtable.data.v2.stub.sql.MetadataErrorHandlingCallable; import com.google.cloud.bigtable.data.v2.stub.sql.PlanRefreshingCallable; import com.google.cloud.bigtable.data.v2.stub.sql.SqlRowMergingCallable; -import com.google.cloud.bigtable.gaxx.retrying.ApiResultRetryAlgorithm; import com.google.cloud.bigtable.gaxx.retrying.RetryInfoRetryAlgorithm; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Functions; @@ -785,12 +784,9 @@ private UnaryCallable createMutateRowsBas ServerStreamingCallable withAttemptTracer = new BigtableTracerStreamingCallable<>(convertException); - BasicResultRetryAlgorithm resultRetryAlgorithm; - if (settings.getEnableRetryInfo()) { - resultRetryAlgorithm = new RetryInfoRetryAlgorithm<>(); - } else { - resultRetryAlgorithm = new ApiResultRetryAlgorithm<>(); - } + BasicResultRetryAlgorithm resultRetryAlgorithm = + new RetryInfoRetryAlgorithm<>(); + MutateRowsPartialErrorRetryAlgorithm mutateRowsPartialErrorRetryAlgorithm = new MutateRowsPartialErrorRetryAlgorithm(resultRetryAlgorithm); @@ -810,11 +806,8 @@ private UnaryCallable createMutateRowsBas settings.bulkMutateRowsSettings().getRetryableCodes(), retryAlgorithm); - UnaryCallable withCookie = baseCallable; - - if (settings.getEnableRoutingCookie()) { - withCookie = new CookiesUnaryCallable<>(baseCallable); - } + UnaryCallable withCookie = + new CookiesUnaryCallable<>(baseCallable); UnaryCallable flowControlCallable = null; if (settings.bulkMutateRowsSettings().isLatencyBasedThrottlingEnabled()) { @@ -1319,56 +1312,31 @@ ServerStreamingCallSettings convertUnaryToServerStreamingSettings( private UnaryCallable withRetries( UnaryCallable innerCallable, UnaryCallSettings unaryCallSettings) { - UnaryCallable retrying; - if (settings.getEnableRetryInfo()) { - retrying = - com.google.cloud.bigtable.gaxx.retrying.Callables.retrying( - innerCallable, unaryCallSettings, bigtableClientContext.getClientContext()); - } else { - retrying = - Callables.retrying( - innerCallable, unaryCallSettings, bigtableClientContext.getClientContext()); - } - if (settings.getEnableRoutingCookie()) { - return new CookiesUnaryCallable<>(retrying); - } - return retrying; + UnaryCallable retrying = + com.google.cloud.bigtable.gaxx.retrying.Callables.retrying( + innerCallable, unaryCallSettings, bigtableClientContext.getClientContext()); + return new CookiesUnaryCallable<>(retrying); } private ServerStreamingCallable withRetries( ServerStreamingCallable innerCallable, ServerStreamingCallSettings serverStreamingCallSettings) { - ServerStreamingCallable retrying; - if (settings.getEnableRetryInfo()) { - retrying = - com.google.cloud.bigtable.gaxx.retrying.Callables.retrying( - innerCallable, serverStreamingCallSettings, bigtableClientContext.getClientContext()); - } else { - retrying = - Callables.retrying( - innerCallable, serverStreamingCallSettings, bigtableClientContext.getClientContext()); - } - if (settings.getEnableRoutingCookie()) { - return new CookiesServerStreamingCallable<>(retrying); - } - return retrying; + ServerStreamingCallable retrying = + com.google.cloud.bigtable.gaxx.retrying.Callables.retrying( + innerCallable, serverStreamingCallSettings, bigtableClientContext.getClientContext()); + + return new CookiesServerStreamingCallable<>(retrying); } private ServerStreamingCallable largeRowWithRetries( ServerStreamingCallable innerCallable, ServerStreamingCallSettings serverStreamingCallSettings) { - // Retrying algorithm in retryingForLargeRows also takes RetryInfo into consideration, so we - // skip the check for settings.getEnableRetryInfo here - ServerStreamingCallable retrying; - retrying = + ServerStreamingCallable retrying = com.google.cloud.bigtable.gaxx.retrying.Callables.retryingForLargeRows( innerCallable, serverStreamingCallSettings, bigtableClientContext.getClientContext()); - if (settings.getEnableRoutingCookie()) { - return new CookiesServerStreamingCallable<>(retrying); - } - return retrying; + return new CookiesServerStreamingCallable<>(retrying); } // diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index f0c959cc67..003823f5fc 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -65,7 +65,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.threeten.bp.Duration; @@ -99,9 +98,6 @@ * } */ public class EnhancedBigtableStubSettings extends StubSettings { - private static final Logger logger = - Logger.getLogger(EnhancedBigtableStubSettings.class.getName()); - // The largest message that can be received is a 256 MB ReadRowsResponse. private static final int MAX_MESSAGE_SIZE = 256 * 1024 * 1024; private static final String SERVER_DEFAULT_APP_PROFILE_ID = ""; @@ -145,7 +141,6 @@ public class EnhancedBigtableStubSettings extends StubSettings primedTableIds; - private final boolean enableRoutingCookie; - private final boolean enableRetryInfo; private final ServerStreamingCallSettings readRowsSettings; private final UnaryCallSettings readRowSettings; @@ -279,7 +269,7 @@ public class EnhancedBigtableStubSettings extends StubSettings getPrimedTableIds() { - return primedTableIds; + return ImmutableList.of(); } /** @@ -384,21 +371,19 @@ public MetricsProvider getMetricsProvider() { } /** - * Gets if routing cookie is enabled. If true, client will retry a request with extra metadata - * server sent back. + * @deprecated routing cookies are always on. */ - @BetaApi("Routing cookie is not currently stable and may change in the future") + @Deprecated public boolean getEnableRoutingCookie() { - return enableRoutingCookie; + return true; } /** - * Gets if RetryInfo is enabled. If true, client bases retry decision and back off time on server - * returned RetryInfo value. Otherwise, client uses {@link RetrySettings}. + * @deprecated RetryInfo is now always on. */ - @BetaApi("RetryInfo is not currently stable and may change in the future") + @Deprecated public boolean getEnableRetryInfo() { - return enableRetryInfo; + return true; } /** @@ -745,10 +730,7 @@ public static class Builder extends StubSettings.Builder primedTableIds; private String jwtAudience; - private boolean enableRoutingCookie; - private boolean enableRetryInfo; private final ServerStreamingCallSettings.Builder readRowsSettings; private final UnaryCallSettings.Builder readRowSettings; @@ -768,7 +750,7 @@ public static class Builder extends StubSettings.Builder prepareQuerySettings; - private FeatureFlags.Builder featureFlags; + private final FeatureFlags.Builder featureFlags; private MetricsProvider metricsProvider; @Nullable private String metricsEndpoint; @@ -785,10 +767,7 @@ public static class Builder extends StubSettings.Builder getPrimedTableIds() { - return primedTableIds; + return ImmutableList.of(); } /** @@ -1159,41 +1134,35 @@ String getJwtAudience() { } /** - * Sets if routing cookie is enabled. If true, client will retry a request with extra metadata - * server sent back. + * @deprecated this now a no-op as routing cookies are always on. */ - @BetaApi("Routing cookie is not currently stable and may change in the future") + @Deprecated public Builder setEnableRoutingCookie(boolean enableRoutingCookie) { - this.enableRoutingCookie = enableRoutingCookie; return this; } /** - * Gets if routing cookie is enabled. If true, client will retry a request with extra metadata - * server sent back. + * @deprecated routing cookies are always on. */ - @BetaApi("Routing cookie is not currently stable and may change in the future") + @Deprecated public boolean getEnableRoutingCookie() { - return enableRoutingCookie; + return true; } /** - * Sets if RetryInfo is enabled. If true, client bases retry decision and back off time on - * server returned RetryInfo value. Otherwise, client uses {@link RetrySettings}. + * @deprecated This is a no-op, RetryInfo is always used now. */ - @BetaApi("RetryInfo is not currently stable and may change in the future") + @Deprecated public Builder setEnableRetryInfo(boolean enableRetryInfo) { - this.enableRetryInfo = enableRetryInfo; return this; } /** - * Gets if RetryInfo is enabled. If true, client bases retry decision and back off time on - * server returned RetryInfo value. Otherwise, client uses {@link RetrySettings}. + * @deprecated RetryInfo is always on. */ - @BetaApi("RetryInfo is not currently stable and may change in the future") + @Deprecated public boolean getEnableRetryInfo() { - return enableRetryInfo; + return true; } /** Returns the builder for the settings used for calls to readRows. */ @@ -1283,8 +1252,8 @@ public EnhancedBigtableStubSettings build() { featureFlags.setMutateRowsRateLimit2(true); } - featureFlags.setRoutingCookie(this.getEnableRoutingCookie()); - featureFlags.setRetryInfo(this.getEnableRetryInfo()); + featureFlags.setRoutingCookie(true); + featureFlags.setRetryInfo(true); // client_Side_metrics_enabled feature flag is only set when a user is running with a // DefaultMetricsProvider. This may cause false negatives when a user registered the // metrics on their CustomOpenTelemetryMetricsProvider. @@ -1325,9 +1294,6 @@ public String toString() { .add("instanceId", instanceId) .add("appProfileId", appProfileId) .add("isRefreshingChannel", isRefreshingChannel) - .add("primedTableIds", primedTableIds) - .add("enableRoutingCookie", enableRoutingCookie) - .add("enableRetryInfo", enableRetryInfo) .add("readRowsSettings", readRowsSettings) .add("readRowSettings", readRowSettings) .add("sampleRowKeysSettings", sampleRowKeysSettings) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/CookiesHolderTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/CookiesHolderTest.java index bf02ce447a..648cff4809 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/CookiesHolderTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/CookiesHolderTest.java @@ -69,7 +69,6 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -673,58 +672,6 @@ public void testCookieSetWithBigtableClientFactory() throws Exception { } } - @Test - public void testDisableRoutingCookie() throws IOException { - // This test disables routing cookie in the client settings and ensures that none of the routing - // cookie - // is added. - settings.stubSettings().setEnableRoutingCookie(false); - try (BigtableDataClient client = BigtableDataClient.create(settings.build())) { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = Lists.newArrayList(client.readRows(Query.create("fake-table"))); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - client.mutateRow(RowMutation.create("fake-table", "key").setCell("cf", "q", "v")); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - client.bulkMutateRows( - BulkMutation.create("fake-table") - .add(RowMutationEntry.create("key").setCell("cf", "q", "v"))); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - client.sampleRowKeys("fake-table"); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - client.checkAndMutateRow( - ConditionalRowMutation.create("fake-table", "key") - .then(Mutation.create().setCell("cf", "q", "v"))); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - client.readModifyWriteRow( - ReadModifyWriteRow.create("fake-table", "key").append("cf", "q", "v")); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored2 = - Lists.newArrayList(client.generateInitialChangeStreamPartitions("fake-table")); - assertThat(fakeService.count.get()).isEqualTo(2); - fakeService.count.set(0); - - for (ChangeStreamRecord record : - client.readChangeStream(ReadChangeStreamQuery.create("fake-table"))) {} - - assertThat(fakeService.count.get()).isEqualTo(2); - - assertThat(methods).isEmpty(); - } - } - static class FakeService extends BigtableGrpc.BigtableImplBase { private volatile boolean returnCookie = true; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java index aecad0cc12..9de6319182 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java @@ -94,8 +94,6 @@ public void settingsAreNotLostTest() { .setCredentialsProvider(credentialsProvider) .setStreamWatchdogProvider(watchdogProvider) .setStreamWatchdogCheckInterval(watchdogInterval) - .setEnableRoutingCookie(enableRoutingCookie) - .setEnableRetryInfo(enableRetryInfo) .setMetricsEndpoint(metricsEndpoint); verifyBuilder( @@ -160,8 +158,6 @@ private void verifyBuilder( assertThat(builder.getCredentialsProvider()).isEqualTo(credentialsProvider); assertThat(builder.getStreamWatchdogProvider()).isSameInstanceAs(watchdogProvider); assertThat(builder.getStreamWatchdogCheckInterval()).isEqualTo(watchdogInterval); - assertThat(builder.getEnableRoutingCookie()).isEqualTo(enableRoutingCookie); - assertThat(builder.getEnableRetryInfo()).isEqualTo(enableRetryInfo); assertThat(builder.getMetricsEndpoint()).isEqualTo(metricsEndpoint); } @@ -186,8 +182,6 @@ private void verifySettings( assertThat(settings.getCredentialsProvider()).isEqualTo(credentialsProvider); assertThat(settings.getStreamWatchdogProvider()).isSameInstanceAs(watchdogProvider); assertThat(settings.getStreamWatchdogCheckInterval()).isEqualTo(watchdogInterval); - assertThat(settings.getEnableRoutingCookie()).isEqualTo(enableRoutingCookie); - assertThat(settings.getEnableRetryInfo()).isEqualTo(enableRetryInfo); assertThat(settings.getMetricsEndpoint()).isEqualTo(metricsEndpoint); } @@ -920,81 +914,11 @@ public void isRefreshingChannelFalseValueTest() { assertThat(builder.build().toBuilder().isRefreshingChannel()).isFalse(); } - @Test - public void routingCookieIsEnabled() throws IOException { - String dummyProjectId = "my-project"; - String dummyInstanceId = "my-instance"; - CredentialsProvider credentialsProvider = Mockito.mock(CredentialsProvider.class); - Mockito.when(credentialsProvider.getCredentials()).thenReturn(new FakeCredentials()); - EnhancedBigtableStubSettings.Builder builder = - EnhancedBigtableStubSettings.newBuilder() - .setProjectId(dummyProjectId) - .setInstanceId(dummyInstanceId) - .setCredentialsProvider(credentialsProvider); - - assertThat(builder.getEnableRoutingCookie()).isTrue(); - assertThat(builder.build().getEnableRoutingCookie()).isTrue(); - assertThat(builder.build().toBuilder().getEnableRoutingCookie()).isTrue(); - } - - @Test - public void enableRetryInfoDefaultValueTest() throws IOException { - String dummyProjectId = "my-project"; - String dummyInstanceId = "my-instance"; - CredentialsProvider credentialsProvider = Mockito.mock(CredentialsProvider.class); - Mockito.when(credentialsProvider.getCredentials()).thenReturn(new FakeCredentials()); - EnhancedBigtableStubSettings.Builder builder = - EnhancedBigtableStubSettings.newBuilder() - .setProjectId(dummyProjectId) - .setInstanceId(dummyInstanceId) - .setCredentialsProvider(credentialsProvider); - assertThat(builder.getEnableRetryInfo()).isTrue(); - assertThat(builder.build().getEnableRetryInfo()).isTrue(); - assertThat(builder.build().toBuilder().getEnableRetryInfo()).isTrue(); - } - - @Test - public void routingCookieFalseValueSet() throws IOException { - String dummyProjectId = "my-project"; - String dummyInstanceId = "my-instance"; - CredentialsProvider credentialsProvider = Mockito.mock(CredentialsProvider.class); - Mockito.when(credentialsProvider.getCredentials()).thenReturn(new FakeCredentials()); - EnhancedBigtableStubSettings.Builder builder = - EnhancedBigtableStubSettings.newBuilder() - .setProjectId(dummyProjectId) - .setInstanceId(dummyInstanceId) - .setEnableRoutingCookie(false) - .setCredentialsProvider(credentialsProvider); - assertThat(builder.getEnableRoutingCookie()).isFalse(); - assertThat(builder.build().getEnableRoutingCookie()).isFalse(); - assertThat(builder.build().toBuilder().getEnableRoutingCookie()).isFalse(); - } - - @Test - public void enableRetryInfoFalseValueTest() throws IOException { - String dummyProjectId = "my-project"; - String dummyInstanceId = "my-instance"; - CredentialsProvider credentialsProvider = Mockito.mock(CredentialsProvider.class); - Mockito.when(credentialsProvider.getCredentials()).thenReturn(new FakeCredentials()); - EnhancedBigtableStubSettings.Builder builder = - EnhancedBigtableStubSettings.newBuilder() - .setProjectId(dummyProjectId) - .setInstanceId(dummyInstanceId) - .setEnableRetryInfo(false) - .setCredentialsProvider(credentialsProvider); - assertThat(builder.getEnableRetryInfo()).isFalse(); - assertThat(builder.build().getEnableRetryInfo()).isFalse(); - assertThat(builder.build().toBuilder().getEnableRetryInfo()).isFalse(); - } - static final String[] SETTINGS_LIST = { "projectId", "instanceId", "appProfileId", "isRefreshingChannel", - "primedTableIds", - "enableRoutingCookie", - "enableRetryInfo", "readRowsSettings", "readRowSettings", "sampleRowKeysSettings", @@ -1025,17 +949,12 @@ public void testToString() { .build(); checkToString(defaultSettings); - assertThat(defaultSettings.toString()).contains("primedTableIds=[]"); EnhancedBigtableStubSettings settings = - defaultSettings.toBuilder() - .setPrimedTableIds("2", "12", "85", "06") - .setEndpoint("example.com:1234") - .build(); + defaultSettings.toBuilder().setEndpoint("example.com:1234").build(); checkToString(settings); assertThat(settings.toString()).contains("endpoint=example.com:1234"); - assertThat(settings.toString()).contains("primedTableIds=[2, 12, 85, 06]"); int nonStaticFields = 0; for (Field field : EnhancedBigtableStubSettings.class.getDeclaredFields()) { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java index fbafe50f47..1531506a11 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java @@ -698,10 +698,7 @@ public void testBulkMutationFlowControllerConfigured() throws Exception { public void testCallContextPropagatedInMutationBatcher() throws IOException, InterruptedException, ExecutionException { EnhancedBigtableStubSettings settings = - defaultSettings.toBuilder() - .setRefreshingChannel(true) - .setPrimedTableIds("table1", "table2") - .build(); + defaultSettings.toBuilder().setRefreshingChannel(true).build(); try (EnhancedBigtableStub stub = EnhancedBigtableStub.create(settings)) { // clear the previous contexts @@ -728,10 +725,7 @@ public void testCallContextPropagatedInMutationBatcher() public void testCallContextPropagatedInReadBatcher() throws IOException, InterruptedException, ExecutionException { EnhancedBigtableStubSettings settings = - defaultSettings.toBuilder() - .setRefreshingChannel(true) - .setPrimedTableIds("table1", "table2") - .build(); + defaultSettings.toBuilder().setRefreshingChannel(true).build(); try (EnhancedBigtableStub stub = EnhancedBigtableStub.create(settings)) { // clear the previous contexts diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RetryInfoTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RetryInfoTest.java index ea4b46a713..c206eb20a6 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RetryInfoTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RetryInfoTest.java @@ -243,29 +243,11 @@ public void testReadRowNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed(() -> client.readRow("table", "row"), false); } - @Test - public void testReadRowDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled(() -> newClient.readRow("table", "row")); - } - } - @Test public void testReadRowServerNotReturningRetryInfo() { verifyNoRetryInfo(() -> client.readRow("table", "row"), true); } - @Test - public void testReadRowServerNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo(() -> newClient.readRow("table", "row"), true); - } - } - @Test public void testReadRowsNonRetraybleErrorWithRetryInfo() { verifyRetryInfoIsUsed( @@ -276,19 +258,6 @@ public void testReadRowsNonRetraybleErrorWithRetryInfo() { false); } - @Test - public void testReadRowsDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = Lists.newArrayList(newClient.readRows(Query.create("table"))); - }); - } - } - @Test public void testReadRowsServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -299,20 +268,6 @@ public void testReadRowsServerNotReturningRetryInfo() { true); } - @Test - public void testReadRowsServerNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = Lists.newArrayList(newClient.readRows(Query.create("table"))); - }, - true); - } - } - @Test public void testMutateRowsNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed( @@ -323,19 +278,6 @@ public void testMutateRowsNonRetryableErrorWithRetryInfo() { false); } - @Test - public void testMutateRowsDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled( - () -> - newClient.bulkMutateRows( - BulkMutation.create("fake-table") - .add(RowMutationEntry.create("row-key-1").setCell("cf", "q", "v")))); - } - } - @Test public void testMutateRowsServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -346,101 +288,28 @@ public void testMutateRowsServerNotReturningRetryInfo() { true); } - @Test - public void testMutateRowsServerNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> - newClient.bulkMutateRows( - BulkMutation.create("fake-table") - .add(RowMutationEntry.create("row-key-1").setCell("cf", "q", "v"))), - true); - } - } - @Test public void testMutateRowNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed( () -> client.mutateRow(RowMutation.create("table", "key").setCell("cf", "q", "v")), false); } - @Test - public void testMutateRowDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - - verifyRetryInfoCanBeDisabled( - () -> newClient.mutateRow(RowMutation.create("table", "key").setCell("cf", "q", "v"))); - } - } - @Test public void testMutateRowServerNotReturningRetryInfo() { verifyNoRetryInfo( () -> client.mutateRow(RowMutation.create("table", "key").setCell("cf", "q", "v")), true); } - @Test - public void testMutateRowServerNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> newClient.mutateRow(RowMutation.create("table", "key").setCell("cf", "q", "v")), - true); - } - } - @Test public void testSampleRowKeysNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed(() -> client.sampleRowKeys("table"), false); } - @Test - public void testSampleRowKeysDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled(() -> newClient.sampleRowKeys("table")); - } - } - @Test public void testSampleRowKeysServerNotReturningRetryInfo() { verifyNoRetryInfo(() -> client.sampleRowKeys("table"), true); } - @Test - public void testSampleRowKeysServerNotReturningRetryInfoClientDisabledHandling() - throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo(() -> newClient.sampleRowKeys("table"), true); - } - } - - @Test - public void testCheckAndMutateDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient client = BigtableDataClient.create(settings.build())) { - ApiException exception = enqueueNonRetryableExceptionWithDelay(defaultDelay); - try { - client.checkAndMutateRow( - ConditionalRowMutation.create("table", "key") - .condition(Filters.FILTERS.value().regex("old-value")) - .then(Mutation.create().setCell("cf", "q", "v"))); - } catch (ApiException e) { - assertThat(e.getStatusCode()).isEqualTo(exception.getStatusCode()); - } - assertThat(attemptCounter.get()).isEqualTo(1); - } - } - @Test public void testCheckAndMutateServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -452,37 +321,6 @@ public void testCheckAndMutateServerNotReturningRetryInfo() { false); } - @Test - public void testCheckAndMutateServerNotReturningRetryInfoClientDisabledHandling() - throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> - newClient.checkAndMutateRow( - ConditionalRowMutation.create("table", "key") - .condition(Filters.FILTERS.value().regex("old-value")) - .then(Mutation.create().setCell("cf", "q", "v"))), - false); - } - } - - @Test - public void testReadModifyWriteDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient client = BigtableDataClient.create(settings.build())) { - ApiException exception = enqueueNonRetryableExceptionWithDelay(defaultDelay); - try { - client.readModifyWriteRow(ReadModifyWriteRow.create("table", "row").append("cf", "q", "v")); - } catch (ApiException e) { - assertThat(e.getStatusCode()).isEqualTo(exception.getStatusCode()); - } - assertThat(attemptCounter.get()).isEqualTo(1); - } - } - @Test public void testReadModifyWriteServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -492,19 +330,6 @@ public void testReadModifyWriteServerNotReturningRetryInfo() { false); } - @Test - public void testReadModifyWriteNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> - newClient.readModifyWriteRow( - ReadModifyWriteRow.create("table", "row").append("cf", "q", "v")), - false); - } - } - @Test public void testReadChangeStreamNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed( @@ -516,21 +341,6 @@ public void testReadChangeStreamNonRetryableErrorWithRetryInfo() { false); } - @Test - public void testReadChangeStreamDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = - Lists.newArrayList( - newClient.readChangeStream(ReadChangeStreamQuery.create("table"))); - }); - } - } - @Test public void testReadChangeStreamServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -542,23 +352,6 @@ public void testReadChangeStreamServerNotReturningRetryInfo() { true); } - @Test - public void testReadChangeStreamNotReturningRetryInfoClientDisabledHandling() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = - Lists.newArrayList( - newClient.readChangeStream(ReadChangeStreamQuery.create("table"))); - }, - true, - com.google.protobuf.Duration.newBuilder().setSeconds(5).setNanos(0).build()); - } - } - @Test public void testGenerateInitialChangeStreamPartitionNonRetryableError() { verifyRetryInfoIsUsed( @@ -570,20 +363,6 @@ public void testGenerateInitialChangeStreamPartitionNonRetryableError() { false); } - @Test - public void testGenerateInitialChangeStreamPartitionDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyRetryInfoCanBeDisabled( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = - Lists.newArrayList(newClient.generateInitialChangeStreamPartitions("table")); - }); - } - } - @Test public void testGenerateInitialChangeStreamServerNotReturningRetryInfo() { verifyNoRetryInfo( @@ -595,55 +374,17 @@ public void testGenerateInitialChangeStreamServerNotReturningRetryInfo() { true); } - @Test - public void testGenerateInitialChangeStreamServerNotReturningRetryInfoClientDisabledHandling() - throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> { - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - ArrayList ignored = - Lists.newArrayList(newClient.generateInitialChangeStreamPartitions("table")); - }, - true); - } - } - @Test public void testPrepareQueryNonRetryableErrorWithRetryInfo() { verifyRetryInfoIsUsed( () -> client.prepareStatement("SELECT * FROM table", new HashMap<>()), false); } - @Test - public void testPrepareQueryDisableRetryInfo() throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - - verifyRetryInfoCanBeDisabled( - () -> newClient.prepareStatement("SELECT * FROM table", new HashMap<>())); - } - } - @Test public void testPrepareQueryServerNotReturningRetryInfo() { verifyNoRetryInfo(() -> client.prepareStatement("SELECT * FROM table", new HashMap<>()), true); } - @Test - public void testPrepareQueryServerNotReturningRetryInfoClientDisabledHandling() - throws IOException { - settings.stubSettings().setEnableRetryInfo(false); - - try (BigtableDataClient newClient = BigtableDataClient.create(settings.build())) { - verifyNoRetryInfo( - () -> newClient.prepareStatement("SELECT * FROM table", new HashMap<>()), true); - } - } - // Test the case where server returns retry info and client enables handling of retry info private void verifyRetryInfoIsUsed(Runnable runnable, boolean retryableError) { if (retryableError) { From 99b14129bff404ef396a12df0d332cd4f6021dd2 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Mon, 23 Feb 2026 15:44:27 -0500 Subject: [PATCH 09/33] chore: factor out per operation settings into a separate class (#2796) Currently Stubs get access to both BigtableClientContext and EnhancedStubSettings. And since BigtableClientContext is derived from EnhancedStubSettings there is quite a bit of overlap settings which makes it hard to keep them consistent. This PR builds towards fixing this by removing access to EnhancedStubSettings and replacing it with disjoint derivative classes. This PR moves all of the per-op settings into a separate class. The next step will be to copy the remaining bits to BigtableClientContext and have EnhancedBigtableStub depend solely on BigtableClientContext and ClientOperationSettings. --- .../data/v2/stub/ClientOperationSettings.java | 404 ++++++++++++++++++ .../v2/stub/EnhancedBigtableStubSettings.java | 391 ++--------------- .../EnhancedBigtableStubSettingsTest.java | 14 +- 3 files changed, 438 insertions(+), 371 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java new file mode 100644 index 0000000000..8252b4b22a --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java @@ -0,0 +1,404 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub; + +import com.google.api.gax.batching.BatchingSettings; +import com.google.api.gax.batching.FlowControlSettings; +import com.google.api.gax.batching.FlowController; +import com.google.api.gax.retrying.RetrySettings; +import com.google.api.gax.rpc.ServerStreamingCallSettings; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.UnaryCallSettings; +import com.google.bigtable.v2.PingAndWarmRequest; +import com.google.cloud.bigtable.data.v2.internal.PrepareQueryRequest; +import com.google.cloud.bigtable.data.v2.internal.PrepareResponse; +import com.google.cloud.bigtable.data.v2.internal.SqlRow; +import com.google.cloud.bigtable.data.v2.models.ChangeStreamRecord; +import com.google.cloud.bigtable.data.v2.models.ConditionalRowMutation; +import com.google.cloud.bigtable.data.v2.models.KeyOffset; +import com.google.cloud.bigtable.data.v2.models.Query; +import com.google.cloud.bigtable.data.v2.models.Range; +import com.google.cloud.bigtable.data.v2.models.ReadChangeStreamQuery; +import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow; +import com.google.cloud.bigtable.data.v2.models.Row; +import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.models.sql.BoundStatement; +import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor; +import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Set; +import org.threeten.bp.Duration; + +class ClientOperationSettings { + private static final Set IDEMPOTENT_RETRY_CODES = + ImmutableSet.of(StatusCode.Code.DEADLINE_EXCEEDED, StatusCode.Code.UNAVAILABLE); + + // Copy of default retrying settings in the yaml + private static final RetrySettings IDEMPOTENT_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setInitialRpcTimeout(Duration.ofSeconds(20)) + .setRpcTimeoutMultiplier(1.0) + .setMaxRpcTimeout(Duration.ofSeconds(20)) + .setTotalTimeout(Duration.ofMinutes(10)) + .build(); + + // Allow retrying ABORTED statuses. These will be returned by the server when the client is + // too slow to read the rows. This makes sense for the java client because retries happen + // after the row merging logic. Which means that the retry will not be invoked until the + // current buffered chunks are consumed. + private static final Set READ_ROWS_RETRY_CODES = + ImmutableSet.builder() + .addAll(IDEMPOTENT_RETRY_CODES) + .add(StatusCode.Code.ABORTED) + .build(); + + // Priming request should have a shorter timeout + private static final Duration PRIME_REQUEST_TIMEOUT = Duration.ofSeconds(30); + + private static final RetrySettings READ_ROWS_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2.0) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setMaxAttempts(10) + .setInitialRpcTimeout(Duration.ofMinutes(30)) + .setRpcTimeoutMultiplier(2.0) + .setMaxRpcTimeout(Duration.ofMinutes(30)) + .setTotalTimeout(Duration.ofHours(12)) + .build(); + + private static final RetrySettings MUTATE_ROWS_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setInitialRpcTimeout(Duration.ofMinutes(1)) + .setRpcTimeoutMultiplier(1.0) + .setMaxRpcTimeout(Duration.ofMinutes(1)) + .setTotalTimeout(Duration.ofMinutes(10)) + .build(); + + private static final Set GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_CODES = + ImmutableSet.builder() + .addAll(IDEMPOTENT_RETRY_CODES) + .add(StatusCode.Code.ABORTED) + .build(); + + private static final RetrySettings GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2.0) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setMaxAttempts(10) + .setInitialRpcTimeout(Duration.ofMinutes(1)) + .setRpcTimeoutMultiplier(2.0) + .setMaxRpcTimeout(Duration.ofMinutes(10)) + .setTotalTimeout(Duration.ofMinutes(60)) + .build(); + + // Allow retrying ABORTED statuses. These will be returned by the server when the client is + // too slow to read the change stream records. This makes sense for the java client because + // retries happen after the mutation merging logic. Which means that the retry will not be + // invoked until the current buffered change stream mutations are consumed. + private static final Set READ_CHANGE_STREAM_RETRY_CODES = + ImmutableSet.builder() + .addAll(IDEMPOTENT_RETRY_CODES) + .add(StatusCode.Code.ABORTED) + .build(); + + private static final RetrySettings READ_CHANGE_STREAM_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2.0) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setMaxAttempts(10) + .setInitialRpcTimeout(Duration.ofMinutes(5)) + .setRpcTimeoutMultiplier(2.0) + .setMaxRpcTimeout(Duration.ofMinutes(5)) + .setTotalTimeout(Duration.ofHours(12)) + .build(); + + // Allow retrying ABORTED statuses. These will be returned by the server when the client is + // too slow to read the responses. + private static final Set EXECUTE_QUERY_RETRY_CODES = + ImmutableSet.builder() + .addAll(IDEMPOTENT_RETRY_CODES) + .add(StatusCode.Code.ABORTED) + .build(); + + // We use the same configuration as READ_ROWS + private static final RetrySettings EXECUTE_QUERY_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2.0) + .setMaxRetryDelay(Duration.ofMinutes(1)) + .setMaxAttempts(10) + .setInitialRpcTimeout(Duration.ofMinutes(30)) + .setRpcTimeoutMultiplier(1.0) + .setMaxRpcTimeout(Duration.ofMinutes(30)) + .setTotalTimeout(Duration.ofHours(12)) + .build(); + + // Similar to IDEMPOTENT but with a lower initial rpc timeout since we expect + // these calls to be quick in most circumstances + private static final RetrySettings PREPARE_QUERY_RETRY_SETTINGS = + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ofMillis(10)) + .setRetryDelayMultiplier(2) + .setMaxRetryDelay(Duration.ofMinutes(1)) + // TODO: fix the settings: initial attempt deadline: 5s, max is 20s but multiplier is 1 + .setInitialRpcTimeout(Duration.ofSeconds(5)) + .setRpcTimeoutMultiplier(1.0) + .setMaxRpcTimeout(Duration.ofSeconds(20)) + .setTotalTimeout(Duration.ofMinutes(10)) + .build(); + + final ServerStreamingCallSettings readRowsSettings; + final UnaryCallSettings readRowSettings; + final UnaryCallSettings> sampleRowKeysSettings; + final UnaryCallSettings mutateRowSettings; + final BigtableBatchingCallSettings bulkMutateRowsSettings; + final BigtableBulkReadRowsCallSettings bulkReadRowsSettings; + final UnaryCallSettings checkAndMutateRowSettings; + final UnaryCallSettings readModifyWriteRowSettings; + final ServerStreamingCallSettings + generateInitialChangeStreamPartitionsSettings; + final ServerStreamingCallSettings + readChangeStreamSettings; + final UnaryCallSettings pingAndWarmSettings; + final ServerStreamingCallSettings executeQuerySettings; + final UnaryCallSettings prepareQuerySettings; + + ClientOperationSettings(Builder builder) { + // Since point reads, streaming reads, bulk reads share the same base callable that converts + // grpc errors into ApiExceptions, they must have the same retry codes. + Preconditions.checkState( + builder + .readRowSettings + .getRetryableCodes() + .equals(builder.readRowsSettings.getRetryableCodes()), + "Single ReadRow retry codes must match ReadRows retry codes"); + Preconditions.checkState( + builder + .bulkReadRowsSettings + .getRetryableCodes() + .equals(builder.readRowsSettings.getRetryableCodes()), + "Bulk ReadRow retry codes must match ReadRows retry codes"); + + // Per method settings. + readRowsSettings = builder.readRowsSettings.build(); + readRowSettings = builder.readRowSettings.build(); + sampleRowKeysSettings = builder.sampleRowKeysSettings.build(); + mutateRowSettings = builder.mutateRowSettings.build(); + bulkMutateRowsSettings = builder.bulkMutateRowsSettings.build(); + bulkReadRowsSettings = builder.bulkReadRowsSettings.build(); + checkAndMutateRowSettings = builder.checkAndMutateRowSettings.build(); + readModifyWriteRowSettings = builder.readModifyWriteRowSettings.build(); + generateInitialChangeStreamPartitionsSettings = + builder.generateInitialChangeStreamPartitionsSettings.build(); + readChangeStreamSettings = builder.readChangeStreamSettings.build(); + pingAndWarmSettings = builder.pingAndWarmSettings.build(); + executeQuerySettings = builder.executeQuerySettings.build(); + prepareQuerySettings = builder.prepareQuerySettings.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("readRowsSettings", readRowsSettings) + .add("readRowSettings", readRowSettings) + .add("sampleRowKeysSettings", sampleRowKeysSettings) + .add("mutateRowSettings", mutateRowSettings) + .add("bulkMutateRowsSettings", bulkMutateRowsSettings) + .add("bulkReadRowsSettings", bulkReadRowsSettings) + .add("checkAndMutateRowSettings", checkAndMutateRowSettings) + .add("readModifyWriteRowSettings", readModifyWriteRowSettings) + .add( + "generateInitialChangeStreamPartitionsSettings", + generateInitialChangeStreamPartitionsSettings) + .add("readChangeStreamSettings", readChangeStreamSettings) + .add("pingAndWarmSettings", pingAndWarmSettings) + .add("executeQuerySettings", executeQuerySettings) + .add("prepareQuerySettings", prepareQuerySettings) + .toString(); + } + + static class Builder { + ServerStreamingCallSettings.Builder readRowsSettings; + UnaryCallSettings.Builder readRowSettings; + UnaryCallSettings.Builder> sampleRowKeysSettings; + UnaryCallSettings.Builder mutateRowSettings; + BigtableBatchingCallSettings.Builder bulkMutateRowsSettings; + BigtableBulkReadRowsCallSettings.Builder bulkReadRowsSettings; + UnaryCallSettings.Builder checkAndMutateRowSettings; + UnaryCallSettings.Builder readModifyWriteRowSettings; + ServerStreamingCallSettings.Builder + generateInitialChangeStreamPartitionsSettings; + ServerStreamingCallSettings.Builder + readChangeStreamSettings; + UnaryCallSettings.Builder pingAndWarmSettings; + ServerStreamingCallSettings.Builder executeQuerySettings; + UnaryCallSettings.Builder prepareQuerySettings; + + Builder() { + BigtableStubSettings.Builder baseDefaults = BigtableStubSettings.newBuilder(); + + readRowsSettings = ServerStreamingCallSettings.newBuilder(); + + readRowsSettings + .setRetryableCodes(READ_ROWS_RETRY_CODES) + .setRetrySettings(READ_ROWS_RETRY_SETTINGS) + .setIdleTimeout(Duration.ofMinutes(5)) + .setWaitTimeout(Duration.ofMinutes(5)); + + readRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + readRowSettings + .setRetryableCodes(readRowsSettings.getRetryableCodes()) + .setRetrySettings(IDEMPOTENT_RETRY_SETTINGS); + + sampleRowKeysSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + sampleRowKeysSettings + .setRetryableCodes(IDEMPOTENT_RETRY_CODES) + .setRetrySettings( + IDEMPOTENT_RETRY_SETTINGS.toBuilder() + .setInitialRpcTimeout(Duration.ofMinutes(5)) + .setMaxRpcTimeout(Duration.ofMinutes(5)) + .build()); + + mutateRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + copyRetrySettings(baseDefaults.mutateRowSettings(), mutateRowSettings); + + long maxBulkMutateElementPerBatch = 100L; + long maxBulkMutateOutstandingElementCount = 20_000L; + + bulkMutateRowsSettings = + BigtableBatchingCallSettings.newBuilder(new MutateRowsBatchingDescriptor()) + .setRetryableCodes(IDEMPOTENT_RETRY_CODES) + .setRetrySettings(MUTATE_ROWS_RETRY_SETTINGS) + .setBatchingSettings( + BatchingSettings.newBuilder() + .setIsEnabled(true) + .setElementCountThreshold(maxBulkMutateElementPerBatch) + .setRequestByteThreshold(20L * 1024 * 1024) + .setDelayThreshold(Duration.ofSeconds(1)) + .setFlowControlSettings( + FlowControlSettings.newBuilder() + .setLimitExceededBehavior(FlowController.LimitExceededBehavior.Block) + .setMaxOutstandingRequestBytes(100L * 1024 * 1024) + .setMaxOutstandingElementCount(maxBulkMutateOutstandingElementCount) + .build()) + .build()); + + long maxBulkReadElementPerBatch = 100L; + long maxBulkReadRequestSizePerBatch = 400L * 1024L; + long maxBulkReadOutstandingElementCount = 20_000L; + + bulkReadRowsSettings = + BigtableBulkReadRowsCallSettings.newBuilder(new ReadRowsBatchingDescriptor()) + .setRetryableCodes(readRowsSettings.getRetryableCodes()) + .setRetrySettings(IDEMPOTENT_RETRY_SETTINGS) + .setBatchingSettings( + BatchingSettings.newBuilder() + .setElementCountThreshold(maxBulkReadElementPerBatch) + .setRequestByteThreshold(maxBulkReadRequestSizePerBatch) + .setDelayThreshold(Duration.ofSeconds(1)) + .setFlowControlSettings( + FlowControlSettings.newBuilder() + .setLimitExceededBehavior(FlowController.LimitExceededBehavior.Block) + .setMaxOutstandingElementCount(maxBulkReadOutstandingElementCount) + .build()) + .build()); + + checkAndMutateRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + copyRetrySettings(baseDefaults.checkAndMutateRowSettings(), checkAndMutateRowSettings); + + readModifyWriteRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + copyRetrySettings(baseDefaults.readModifyWriteRowSettings(), readModifyWriteRowSettings); + + generateInitialChangeStreamPartitionsSettings = ServerStreamingCallSettings.newBuilder(); + generateInitialChangeStreamPartitionsSettings + .setRetryableCodes(GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_CODES) + .setRetrySettings(GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_SETTINGS) + .setIdleTimeout(Duration.ofMinutes(5)) + .setWaitTimeout(Duration.ofMinutes(1)); + + readChangeStreamSettings = ServerStreamingCallSettings.newBuilder(); + readChangeStreamSettings + .setRetryableCodes(READ_CHANGE_STREAM_RETRY_CODES) + .setRetrySettings(READ_CHANGE_STREAM_RETRY_SETTINGS) + .setIdleTimeout(Duration.ofMinutes(5)) + .setWaitTimeout(Duration.ofMinutes(1)); + + pingAndWarmSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + pingAndWarmSettings.setRetrySettings( + RetrySettings.newBuilder() + .setMaxAttempts(1) + .setInitialRpcTimeout(PRIME_REQUEST_TIMEOUT) + .setMaxRpcTimeout(PRIME_REQUEST_TIMEOUT) + .setTotalTimeout(PRIME_REQUEST_TIMEOUT) + .build()); + + executeQuerySettings = ServerStreamingCallSettings.newBuilder(); + executeQuerySettings + .setRetryableCodes(EXECUTE_QUERY_RETRY_CODES) + .setRetrySettings(EXECUTE_QUERY_RETRY_SETTINGS) + .setIdleTimeout(Duration.ofMinutes(5)) + .setWaitTimeout(Duration.ofMinutes(5)); + + prepareQuerySettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + prepareQuerySettings + .setRetryableCodes(IDEMPOTENT_RETRY_CODES) + .setRetrySettings(PREPARE_QUERY_RETRY_SETTINGS); + } + + Builder(ClientOperationSettings settings) { + readRowsSettings = settings.readRowsSettings.toBuilder(); + readRowSettings = settings.readRowSettings.toBuilder(); + sampleRowKeysSettings = settings.sampleRowKeysSettings.toBuilder(); + mutateRowSettings = settings.mutateRowSettings.toBuilder(); + bulkMutateRowsSettings = settings.bulkMutateRowsSettings.toBuilder(); + bulkReadRowsSettings = settings.bulkReadRowsSettings.toBuilder(); + checkAndMutateRowSettings = settings.checkAndMutateRowSettings.toBuilder(); + readModifyWriteRowSettings = settings.readModifyWriteRowSettings.toBuilder(); + generateInitialChangeStreamPartitionsSettings = + settings.generateInitialChangeStreamPartitionsSettings.toBuilder(); + readChangeStreamSettings = settings.readChangeStreamSettings.toBuilder(); + pingAndWarmSettings = settings.pingAndWarmSettings.toBuilder(); + executeQuerySettings = settings.executeQuerySettings.toBuilder(); + prepareQuerySettings = settings.prepareQuerySettings.toBuilder(); + } + + /** + * Copies settings from unary RPC to another. This is necessary when modifying request and + * response types while trying to retain retry settings. + */ + private static void copyRetrySettings( + UnaryCallSettings.Builder source, UnaryCallSettings.Builder dest) { + dest.setRetryableCodes(source.getRetryableCodes()); + dest.setRetrySettings(source.getRetrySettings()); + } + + ClientOperationSettings build() { + return new ClientOperationSettings(this); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 003823f5fc..0ce0c7b299 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -21,7 +21,6 @@ import com.google.api.gax.batching.BatchingSettings; import com.google.api.gax.batching.FlowControlSettings; import com.google.api.gax.batching.FlowController; -import com.google.api.gax.batching.FlowController.LimitExceededBehavior; import com.google.api.gax.core.GoogleCredentialsProvider; import com.google.api.gax.grpc.ChannelPoolSettings; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; @@ -50,13 +49,10 @@ import com.google.cloud.bigtable.data.v2.models.sql.BoundStatement; import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor; -import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -64,7 +60,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.threeten.bp.Duration; @@ -110,120 +105,6 @@ public class EnhancedBigtableStubSettings extends StubSettings IDEMPOTENT_RETRY_CODES = - ImmutableSet.of(Code.DEADLINE_EXCEEDED, Code.UNAVAILABLE); - - // Copy of default retrying settings in the yaml - private static final RetrySettings IDEMPOTENT_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setInitialRpcTimeout(Duration.ofSeconds(20)) - .setRpcTimeoutMultiplier(1.0) - .setMaxRpcTimeout(Duration.ofSeconds(20)) - .setTotalTimeout(Duration.ofMinutes(10)) - .build(); - - // Allow retrying ABORTED statuses. These will be returned by the server when the client is - // too slow to read the rows. This makes sense for the java client because retries happen - // after the row merging logic. Which means that the retry will not be invoked until the - // current buffered chunks are consumed. - private static final Set READ_ROWS_RETRY_CODES = - ImmutableSet.builder().addAll(IDEMPOTENT_RETRY_CODES).add(Code.ABORTED).build(); - - // Priming request should have a shorter timeout - private static Duration PRIME_REQUEST_TIMEOUT = Duration.ofSeconds(30); - - private static final RetrySettings READ_ROWS_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2.0) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setMaxAttempts(10) - .setInitialRpcTimeout(Duration.ofMinutes(30)) - .setRpcTimeoutMultiplier(2.0) - .setMaxRpcTimeout(Duration.ofMinutes(30)) - .setTotalTimeout(Duration.ofHours(12)) - .build(); - - private static final RetrySettings MUTATE_ROWS_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setInitialRpcTimeout(Duration.ofMinutes(1)) - .setRpcTimeoutMultiplier(1.0) - .setMaxRpcTimeout(Duration.ofMinutes(1)) - .setTotalTimeout(Duration.ofMinutes(10)) - .build(); - - private static final Set GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_CODES = - ImmutableSet.builder().addAll(IDEMPOTENT_RETRY_CODES).add(Code.ABORTED).build(); - - private static final RetrySettings GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2.0) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setMaxAttempts(10) - .setInitialRpcTimeout(Duration.ofMinutes(1)) - .setRpcTimeoutMultiplier(2.0) - .setMaxRpcTimeout(Duration.ofMinutes(10)) - .setTotalTimeout(Duration.ofMinutes(60)) - .build(); - - // Allow retrying ABORTED statuses. These will be returned by the server when the client is - // too slow to read the change stream records. This makes sense for the java client because - // retries happen after the mutation merging logic. Which means that the retry will not be - // invoked until the current buffered change stream mutations are consumed. - private static final Set READ_CHANGE_STREAM_RETRY_CODES = - ImmutableSet.builder().addAll(IDEMPOTENT_RETRY_CODES).add(Code.ABORTED).build(); - - private static final RetrySettings READ_CHANGE_STREAM_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2.0) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setMaxAttempts(10) - .setJittered(true) - .setInitialRpcTimeout(Duration.ofMinutes(5)) - .setRpcTimeoutMultiplier(2.0) - .setMaxRpcTimeout(Duration.ofMinutes(5)) - .setTotalTimeout(Duration.ofHours(12)) - .build(); - - // Allow retrying ABORTED statuses. These will be returned by the server when the client is - // too slow to read the responses. - private static final Set EXECUTE_QUERY_RETRY_CODES = - ImmutableSet.builder().addAll(IDEMPOTENT_RETRY_CODES).add(Code.ABORTED).build(); - - // We use the same configuration as READ_ROWS - private static final RetrySettings EXECUTE_QUERY_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2.0) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setMaxAttempts(10) - .setInitialRpcTimeout(Duration.ofMinutes(30)) - .setRpcTimeoutMultiplier(1.0) - .setMaxRpcTimeout(Duration.ofMinutes(30)) - .setTotalTimeout(Duration.ofHours(12)) - .build(); - - // Similar to IDEMPOTENT but with a lower initial rpc timeout since we expect - // these calls to be quick in most circumstances - private static final RetrySettings PREPARE_QUERY_RETRY_SETTINGS = - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ofMillis(10)) - .setRetryDelayMultiplier(2) - .setMaxRetryDelay(Duration.ofMinutes(1)) - .setInitialRpcTimeout(Duration.ofSeconds(5)) - .setRpcTimeoutMultiplier(1.0) - .setMaxRpcTimeout(Duration.ofSeconds(20)) - .setTotalTimeout(Duration.ofMinutes(10)) - .build(); - /** * Scopes that are equivalent to JWT's audience. * @@ -249,21 +130,7 @@ public class EnhancedBigtableStubSettings extends StubSettings readRowsSettings; - private final UnaryCallSettings readRowSettings; - private final UnaryCallSettings> sampleRowKeysSettings; - private final UnaryCallSettings mutateRowSettings; - private final BigtableBatchingCallSettings bulkMutateRowsSettings; - private final BigtableBulkReadRowsCallSettings bulkReadRowsSettings; - private final UnaryCallSettings checkAndMutateRowSettings; - private final UnaryCallSettings readModifyWriteRowSettings; - private final ServerStreamingCallSettings - generateInitialChangeStreamPartitionsSettings; - private final ServerStreamingCallSettings - readChangeStreamSettings; - private final UnaryCallSettings pingAndWarmSettings; - private final ServerStreamingCallSettings executeQuerySettings; - private final UnaryCallSettings prepareQuerySettings; + private final ClientOperationSettings perOpSettings; private final FeatureFlags featureFlags; @@ -275,21 +142,6 @@ public class EnhancedBigtableStubSettings extends StubSettings */ public ServerStreamingCallSettings readRowsSettings() { - return readRowsSettings; + return perOpSettings.readRowsSettings; } /** @@ -500,7 +338,7 @@ public ServerStreamingCallSettings readRowsSettings() { * */ public UnaryCallSettings> sampleRowKeysSettings() { - return sampleRowKeysSettings; + return perOpSettings.sampleRowKeysSettings; } /** @@ -525,7 +363,7 @@ public UnaryCallSettings> sampleRowKeysSettings() { * @see RetrySettings for more explanation. */ public UnaryCallSettings readRowSettings() { - return readRowSettings; + return perOpSettings.readRowSettings; } /** @@ -550,7 +388,7 @@ public UnaryCallSettings readRowSettings() { * @see RetrySettings for more explanation. */ public UnaryCallSettings mutateRowSettings() { - return mutateRowSettings; + return perOpSettings.mutateRowSettings; } /** @@ -597,7 +435,7 @@ public UnaryCallSettings mutateRowSettings() { * related configuration explanation. */ public BigtableBatchingCallSettings bulkMutateRowsSettings() { - return bulkMutateRowsSettings; + return perOpSettings.bulkMutateRowsSettings; } /** @@ -638,7 +476,7 @@ public BigtableBatchingCallSettings bulkMutateRowsSettings() { * @see BatchingSettings for batch related configuration explanation. */ public BigtableBulkReadRowsCallSettings bulkReadRowsSettings() { - return bulkReadRowsSettings; + return perOpSettings.bulkReadRowsSettings; } /** @@ -652,7 +490,7 @@ public BigtableBulkReadRowsCallSettings bulkReadRowsSettings() { * @see RetrySettings for more explanation. */ public UnaryCallSettings checkAndMutateRowSettings() { - return checkAndMutateRowSettings; + return perOpSettings.checkAndMutateRowSettings; } /** @@ -666,21 +504,21 @@ public UnaryCallSettings checkAndMutateRowSetti * @see RetrySettings for more explanation. */ public UnaryCallSettings readModifyWriteRowSettings() { - return readModifyWriteRowSettings; + return perOpSettings.readModifyWriteRowSettings; } public ServerStreamingCallSettings generateInitialChangeStreamPartitionsSettings() { - return generateInitialChangeStreamPartitionsSettings; + return perOpSettings.generateInitialChangeStreamPartitionsSettings; } public ServerStreamingCallSettings readChangeStreamSettings() { - return readChangeStreamSettings; + return perOpSettings.readChangeStreamSettings; } public ServerStreamingCallSettings executeQuerySettings() { - return executeQuerySettings; + return perOpSettings.executeQuerySettings; } /** @@ -706,7 +544,7 @@ public ServerStreamingCallSettings executeQuerySettings( * @see RetrySettings for more explanation. */ public UnaryCallSettings prepareQuerySettings() { - return prepareQuerySettings; + return perOpSettings.prepareQuerySettings; } /** @@ -715,7 +553,7 @@ public UnaryCallSettings prepareQuerySetti *

    By default the retries are disabled for PingAndWarm and deadline is set to 30 seconds. */ UnaryCallSettings pingAndWarmSettings() { - return pingAndWarmSettings; + return perOpSettings.pingAndWarmSettings; } /** Returns a builder containing all the values of this settings class. */ @@ -732,23 +570,7 @@ public static class Builder extends StubSettings.Builder readRowsSettings; - private final UnaryCallSettings.Builder readRowSettings; - private final UnaryCallSettings.Builder> sampleRowKeysSettings; - private final UnaryCallSettings.Builder mutateRowSettings; - private final BigtableBatchingCallSettings.Builder bulkMutateRowsSettings; - private final BigtableBulkReadRowsCallSettings.Builder bulkReadRowsSettings; - private final UnaryCallSettings.Builder - checkAndMutateRowSettings; - private final UnaryCallSettings.Builder readModifyWriteRowSettings; - private final ServerStreamingCallSettings.Builder - generateInitialChangeStreamPartitionsSettings; - private final ServerStreamingCallSettings.Builder - readChangeStreamSettings; - private final UnaryCallSettings.Builder pingAndWarmSettings; - private final ServerStreamingCallSettings.Builder executeQuerySettings; - private final UnaryCallSettings.Builder - prepareQuerySettings; + private final ClientOperationSettings.Builder perOpSettings; private final FeatureFlags.Builder featureFlags; @@ -780,113 +602,7 @@ private Builder() { setStreamWatchdogCheckInterval(baseDefaults.getStreamWatchdogCheckInterval()); setStreamWatchdogProvider(baseDefaults.getStreamWatchdogProvider()); - // Per-method settings using baseSettings for defaults. - readRowsSettings = ServerStreamingCallSettings.newBuilder(); - - readRowsSettings - .setRetryableCodes(READ_ROWS_RETRY_CODES) - .setRetrySettings(READ_ROWS_RETRY_SETTINGS) - .setIdleTimeout(Duration.ofMinutes(5)) - .setWaitTimeout(Duration.ofMinutes(5)); - - readRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - readRowSettings - .setRetryableCodes(readRowsSettings.getRetryableCodes()) - .setRetrySettings(IDEMPOTENT_RETRY_SETTINGS); - - sampleRowKeysSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - sampleRowKeysSettings - .setRetryableCodes(IDEMPOTENT_RETRY_CODES) - .setRetrySettings( - IDEMPOTENT_RETRY_SETTINGS.toBuilder() - .setInitialRpcTimeout(Duration.ofMinutes(5)) - .setMaxRpcTimeout(Duration.ofMinutes(5)) - .build()); - - mutateRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - copyRetrySettings(baseDefaults.mutateRowSettings(), mutateRowSettings); - - long maxBulkMutateElementPerBatch = 100L; - long maxBulkMutateOutstandingElementCount = 20_000L; - - bulkMutateRowsSettings = - BigtableBatchingCallSettings.newBuilder(new MutateRowsBatchingDescriptor()) - .setRetryableCodes(IDEMPOTENT_RETRY_CODES) - .setRetrySettings(MUTATE_ROWS_RETRY_SETTINGS) - .setBatchingSettings( - BatchingSettings.newBuilder() - .setIsEnabled(true) - .setElementCountThreshold(maxBulkMutateElementPerBatch) - .setRequestByteThreshold(20L * 1024 * 1024) - .setDelayThreshold(Duration.ofSeconds(1)) - .setFlowControlSettings( - FlowControlSettings.newBuilder() - .setLimitExceededBehavior(LimitExceededBehavior.Block) - .setMaxOutstandingRequestBytes(100L * 1024 * 1024) - .setMaxOutstandingElementCount(maxBulkMutateOutstandingElementCount) - .build()) - .build()); - - long maxBulkReadElementPerBatch = 100L; - long maxBulkReadRequestSizePerBatch = 400L * 1024L; - long maxBulkReadOutstandingElementCount = 20_000L; - - bulkReadRowsSettings = - BigtableBulkReadRowsCallSettings.newBuilder(new ReadRowsBatchingDescriptor()) - .setRetryableCodes(readRowsSettings.getRetryableCodes()) - .setRetrySettings(IDEMPOTENT_RETRY_SETTINGS) - .setBatchingSettings( - BatchingSettings.newBuilder() - .setElementCountThreshold(maxBulkReadElementPerBatch) - .setRequestByteThreshold(maxBulkReadRequestSizePerBatch) - .setDelayThreshold(Duration.ofSeconds(1)) - .setFlowControlSettings( - FlowControlSettings.newBuilder() - .setLimitExceededBehavior(LimitExceededBehavior.Block) - .setMaxOutstandingElementCount(maxBulkReadOutstandingElementCount) - .build()) - .build()); - - checkAndMutateRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - copyRetrySettings(baseDefaults.checkAndMutateRowSettings(), checkAndMutateRowSettings); - - readModifyWriteRowSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - copyRetrySettings(baseDefaults.readModifyWriteRowSettings(), readModifyWriteRowSettings); - - generateInitialChangeStreamPartitionsSettings = ServerStreamingCallSettings.newBuilder(); - generateInitialChangeStreamPartitionsSettings - .setRetryableCodes(GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_CODES) - .setRetrySettings(GENERATE_INITIAL_CHANGE_STREAM_PARTITIONS_RETRY_SETTINGS) - .setIdleTimeout(Duration.ofMinutes(5)) - .setWaitTimeout(Duration.ofMinutes(1)); - - readChangeStreamSettings = ServerStreamingCallSettings.newBuilder(); - readChangeStreamSettings - .setRetryableCodes(READ_CHANGE_STREAM_RETRY_CODES) - .setRetrySettings(READ_CHANGE_STREAM_RETRY_SETTINGS) - .setIdleTimeout(Duration.ofMinutes(5)) - .setWaitTimeout(Duration.ofMinutes(1)); - - pingAndWarmSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - pingAndWarmSettings.setRetrySettings( - RetrySettings.newBuilder() - .setMaxAttempts(1) - .setInitialRpcTimeout(PRIME_REQUEST_TIMEOUT) - .setMaxRpcTimeout(PRIME_REQUEST_TIMEOUT) - .setTotalTimeout(PRIME_REQUEST_TIMEOUT) - .build()); - - executeQuerySettings = ServerStreamingCallSettings.newBuilder(); - executeQuerySettings - .setRetryableCodes(EXECUTE_QUERY_RETRY_CODES) - .setRetrySettings(EXECUTE_QUERY_RETRY_SETTINGS) - .setIdleTimeout(Duration.ofMinutes(5)) - .setWaitTimeout(Duration.ofMinutes(5)); - - prepareQuerySettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - prepareQuerySettings - .setRetryableCodes(IDEMPOTENT_RETRY_CODES) - .setRetrySettings(PREPARE_QUERY_RETRY_SETTINGS); + perOpSettings = new ClientOperationSettings.Builder(); featureFlags = FeatureFlags.newBuilder() @@ -908,38 +624,11 @@ private Builder(EnhancedBigtableStubSettings settings) { areInternalMetricsEnabled = settings.areInternalMetricsEnabled; jwtAudience = settings.jwtAudience; - // Per method settings. - readRowsSettings = settings.readRowsSettings.toBuilder(); - readRowSettings = settings.readRowSettings.toBuilder(); - sampleRowKeysSettings = settings.sampleRowKeysSettings.toBuilder(); - mutateRowSettings = settings.mutateRowSettings.toBuilder(); - bulkMutateRowsSettings = settings.bulkMutateRowsSettings.toBuilder(); - bulkReadRowsSettings = settings.bulkReadRowsSettings.toBuilder(); - checkAndMutateRowSettings = settings.checkAndMutateRowSettings.toBuilder(); - readModifyWriteRowSettings = settings.readModifyWriteRowSettings.toBuilder(); - generateInitialChangeStreamPartitionsSettings = - settings.generateInitialChangeStreamPartitionsSettings.toBuilder(); - readChangeStreamSettings = settings.readChangeStreamSettings.toBuilder(); - pingAndWarmSettings = settings.pingAndWarmSettings.toBuilder(); - executeQuerySettings = settings.executeQuerySettings().toBuilder(); - prepareQuerySettings = settings.prepareQuerySettings().toBuilder(); - featureFlags = settings.featureFlags.toBuilder(); - } - - // + this.perOpSettings = new ClientOperationSettings.Builder(settings.perOpSettings); - /** - * Copies settings from unary RPC to another. This is necessary when modifying request and - * response types while trying to retain retry settings. - */ - private static void copyRetrySettings( - UnaryCallSettings.Builder source, UnaryCallSettings.Builder dest) { - dest.setRetryableCodes(source.getRetryableCodes()); - dest.setRetrySettings(source.getRetrySettings()); + featureFlags = settings.featureFlags.toBuilder(); } - // - // /** * Sets the project id of that target instance. This setting is required. All RPCs will be made @@ -1167,48 +856,48 @@ public boolean getEnableRetryInfo() { /** Returns the builder for the settings used for calls to readRows. */ public ServerStreamingCallSettings.Builder readRowsSettings() { - return readRowsSettings; + return perOpSettings.readRowsSettings; } /** Returns the builder for the settings used for point reads using readRow. */ public UnaryCallSettings.Builder readRowSettings() { - return readRowSettings; + return perOpSettings.readRowSettings; } /** Returns the builder for the settings used for calls to SampleRowKeysSettings. */ public UnaryCallSettings.Builder> sampleRowKeysSettings() { - return sampleRowKeysSettings; + return perOpSettings.sampleRowKeysSettings; } /** Returns the builder for the settings used for calls to MutateRow. */ public UnaryCallSettings.Builder mutateRowSettings() { - return mutateRowSettings; + return perOpSettings.mutateRowSettings; } /** Returns the builder for the settings used for calls to MutateRows. */ public BigtableBatchingCallSettings.Builder bulkMutateRowsSettings() { - return bulkMutateRowsSettings; + return perOpSettings.bulkMutateRowsSettings; } /** Returns the builder for the settings used for calls to MutateRows. */ public BigtableBulkReadRowsCallSettings.Builder bulkReadRowsSettings() { - return bulkReadRowsSettings; + return perOpSettings.bulkReadRowsSettings; } /** Returns the builder for the settings used for calls to CheckAndMutateRow. */ public UnaryCallSettings.Builder checkAndMutateRowSettings() { - return checkAndMutateRowSettings; + return perOpSettings.checkAndMutateRowSettings; } /** Returns the builder with the settings used for calls to ReadModifyWriteRow. */ public UnaryCallSettings.Builder readModifyWriteRowSettings() { - return readModifyWriteRowSettings; + return perOpSettings.readModifyWriteRowSettings; } /** Returns the builder for the settings used for calls to ReadChangeStream. */ public ServerStreamingCallSettings.Builder readChangeStreamSettings() { - return readChangeStreamSettings; + return perOpSettings.readChangeStreamSettings; } /** @@ -1216,12 +905,12 @@ public UnaryCallSettings.Builder readModifyWriteRowSett */ public ServerStreamingCallSettings.Builder generateInitialChangeStreamPartitionsSettings() { - return generateInitialChangeStreamPartitionsSettings; + return perOpSettings.generateInitialChangeStreamPartitionsSettings; } /** Returns the builder with the settings used for calls to PingAndWarm. */ public UnaryCallSettings.Builder pingAndWarmSettings() { - return pingAndWarmSettings; + return perOpSettings.pingAndWarmSettings; } /** @@ -1232,13 +921,13 @@ public UnaryCallSettings.Builder pingAndWarmSettings() */ @BetaApi public ServerStreamingCallSettings.Builder executeQuerySettings() { - return executeQuerySettings; + return perOpSettings.executeQuerySettings; } /** Returns the builder with the settings used for calls to PrepareQuery */ @BetaApi public UnaryCallSettings.Builder prepareQuerySettings() { - return prepareQuerySettings; + return perOpSettings.prepareQuerySettings; } @SuppressWarnings("unchecked") @@ -1294,21 +983,7 @@ public String toString() { .add("instanceId", instanceId) .add("appProfileId", appProfileId) .add("isRefreshingChannel", isRefreshingChannel) - .add("readRowsSettings", readRowsSettings) - .add("readRowSettings", readRowSettings) - .add("sampleRowKeysSettings", sampleRowKeysSettings) - .add("mutateRowSettings", mutateRowSettings) - .add("bulkMutateRowsSettings", bulkMutateRowsSettings) - .add("bulkReadRowsSettings", bulkReadRowsSettings) - .add("checkAndMutateRowSettings", checkAndMutateRowSettings) - .add("readModifyWriteRowSettings", readModifyWriteRowSettings) - .add( - "generateInitialChangeStreamPartitionsSettings", - generateInitialChangeStreamPartitionsSettings) - .add("readChangeStreamSettings", readChangeStreamSettings) - .add("pingAndWarmSettings", pingAndWarmSettings) - .add("executeQuerySettings", executeQuerySettings) - .add("prepareQuerySettings", prepareQuerySettings) + .add("perOpSettings", perOpSettings) .add("metricsProvider", metricsProvider) .add("metricsEndpoint", metricsEndpoint) .add("areInternalMetricsEnabled", areInternalMetricsEnabled) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java index 9de6319182..ad0de696a3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java @@ -919,19 +919,7 @@ public void isRefreshingChannelFalseValueTest() { "instanceId", "appProfileId", "isRefreshingChannel", - "readRowsSettings", - "readRowSettings", - "sampleRowKeysSettings", - "mutateRowSettings", - "bulkMutateRowsSettings", - "bulkReadRowsSettings", - "checkAndMutateRowSettings", - "readModifyWriteRowSettings", - "generateInitialChangeStreamPartitionsSettings", - "readChangeStreamSettings", - "pingAndWarmSettings", - "executeQuerySettings", - "prepareQuerySettings", + "perOpSettings", "metricsProvider", "metricsEndpoint", "areInternalMetricsEnabled", From e67d86931e4bd21ed5cc152131160128faeb5acf Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Mon, 23 Feb 2026 16:44:24 -0500 Subject: [PATCH 10/33] chore: move tracer creation to bigtable client context (#2797) - BigtableClientContext stores the resource anchor (instance name/app profile) - BigtableClientContext stores the opencensus deps - This allows it to create all of the tracers during initialization - BigtableClientContext can create lightweight copies of itself for BigtableDataClientFactory - close logic of shared contexts is now stored in BigtableClientContext via isChild --- .../bigtable/data/v2/BigtableDataClient.java | 3 +- .../data/v2/BigtableDataClientFactory.java | 67 +++------ .../data/v2/stub/BigtableClientContext.java | 105 +++++++++++-- .../data/v2/stub/EnhancedBigtableStub.java | 140 +----------------- .../bigtable/data/v2/stub/metrics/Util.java | 60 ++++++++ .../metrics/BigtableTracerCallableTest.java | 39 ++--- .../v2/stub/metrics/MetricsTracerTest.java | 21 +-- 7 files changed, 205 insertions(+), 230 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java index cef5e58f3a..5af8e9dc96 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java @@ -187,8 +187,7 @@ public static BigtableDataClient create(BigtableDataSettings settings) throws IO */ static BigtableDataClient createWithClientContext( BigtableDataSettings settings, BigtableClientContext context) throws IOException { - EnhancedBigtableStub stub = - EnhancedBigtableStub.createWithClientContext(settings.getStubSettings(), context); + EnhancedBigtableStub stub = new EnhancedBigtableStub(settings.getStubSettings(), context); return new BigtableDataClient(stub); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java index 47cf7b15ed..544d75d6a7 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java @@ -16,9 +16,8 @@ package com.google.cloud.bigtable.data.v2; import com.google.api.core.BetaApi; -import com.google.api.gax.rpc.ClientContext; +import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext; -import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import java.io.IOException; import javax.annotation.Nonnull; @@ -75,8 +74,7 @@ public final class BigtableDataClientFactory implements AutoCloseable { public static BigtableDataClientFactory create(BigtableDataSettings defaultSettings) throws IOException { BigtableClientContext sharedClientContext = - EnhancedBigtableStub.createBigtableClientContext(defaultSettings.getStubSettings()); - + BigtableClientContext.create(defaultSettings.getStubSettings()); return new BigtableDataClientFactory(sharedClientContext, defaultSettings); } @@ -107,17 +105,11 @@ public void close() throws Exception { */ public BigtableDataClient createDefault() { try { - ClientContext clientContext = - sharedClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - defaultSettings.getStubSettings(), - sharedClientContext.getBuiltinOpenTelemetry(), - sharedClientContext.getUserOpenTelemetry())) - .build(); + BigtableClientContext ctx = + sharedClientContext.createChild( + sharedClientContext.getInstanceName(), sharedClientContext.getAppProfileId()); - return BigtableDataClient.createWithClientContext( - defaultSettings, sharedClientContext.withClientContext(clientContext)); + return BigtableDataClient.createWithClientContext(defaultSettings, ctx); } catch (IOException e) { // Should never happen because the connection has been established already throw new RuntimeException( @@ -137,17 +129,12 @@ public BigtableDataClient createDefault() { public BigtableDataClient createForAppProfile(@Nonnull String appProfileId) throws IOException { BigtableDataSettings settings = defaultSettings.toBuilder().setAppProfileId(appProfileId).build(); + BigtableClientContext ctx = + sharedClientContext.createChild( + InstanceName.of(settings.getProjectId(), settings.getInstanceId()), + settings.getAppProfileId()); - ClientContext clientContext = - sharedClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), - sharedClientContext.getBuiltinOpenTelemetry(), - sharedClientContext.getUserOpenTelemetry())) - .build(); - return BigtableDataClient.createWithClientContext( - settings, sharedClientContext.withClientContext(clientContext)); + return BigtableDataClient.createWithClientContext(settings, ctx); } /** @@ -167,18 +154,12 @@ public BigtableDataClient createForInstance(@Nonnull String projectId, @Nonnull .setInstanceId(instanceId) .setDefaultAppProfileId() .build(); + BigtableClientContext ctx = + sharedClientContext.createChild( + InstanceName.of(settings.getProjectId(), settings.getInstanceId()), + settings.getAppProfileId()); - ClientContext clientContext = - sharedClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), - sharedClientContext.getBuiltinOpenTelemetry(), - sharedClientContext.getUserOpenTelemetry())) - .build(); - - return BigtableDataClient.createWithClientContext( - settings, sharedClientContext.withClientContext(clientContext)); + return BigtableDataClient.createWithClientContext(settings, ctx); } /** @@ -199,15 +180,11 @@ public BigtableDataClient createForInstance( .setInstanceId(instanceId) .setAppProfileId(appProfileId) .build(); - ClientContext clientContext = - sharedClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), - sharedClientContext.getBuiltinOpenTelemetry(), - sharedClientContext.getUserOpenTelemetry())) - .build(); - return BigtableDataClient.createWithClientContext( - settings, sharedClientContext.withClientContext(clientContext)); + BigtableClientContext ctx = + sharedClientContext.createChild( + InstanceName.of(settings.getProjectId(), settings.getInstanceId()), + settings.getAppProfileId()); + + return BigtableDataClient.createWithClientContext(settings, ctx); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index d71355d6cd..c89f368190 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -24,18 +24,25 @@ import com.google.api.gax.core.FixedExecutorProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.auth.Credentials; import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; +import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.Util; import com.google.cloud.bigtable.gaxx.grpc.BigtableTransportChannelProvider; import com.google.cloud.bigtable.gaxx.grpc.ChannelPrimer; +import com.google.common.collect.ImmutableList; import io.grpc.ManagedChannelBuilder; import io.grpc.opentelemetry.GrpcOpenTelemetry; +import io.opencensus.stats.Stats; +import io.opencensus.stats.StatsRecorder; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.Tags; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.IOException; @@ -55,15 +62,30 @@ public class BigtableClientContext { private static final Logger logger = Logger.getLogger(BigtableClientContext.class.getName()); + private final boolean isChild; + private final InstanceName instanceName; + private final String appProfileId; + private final ApiTracerFactory userTracerFactory; @Nullable private final OpenTelemetrySdk builtinOpenTelemetry; @Nullable private final OpenTelemetry userOpenTelemetry; private final ClientContext clientContext; // the background executor shared for OTEL instances and monitoring client and all other // background tasks private final ExecutorProvider backgroundExecutorProvider; + private final Tagger ocTagger; + private final StatsRecorder ocRecorder; public static BigtableClientContext create(EnhancedBigtableStubSettings settings) throws IOException { + return create(settings, Tags.getTagger(), Stats.getStatsRecorder()); + } + + public static BigtableClientContext create( + EnhancedBigtableStubSettings settings, Tagger ocTagger, StatsRecorder ocRecorder) + throws IOException { + InstanceName instanceName = InstanceName.of(settings.getProjectId(), settings.getInstanceId()); + String appProfileId = settings.getAppProfileId(); + EnhancedBigtableStubSettings.Builder builder = settings.toBuilder(); // Set up credentials @@ -85,6 +107,8 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings FixedExecutorProvider.create(backgroundExecutor, shouldAutoClose); builder.setBackgroundExecutorProvider(executorProvider); + ApiTracerFactory userTracerFactory = settings.getTracerFactory(); + // Set up OpenTelemetry @Nullable OpenTelemetry userOtel = null; if (settings.getMetricsProvider() instanceof CustomOpenTelemetryMetricsProvider) { @@ -158,7 +182,17 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings channelPoolMetricsTracer.start(clientContext.getExecutor()); } - return new BigtableClientContext(clientContext, builtinOtel, userOtel, executorProvider); + return new BigtableClientContext( + false, + instanceName, + appProfileId, + clientContext, + userTracerFactory, + builtinOtel, + userOtel, + ocTagger, + ocRecorder, + executorProvider); } private static void configureGrpcOtel( @@ -189,14 +223,53 @@ private static void configureGrpcOtel( } private BigtableClientContext( + boolean isChild, + InstanceName instanceName, + String appProfileId, ClientContext clientContext, - @Nullable OpenTelemetrySdk internalOtel, - @Nullable OpenTelemetry userOpenTelemetry, - ExecutorProvider backgroundExecutorProvider) { - this.clientContext = clientContext; - this.userOpenTelemetry = userOpenTelemetry; - this.builtinOpenTelemetry = internalOtel; + ApiTracerFactory userTracerFactory, + @Nullable OpenTelemetrySdk builtinOtel, + @Nullable OpenTelemetry userOtel, + Tagger ocTagger, + StatsRecorder ocRecorder, + ExecutorProvider backgroundExecutorProvider) + throws IOException { + this.isChild = isChild; + this.instanceName = instanceName; + this.appProfileId = appProfileId; + + this.userTracerFactory = userTracerFactory; + this.builtinOpenTelemetry = builtinOtel; + this.userOpenTelemetry = userOtel; + this.ocTagger = ocTagger; + this.ocRecorder = ocRecorder; this.backgroundExecutorProvider = backgroundExecutorProvider; + + ImmutableList.Builder tracerFactories = ImmutableList.builder(); + tracerFactories + .add(Util.createOCTracingFactory(instanceName, appProfileId)) + .add(Util.createOCMetricsFactory(instanceName, appProfileId, ocTagger, ocRecorder)) + .add(userTracerFactory); + + if (builtinOtel != null) { + tracerFactories.add(Util.createOtelMetricsFactory(builtinOtel, instanceName, appProfileId)); + } + if (userOtel != null) { + tracerFactories.add(Util.createOtelMetricsFactory(userOtel, instanceName, appProfileId)); + } + + this.clientContext = + clientContext.toBuilder() + .setTracerFactory(new CompositeTracerFactory(tracerFactories.build())) + .build(); + } + + public InstanceName getInstanceName() { + return instanceName; + } + + public String getAppProfileId() { + return appProfileId; } @Nullable @@ -213,12 +286,26 @@ public ClientContext getClientContext() { return this.clientContext; } - public BigtableClientContext withClientContext(ClientContext clientContext) { + public BigtableClientContext createChild(InstanceName instanceName, String appProfileId) + throws IOException { return new BigtableClientContext( - clientContext, builtinOpenTelemetry, userOpenTelemetry, backgroundExecutorProvider); + true, + instanceName, + appProfileId, + clientContext, + userTracerFactory, + builtinOpenTelemetry, + userOpenTelemetry, + ocTagger, + ocRecorder, + backgroundExecutorProvider); } public void close() throws Exception { + if (isChild) { + return; + } + for (BackgroundResource resource : clientContext.getBackgroundResources()) { resource.close(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index b3cc8d3655..bf94964434 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -15,18 +15,12 @@ */ package com.google.cloud.bigtable.data.v2.stub; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; - import com.google.api.core.ApiFuture; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; import com.google.api.gax.batching.Batcher; import com.google.api.gax.batching.BatcherImpl; import com.google.api.gax.batching.FlowController; -import com.google.api.gax.grpc.GaxGrpcProperties; import com.google.api.gax.grpc.GrpcCallContext; import com.google.api.gax.grpc.GrpcCallSettings; import com.google.api.gax.grpc.GrpcRawCallableFactory; @@ -45,8 +39,6 @@ import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallSettings; import com.google.api.gax.rpc.UnaryCallable; -import com.google.api.gax.tracing.ApiTracerFactory; -import com.google.api.gax.tracing.OpencensusTracerFactory; import com.google.api.gax.tracing.SpanName; import com.google.api.gax.tracing.TracedServerStreamingCallable; import com.google.api.gax.tracing.TracedUnaryCallable; @@ -64,7 +56,6 @@ import com.google.bigtable.v2.ReadRowsResponse; import com.google.bigtable.v2.RowRange; import com.google.bigtable.v2.SampleRowKeysResponse; -import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.internal.NameUtil; import com.google.cloud.bigtable.data.v2.internal.PrepareQueryRequest; import com.google.cloud.bigtable.data.v2.internal.PrepareResponse; @@ -96,10 +87,6 @@ import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamUserCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerStreamingCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerUnaryCallable; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; -import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; -import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory; -import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersServerStreamingCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersUnaryCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.TracedBatcherUnaryCallable; @@ -122,22 +109,12 @@ import com.google.cloud.bigtable.data.v2.stub.sql.PlanRefreshingCallable; import com.google.cloud.bigtable.data.v2.stub.sql.SqlRowMergingCallable; import com.google.cloud.bigtable.gaxx.retrying.RetryInfoRetryAlgorithm; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Functions; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; import io.grpc.MethodDescriptor; -import io.opencensus.stats.Stats; -import io.opencensus.stats.StatsRecorder; -import io.opencensus.tags.TagKey; -import io.opencensus.tags.TagValue; -import io.opencensus.tags.Tagger; -import io.opencensus.tags.Tags; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.Attributes; import java.io.IOException; import java.time.Duration; import java.util.List; @@ -167,7 +144,6 @@ public class EnhancedBigtableStub implements AutoCloseable { private final EnhancedBigtableStubSettings settings; private final BigtableClientContext bigtableClientContext; - private final boolean closeClientContext; private final RequestContext requestContext; private final FlowController bulkMutationFlowController; private final DynamicFlowControlStats bulkMutationDynamicFlowControlStats; @@ -198,117 +174,19 @@ public class EnhancedBigtableStub implements AutoCloseable { public static EnhancedBigtableStub create(EnhancedBigtableStubSettings settings) throws IOException { - BigtableClientContext bigtableClientContext = createBigtableClientContext(settings); - ClientContext contextWithTracer = - bigtableClientContext.getClientContext().toBuilder() - .setTracerFactory( - createBigtableTracerFactory( - settings, - bigtableClientContext.getBuiltinOpenTelemetry(), - bigtableClientContext.getUserOpenTelemetry())) - .build(); - bigtableClientContext = bigtableClientContext.withClientContext(contextWithTracer); + BigtableClientContext bigtableClientContext = BigtableClientContext.create(settings); return new EnhancedBigtableStub(settings, bigtableClientContext); } - public static EnhancedBigtableStub createWithClientContext( - EnhancedBigtableStubSettings settings, BigtableClientContext clientContext) - throws IOException { - - return new EnhancedBigtableStub(settings, clientContext, false); - } - - public static BigtableClientContext createBigtableClientContext( - EnhancedBigtableStubSettings settings) throws IOException { - return BigtableClientContext.create(settings); - } - - public static ApiTracerFactory createBigtableTracerFactory( - EnhancedBigtableStubSettings settings, - @Nullable OpenTelemetry builtinOtel, - @Nullable OpenTelemetry userOtel) - throws IOException { - return createBigtableTracerFactory( - settings, Tags.getTagger(), Stats.getStatsRecorder(), builtinOtel, userOtel); - } - - @VisibleForTesting - public static ApiTracerFactory createBigtableTracerFactory( - EnhancedBigtableStubSettings settings, - Tagger tagger, - StatsRecorder stats, - @Nullable OpenTelemetry builtinOtel, - @Nullable OpenTelemetry userOtel) - throws IOException { - String projectId = settings.getProjectId(); - String instanceId = settings.getInstanceId(); - String appProfileId = settings.getAppProfileId(); - - ImmutableMap attributes = - ImmutableMap.builder() - .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID, TagValue.create(projectId)) - .put(RpcMeasureConstants.BIGTABLE_INSTANCE_ID, TagValue.create(instanceId)) - .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, TagValue.create(appProfileId)) - .build(); - - ImmutableList.Builder tracerFactories = ImmutableList.builder(); - tracerFactories - .add( - // Add OpenCensus Tracing - new OpencensusTracerFactory( - ImmutableMap.builder() - // Annotate traces with the same tags as metrics - .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), projectId) - .put(RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), instanceId) - .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), appProfileId) - // Also annotate traces with library versions - .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) - .put("grpc", GaxGrpcProperties.getGrpcVersion()) - .put("gapic", Version.VERSION) - .build())) - // Add OpenCensus Metrics - .add(MetricsTracerFactory.create(tagger, stats, attributes)) - // Add user configured tracer - .add(settings.getTracerFactory()); - - if (builtinOtel != null) { - tracerFactories.add( - BuiltinMetricsTracerFactory.create(builtinOtel, createBuiltinAttributes(settings))); - } - if (userOtel != null) { - tracerFactories.add( - BuiltinMetricsTracerFactory.create(userOtel, createBuiltinAttributes(settings))); - } - return new CompositeTracerFactory(tracerFactories.build()); - } - - static Attributes createBuiltinAttributes(EnhancedBigtableStubSettings settings) { - return Attributes.of( - BIGTABLE_PROJECT_ID_KEY, - settings.getProjectId(), - INSTANCE_ID_KEY, - settings.getInstanceId(), - APP_PROFILE_KEY, - settings.getAppProfileId(), - CLIENT_NAME_KEY, - "bigtable-java/" + Version.VERSION); - } - public EnhancedBigtableStub( EnhancedBigtableStubSettings settings, BigtableClientContext clientContext) { - this(settings, clientContext, true); - } - - public EnhancedBigtableStub( - EnhancedBigtableStubSettings settings, - BigtableClientContext clientContext, - boolean closeClientContext) { this.settings = settings; this.bigtableClientContext = clientContext; - this.closeClientContext = closeClientContext; this.requestContext = RequestContext.create( - settings.getProjectId(), settings.getInstanceId(), settings.getAppProfileId()); + clientContext.getInstanceName().getProject(), + clientContext.getInstanceName().getInstance(), + clientContext.getAppProfileId()); this.bulkMutationFlowController = new FlowController(settings.bulkMutateRowsSettings().getDynamicFlowControlSettings()); this.bulkMutationDynamicFlowControlStats = new DynamicFlowControlStats(); @@ -1430,12 +1308,10 @@ private SpanName getSpanName(String methodName) { @Override public void close() { - if (closeClientContext) { - try { - bigtableClientContext.close(); - } catch (Exception e) { - throw new IllegalStateException("failed to close client context", e); - } + try { + bigtableClientContext.close(); + } catch (Exception e) { + throw new IllegalStateException("failed to close client context", e); } } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index f5ba5f52fc..cc341c994e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -15,11 +15,19 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; + import com.google.api.core.InternalApi; +import com.google.api.gax.grpc.GaxGrpcProperties; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StatusCode.Code; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.OpencensusTracerFactory; import com.google.auth.Credentials; import com.google.bigtable.v2.AuthorizedViewName; import com.google.bigtable.v2.CheckAndMutateRowRequest; @@ -35,6 +43,7 @@ import com.google.bigtable.v2.ResponseParams; import com.google.bigtable.v2.SampleRowKeysRequest; import com.google.bigtable.v2.TableName; +import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Suppliers; @@ -44,6 +53,12 @@ import io.grpc.Status; import io.grpc.StatusException; import io.grpc.StatusRuntimeException; +import io.opencensus.stats.StatsRecorder; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -225,4 +240,49 @@ public static String formatZoneIdMetricLabel( .filter(s -> !s.isEmpty()) .orElse("global"); } + + public static ApiTracerFactory createOCTracingFactory( + InstanceName instanceName, String appProfileId) { + return new OpencensusTracerFactory( + ImmutableMap.builder() + // Annotate traces with the same tags as metrics + .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), instanceName.getProject()) + .put(RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), instanceName.getInstance()) + .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), appProfileId) + // Also annotate traces with library versions + .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) + .put("grpc", GaxGrpcProperties.getGrpcVersion()) + .put("gapic", Version.VERSION) + .build()); + } + + public static ApiTracerFactory createOCMetricsFactory( + InstanceName instanceName, String appProfileId, Tagger tagger, StatsRecorder stats) { + + ImmutableMap attributes = + ImmutableMap.builder() + .put( + RpcMeasureConstants.BIGTABLE_PROJECT_ID, TagValue.create(instanceName.getProject())) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID, + TagValue.create(instanceName.getInstance())) + .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, TagValue.create(appProfileId)) + .build(); + return MetricsTracerFactory.create(tagger, stats, attributes); + } + + public static BuiltinMetricsTracerFactory createOtelMetricsFactory( + OpenTelemetry otel, InstanceName instanceName, String appProfileId) throws IOException { + Attributes attributes = + Attributes.of( + BIGTABLE_PROJECT_ID_KEY, + instanceName.getProject(), + INSTANCE_ID_KEY, + instanceName.getInstance(), + APP_PROFILE_KEY, + appProfileId, + CLIENT_NAME_KEY, + "bigtable-java/" + Version.VERSION); + return BuiltinMetricsTracerFactory.create(otel, attributes); + } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java index 639228b8e3..0f84417a70 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import com.google.api.gax.rpc.ClientContext; import com.google.api.gax.rpc.ServerStream; import com.google.api.gax.rpc.UnavailableException; import com.google.bigtable.v2.BigtableGrpc.BigtableImplBase; @@ -125,24 +124,17 @@ public void sendHeaders(Metadata headers) { .setProjectId(PROJECT_ID) .setInstanceId(INSTANCE_ID) .setAppProfileId(APP_PROFILE_ID) + // only testing opencensus + .setMetricsProvider(NoopMetricsProvider.INSTANCE) + .disableInternalMetrics() .build(); - BigtableClientContext bigtableClientContext = - EnhancedBigtableStub.createBigtableClientContext(settings.getStubSettings()); - ClientContext clientContext = - bigtableClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), - Tags.getTagger(), - localStats.getStatsRecorder(), - null, - null)) - .build(); attempts = settings.getStubSettings().readRowsSettings().getRetrySettings().getMaxAttempts(); stub = new EnhancedBigtableStub( - settings.getStubSettings(), bigtableClientContext.withClientContext(clientContext)); + settings.getStubSettings(), + BigtableClientContext.create( + settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder())); // Create another server without injecting the server-timing header and another stub that // connects to it. @@ -153,24 +145,17 @@ public void sendHeaders(Metadata headers) { .setProjectId(PROJECT_ID) .setInstanceId(INSTANCE_ID) .setAppProfileId(APP_PROFILE_ID) + .setMetricsProvider(NoopMetricsProvider.INSTANCE) + .disableInternalMetrics() .build(); - BigtableClientContext noHeaderBigtableClientContext = - EnhancedBigtableStub.createBigtableClientContext(noHeaderSettings.getStubSettings()); - ClientContext noHeaderClientContext = - noHeaderBigtableClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - noHeaderSettings.getStubSettings(), - Tags.getTagger(), - localStats.getStatsRecorder(), - null, - null)) - .build(); noHeaderStub = new EnhancedBigtableStub( noHeaderSettings.getStubSettings(), - noHeaderBigtableClientContext.withClientContext(noHeaderClientContext)); + BigtableClientContext.create( + noHeaderSettings.getStubSettings(), + Tags.getTagger(), + localStats.getStatsRecorder())); } @After diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java index 47b64fba14..ab2fe6e205 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java @@ -25,7 +25,6 @@ import com.google.api.gax.batching.FlowController; import com.google.api.gax.grpc.GrpcCallContext; import com.google.api.gax.rpc.ApiCallContext; -import com.google.api.gax.rpc.ClientContext; import com.google.bigtable.v2.BigtableGrpc; import com.google.bigtable.v2.MutateRowsRequest; import com.google.bigtable.v2.MutateRowsResponse; @@ -120,23 +119,15 @@ public void setUp() throws Exception { .setProjectId(PROJECT_ID) .setInstanceId(INSTANCE_ID) .setAppProfileId(APP_PROFILE_ID) + .setMetricsProvider(NoopMetricsProvider.INSTANCE) + .disableInternalMetrics() .build(); BigtableClientContext bigtableClientContext = - EnhancedBigtableStub.createBigtableClientContext(settings.getStubSettings()); - ClientContext clientContext = - bigtableClientContext.getClientContext().toBuilder() - .setTracerFactory( - EnhancedBigtableStub.createBigtableTracerFactory( - settings.getStubSettings(), - Tags.getTagger(), - localStats.getStatsRecorder(), - null, - null)) - .build(); - stub = - new EnhancedBigtableStub( - settings.getStubSettings(), bigtableClientContext.withClientContext(clientContext)); + BigtableClientContext.create( + settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder()); + + stub = new EnhancedBigtableStub(settings.getStubSettings(), bigtableClientContext); } @After From d9b9fda6a4d883ffe3fe3dca8f44aaacf3ea5c24 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Tue, 24 Feb 2026 00:35:47 -0500 Subject: [PATCH 11/33] chore: decompose stubsettings into perOpSettings & BtClientContext (#2799) This avoids any overlap between the 2 classes being passed to a stub and avoids setting inconsistencies Change-Id: I22beae49e1e4118930c7ed9636576a42d397f7ce --- .../bigtable/data/v2/BigtableDataClient.java | 12 -- .../data/v2/BigtableDataClientFactory.java | 46 +++----- .../data/v2/stub/ClientOperationSettings.java | 4 +- .../data/v2/stub/EnhancedBigtableStub.java | 108 +++++++++--------- .../v2/stub/EnhancedBigtableStubSettings.java | 5 + .../metrics/BigtableTracerCallableTest.java | 4 +- .../metrics/BuiltinMetricsTracerTest.java | 4 +- .../v2/stub/metrics/MetricsTracerTest.java | 4 +- 8 files changed, 82 insertions(+), 105 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java index 5af8e9dc96..b659a02175 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java @@ -54,7 +54,6 @@ import com.google.cloud.bigtable.data.v2.models.sql.PreparedStatement; import com.google.cloud.bigtable.data.v2.models.sql.ResultSet; import com.google.cloud.bigtable.data.v2.models.sql.SqlType; -import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import com.google.cloud.bigtable.data.v2.stub.sql.SqlServerStream; import com.google.common.util.concurrent.MoreExecutors; @@ -180,17 +179,6 @@ public static BigtableDataClient create(BigtableDataSettings settings) throws IO return new BigtableDataClient(stub); } - /** - * Constructs an instance of BigtableDataClient with the provided client context. This is used by - * {@link BigtableDataClientFactory} and the client context will not be closed unless {@link - * BigtableDataClientFactory#close()} is called. - */ - static BigtableDataClient createWithClientContext( - BigtableDataSettings settings, BigtableClientContext context) throws IOException { - EnhancedBigtableStub stub = new EnhancedBigtableStub(settings.getStubSettings(), context); - return new BigtableDataClient(stub); - } - @InternalApi("Visible for testing") BigtableDataClient(EnhancedBigtableStub stub) { this.stub = stub; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java index 544d75d6a7..d73fbe2a12 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java @@ -18,6 +18,8 @@ import com.google.api.core.BetaApi; import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext; +import com.google.cloud.bigtable.data.v2.stub.ClientOperationSettings; +import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import java.io.IOException; import javax.annotation.Nonnull; @@ -61,9 +63,8 @@ */ @BetaApi("This feature is currently experimental and can change in the future") public final class BigtableDataClientFactory implements AutoCloseable { - - private final BigtableDataSettings defaultSettings; private final BigtableClientContext sharedClientContext; + private final ClientOperationSettings perOpSettings; /** * Create a instance of this factory. @@ -75,13 +76,14 @@ public static BigtableDataClientFactory create(BigtableDataSettings defaultSetti throws IOException { BigtableClientContext sharedClientContext = BigtableClientContext.create(defaultSettings.getStubSettings()); - return new BigtableDataClientFactory(sharedClientContext, defaultSettings); + ClientOperationSettings perOpSettings = defaultSettings.getStubSettings().getPerOpSettings(); + return new BigtableDataClientFactory(sharedClientContext, perOpSettings); } private BigtableDataClientFactory( - BigtableClientContext sharedClientContext, BigtableDataSettings defaultSettings) { + BigtableClientContext sharedClientContext, ClientOperationSettings perOpSettings) { this.sharedClientContext = sharedClientContext; - this.defaultSettings = defaultSettings; + this.perOpSettings = perOpSettings; } /** @@ -109,7 +111,7 @@ public BigtableDataClient createDefault() { sharedClientContext.createChild( sharedClientContext.getInstanceName(), sharedClientContext.getAppProfileId()); - return BigtableDataClient.createWithClientContext(defaultSettings, ctx); + return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } catch (IOException e) { // Should never happen because the connection has been established already throw new RuntimeException( @@ -127,14 +129,10 @@ public BigtableDataClient createDefault() { * release all resources, first close all of the created clients and then this factory instance. */ public BigtableDataClient createForAppProfile(@Nonnull String appProfileId) throws IOException { - BigtableDataSettings settings = - defaultSettings.toBuilder().setAppProfileId(appProfileId).build(); BigtableClientContext ctx = - sharedClientContext.createChild( - InstanceName.of(settings.getProjectId(), settings.getInstanceId()), - settings.getAppProfileId()); + sharedClientContext.createChild(sharedClientContext.getInstanceName(), appProfileId); - return BigtableDataClient.createWithClientContext(settings, ctx); + return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } /** @@ -148,18 +146,10 @@ public BigtableDataClient createForAppProfile(@Nonnull String appProfileId) thro */ public BigtableDataClient createForInstance(@Nonnull String projectId, @Nonnull String instanceId) throws IOException { - BigtableDataSettings settings = - defaultSettings.toBuilder() - .setProjectId(projectId) - .setInstanceId(instanceId) - .setDefaultAppProfileId() - .build(); BigtableClientContext ctx = - sharedClientContext.createChild( - InstanceName.of(settings.getProjectId(), settings.getInstanceId()), - settings.getAppProfileId()); + sharedClientContext.createChild(InstanceName.of(projectId, instanceId), ""); - return BigtableDataClient.createWithClientContext(settings, ctx); + return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } /** @@ -174,17 +164,9 @@ public BigtableDataClient createForInstance(@Nonnull String projectId, @Nonnull public BigtableDataClient createForInstance( @Nonnull String projectId, @Nonnull String instanceId, @Nonnull String appProfileId) throws IOException { - BigtableDataSettings settings = - defaultSettings.toBuilder() - .setProjectId(projectId) - .setInstanceId(instanceId) - .setAppProfileId(appProfileId) - .build(); BigtableClientContext ctx = - sharedClientContext.createChild( - InstanceName.of(settings.getProjectId(), settings.getInstanceId()), - settings.getAppProfileId()); + sharedClientContext.createChild(InstanceName.of(projectId, instanceId), appProfileId); - return BigtableDataClient.createWithClientContext(settings, ctx); + return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java index 8252b4b22a..540eb08cc8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ClientOperationSettings.java @@ -15,6 +15,7 @@ */ package com.google.cloud.bigtable.data.v2.stub; +import com.google.api.core.InternalApi; import com.google.api.gax.batching.BatchingSettings; import com.google.api.gax.batching.FlowControlSettings; import com.google.api.gax.batching.FlowController; @@ -45,7 +46,8 @@ import java.util.Set; import org.threeten.bp.Duration; -class ClientOperationSettings { +@InternalApi +public class ClientOperationSettings { private static final Set IDEMPOTENT_RETRY_CODES = ImmutableSet.of(StatusCode.Code.DEADLINE_EXCEEDED, StatusCode.Code.UNAVAILABLE); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index bf94964434..6f0ffdc60f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -141,7 +141,7 @@ public class EnhancedBigtableStub implements AutoCloseable { private static final String CLIENT_NAME = "Bigtable"; private static final long FLOW_CONTROL_ADJUSTING_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20); - private final EnhancedBigtableStubSettings settings; + private final ClientOperationSettings perOpSettings; private final BigtableClientContext bigtableClientContext; private final RequestContext requestContext; @@ -175,12 +175,12 @@ public class EnhancedBigtableStub implements AutoCloseable { public static EnhancedBigtableStub create(EnhancedBigtableStubSettings settings) throws IOException { BigtableClientContext bigtableClientContext = BigtableClientContext.create(settings); - return new EnhancedBigtableStub(settings, bigtableClientContext); + return new EnhancedBigtableStub(settings.getPerOpSettings(), bigtableClientContext); } public EnhancedBigtableStub( - EnhancedBigtableStubSettings settings, BigtableClientContext clientContext) { - this.settings = settings; + ClientOperationSettings perOpSettings, BigtableClientContext clientContext) { + this.perOpSettings = perOpSettings; this.bigtableClientContext = clientContext; this.requestContext = RequestContext.create( @@ -188,7 +188,7 @@ public EnhancedBigtableStub( clientContext.getInstanceName().getInstance(), clientContext.getAppProfileId()); this.bulkMutationFlowController = - new FlowController(settings.bulkMutateRowsSettings().getDynamicFlowControlSettings()); + new FlowController(perOpSettings.bulkMutateRowsSettings.getDynamicFlowControlSettings()); this.bulkMutationDynamicFlowControlStats = new DynamicFlowControlStats(); readRowsCallable = createReadRowsCallable(new DefaultRowAdapter()); @@ -230,7 +230,7 @@ public EnhancedBigtableStub( @BetaApi("This surface is stable yet it might be removed in the future.") public ServerStreamingCallable createReadRowsRawCallable( RowAdapter rowAdapter) { - return createReadRowsBaseCallable(settings.readRowsSettings(), rowAdapter) + return createReadRowsBaseCallable(perOpSettings.readRowsSettings, rowAdapter) .withDefaultCallContext(bigtableClientContext.getClientContext().getDefaultCallContext()); } @@ -251,7 +251,7 @@ public ServerStreamingCallable createReadRowsRawCa public ServerStreamingCallable createReadRowsCallable( RowAdapter rowAdapter) { ServerStreamingCallable readRowsCallable = - createReadRowsBaseCallable(settings.readRowsSettings(), rowAdapter); + createReadRowsBaseCallable(perOpSettings.readRowsSettings, rowAdapter); ServerStreamingCallable readRowsUserCallable = new ReadRowsUserCallable<>(readRowsCallable, requestContext); @@ -267,7 +267,7 @@ public ServerStreamingCallable createReadRowsCallable( bigtableClientContext .getClientContext() .getDefaultCallContext() - .withRetrySettings(settings.readRowsSettings().getRetrySettings())); + .withRetrySettings(perOpSettings.readRowsSettings.getRetrySettings())); } /** @@ -290,8 +290,8 @@ public UnaryCallable createReadRowCallable(RowAdapter ServerStreamingCallable readRowsCallable = createReadRowsBaseCallable( ServerStreamingCallSettings.newBuilder() - .setRetryableCodes(settings.readRowSettings().getRetryableCodes()) - .setRetrySettings(settings.readRowSettings().getRetrySettings()) + .setRetryableCodes(perOpSettings.readRowSettings.getRetryableCodes()) + .setRetrySettings(perOpSettings.readRowSettings.getRetrySettings()) .setIdleTimeoutDuration(Duration.ZERO) .setWaitTimeoutDuration(Duration.ZERO) .build(), @@ -307,7 +307,7 @@ public UnaryCallable createReadRowCallable(RowAdapter readRowCallable, clientContext .getDefaultCallContext() - .withRetrySettings(settings.readRowSettings().getRetrySettings()), + .withRetrySettings(perOpSettings.readRowSettings.getRetrySettings()), clientContext.getTracerFactory(), getSpanName("ReadRow"), /* allowNoResponses= */ true); @@ -410,7 +410,7 @@ public ServerStreamingCallable createSkipLargeRowsCall RowAdapter rowAdapter) { ServerStreamingCallSettings readRowsSettings = - (ServerStreamingCallSettings) settings.readRowsSettings(); + (ServerStreamingCallSettings) perOpSettings.readRowsSettings; ServerStreamingCallable base = GrpcRawCallableFactory.createServerStreamingCallable( @@ -501,7 +501,7 @@ public ServerStreamingCallable createSkipLargeRowsCall private UnaryCallable> createBulkReadRowsCallable( RowAdapter rowAdapter) { ServerStreamingCallable readRowsCallable = - createReadRowsBaseCallable(settings.readRowsSettings(), rowAdapter); + createReadRowsBaseCallable(perOpSettings.readRowsSettings, rowAdapter); ServerStreamingCallable readRowsUserCallable = new ReadRowsUserCallable<>(readRowsCallable, requestContext); @@ -521,7 +521,7 @@ private UnaryCallable> createBulkReadRowsCallable( bigtableClientContext .getClientContext() .getDefaultCallContext() - .withRetrySettings(settings.readRowsSettings().getRetrySettings())); + .withRetrySettings(perOpSettings.readRowsSettings.getRetrySettings())); } /** @@ -571,7 +571,7 @@ public ApiFuture> futureCall(String s, ApiCallContext apiCallCon composeRequestParams( r.getAppProfileId(), r.getTableName(), r.getAuthorizedViewName())) .build(), - settings.sampleRowKeysSettings().getRetryableCodes()); + perOpSettings.sampleRowKeysSettings.getRetryableCodes()); UnaryCallable> spoolable = base.all(); @@ -583,7 +583,7 @@ public ApiFuture> futureCall(String s, ApiCallContext apiCallCon withAttemptTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders); UnaryCallable> - retryable = withRetries(withAttemptTracer, settings.sampleRowKeysSettings()); + retryable = withRetries(withAttemptTracer, perOpSettings.sampleRowKeysSettings); return createUserFacingUnaryCallable( methodName, @@ -592,7 +592,7 @@ public ApiFuture> futureCall(String s, ApiCallContext apiCallCon bigtableClientContext .getClientContext() .getDefaultCallContext() - .withRetrySettings(settings.sampleRowKeysSettings().getRetrySettings()))); + .withRetrySettings(perOpSettings.sampleRowKeysSettings.getRetrySettings()))); } /** @@ -609,7 +609,7 @@ private UnaryCallable createMutateRowCallable() { req -> composeRequestParams( req.getAppProfileId(), req.getTableName(), req.getAuthorizedViewName()), - settings.mutateRowSettings(), + perOpSettings.mutateRowSettings, req -> req.toProto(requestContext), resp -> null); } @@ -643,12 +643,12 @@ private UnaryCallable createMutateRowsBas composeRequestParams( r.getAppProfileId(), r.getTableName(), r.getAuthorizedViewName())) .build(), - settings.bulkMutateRowsSettings().getRetryableCodes()); + perOpSettings.bulkMutateRowsSettings.getRetryableCodes()); ServerStreamingCallable callable = new StatsHeadersServerStreamingCallable<>(base); - if (settings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled()) { + if (perOpSettings.bulkMutateRowsSettings.isServerInitiatedFlowControlEnabled()) { callable = new RateLimitingServerStreamingCallable(callable); } @@ -672,7 +672,7 @@ private UnaryCallable createMutateRowsBas new RetryAlgorithm<>( mutateRowsPartialErrorRetryAlgorithm, new ExponentialRetryAlgorithm( - settings.bulkMutateRowsSettings().getRetrySettings(), clientContext.getClock())); + perOpSettings.bulkMutateRowsSettings.getRetrySettings(), clientContext.getClock())); RetryingExecutorWithContext retryingExecutor = new ScheduledRetryingExecutor<>(retryAlgorithm, clientContext.getExecutor()); @@ -681,20 +681,20 @@ private UnaryCallable createMutateRowsBas clientContext.getDefaultCallContext(), withAttemptTracer, retryingExecutor, - settings.bulkMutateRowsSettings().getRetryableCodes(), + perOpSettings.bulkMutateRowsSettings.getRetryableCodes(), retryAlgorithm); UnaryCallable withCookie = new CookiesUnaryCallable<>(baseCallable); UnaryCallable flowControlCallable = null; - if (settings.bulkMutateRowsSettings().isLatencyBasedThrottlingEnabled()) { + if (perOpSettings.bulkMutateRowsSettings.isLatencyBasedThrottlingEnabled()) { flowControlCallable = new DynamicFlowControlCallable( withCookie, bulkMutationFlowController, bulkMutationDynamicFlowControlStats, - settings.bulkMutateRowsSettings().getTargetRpcLatencyMs(), + perOpSettings.bulkMutateRowsSettings.getTargetRpcLatencyMs(), FLOW_CONTROL_ADJUSTING_INTERVAL_MS); } UnaryCallable userFacing = @@ -713,7 +713,7 @@ private UnaryCallable createMutateRowsBas return traced.withDefaultCallContext( clientContext .getDefaultCallContext() - .withRetrySettings(settings.bulkMutateRowsSettings().getRetrySettings())); + .withRetrySettings(perOpSettings.bulkMutateRowsSettings.getRetrySettings())); } /** @@ -738,10 +738,10 @@ private UnaryCallable createMutateRowsBas public Batcher newMutateRowsBatcher( @Nonnull String tableId, @Nullable GrpcCallContext ctx) { return new BatcherImpl<>( - settings.bulkMutateRowsSettings().getBatchingDescriptor(), + perOpSettings.bulkMutateRowsSettings.getBatchingDescriptor(), bulkMutateRowsCallable, BulkMutation.create(tableId), - settings.bulkMutateRowsSettings().getBatchingSettings(), + perOpSettings.bulkMutateRowsSettings.getBatchingSettings(), bigtableClientContext.getClientContext().getExecutor(), bulkMutationFlowController, MoreObjects.firstNonNull( @@ -770,10 +770,10 @@ public Batcher newMutateRowsBatcher( public Batcher newMutateRowsBatcher( TargetId targetId, @Nullable GrpcCallContext ctx) { return new BatcherImpl<>( - settings.bulkMutateRowsSettings().getBatchingDescriptor(), + perOpSettings.bulkMutateRowsSettings.getBatchingDescriptor(), bulkMutateRowsCallable, BulkMutation.create(targetId), - settings.bulkMutateRowsSettings().getBatchingSettings(), + perOpSettings.bulkMutateRowsSettings.getBatchingSettings(), bigtableClientContext.getClientContext().getExecutor(), bulkMutationFlowController, MoreObjects.firstNonNull( @@ -799,10 +799,10 @@ public Batcher newBulkReadRowsBatcher( @Nonnull Query query, @Nullable GrpcCallContext ctx) { Preconditions.checkNotNull(query, "query cannot be null"); return new BatcherImpl<>( - settings.bulkReadRowsSettings().getBatchingDescriptor(), + perOpSettings.bulkReadRowsSettings.getBatchingDescriptor(), bulkReadRowsCallable, query, - settings.bulkReadRowsSettings().getBatchingSettings(), + perOpSettings.bulkReadRowsSettings.getBatchingSettings(), bigtableClientContext.getClientContext().getExecutor(), null, MoreObjects.firstNonNull( @@ -824,7 +824,7 @@ private UnaryCallable createCheckAndMutateRowCa req -> composeRequestParams( req.getAppProfileId(), req.getTableName(), req.getAuthorizedViewName()), - settings.checkAndMutateRowSettings(), + perOpSettings.checkAndMutateRowSettings, req -> req.toProto(requestContext), CheckAndMutateRowResponse::getPredicateMatched); } @@ -847,7 +847,7 @@ private UnaryCallable createReadModifyWriteRowCallable( req -> composeRequestParams( req.getAppProfileId(), req.getTableName(), req.getAuthorizedViewName()), - settings.readModifyWriteRowSettings(), + perOpSettings.readModifyWriteRowSettings, req -> req.toProto(requestContext), resp -> rowAdapter.createRowFromProto(resp.getRow())); } @@ -881,7 +881,7 @@ private UnaryCallable createReadModifyWriteRowCallable( .setParamsExtractor( r -> composeRequestParams(r.getAppProfileId(), r.getTableName(), "")) .build(), - settings.generateInitialChangeStreamPartitionsSettings().getRetryableCodes()); + perOpSettings.generateInitialChangeStreamPartitionsSettings.getRetryableCodes()); ServerStreamingCallable userCallable = new GenerateInitialChangeStreamPartitionsUserCallable(base, requestContext); @@ -900,13 +900,13 @@ private UnaryCallable createReadModifyWriteRowCallable( ServerStreamingCallSettings innerSettings = ServerStreamingCallSettings.newBuilder() .setRetryableCodes( - settings.generateInitialChangeStreamPartitionsSettings().getRetryableCodes()) + perOpSettings.generateInitialChangeStreamPartitionsSettings.getRetryableCodes()) .setRetrySettings( - settings.generateInitialChangeStreamPartitionsSettings().getRetrySettings()) + perOpSettings.generateInitialChangeStreamPartitionsSettings.getRetrySettings()) .setIdleTimeout( - settings.generateInitialChangeStreamPartitionsSettings().getIdleTimeout()) + perOpSettings.generateInitialChangeStreamPartitionsSettings.getIdleTimeout()) .setWaitTimeout( - settings.generateInitialChangeStreamPartitionsSettings().getWaitTimeout()) + perOpSettings.generateInitialChangeStreamPartitionsSettings.getWaitTimeout()) .build(); ServerStreamingCallable watched = @@ -926,7 +926,7 @@ private UnaryCallable createReadModifyWriteRowCallable( clientContext .getDefaultCallContext() .withRetrySettings( - settings.generateInitialChangeStreamPartitionsSettings().getRetrySettings())); + perOpSettings.generateInitialChangeStreamPartitionsSettings.getRetrySettings())); } /** @@ -955,7 +955,7 @@ private UnaryCallable createReadModifyWriteRowCallable( .setParamsExtractor( r -> composeRequestParams(r.getAppProfileId(), r.getTableName(), "")) .build(), - settings.readChangeStreamSettings().getRetryableCodes()); + perOpSettings.readChangeStreamSettings.getRetryableCodes()); ServerStreamingCallable withStatsHeaders = new StatsHeadersServerStreamingCallable<>(base); @@ -975,10 +975,10 @@ private UnaryCallable createReadModifyWriteRowCallable( ServerStreamingCallSettings.newBuilder() .setResumptionStrategy( new ReadChangeStreamResumptionStrategy<>(changeStreamRecordAdapter)) - .setRetryableCodes(settings.readChangeStreamSettings().getRetryableCodes()) - .setRetrySettings(settings.readChangeStreamSettings().getRetrySettings()) - .setIdleTimeout(settings.readChangeStreamSettings().getIdleTimeout()) - .setWaitTimeout(settings.readChangeStreamSettings().getWaitTimeout()) + .setRetryableCodes(perOpSettings.readChangeStreamSettings.getRetryableCodes()) + .setRetrySettings(perOpSettings.readChangeStreamSettings.getRetrySettings()) + .setIdleTimeout(perOpSettings.readChangeStreamSettings.getIdleTimeout()) + .setWaitTimeout(perOpSettings.readChangeStreamSettings.getWaitTimeout()) .build(); ServerStreamingCallable watched = @@ -1002,7 +1002,7 @@ private UnaryCallable createReadModifyWriteRowCallable( return traced.withDefaultCallContext( clientContext .getDefaultCallContext() - .withRetrySettings(settings.readChangeStreamSettings().getRetrySettings())); + .withRetrySettings(perOpSettings.readChangeStreamSettings.getRetrySettings())); } /** @@ -1039,7 +1039,7 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { } }) .build(), - settings.executeQuerySettings().getRetryableCodes()); + perOpSettings.executeQuerySettings.getRetryableCodes()); ServerStreamingCallable withStatsHeaders = new StatsHeadersServerStreamingCallable<>(base); @@ -1060,10 +1060,10 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { ServerStreamingCallSettings retrySettings = ServerStreamingCallSettings.newBuilder() .setResumptionStrategy(new ExecuteQueryResumptionStrategy()) - .setRetryableCodes(settings.executeQuerySettings().getRetryableCodes()) - .setRetrySettings(settings.executeQuerySettings().getRetrySettings()) - .setIdleTimeout(settings.executeQuerySettings().getIdleTimeout()) - .setWaitTimeout(settings.executeQuerySettings().getWaitTimeout()) + .setRetryableCodes(perOpSettings.executeQuerySettings.getRetryableCodes()) + .setRetrySettings(perOpSettings.executeQuerySettings.getRetrySettings()) + .setIdleTimeout(perOpSettings.executeQuerySettings.getIdleTimeout()) + .setWaitTimeout(perOpSettings.executeQuerySettings.getWaitTimeout()) .build(); // Retries need to happen before row merging, because the resumeToken is part @@ -1078,8 +1078,8 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { ServerStreamingCallSettings watchdogSettings = ServerStreamingCallSettings.newBuilder() - .setIdleTimeout(settings.executeQuerySettings().getIdleTimeout()) - .setWaitTimeout(settings.executeQuerySettings().getWaitTimeout()) + .setIdleTimeout(perOpSettings.executeQuerySettings.getIdleTimeout()) + .setWaitTimeout(perOpSettings.executeQuerySettings.getWaitTimeout()) .build(); // Watchdog needs to stay above the metadata error handling so that watchdog errors @@ -1099,14 +1099,14 @@ public Map extract(ExecuteQueryRequest executeQueryRequest) { traced.withDefaultCallContext( clientContext .getDefaultCallContext() - .withRetrySettings(settings.executeQuerySettings().getRetrySettings()))); + .withRetrySettings(perOpSettings.executeQuerySettings.getRetrySettings()))); } private UnaryCallable createPrepareQueryCallable() { return createUnaryCallable( BigtableGrpc.getPrepareQueryMethod(), req -> composeInstanceLevelRequestParams(req.getInstanceName(), req.getAppProfileId()), - settings.prepareQuerySettings(), + perOpSettings.prepareQuerySettings, req -> req.toProto(requestContext), PrepareResponse::fromProto); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 0ce0c7b299..1a416d51e4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -237,6 +237,11 @@ public boolean areInternalMetricsEnabled() { return areInternalMetricsEnabled; } + @InternalApi + public ClientOperationSettings getPerOpSettings() { + return perOpSettings; + } + /** Returns a builder for the default ChannelProvider for this service. */ public static InstantiatingGrpcChannelProvider.Builder defaultGrpcTransportProviderBuilder() { InstantiatingGrpcChannelProvider.Builder grpcTransportProviderBuilder = diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java index 0f84417a70..f9b0e56ac5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java @@ -132,7 +132,7 @@ public void sendHeaders(Metadata headers) { attempts = settings.getStubSettings().readRowsSettings().getRetrySettings().getMaxAttempts(); stub = new EnhancedBigtableStub( - settings.getStubSettings(), + settings.getStubSettings().getPerOpSettings(), BigtableClientContext.create( settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder())); @@ -151,7 +151,7 @@ public void sendHeaders(Metadata headers) { noHeaderStub = new EnhancedBigtableStub( - noHeaderSettings.getStubSettings(), + noHeaderSettings.getStubSettings().getPerOpSettings(), BigtableClientContext.create( noHeaderSettings.getStubSettings(), Tags.getTagger(), diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 3d0e6425d9..1ffccab7dd 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -70,7 +70,6 @@ import com.google.cloud.bigtable.data.v2.models.RowMutation; import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; import com.google.cloud.bigtable.data.v2.models.TableId; -import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import com.google.common.base.Stopwatch; @@ -287,8 +286,7 @@ public void sendHeaders(Metadata headers) { return builder.proxyDetector(delayProxyDetector).intercept(outstandingRpcCounter); }); stubSettingsBuilder.setTransportChannelProvider(channelProvider.build()); - EnhancedBigtableStubSettings stubSettings = stubSettingsBuilder.build(); - stub = new EnhancedBigtableStub(stubSettings, BigtableClientContext.create(stubSettings)); + stub = EnhancedBigtableStub.create(stubSettingsBuilder.build()); } @After diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java index ab2fe6e205..da864bf495 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java @@ -127,7 +127,9 @@ public void setUp() throws Exception { BigtableClientContext.create( settings.getStubSettings(), Tags.getTagger(), localStats.getStatsRecorder()); - stub = new EnhancedBigtableStub(settings.getStubSettings(), bigtableClientContext); + stub = + new EnhancedBigtableStub( + settings.getStubSettings().getPerOpSettings(), bigtableClientContext); } @After From 6b2f585b30add7be2560b075bd1e6498859c0115 Mon Sep 17 00:00:00 2001 From: Lixia Chen Date: Tue, 24 Feb 2026 11:13:08 -0500 Subject: [PATCH 12/33] test: Wrap createTestAuthorizedView with retries (#2789) * test: Wrap createTestAuthorizedView with retries Change-Id: I307ff677ffcd007999402b2e55d99226b2be68bc * test: use exponential backoff Change-Id: Ied51f268f8b68f692d97626e7a84a72236901198 --------- Co-authored-by: Mattie Fu --- .../cloud/bigtable/data/v2/it/ReadIT.java | 4 ++- .../AuthorizedViewTestHelper.java | 33 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java index 95ed16817e..ce45b0fdf1 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java @@ -73,6 +73,7 @@ @RunWith(JUnit4.class) public class ReadIT { + private String prefix; @ClassRule public static TestEnvRule testEnvRule = new TestEnvRule(); @@ -460,7 +461,7 @@ public void rangeQueries() { } @Test - public void rangeQueriesOnAuthorizedView() { + public void rangeQueriesOnAuthorizedView() throws InterruptedException { assume() .withMessage("AuthorizedView is not supported on Emulator") .that(testEnvRule.env()) @@ -761,6 +762,7 @@ public void onSuccess(Row result) { } static class AccumulatingObserver implements ResponseObserver { + final List responses = Lists.newArrayList(); final SettableApiFuture completionFuture = SettableApiFuture.create(); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java index 83c40403f8..c501f80e5d 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java @@ -16,18 +16,26 @@ package com.google.cloud.bigtable.misc_utilities; +import com.google.api.gax.rpc.UnavailableException; import com.google.cloud.bigtable.admin.v2.models.AuthorizedView; import com.google.cloud.bigtable.admin.v2.models.CreateAuthorizedViewRequest; import com.google.cloud.bigtable.admin.v2.models.FamilySubsets; import com.google.cloud.bigtable.admin.v2.models.SubsetView; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; public class AuthorizedViewTestHelper { + public static String AUTHORIZED_VIEW_ROW_PREFIX = "row#"; public static String AUTHORIZED_VIEW_COLUMN_QUALIFIER = "qualifier"; - public static AuthorizedView createTestAuthorizedView(TestEnvRule testEnvRule) { + private static final Logger logger = Logger.getLogger(AuthorizedViewTestHelper.class.getName()); + + public static AuthorizedView createTestAuthorizedView(TestEnvRule testEnvRule) + throws InterruptedException { String tableId = testEnvRule.env().getTableId(); String authorizedViewId = UUID.randomUUID().toString(); CreateAuthorizedViewRequest request = @@ -40,6 +48,27 @@ public static AuthorizedView createTestAuthorizedView(TestEnvRule testEnvRule) { FamilySubsets.create() .addQualifierPrefix(AUTHORIZED_VIEW_COLUMN_QUALIFIER))) .setDeletionProtection(false); - return testEnvRule.env().getTableAdminClient().createAuthorizedView(request); + int retryCount = 0; + int maxRetries = 10; + while (true) { + try { + return testEnvRule.env().getTableAdminClient().createAuthorizedView(request); + } catch (UnavailableException e) { + if (++retryCount == maxRetries) { + throw e; + } + logger.log( + Level.INFO, + "Retrying createAuthorizedView " + + authorizedViewId + + " in table " + + tableId + + ", retryCount: " + + retryCount); + // Exponential backoff delay starting at 100ms. + double expSleep = 100 * Math.pow(2, retryCount); + Thread.sleep((long) Math.min(expSleep, TimeUnit.MINUTES.toMillis(1))); + } + } } } From d201b00a7a7ff6e65e40b1486088657eed58846e Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Tue, 24 Feb 2026 17:37:04 -0500 Subject: [PATCH 13/33] chore: define strongly typed metric wrappers (#2801) * chore: define strongly typed metric wrappers Change-Id: Ia788364f22d718850f2909826e4a356442c9a009 * chore: generate libraries at Tue Feb 24 18:57:54 UTC 2026 * add a couple of missing metrics Change-Id: Iaa1b72947298c9ec1cdbf78c930787fce603d03a * remove jetstream leak Change-Id: I46cf492d6285260f9e40fb2128f9cd5ad1624ca5 * chore: generate libraries at Tue Feb 24 19:56:14 UTC 2026 * copyrights Change-Id: I7da59e27f80ca676cafe34a7deed3c8602763f8c * move uid generation to EnvInfo so that its deferred until export time instead of client initialization Change-Id: I164e224da667085efee83178d5d9c32b17c8c36b * clean up buckets Change-Id: Ifdc19c86e0a98ccd085597104554ec80a0beafcb * add missing debug tags Change-Id: I402a0c3b9de4927a34acf4d09f98d62dd055e962 * typo in desc Change-Id: Ibfccad890697f208d0fc63525deb9987018545bc * fix copy/paste error Change-Id: I22338065e271a17bd8f84fdd185e95e4a0d09ed9 * format Change-Id: Ibcf9583c49018e7a638fa59f2987c38e594a7a8f --------- Co-authored-by: cloud-java-bot --- google-cloud-bigtable/pom.xml | 15 ++ .../data/v2/internal/csm/MetricRegistry.java | 218 ++++++++++++++++++ .../data/v2/internal/csm/Pacemaker.java | 48 ++++ .../internal/csm/attributes/ClientInfo.java | 53 +++++ .../v2/internal/csm/attributes/EnvInfo.java | 189 +++++++++++++++ .../internal/csm/attributes/MethodInfo.java | 43 ++++ .../data/v2/internal/csm/attributes/Util.java | 46 ++++ .../ClientBatchWriteFlowControlFactor.java | 69 ++++++ .../ClientBatchWriteFlowControlTargetQps.java | 61 +++++ .../ClientChannelPoolOutstandingRpcs.java | 79 +++++++ .../csm/metrics/ClientDpCompatGuage.java | 73 ++++++ .../ClientPerConnectionErrorCount.java | 110 +++++++++ .../v2/internal/csm/metrics/Constants.java | 121 ++++++++++ .../v2/internal/csm/metrics/GrpcMetric.java | 65 ++++++ .../internal/csm/metrics/MetricWrapper.java | 103 +++++++++ .../internal/csm/metrics/PacemakerDelay.java | 76 ++++++ .../TableApplicationBlockingLatency.java | 86 +++++++ .../csm/metrics/TableAttemptLatency.java | 88 +++++++ .../csm/metrics/TableAttemptLatency2.java | 97 ++++++++ .../metrics/TableClientBlockingLatency.java | 86 +++++++ .../metrics/TableConnectivityErrorCount.java | 87 +++++++ .../csm/metrics/TableDebugTagCount.java | 79 +++++++ .../metrics/TableFirstResponseLatency.java | 91 ++++++++ .../csm/metrics/TableOperationLatency.java | 90 ++++++++ .../csm/metrics/TableRemainingDeadline.java | 91 ++++++++ .../internal/csm/metrics/TableRetryCount.java | 83 +++++++ .../csm/metrics/TableServerLatency.java | 90 ++++++++ .../v2/internal/csm/metrics/package-info.java | 19 ++ .../v2/internal/csm/schema/ClientSchema.java | 77 +++++++ .../internal/csm/schema/GrpcClientSchema.java | 78 +++++++ .../data/v2/internal/csm/schema/Schema.java | 100 ++++++++ .../v2/internal/csm/schema/TableSchema.java | 63 +++++ .../csm/attributes/ClientInfoTest.java | 34 +++ .../internal/csm/attributes/EnvInfoTest.java | 128 ++++++++++ .../v2/internal/csm/attributes/UtilTest.java | 30 +++ 35 files changed, 2866 insertions(+) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientDpCompatGuage.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/GrpcMetric.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/MetricWrapper.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/PacemakerDelay.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/package-info.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/Schema.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfoTest.java create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 102de75784..76285f5dfa 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -338,11 +338,26 @@ junit test + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter + test + org.mockito mockito-core test + + org.mockito + mockito-junit-jupiter + test + com.google.guava guava-testlib diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java new file mode 100644 index 0000000000..f485e79e4f --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java @@ -0,0 +1,218 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm; + +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientBatchWriteFlowControlFactor; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientBatchWriteFlowControlTargetQps; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientChannelPoolOutstandingRpcs; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientDpCompatGuage; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientPerConnectionErrorCount; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.GrpcMetric; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.MetricWrapper; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.PacemakerDelay; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableApplicationBlockingLatency; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableAttemptLatency; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableAttemptLatency2; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableClientBlockingLatency; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableConnectivityErrorCount; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableDebugTagCount; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableFirstResponseLatency; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableOperationLatency; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableRemainingDeadline; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableRetryCount; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableServerLatency; +import com.google.common.collect.ImmutableList; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Repository for all client metrics. This class has 2 audiences: + * + *

      + *
    • VRpcTracer, which reference each metric directly + *
    • Exporter, which will look up each metric by name and use the {@link MetricWrapper} + * interface to augment the {@code MonitoredResource} and {@code Metric Labels} + *
    + */ +public class MetricRegistry { + static final String METER_NAME = "bigtable.googleapis.com/internal/client/"; + + final TableOperationLatency operationLatencyMetric; + final TableAttemptLatency attemptLatencyMetric; + final TableAttemptLatency2 attemptLatency2Metric; + final TableRetryCount retryCountMetric; + final TableFirstResponseLatency firstResponseLantencyMetric; + final TableServerLatency serverLatencyMetric; + final ClientChannelPoolOutstandingRpcs channelPoolOutstandingRpcsMetric; + final TableConnectivityErrorCount connectivityErrorCountMetric; + final ClientDpCompatGuage dpCompatGuageMetric; + final TableApplicationBlockingLatency applicationBlockingLatencyMetric; + final TableClientBlockingLatency clientBlockingLatencyMetric; + final ClientPerConnectionErrorCount perConnectionErrorCountMetric; + final TableRemainingDeadline remainingDeadlineMetric; + final ClientBatchWriteFlowControlFactor batchWriteFlowControlFactorMetric; + final ClientBatchWriteFlowControlTargetQps batchWriteFlowControlTargetQpsMetric; + + final TableDebugTagCount debugTagCountMetric; + final PacemakerDelay pacemakerDelayMetric; + + private final Map> metrics = new HashMap<>(); + private final List grpcMetricNames = new ArrayList<>(); + + public MetricRegistry() { + operationLatencyMetric = register(new TableOperationLatency()); + attemptLatencyMetric = register(new TableAttemptLatency()); + attemptLatency2Metric = register(new TableAttemptLatency2()); + retryCountMetric = register(new TableRetryCount()); + firstResponseLantencyMetric = register(new TableFirstResponseLatency()); + serverLatencyMetric = register(new TableServerLatency()); + channelPoolOutstandingRpcsMetric = register(new ClientChannelPoolOutstandingRpcs()); + connectivityErrorCountMetric = register(new TableConnectivityErrorCount()); + applicationBlockingLatencyMetric = register(new TableApplicationBlockingLatency()); + clientBlockingLatencyMetric = register(new TableClientBlockingLatency()); + perConnectionErrorCountMetric = register(new ClientPerConnectionErrorCount()); + dpCompatGuageMetric = register(new ClientDpCompatGuage()); + remainingDeadlineMetric = register(new TableRemainingDeadline()); + batchWriteFlowControlFactorMetric = register(new ClientBatchWriteFlowControlFactor()); + batchWriteFlowControlTargetQpsMetric = register(new ClientBatchWriteFlowControlTargetQps()); + + debugTagCountMetric = register(new TableDebugTagCount()); + pacemakerDelayMetric = register(new PacemakerDelay()); + + // From + // https://github.com/grpc/grpc-java/blob/31fdb6c2268b4b1c8ba6c995ee46c58e84a831aa/rls/src/main/java/io/grpc/rls/CachingRlsLbClient.java#L138-L165 + registerGrpcMetric( + "grpc.client.attempt.duration", + ImmutableList.of("grpc.lb.locality", "grpc.status", "grpc.method", "grpc.target")); + registerGrpcMetric( + "grpc.lb.rls.default_target_picks", + ImmutableList.of( + "grpc.target", + "grpc.lb.rls.server_target", + "grpc.lb.rls.data_plane_target", + "grpc.lb.pick_result")); + registerGrpcMetric( + "grpc.lb.rls.target_picks", + ImmutableList.of( + "grpc.target", + "grpc.lb.rls.server_target", + "grpc.lb.rls.data_plane_target", + "grpc.lb.pick_result")); + registerGrpcMetric( + "grpc.lb.rls.failed_picks", ImmutableList.of("grpc.target", "grpc.lb.rls.server_target")); + + // From + // https://github.com/grpc/grpc-java/blob/31fdb6c2268b4b1c8ba6c995ee46c58e84a831aa/xds/src/main/java/io/grpc/xds/XdsClientMetricReporterImpl.java#L67-L94 + // TODO: "grpc.xds_client.connected" + registerGrpcMetric( + "grpc.xds_client.server_failure", ImmutableList.of("grpc.target", "grpc.xds.server")); + // TODO: "grpc.xds_client.resource_updates_valid", + registerGrpcMetric( + "grpc.xds_client.resource_updates_invalid", + ImmutableList.of("grpc.target", "grpc.xds.server", "grpc.xds.resource_type")); + // TODO: "grpc.xds_client.resources" + + // From + // https://github.com/grpc/proposal/blob/86990145a7cef9e5473a132709b2556fec00c4c6/A94-subchannel-otel-metrics.md + registerGrpcMetric( + "grpc.subchannel.disconnections", + ImmutableList.of( + "grpc.target", "grpc.lb.backend_service", "grpc.lb.locality", "grpc.disconnect_error")); + + registerGrpcMetric( + "grpc.subchannel.connection_attempts_succeeded", + ImmutableList.of("grpc.target", "grpc.lb.backend_service", "grpc.lb.locality")); + + registerGrpcMetric( + "grpc.subchannel.connection_attempts_failed", + ImmutableList.of("grpc.target", "grpc.lb.backend_service", "grpc.lb.locality")); + + registerGrpcMetric( + "grpc.subchannel.open_connections", + ImmutableList.of( + "grpc.target", "grpc.security_level", "grpc.lb.backend_service", "grpc.lb.locality")); + } + + private void registerGrpcMetric(String name, List labels) { + grpcMetricNames.add(name); + register(new GrpcMetric(name, labels)); + } + + private > T register(T instrument) { + metrics.put(instrument.getName(), instrument); + return instrument; + } + + List getGrpcMetricNames() { + return ImmutableList.copyOf(grpcMetricNames); + } + + MetricWrapper getMetric(String name) { + return metrics.get(name); + } + + public RecorderRegistry newRecorderRegistry(MeterProvider meterProvider) { + return new RecorderRegistry(meterProvider.get(METER_NAME)); + } + + public class RecorderRegistry { + public final TableOperationLatency.Recorder operationLatency; + public final TableAttemptLatency.Recorder attemptLatency; + public final TableAttemptLatency2.Recorder attemptLatency2; + public final TableRetryCount.Recorder retryCount; + public final TableFirstResponseLatency.Recorder firstResponseLantency; + public final TableServerLatency.Recorder serverLatency; + public final ClientChannelPoolOutstandingRpcs.Recorder channelPoolOutstandingRpcs; + public final TableConnectivityErrorCount.Recorder connectivityErrorCount; + public final ClientDpCompatGuage.Recorder dpCompatGuage; + public final TableApplicationBlockingLatency.Recorder applicationBlockingLatency; + public final TableClientBlockingLatency.Recorder clientBlockingLatency; + public final ClientPerConnectionErrorCount.Recorder perConnectionErrorCount; + public final TableRemainingDeadline.Recorder remainingDeadline; + public final ClientBatchWriteFlowControlTargetQps.Recorder batchWriteFlowControlTargetQps; + public final ClientBatchWriteFlowControlFactor.Recorder batchWriteFlowControlFactor; + + public final TableDebugTagCount.Recorder debugTagCount; + + final PacemakerDelay.Recorder pacemakerDelay; + + private RecorderRegistry(Meter meter) { + operationLatency = operationLatencyMetric.newRecorder(meter); + attemptLatency = attemptLatencyMetric.newRecorder(meter); + attemptLatency2 = attemptLatency2Metric.newRecorder(meter); + retryCount = retryCountMetric.newRecorder(meter); + firstResponseLantency = firstResponseLantencyMetric.newRecorder(meter); + serverLatency = serverLatencyMetric.newRecorder(meter); + channelPoolOutstandingRpcs = channelPoolOutstandingRpcsMetric.newRecorder(meter); + connectivityErrorCount = connectivityErrorCountMetric.newRecorder(meter); + dpCompatGuage = dpCompatGuageMetric.newRecorder(meter); + applicationBlockingLatency = applicationBlockingLatencyMetric.newRecorder(meter); + clientBlockingLatency = clientBlockingLatencyMetric.newRecorder(meter); + perConnectionErrorCount = perConnectionErrorCountMetric.newRecorder(meter); + remainingDeadline = remainingDeadlineMetric.newRecorder(meter); + batchWriteFlowControlTargetQps = batchWriteFlowControlTargetQpsMetric.newRecorder(meter); + batchWriteFlowControlFactor = batchWriteFlowControlFactorMetric.newRecorder(meter); + + debugTagCount = debugTagCountMetric.newRecorder(meter); + pacemakerDelay = pacemakerDelayMetric.newRecorder(meter); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java new file mode 100644 index 0000000000..cb2a0c9f19 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java @@ -0,0 +1,48 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm; + +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import java.time.Duration; +import java.time.Instant; + +class Pacemaker implements Runnable { + + static final Duration PACEMAKER_INTERVAL = Duration.ofMillis(100); + + private final RecorderRegistry registry; + private final ClientInfo clientInfo; + private final String executorName; + + private Instant prev; + + Pacemaker(RecorderRegistry registry, ClientInfo clientInfo, String name) { + this.prev = Instant.now(); + this.registry = registry; + this.clientInfo = clientInfo; + this.executorName = name; + } + + @Override + public void run() { + Instant current = Instant.now(); + Duration delta = Duration.between(prev, current).minus(PACEMAKER_INTERVAL); + prev = current; + registry.pacemakerDelay.record( + clientInfo, executorName, delta.isNegative() ? Duration.ZERO : delta); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java new file mode 100644 index 0000000000..0d4717dfe9 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java @@ -0,0 +1,53 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import com.google.auto.value.AutoValue; +import com.google.bigtable.v2.InstanceName; +import com.google.cloud.bigtable.Version; + +/** + * A value class to capture parameters that the client was instantiated with. These parameters will + * be used by the Exporter to derive MonitoredResource for GrpcMetrics. + */ +@AutoValue +public abstract class ClientInfo { + /** The name and version of the client. */ + public abstract String getClientName(); + + /** A unique identifier to disambiguate TimeSeries from multiple processes on the same VM. */ + public abstract InstanceName getInstanceName(); + + public abstract String getAppProfileId(); + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_ClientInfo.Builder().setClientName("java-bigtable/" + Version.VERSION); + } + + @AutoValue.Builder + public abstract static class Builder { + protected abstract Builder setClientName(String name); + + public abstract Builder setInstanceName(InstanceName name); + + public abstract Builder setAppProfileId(String appProfileId); + + public abstract ClientInfo build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java new file mode 100644 index 0000000000..cfc9182881 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java @@ -0,0 +1,189 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import com.google.auto.value.AutoValue; +import com.google.cloud.opentelemetry.detection.AttributeKeys; +import com.google.cloud.opentelemetry.detection.DetectedPlatform; +import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +/** + * Environment attributes, lazily extracted by the Exporter. + * + *

    The information will be extracted from the GCE metadata service and environment. + */ +@AutoValue +public abstract class EnvInfo { + private static final Logger logger = Logger.getLogger(EnvInfo.class.getName()); + + private static final Map SUPPORTED_PLATFORM_MAP = + ImmutableMap.of( + GCPPlatformDetector.SupportedPlatform.GOOGLE_COMPUTE_ENGINE, "gcp_compute_engine", + GCPPlatformDetector.SupportedPlatform.GOOGLE_KUBERNETES_ENGINE, "gcp_kubernetes_engine"); + + private static final AtomicLong uidSuffix = new AtomicLong(0); + + public abstract String getUid(); + + /** The Google platform running this client. ie. gcp_compute_engine */ + public abstract String getPlatform(); + + /** The Google project that the VM belongs to. */ + public abstract String getProject(); + + /** The geographic region that the VM is located in. */ + public abstract String getRegion(); + + /** The numeric GCE vm instance id. */ + public abstract String getHostId(); + + /** The hostname of the vm or container running the client. For gke, this will be the pod name. */ + public abstract String getHostName(); + + public static Builder builder() { + return new AutoValue_EnvInfo.Builder().setUid(computeUid() + "-" + uidSuffix.getAndIncrement()); + } + + @AutoValue.Builder + public abstract static class Builder { + protected abstract Builder setUid(String uid); + + public abstract Builder setPlatform(String platform); + + public abstract Builder setProject(String project); + + public abstract Builder setRegion(String region); + + public abstract Builder setHostId(String hostId); + + public abstract Builder setHostName(String hostName); + + public abstract EnvInfo build(); + } + + private static String computeUid() { + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + // If jvm doesn't have the expected format, fallback to the local hostname + if (jvmName.indexOf('@') < 1) { + String hostname = "localhost"; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + logger.log(Level.INFO, "Unable to get the hostname.", e); + } + // Generate a random number and use the same format "random_number@hostname". + return "java-" + UUID.randomUUID() + "@" + hostname; + } + return "java-" + UUID.randomUUID() + jvmName; + } + + public static EnvInfo detect() { + return detect( + GCPPlatformDetector.DEFAULT_INSTANCE.detectPlatform(), + System::getenv, + () -> { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + }); + } + + @Nullable + static EnvInfo detect( + DetectedPlatform detectedPlatform, + Function envGetter, + Supplier hostnameSupplier) { + @Nullable + String cloud_platform = SUPPORTED_PLATFORM_MAP.get(detectedPlatform.getSupportedPlatform()); + if (cloud_platform == null) { + return EnvInfo.builder() + .setPlatform("unknown") + .setHostName(detectHostname(envGetter, hostnameSupplier)) + .setRegion("global") + .setProject("") + .setHostId("") + .build(); + } + + Map attrs = detectedPlatform.getAttributes(); + ImmutableList locationKeys = + ImmutableList.of( + AttributeKeys.GCE_CLOUD_REGION, + AttributeKeys.GCE_AVAILABILITY_ZONE, + AttributeKeys.GKE_LOCATION_TYPE_REGION, + AttributeKeys.GKE_CLUSTER_LOCATION); + + String region = + locationKeys.stream().map(attrs::get).filter(Objects::nonNull).findFirst().orElse("global"); + + // Deal with possibility of a zone. Zones are of the form us-east1-c, but we want a region + // which, which is us-east1. + region = Splitter.on('-').splitToStream(region).limit(2).collect(Collectors.joining("-")); + + String hostname = attrs.get(AttributeKeys.GCE_INSTANCE_NAME); + // TODO: add support for cloud run & gae by looking at SERVERLESS_COMPUTE_NAME & GAE_MODULE_NAME + if (hostname == null) { + hostname = detectHostname(envGetter, hostnameSupplier); + } + + String hostId = Optional.ofNullable(attrs.get(AttributeKeys.GCE_INSTANCE_ID)).orElse(""); + + return builder() + .setPlatform(cloud_platform) + .setProject(detectedPlatform.getProjectId()) + .setRegion(region) + .setHostId(hostId) + .setHostName(hostname) + .build(); + } + + private static String detectHostname( + Function envGetter, Supplier hostnameSupplier) { + String hostname = envGetter.apply("HOSTNAME"); + + if (hostname == null) { + try { + hostname = hostnameSupplier.get(); + } catch (RuntimeException e) { + logger.log(Level.WARNING, "failed to detect hostname", e); + } + } + if (hostname == null) { + hostname = ""; + } + return hostname; + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java new file mode 100644 index 0000000000..122e5fe5ba --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java @@ -0,0 +1,43 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import com.google.auto.value.AutoValue; + +/** Method specific attributes. */ +@AutoValue +public abstract class MethodInfo { + + /** The name of the method. ie "Bigtable.ReadRow" */ + public abstract String getName(); + + /** If the method is streaming (ie a scan). */ + public abstract boolean getStreaming(); + + public static Builder builder() { + return new AutoValue_MethodInfo.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setName(String name); + + public abstract Builder setStreaming(boolean isStreaming); + + public abstract MethodInfo build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java new file mode 100644 index 0000000000..cf9c2a114e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java @@ -0,0 +1,46 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import com.google.bigtable.v2.PeerInfo.TransportType; +import com.google.common.base.Preconditions; +import java.util.Locale; + +public class Util { + static final String TRANSPORT_TYPE_PREFIX = "TRANSPORT_TYPE_"; + + public static String transportTypeToString(TransportType transportType) { + + Preconditions.checkArgument( + transportType.name().startsWith(TRANSPORT_TYPE_PREFIX) + || transportType == TransportType.UNRECOGNIZED, + "TransportType values must start with %s", + TRANSPORT_TYPE_PREFIX); + + if (transportType == TransportType.TRANSPORT_TYPE_UNKNOWN) { + return "session_none"; + } + if (transportType == TransportType.UNRECOGNIZED) { + return "session_unrecognized"; + } + + return transportType + .name() + .substring(TRANSPORT_TYPE_PREFIX.length()) + .toLowerCase(Locale.ENGLISH); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java new file mode 100644 index 0000000000..2c5a989d51 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.Meter; + +public class ClientBatchWriteFlowControlFactor extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/batch_write_flow_control_factor"; + + public ClientBatchWriteFlowControlFactor() { + super(ClientSchema.INSTANCE, NAME); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleGauge instrument; + + private Recorder(Meter meter) { + this.instrument = + meter + .gaugeBuilder(NAME) + .setDescription( + "The distribution of batch write flow control factors received from the server.") + .setUnit("1") + .build(); + } + + public void record( + ClientInfo clientInfo, + Status.Code code, + boolean applied, + MethodInfo methodInfo, + double factor) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.APPLIED_KEY, applied) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .build(); + + instrument.set(factor, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java new file mode 100644 index 0000000000..fb6f55894f --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java @@ -0,0 +1,61 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.Meter; + +public class ClientBatchWriteFlowControlTargetQps extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/batch_write_flow_control_target_qps"; + + public ClientBatchWriteFlowControlTargetQps() { + super(ClientSchema.INSTANCE, NAME); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleGauge instrument; + + private Recorder(Meter meter) { + this.instrument = + meter + .gaugeBuilder(NAME) + .setDescription( + "The current target QPS of the client under batch write flow control.") + .setUnit("1") + .build(); + } + + public void record(ClientInfo clientInfo, MethodInfo methodInfo, double qps) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .build(); + + instrument.set(qps, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java new file mode 100644 index 0000000000..addd28a533 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.PeerInfo.TransportType; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolSettings.LoadBalancingStrategy; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.util.List; +import java.util.stream.Collectors; + +public class ClientChannelPoolOutstandingRpcs extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/connection_pool/outstanding_rpcs"; + + private static final List BUCKETS = + Buckets.generateLinearSeq(0d, 200d, 5).stream() + .map(Double::longValue) + .collect(Collectors.toList()); + + public ClientChannelPoolOutstandingRpcs() { + super(ClientSchema.INSTANCE, NAME); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongHistogram instrument; + + private Recorder(Meter meter) { + this.instrument = + meter + .histogramBuilder(NAME) + .ofLongs() + .setExplicitBucketBoundariesAdvice(BUCKETS) + .setDescription( + "A distribution of the number of outstanding RPCs per connection in the client" + + " pool, sampled periodically.") + .setUnit("1") + .build(); + } + + public void record( + ClientInfo clientInfo, + TransportType transportType, + LoadBalancingStrategy lbPolicy, + boolean isStreaming, + long rpcCount) { + instrument.record( + rpcCount, + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.TRANSPORT_TYPE, Util.transportTypeToString(transportType)) + .put(MetricLabels.CHANNEL_POOL_LB_POLICY, lbPolicy.name()) + .put(MetricLabels.STREAMING_KEY, isStreaming) + .build()); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientDpCompatGuage.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientDpCompatGuage.java new file mode 100644 index 0000000000..9746e67448 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientDpCompatGuage.java @@ -0,0 +1,73 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import io.opentelemetry.api.metrics.LongGauge; +import io.opentelemetry.api.metrics.Meter; + +public class ClientDpCompatGuage extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/direct_access/compatible"; + + public ClientDpCompatGuage() { + super(ClientSchema.INSTANCE, NAME); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongGauge instrument; + + private Recorder(Meter meter) { + this.instrument = + meter + .gaugeBuilder(NAME) + .ofLongs() + .setDescription( + "Reports 1 if the environment is eligible for DirectPath, 0 otherwise. Based on" + + " an attempt at startup.") + .setUnit("1") + .build(); + } + + // TODO: replace ipPreference with an enum + public void recordSuccess(ClientInfo clientInfo, String ipPreference) { + instrument.set( + 1, + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.DP_REASON_KEY, "") + .put(MetricLabels.DP_IP_PREFERENCE_KEY, ipPreference) + .build()); + } + + // TODO: replace reason with an enum + public void recordFailure(ClientInfo clientInfo, String reason) { + instrument.set( + 1, + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.DP_REASON_KEY, reason) + .put(MetricLabels.DP_IP_PREFERENCE_KEY, "") + .build()); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java new file mode 100644 index 0000000000..25ede477fb --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java @@ -0,0 +1,110 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.util.List; +import java.util.Set; + +public class ClientPerConnectionErrorCount extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/per_connection_error_count"; + + static final List BUCKETS = + ImmutableList.builder() + .add(0L) + .addAll(Buckets.generateGeometricSeq(1, 64)) + .addAll(Buckets.generateGeometricSeq(125, 1_000_000L)) + .build(); + // This metric migrated from gce/gke schemas to bigtable_client + // So a lot of the metric labels overlap with the resource labels. + // we need special handling since the logic in MetricWrapper assumes that there is no + // overlap. + private static final Set> METRIC_LABELS = + ImmutableSet.of( + MetricLabels.BIGTABLE_PROJECT_ID_KEY, + MetricLabels.CLIENT_UID, + MetricLabels.INSTANCE_ID_KEY, + MetricLabels.CLIENT_NAME, + MetricLabels.APP_PROFILE_KEY); + + public ClientPerConnectionErrorCount() { + super(ClientSchema.INSTANCE, NAME); + } + + // Override the default metric labels to account for backwards compatibility. + // This metric used to live under bigtable_table, and has moved to bigtable_client + // The new schema duplicates some of the metric labels. However the default implementation + // in MetricWrapper will remove all resource labels from the metric labels. + // To maintain backwards compatibility, this metric override the extractMetricLabels + // to always emit the duplicate metric labels. + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + metricAttrs.forEach( + (k, v) -> { + if (METRIC_LABELS.contains(k) && v != null) { + builder.put(k.getKey(), v.toString()); + } + }); + builder.put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()); + return builder.build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .ofLongs() + .setDescription("Distribution of counts of channels per 'error count per minute'.") + .setUnit("1") + .setExplicitBucketBoundariesAdvice(BUCKETS) + .build(); + } + + public void record(ClientInfo clientInfo, long value) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject()) + .put(MetricLabels.INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .build(); + instrument.record(value, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java new file mode 100644 index 0000000000..768f451e0e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java @@ -0,0 +1,121 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.common.collect.ImmutableList; +import io.opentelemetry.api.common.AttributeKey; +import java.util.ArrayList; +import java.util.List; + +public final class Constants { + private Constants() {} + + public static final class MetricLabels { + private MetricLabels() {} + + // TODO: remove overlapping attributes + // Project & Instance overlap with resource labels because they were migrated from + // an old gce/gke schema to support per_connection_error_count metric + @Deprecated + public static final AttributeKey BIGTABLE_PROJECT_ID_KEY = + AttributeKey.stringKey("project_id"); + + @Deprecated + public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + + public static final AttributeKey TRANSPORT_TYPE = + AttributeKey.stringKey("transport_type"); + public static final AttributeKey TRANSPORT_REGION = + AttributeKey.stringKey("transport_region"); + public static final AttributeKey TRANSPORT_ZONE = + AttributeKey.stringKey("transport_zone"); + public static final AttributeKey TRANSPORT_SUBZONE = + AttributeKey.stringKey("transport_subzone"); + + public static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid"); + public static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); + public static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method"); + public static final AttributeKey STREAMING_KEY = AttributeKey.booleanKey("streaming"); + public static final AttributeKey APP_PROFILE_KEY = + AttributeKey.stringKey("app_profile"); + public static final AttributeKey DEBUG_TAG_KEY = AttributeKey.stringKey("tag"); + + static final AttributeKey APPLIED_KEY = AttributeKey.booleanKey("applied"); + + static final AttributeKey CHANNEL_POOL_LB_POLICY = AttributeKey.stringKey("lb_policy"); + static final AttributeKey DP_REASON_KEY = AttributeKey.stringKey("reason"); + static final AttributeKey DP_IP_PREFERENCE_KEY = AttributeKey.stringKey("reason"); + + public static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); + + static final AttributeKey EXECUTOR_KEY = AttributeKey.stringKey("executor"); + } + + static final class Units { + private Units() {} + + static final String MILLISECOND = "ms"; + static final String MICROSECOND = "us"; + static final String COUNT = "1"; + } + + static final class Buckets { + static final List AGGREGATION_WITH_MILLIS_HISTOGRAM = + ImmutableList.builder() + // Match `bigtable.googleapis.com/frontend_server/handler_latencies` buckets + .addAll(generateLinearSeq(0, 3.0, 0.1)) + .add(4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0) + .add(100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 900.0) + .add(1000.0, 2000.0, 3000.0, 4000.0, 5000.0, 6000.0, 10000.0, 20000.0, 50000.0) + .add(100000.0, 200000.0, 500000.0, 1000000.0, 2000000.0, 5000000.0) + .build(); + + @SuppressWarnings("SameParameterValue") + static List generateLinearSeq(double start, double end, double increment) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; true; i++) { + double next = start + (increment * i); + if (next > end) { + break; + } + builder.add(next); + } + + return builder.build(); + } + + @SuppressWarnings("SameParameterValue") + static List generateExponentialSeq(double start, int count, double factor) { + List buckets = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + buckets.add(start); + start *= factor; + } + + return buckets; + } + + static List generateGeometricSeq(long startClose, long endClosed) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (long i = startClose; i <= endClosed; i *= 2) { + builder.add(i); + } + return builder.build(); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/GrpcMetric.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/GrpcMetric.java new file mode 100644 index 0000000000..e4ddc12165 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/GrpcMetric.java @@ -0,0 +1,65 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.GrpcClientSchema; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import java.util.List; + +/** + * Base class for gRpc metrics that are exported using bigtable_client schema. + * + *

    gRPC doesn't record the bigtable specific metric labels, so they must be passed to the + * exporter via a side channel. + */ +public class GrpcMetric extends MetricWrapper { + public static final String METER_SCOPE = "grpc-java"; + + private final List> metricKeys; + + public GrpcMetric(String name, List metricKeys) { + super(GrpcClientSchema.INSTANCE, name); + this.metricKeys = + metricKeys.stream().map(AttributeKey::stringKey).collect(ImmutableList.toImmutableList()); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo ignored1, ClientInfo ignored2) { + ImmutableMap.Builder attributes = ImmutableMap.builder(); + + for (AttributeKey key : metricKeys) { + String newKeyName = key.getKey().replace('.', '_'); + Object value = metricAttrs.get(key); + if (value != null) { + attributes.put(newKeyName, value.toString()); + } + } + + return attributes.build(); + } + + @Override + public String getExternalName() { + return "bigtable.googleapis.com/internal/client/" + getName().replace('.', '/'); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/MetricWrapper.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/MetricWrapper.java new file mode 100644 index 0000000000..a6c882d820 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/MetricWrapper.java @@ -0,0 +1,103 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.api.MonitoredResource; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.Schema; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.Attributes; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * Base class for all the metrics. + * + *

    Each metric is composed of an OpenTelemetry instrument (ie histogram), and a set of resource + * and metric attributes. Since some of the resource attributes are dynamic, all the resource + * attributes are sent to the instrument as metric {@link Attributes}. Then during the export phase, + * a {@link MonitoredResource} and a set of metric labels are extracted from the collected + * attributes. + * + *

    This base class implements the foundation of this lifecycle: + * + *

      + *
    • The instrument for recording is passed in during construction + *
    • The concrete subclass will define a metric specific typesafe record method to populate the + * metric labels for the instrument + *
    • The list of resource attribute keys are defined by a resource specific subclass and passed + * in during construction. These will be used by {@code MetricWrapper.createMonitoredResource} + * to create the monitored resource during the export phase + *
    • The remaining attributes will be added as metric labels + *
    + */ +public abstract class MetricWrapper { + private final SchemaT schema; + private final String name; + + public MetricWrapper(SchemaT schema, String name) { + this.schema = schema; + this.name = name; + } + + public SchemaT getSchema() { + return schema; + } + + /** + * Used by the Exporter to compose metric labels to be sent to Cloud Monitoring. + * + *

    Extracts metric labels from metric {@link Attributes}. By default, all keys that are not + * listed in {@code resourceKeys} are extracted. However, subclasses can override this method to + * inject data from {@link EnvInfo} and {@link ClientInfo}. + */ + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + metricAttrs.forEach( + (k, v) -> { + if (!getSchema().getResourceKeys().contains(k) && v != null) { + builder.put(k.getKey(), v.toString()); + } + }); + return builder.build(); + } + + /** + * Used by the Exporter to match an instance of this class to the aggregated timeseries to export. + * + *

    Gets the name of the metric. This is used by the exporter to look up this metric definition + * in MetricRegistry during export. + */ + public String getName() { + return name; + } + + /** + * Used by the exporter to post process the metric name from grpc conventions to Cloud Monitoring. + */ + public String getExternalName() { + return getName(); + } + + /** Converts a duration in fractional milliseconds. */ + protected static double toMillis(Duration duration) { + return Math.round(((double) duration.toNanos()) / TimeUnit.MILLISECONDS.toNanos(1) * 100.0) + / 100.0; + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/PacemakerDelay.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/PacemakerDelay.java new file mode 100644 index 0000000000..ec081f2afd --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/PacemakerDelay.java @@ -0,0 +1,76 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema; +import com.google.common.collect.ImmutableList; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; +import java.util.List; + +/** + * Pacemaker delay records the delta between the pacemaker scheduled time and the actual time. When + * the delay is high, it could indicate issues with the machine that the client is running on like + * CPU saturation. + */ +public class PacemakerDelay extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/pacemaker_delays"; + + private static final List BUCKETS = + ImmutableList.builder() + // Up to 67,108,864, ~1 minute in microseconds + .addAll(Buckets.generateExponentialSeq(1.0, 13, 4)) + .build(); + + public PacemakerDelay() { + super(ClientSchema.INSTANCE, NAME); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "Distribution of the delay between the pacemaker firing and the pacemaker task" + + " being scheduled.") + .setUnit(Units.MICROSECOND) + .setExplicitBucketBoundariesAdvice(BUCKETS) + .build(); + } + + public void record(ClientInfo clientInfo, String executorName, Duration delta) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo) + .put(MetricLabels.EXECUTOR_KEY, executorName) + .build(); + instrument.record(delta.toNanos() / 1000.0, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java new file mode 100644 index 0000000000..90e390304e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableApplicationBlockingLatency extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/application_latencies"; + + public TableApplicationBlockingLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + this.instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "The latency of the client application consuming available response data.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java new file mode 100644 index 0000000000..2ba86e89c9 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java @@ -0,0 +1,88 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableAttemptLatency extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies"; + + public TableAttemptLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription("Client observed latency per RPC attempt.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + ResponseParams clusterInfo, + MethodInfo methodInfo, + Status.Code code, + Duration latency) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STREAMING_KEY, methodInfo.getStreaming()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(latency), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java new file mode 100644 index 0000000000..0570559610 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java @@ -0,0 +1,97 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.PeerInfo; +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableAttemptLatency2 extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies2"; + + public TableAttemptLatency2() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription("Client observed latency per RPC attempt.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + PeerInfo peerInfo, + ResponseParams clusterInfo, + MethodInfo methodInfo, + Status.Code code, + Duration latency) { + + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put( + MetricLabels.TRANSPORT_TYPE, + Util.transportTypeToString(peerInfo.getTransportType())) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.TRANSPORT_REGION, "") + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .put(MetricLabels.TRANSPORT_ZONE, peerInfo.getApplicationFrontendZone()) + .put(MetricLabels.TRANSPORT_SUBZONE, peerInfo.getApplicationFrontendSubzone()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.STREAMING_KEY, methodInfo.getStreaming()) + .build(); + + instrument.record(toMillis(latency), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java new file mode 100644 index 0000000000..1d8deca639 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableClientBlockingLatency extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/throttling_latencies"; + + public TableClientBlockingLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "The latency introduced by the client queuing the RPC due to an unavailable" + + " transport or overload.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java new file mode 100644 index 0000000000..95d8fca949 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; + +public class TableConnectivityErrorCount extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/connectivity_error_count"; + + public TableConnectivityErrorCount() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongCounter instrument; + + private Recorder(Meter meter) { + instrument = + meter + .counterBuilder(NAME) + .setDescription( + "Number of requests that failed to reach the Google datacenter. (Requests without" + + " google response headers)") + .setUnit(Units.COUNT) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + long count) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.add(count, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java new file mode 100644 index 0000000000..f8bfc25fb5 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; + +public class TableDebugTagCount extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/debug_tags"; + + public TableDebugTagCount() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongCounter instrument; + + private Recorder(Meter meter) { + instrument = + meter + .counterBuilder(NAME) + .setDescription("A counter of internal client events used for debugging.") + .setUnit(Units.COUNT) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + String tag, + ResponseParams clusterInfo, + long amount) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + .put(MetricLabels.DEBUG_TAG_KEY, tag) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .build(); + instrument.add(amount, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java new file mode 100644 index 0000000000..af5909c054 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java @@ -0,0 +1,91 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableFirstResponseLatency extends MetricWrapper { + private static final String NAME = + "bigtable.googleapis.com/internal/client/first_response_latencies"; + + public TableFirstResponseLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "Latency from operation start until the response headers were received. The" + + " publishing of the measurement will be delayed until the attempt response" + + " has been received.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java new file mode 100644 index 0000000000..b6323cce8b --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableOperationLatency extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/operation_latencies"; + + public TableOperationLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "Total time until final operation success or failure, including retries and" + + " backoff.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STREAMING_KEY, methodInfo.getStreaming()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java new file mode 100644 index 0000000000..3e911d42e6 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java @@ -0,0 +1,91 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableRemainingDeadline extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/remaining_deadline"; + + public TableRemainingDeadline() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "The remaining deadline when the request is sent to grpc. This will either be the" + + " operation timeout, or the remaining deadline from operation timeout after" + + " retries and back offs.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.STATUS_KEY, code.name()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.STREAMING_KEY, methodInfo.getStreaming()) + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java new file mode 100644 index 0000000000..de7b608b4e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java @@ -0,0 +1,83 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; + +public class TableRetryCount extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/retry_count"; + + public TableRetryCount() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final LongCounter instrument; + + private Recorder(Meter meter) { + instrument = + meter + .counterBuilder(NAME) + .setDescription("The number of additional RPCs sent after the initial attempt.") + .setUnit(Units.COUNT) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + long amount) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + instrument.add(amount, attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java new file mode 100644 index 0000000000..b759591113 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Buckets; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units; +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema; +import com.google.common.collect.ImmutableMap; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.time.Duration; + +public class TableServerLatency extends MetricWrapper { + private static final String NAME = "bigtable.googleapis.com/internal/client/server_latencies"; + + public TableServerLatency() { + super(TableSchema.INSTANCE, NAME); + } + + @Override + public ImmutableMap extractMetricLabels( + Attributes metricAttrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ImmutableMap.builder() + .putAll(super.extractMetricLabels(metricAttrs, envInfo, clientInfo)) + .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid()) + .build(); + } + + public Recorder newRecorder(Meter meter) { + return new Recorder(meter); + } + + public class Recorder { + private final DoubleHistogram instrument; + + private Recorder(Meter meter) { + instrument = + meter + .histogramBuilder(NAME) + .setDescription( + "The latency measured from the moment that the RPC entered the Google data center" + + " until the RPC was completed.") + .setUnit(Units.MILLISECOND) + .setExplicitBucketBoundariesAdvice(Buckets.AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + } + + public void record( + ClientInfo clientInfo, + String tableId, + MethodInfo methodInfo, + ResponseParams clusterInfo, + Status.Code code, + Duration duration) { + Attributes attributes = + getSchema() + .createResourceAttrs(clientInfo, tableId, clusterInfo) + .put(MetricLabels.METHOD_KEY, methodInfo.getName()) + .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(MetricLabels.STREAMING_KEY, methodInfo.getStreaming()) + .put(MetricLabels.STATUS_KEY, code.name()) + .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) + // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter + .build(); + + instrument.record(toMillis(duration), attributes); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/package-info.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/package-info.java new file mode 100644 index 0000000000..e6e4fb388c --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.metrics; + +// Implements the metrics from bigtable_googleapis_com/metrics/aliased_metrics.gcl & +// cloud_pulse_monarch/bigtable/metrics diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java new file mode 100644 index 0000000000..11cf90c445 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java @@ -0,0 +1,77 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.schema; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.common.collect.ImmutableList; +import com.google.monitoring.v3.ProjectName; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +/** + * The attributes for this schema are partially populated during the record phase and finalized + * during the export phase with {@link EnvInfo}. This is necessary because resolving {@link EnvInfo} + * is slow and should not happen during client startup. + */ +public final class ClientSchema extends Schema { + // This implements the `bigtable_client` resource defined in + // bigtable_googleapis_com/metrics/resource_types.gcl + + public static final AttributeKey BIGTABLE_PROJECT_ID_KEY = + AttributeKey.stringKey("project_id"); + // Resource labels passed during recording + public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + public static final AttributeKey APP_PROFILE_KEY = AttributeKey.stringKey("app_profile"); + public static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); + + // Resource labels injected during export + private static final DeferredAttr CLIENT_PROJECT = + DeferredAttr.fromEnv("client_project", EnvInfo::getProject); + private static final DeferredAttr CLIENT_REGION = + DeferredAttr.fromEnv("region", EnvInfo::getRegion); + private static final DeferredAttr CLOUD_PLATFORM = + DeferredAttr.fromEnv("cloud_platform", EnvInfo::getPlatform); + private static final DeferredAttr HOST_ID = DeferredAttr.fromEnv("host_id", EnvInfo::getHostId); + private static final DeferredAttr HOST_NAME = + DeferredAttr.fromEnv("host_name", EnvInfo::getHostName); + private static final DeferredAttr UUID = DeferredAttr.fromEnv("uuid", EnvInfo::getUid); + + // Must come after all other static members + public static final ClientSchema INSTANCE = new ClientSchema(); + + public ClientSchema() { + super( + "bigtable_client", + ImmutableList.of(BIGTABLE_PROJECT_ID_KEY, INSTANCE_ID_KEY, APP_PROFILE_KEY, CLIENT_NAME), + ImmutableList.of(CLIENT_PROJECT, CLIENT_REGION, CLOUD_PLATFORM, HOST_ID, HOST_NAME, UUID)); + } + + @Override + public ProjectName extractProjectName(Attributes attrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ProjectName.of(clientInfo.getInstanceName().getProject()); + } + + public AttributesBuilder createResourceAttrs(ClientInfo clientInfo) { + return Attributes.builder() + .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject()) + .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance()) + .put(APP_PROFILE_KEY, clientInfo.getAppProfileId()) + .put(CLIENT_NAME, clientInfo.getClientName()); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java new file mode 100644 index 0000000000..62a8df1d3c --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java @@ -0,0 +1,78 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.schema; + +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.common.collect.ImmutableList; +import com.google.monitoring.v3.ProjectName; +import io.opentelemetry.api.common.Attributes; + +/** + * The attributes for this schema are partially populated during the record phase and finalized + * during the export phase with {@link EnvInfo}. This is necessary because resolving {@link EnvInfo} + * is slow and should not happen during client startup. + */ +public final class GrpcClientSchema extends Schema { + // Unlike the normal ClientSchema, the bigtable resource ids must be injected during export time + private static final DeferredAttr BIGTABLE_PROJECT_ID = + DeferredAttr.fromClientInfo("project_id", ci -> ci.getInstanceName().getProject()); + private static final DeferredAttr INSTANCE_ID = + DeferredAttr.fromClientInfo("instance", ci -> ci.getInstanceName().getInstance()); + private static final DeferredAttr APP_PROFILE_ID = + DeferredAttr.fromClientInfo("app_profile", ClientInfo::getAppProfileId); + private static final DeferredAttr CLIENT_NAME = + DeferredAttr.fromClientInfo("client_name", ClientInfo::getClientName); + + private static final DeferredAttr CLIENT_PROJECT = + DeferredAttr.fromEnv("client_project", EnvInfo::getProject); + private static final DeferredAttr CLIENT_REGION = + DeferredAttr.fromEnv("region", EnvInfo::getRegion); + private static final DeferredAttr CLOUD_PLATFORM = + DeferredAttr.fromEnv("cloud_platform", EnvInfo::getPlatform); + private static final DeferredAttr HOST_ID = DeferredAttr.fromEnv("host_id", EnvInfo::getHostId); + private static final DeferredAttr HOST_NAME = + DeferredAttr.fromEnv("host_name", EnvInfo::getHostName); + private static final DeferredAttr UUID = DeferredAttr.fromEnv("uuid", EnvInfo::getUid); + + // Must come after all other static members + public static final GrpcClientSchema INSTANCE = new GrpcClientSchema(); + + private GrpcClientSchema() { + super( + "bigtable_client", + ImmutableList.of(), + ImmutableList.of( + BIGTABLE_PROJECT_ID, + INSTANCE_ID, + APP_PROFILE_ID, + CLIENT_NAME, + // Same as ClientSchema + CLIENT_PROJECT, + CLIENT_REGION, + CLOUD_PLATFORM, + HOST_ID, + HOST_NAME, + UUID)); + } + + @Override + public ProjectName extractProjectName( + Attributes ignored, EnvInfo ignored2, ClientInfo clientInfo) { + return ProjectName.of(clientInfo.getInstanceName().getProject()); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/Schema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/Schema.java new file mode 100644 index 0000000000..a5d621acbc --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/Schema.java @@ -0,0 +1,100 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.schema; + +import com.google.api.MonitoredResource; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.common.collect.ImmutableList; +import com.google.monitoring.v3.ProjectName; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public abstract class Schema { + private final String name; + private final List> resourceKeys; + private final List deferredAttrs; + + Schema(String name, List> resourceKeys) { + this(name, resourceKeys, ImmutableList.of()); + } + + Schema(String name, List> resourceKeys, List deferredAttrs) { + this.name = name; + this.resourceKeys = resourceKeys; + this.deferredAttrs = deferredAttrs; + } + + public List> getResourceKeys() { + return resourceKeys; + } + + public abstract ProjectName extractProjectName( + Attributes attrs, EnvInfo envInfo, ClientInfo clientInfo); + + public MonitoredResource extractMonitoredResource( + Attributes attrs, EnvInfo envInfo, ClientInfo clientInfo) { + MonitoredResource.Builder builder = MonitoredResource.newBuilder().setType(name); + + for (AttributeKey key : resourceKeys) { + Object value = attrs.get(key); + if (value != null) { + builder.putLabels(key.getKey(), value.toString()); + } + } + for (DeferredAttr a : deferredAttrs) { + builder.putLabels(a.getKey().getKey(), a.getValue(envInfo, clientInfo)); + } + return builder.build(); + } + + public String getName() { + return name; + } + + static class DeferredAttr { + private final AttributeKey name; + private BiFunction extractor; + + static DeferredAttr fromEnv(String name, Function envExtractor) { + return new DeferredAttr( + AttributeKey.stringKey(name), (envInfo, ignored) -> envExtractor.apply(envInfo)); + } + + static DeferredAttr fromClientInfo(String name, Function envExtractor) { + return new DeferredAttr( + AttributeKey.stringKey(name), (ignored, clientInfo) -> envExtractor.apply(clientInfo)); + } + + private DeferredAttr( + AttributeKey name, BiFunction extractor) { + this.name = name; + this.extractor = extractor; + } + + AttributeKey getKey() { + return name; + } + + String getValue(EnvInfo envInfo, ClientInfo clientInfo) { + return extractor.apply(envInfo, clientInfo); + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java new file mode 100644 index 0000000000..f536f73837 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.schema; + +import com.google.bigtable.v2.ResponseParams; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.common.collect.ImmutableList; +import com.google.monitoring.v3.ProjectName; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +public final class TableSchema extends Schema { + // This implements the `bigtable_client_raw` resource defined in + // bigtable_googleapis_com/metrics/resource_types.gcl + + public static final AttributeKey BIGTABLE_PROJECT_ID_KEY = + AttributeKey.stringKey("project_id"); + public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + public static final AttributeKey TABLE_ID_KEY = AttributeKey.stringKey("table"); + public static final AttributeKey CLUSTER_ID_KEY = AttributeKey.stringKey("cluster"); + public static final AttributeKey ZONE_ID_KEY = AttributeKey.stringKey("zone"); + + // Must come after all other static members + public static final TableSchema INSTANCE = new TableSchema(); + + public TableSchema() { + super( + "bigtable_client_raw", + ImmutableList.of( + BIGTABLE_PROJECT_ID_KEY, INSTANCE_ID_KEY, TABLE_ID_KEY, CLUSTER_ID_KEY, ZONE_ID_KEY)); + } + + @Override + public ProjectName extractProjectName(Attributes attrs, EnvInfo envInfo, ClientInfo clientInfo) { + return ProjectName.of(attrs.get(BIGTABLE_PROJECT_ID_KEY)); + } + + public AttributesBuilder createResourceAttrs( + ClientInfo clientInfo, String tableId, ResponseParams clusterInfo) { + return Attributes.builder() + .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject()) + .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance()) + .put(TABLE_ID_KEY, tableId) + .put(CLUSTER_ID_KEY, clusterInfo.getClusterId()) + .put(ZONE_ID_KEY, clusterInfo.getZoneId()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java new file mode 100644 index 0000000000..283c26f514 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.v2.InstanceName; +import org.junit.jupiter.api.Test; + +class ClientInfoTest { + @Test + void testName() { + ClientInfo clientInfo = + ClientInfo.builder() + .setInstanceName(InstanceName.of("fake-project", "fake-instance")) + .setAppProfileId("fake-app-profile") + .build(); + assertThat(clientInfo.getClientName()).containsMatch("java-bigtable/\\d+\\.\\d+\\.\\d+.*"); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfoTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfoTest.java new file mode 100644 index 0000000000..8ab52111aa --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfoTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.cloud.opentelemetry.detection.DetectedPlatform; +import com.google.cloud.opentelemetry.detection.GCPPlatformDetector.SupportedPlatform; +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class EnvInfoTest { + private static final Supplier NULL_HOST = Suppliers.ofInstance(null); + + @SuppressWarnings("UnnecessaryLambda") + private static final Function NULL_ENV = (ignored) -> null; + + @Mock private DetectedPlatform detectedPlatform; + + @Test + void testUid() { + when(detectedPlatform.getSupportedPlatform()).thenReturn(SupportedPlatform.UNKNOWN_PLATFORM); + + EnvInfo info1 = EnvInfo.detect(detectedPlatform, NULL_ENV, NULL_HOST); + EnvInfo info2 = EnvInfo.detect(detectedPlatform, NULL_ENV, NULL_HOST); + + assertThat(info1.getUid()).isNotEmpty(); + assertThat(info2.getUid()).isNotEmpty(); + assertThat(info1.getUid()).isNotEqualTo(info2.getUid()); + } + + @Test + void testUnknown() { + when(detectedPlatform.getSupportedPlatform()).thenReturn(SupportedPlatform.UNKNOWN_PLATFORM); + EnvInfo envInfo = EnvInfo.detect(detectedPlatform, NULL_ENV, NULL_HOST); + assertThat(envInfo.getHostName()).isEmpty(); + assertThat(envInfo.getHostId()).isEmpty(); + assertThat(envInfo.getPlatform()).isEqualTo("unknown"); + assertThat(envInfo.getRegion()).isEqualTo("global"); + } + + @Test + void testGce() { + when(detectedPlatform.getSupportedPlatform()) + .thenReturn(SupportedPlatform.GOOGLE_COMPUTE_ENGINE); + when(detectedPlatform.getProjectId()).thenReturn("my-project"); + when(detectedPlatform.getAttributes()) + .thenReturn( + ImmutableMap.of( + "machine_type", "n2-standard-8", + "availability_zone", "us-central1-c", + "instance_id", "1234567890", + "instance_name", "my-vm-name", + "cloud_region", "us-central1", + "instance_hostname", "my-vm-name.us-central1-c.c.my-project.google.com.internal")); + EnvInfo envInfo = EnvInfo.detect(detectedPlatform, NULL_ENV, NULL_HOST); + assertThat(envInfo.getPlatform()).isEqualTo("gcp_compute_engine"); + assertThat(envInfo.getProject()).isEqualTo("my-project"); + assertThat(envInfo.getRegion()).isEqualTo("us-central1"); + assertThat(envInfo.getHostId()).isEqualTo("1234567890"); + assertThat(envInfo.getHostName()).isEqualTo("my-vm-name"); + } + + @Test + void testGke() { + when(detectedPlatform.getSupportedPlatform()) + .thenReturn(SupportedPlatform.GOOGLE_KUBERNETES_ENGINE); + when(detectedPlatform.getProjectId()).thenReturn("my-project"); + when(detectedPlatform.getAttributes()) + .thenReturn( + ImmutableMap.of( + "gke_cluster_name", "my-cluster", + "gke_cluster_location", "us-central1", + "gke_cluster_location_type", "country-region", + "instance_id", "1234567890")); + Map env = ImmutableMap.of("HOSTNAME", "my-hostname"); + + EnvInfo envInfo = EnvInfo.detect(detectedPlatform, env::get, NULL_HOST); + assertThat(envInfo.getPlatform()).isEqualTo("gcp_kubernetes_engine"); + assertThat(envInfo.getProject()).isEqualTo("my-project"); + assertThat(envInfo.getRegion()).isEqualTo("us-central1"); + assertThat(envInfo.getHostId()).isEqualTo("1234567890"); + assertThat(envInfo.getHostName()).isEqualTo("my-hostname"); + } + + @Test + void testGkeHostanmeFallback() { + when(detectedPlatform.getSupportedPlatform()) + .thenReturn(SupportedPlatform.GOOGLE_KUBERNETES_ENGINE); + when(detectedPlatform.getProjectId()).thenReturn("my-project"); + when(detectedPlatform.getAttributes()) + .thenReturn( + ImmutableMap.of( + "gke_cluster_name", "my-cluster", + "gke_cluster_location", "us-central1", + "gke_cluster_location_type", "country-region", + "instance_id", "1234567890")); + EnvInfo envInfo = EnvInfo.detect(detectedPlatform, NULL_ENV, () -> "my-hostname"); + assertThat(envInfo.getPlatform()).isEqualTo("gcp_kubernetes_engine"); + assertThat(envInfo.getProject()).isEqualTo("my-project"); + assertThat(envInfo.getRegion()).isEqualTo("us-central1"); + assertThat(envInfo.getHostId()).isEqualTo("1234567890"); + assertThat(envInfo.getHostName()).isEqualTo("my-hostname"); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java new file mode 100644 index 0000000000..f75bb81727 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.internal.csm.attributes; + +import com.google.bigtable.v2.PeerInfo.TransportType; +import org.junit.jupiter.api.Test; + +class UtilTest { + @Test + void ensureAllTransportTypeHaveExpectedPrefix() { + for (TransportType type : TransportType.values()) { + // Ensure that no variant throws an error + Util.transportTypeToString(type); + } + } +} From 5de9d30ad136e784e9e5405c0257cd435acd80c8 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 25 Feb 2026 11:04:02 -0500 Subject: [PATCH 14/33] chore: plumb ClientInfo (#2803) * chore: plumb ClientInfo Change-Id: I01806d0bbaa6ba95b3a8e66d9b3fa24806c928f0 * wip Change-Id: If6dd6db3cdc3624ff0a4397fad30fa6a47e935b0 * chore: generate libraries at Tue Feb 24 21:47:05 UTC 2026 * chore: generate libraries at Tue Feb 24 21:49:58 UTC 2026 --------- Co-authored-by: cloud-java-bot --- .../data/v2/BigtableDataClientFactory.java | 6 +- .../data/v2/internal/RequestContext.java | 8 ++ .../data/v2/stub/BigtableClientContext.java | 39 ++++----- .../data/v2/stub/EnhancedBigtableStub.java | 6 +- .../metrics/BuiltinMetricsTracerFactory.java | 24 +++++- .../bigtable/data/v2/stub/metrics/Util.java | 46 +++++----- .../metrics/BuiltinMetricsTracerTest.java | 85 ++++++++++--------- 7 files changed, 114 insertions(+), 100 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java index d73fbe2a12..d529f02eb2 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java @@ -109,7 +109,8 @@ public BigtableDataClient createDefault() { try { BigtableClientContext ctx = sharedClientContext.createChild( - sharedClientContext.getInstanceName(), sharedClientContext.getAppProfileId()); + sharedClientContext.getClientInfo().getInstanceName(), + sharedClientContext.getClientInfo().getAppProfileId()); return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } catch (IOException e) { @@ -130,7 +131,8 @@ public BigtableDataClient createDefault() { */ public BigtableDataClient createForAppProfile(@Nonnull String appProfileId) throws IOException { BigtableClientContext ctx = - sharedClientContext.createChild(sharedClientContext.getInstanceName(), appProfileId); + sharedClientContext.createChild( + sharedClientContext.getClientInfo().getInstanceName(), appProfileId); return new BigtableDataClient(new EnhancedBigtableStub(perOpSettings, ctx)); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java index fc015186aa..2c3213d003 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java @@ -17,6 +17,7 @@ import com.google.api.core.InternalApi; import com.google.auto.value.AutoValue; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import java.io.Serializable; /** @@ -33,6 +34,13 @@ @AutoValue public abstract class RequestContext implements Serializable { + public static RequestContext create(ClientInfo clientInfo) { + return create( + clientInfo.getInstanceName().getProject(), + clientInfo.getInstanceName().getInstance(), + clientInfo.getAppProfileId()); + } + /** Creates a new instance of the {@link RequestContext}. */ public static RequestContext create(String projectId, String instanceId, String appProfileId) { return new AutoValue_RequestContext(projectId, instanceId, appProfileId); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index c89f368190..511dd61c70 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -29,6 +29,7 @@ import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; @@ -63,8 +64,7 @@ public class BigtableClientContext { private static final Logger logger = Logger.getLogger(BigtableClientContext.class.getName()); private final boolean isChild; - private final InstanceName instanceName; - private final String appProfileId; + private final ClientInfo clientInfo; private final ApiTracerFactory userTracerFactory; @Nullable private final OpenTelemetrySdk builtinOpenTelemetry; @Nullable private final OpenTelemetry userOpenTelemetry; @@ -83,8 +83,11 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings public static BigtableClientContext create( EnhancedBigtableStubSettings settings, Tagger ocTagger, StatsRecorder ocRecorder) throws IOException { - InstanceName instanceName = InstanceName.of(settings.getProjectId(), settings.getInstanceId()); - String appProfileId = settings.getAppProfileId(); + ClientInfo clientInfo = + ClientInfo.builder() + .setInstanceName(InstanceName.of(settings.getProjectId(), settings.getInstanceId())) + .setAppProfileId(settings.getAppProfileId()) + .build(); EnhancedBigtableStubSettings.Builder builder = settings.toBuilder(); @@ -184,8 +187,7 @@ public static BigtableClientContext create( return new BigtableClientContext( false, - instanceName, - appProfileId, + clientInfo, clientContext, userTracerFactory, builtinOtel, @@ -224,8 +226,7 @@ private static void configureGrpcOtel( private BigtableClientContext( boolean isChild, - InstanceName instanceName, - String appProfileId, + ClientInfo clientInfo, ClientContext clientContext, ApiTracerFactory userTracerFactory, @Nullable OpenTelemetrySdk builtinOtel, @@ -235,8 +236,7 @@ private BigtableClientContext( ExecutorProvider backgroundExecutorProvider) throws IOException { this.isChild = isChild; - this.instanceName = instanceName; - this.appProfileId = appProfileId; + this.clientInfo = clientInfo; this.userTracerFactory = userTracerFactory; this.builtinOpenTelemetry = builtinOtel; @@ -247,15 +247,15 @@ private BigtableClientContext( ImmutableList.Builder tracerFactories = ImmutableList.builder(); tracerFactories - .add(Util.createOCTracingFactory(instanceName, appProfileId)) - .add(Util.createOCMetricsFactory(instanceName, appProfileId, ocTagger, ocRecorder)) + .add(Util.createOCTracingFactory(clientInfo)) + .add(Util.createOCMetricsFactory(clientInfo, ocTagger, ocRecorder)) .add(userTracerFactory); if (builtinOtel != null) { - tracerFactories.add(Util.createOtelMetricsFactory(builtinOtel, instanceName, appProfileId)); + tracerFactories.add(Util.createOtelMetricsFactory(builtinOtel, clientInfo)); } if (userOtel != null) { - tracerFactories.add(Util.createOtelMetricsFactory(userOtel, instanceName, appProfileId)); + tracerFactories.add(Util.createOtelMetricsFactory(userOtel, clientInfo)); } this.clientContext = @@ -264,12 +264,8 @@ private BigtableClientContext( .build(); } - public InstanceName getInstanceName() { - return instanceName; - } - - public String getAppProfileId() { - return appProfileId; + public ClientInfo getClientInfo() { + return clientInfo; } @Nullable @@ -290,8 +286,7 @@ public BigtableClientContext createChild(InstanceName instanceName, String appPr throws IOException { return new BigtableClientContext( true, - instanceName, - appProfileId, + clientInfo.toBuilder().setInstanceName(instanceName).setAppProfileId(appProfileId).build(), clientContext, userTracerFactory, builtinOpenTelemetry, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 6f0ffdc60f..d28d41ecbc 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -182,11 +182,7 @@ public EnhancedBigtableStub( ClientOperationSettings perOpSettings, BigtableClientContext clientContext) { this.perOpSettings = perOpSettings; this.bigtableClientContext = clientContext; - this.requestContext = - RequestContext.create( - clientContext.getInstanceName().getProject(), - clientContext.getInstanceName().getInstance(), - clientContext.getAppProfileId()); + this.requestContext = RequestContext.create(clientContext.getClientInfo()); this.bulkMutationFlowController = new FlowController(perOpSettings.bulkMutateRowsSettings.getDynamicFlowControlSettings()); this.bulkMutationDynamicFlowControlStats = new DynamicFlowControlStats(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index eb8089b1c6..3d83659a28 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -16,13 +16,17 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES2_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.REMAINING_DEADLINE_NAME; @@ -34,6 +38,8 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; import com.google.api.gax.tracing.SpanName; +import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleGauge; @@ -68,14 +74,24 @@ public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { private final DoubleHistogram batchWriteFlowControlFactorHistogram; public static BuiltinMetricsTracerFactory create( - OpenTelemetry openTelemetry, Attributes attributes) throws IOException { - return new BuiltinMetricsTracerFactory(openTelemetry, attributes); + OpenTelemetry openTelemetry, ClientInfo clientInfo) throws IOException { + return new BuiltinMetricsTracerFactory(openTelemetry, clientInfo); } - BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, Attributes attributes) { - this.attributes = attributes; + BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, ClientInfo clientInfo) { Meter meter = openTelemetry.getMeter(METER_NAME); + this.attributes = + Attributes.of( + BIGTABLE_PROJECT_ID_KEY, + clientInfo.getInstanceName().getProject(), + INSTANCE_ID_KEY, + clientInfo.getInstanceName().getInstance(), + APP_PROFILE_KEY, + clientInfo.getAppProfileId(), + CLIENT_NAME_KEY, + "bigtable-java/" + Version.VERSION); + operationLatenciesHistogram = meter .histogramBuilder(OPERATION_LATENCIES_NAME) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index cc341c994e..862e288a4a 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -15,11 +15,6 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; - import com.google.api.core.InternalApi; import com.google.api.gax.grpc.GaxGrpcProperties; import com.google.api.gax.rpc.ApiCallContext; @@ -45,6 +40,7 @@ import com.google.bigtable.v2.TableName; import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; @@ -58,7 +54,6 @@ import io.opencensus.tags.TagValue; import io.opencensus.tags.Tagger; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -241,14 +236,18 @@ public static String formatZoneIdMetricLabel( .orElse("global"); } - public static ApiTracerFactory createOCTracingFactory( - InstanceName instanceName, String appProfileId) { + public static ApiTracerFactory createOCTracingFactory(ClientInfo clientInfo) { return new OpencensusTracerFactory( ImmutableMap.builder() // Annotate traces with the same tags as metrics - .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), instanceName.getProject()) - .put(RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), instanceName.getInstance()) - .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), appProfileId) + .put( + RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), + clientInfo.getInstanceName().getProject()) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), + clientInfo.getInstanceName().getInstance()) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), clientInfo.getAppProfileId()) // Also annotate traces with library versions .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) .put("grpc", GaxGrpcProperties.getGrpcVersion()) @@ -257,32 +256,25 @@ public static ApiTracerFactory createOCTracingFactory( } public static ApiTracerFactory createOCMetricsFactory( - InstanceName instanceName, String appProfileId, Tagger tagger, StatsRecorder stats) { + ClientInfo clientInfo, Tagger tagger, StatsRecorder stats) { ImmutableMap attributes = ImmutableMap.builder() .put( - RpcMeasureConstants.BIGTABLE_PROJECT_ID, TagValue.create(instanceName.getProject())) + RpcMeasureConstants.BIGTABLE_PROJECT_ID, + TagValue.create(clientInfo.getInstanceName().getProject())) .put( RpcMeasureConstants.BIGTABLE_INSTANCE_ID, - TagValue.create(instanceName.getInstance())) - .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, TagValue.create(appProfileId)) + TagValue.create(clientInfo.getInstanceName().getInstance())) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, + TagValue.create(clientInfo.getAppProfileId())) .build(); return MetricsTracerFactory.create(tagger, stats, attributes); } public static BuiltinMetricsTracerFactory createOtelMetricsFactory( - OpenTelemetry otel, InstanceName instanceName, String appProfileId) throws IOException { - Attributes attributes = - Attributes.of( - BIGTABLE_PROJECT_ID_KEY, - instanceName.getProject(), - INSTANCE_ID_KEY, - instanceName.getInstance(), - APP_PROFILE_KEY, - appProfileId, - CLIENT_NAME_KEY, - "bigtable-java/" + Version.VERSION); - return BuiltinMetricsTracerFactory.create(otel, attributes); + OpenTelemetry otel, ClientInfo clientInfo) { + return new BuiltinMetricsTracerFactory(otel, clientInfo); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 1ffccab7dd..2aaea4a5e5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -54,6 +54,7 @@ import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.StreamController; import com.google.bigtable.v2.BigtableGrpc; +import com.google.bigtable.v2.InstanceName; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowResponse; import com.google.bigtable.v2.MutateRowsRequest; @@ -64,6 +65,7 @@ import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.FakeServiceBuilder; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; @@ -163,7 +165,17 @@ public class BuiltinMetricsTracerTest { private int batchElementCount = 2; - private Attributes baseAttributes; + private ClientInfo clientInfo = + ClientInfo.builder() + .setInstanceName(InstanceName.of(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .build(); + private Attributes expectedBaseAttributes = + Attributes.builder() + .put(BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY, PROJECT_ID) + .put(BuiltinMetricsConstants.INSTANCE_ID_KEY, INSTANCE_ID) + .put(BuiltinMetricsConstants.APP_PROFILE_KEY, APP_PROFILE_ID) + .build(); private InMemoryMetricReader metricReader; @@ -175,13 +187,6 @@ public class BuiltinMetricsTracerTest { public void setUp() throws Exception { metricReader = InMemoryMetricReader.create(); - baseAttributes = - Attributes.builder() - .put(BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY, PROJECT_ID) - .put(BuiltinMetricsConstants.INSTANCE_ID_KEY, INSTANCE_ID) - .put(BuiltinMetricsConstants.APP_PROFILE_KEY, APP_PROFILE_ID) - .build(); - SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader); @@ -192,7 +197,7 @@ public void setUp() throws Exception { OpenTelemetrySdk otel = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - BuiltinMetricsTracerFactory facotry = BuiltinMetricsTracerFactory.create(otel, baseAttributes); + BuiltinMetricsTracerFactory facotry = new BuiltinMetricsTracerFactory(otel, clientInfo); // Add an interceptor to add server-timing in headers ServerInterceptor trailersInterceptor = @@ -302,7 +307,7 @@ public void testReadRowsOperationLatencies() { long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -327,7 +332,7 @@ public void testReadRowsOperationLatenciesOnAuthorizedView() { long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -372,7 +377,7 @@ public void onComplete() {} }); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, FIRST_RESPONSE_TABLE_ID) .put(ZONE_ID_KEY, ZONE) @@ -392,7 +397,7 @@ public void testGfeMetrics() { Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE))); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -409,7 +414,7 @@ public void testGfeMetrics() { MetricData connectivityErrorCountMetricData = getMetricData(metricReader, CONNECTIVITY_ERROR_COUNT_NAME); Attributes expected1 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "UNAVAILABLE") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, "global") @@ -418,7 +423,7 @@ public void testGfeMetrics() { .put(CLIENT_NAME_KEY, CLIENT_NAME) .build(); Attributes expected2 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -473,7 +478,7 @@ public void onComplete() { getMetricData(metricReader, APPLICATION_BLOCKING_LATENCIES_NAME); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) .put(CLUSTER_ID_KEY, CLUSTER) @@ -508,7 +513,7 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti getMetricData(metricReader, APPLICATION_BLOCKING_LATENCIES_NAME); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) .put(CLUSTER_ID_KEY, CLUSTER) @@ -537,7 +542,7 @@ public void testRetryCount() throws InterruptedException { MetricData metricData = getMetricData(metricReader, RETRY_COUNT_NAME); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) .put(CLUSTER_ID_KEY, CLUSTER) @@ -559,7 +564,7 @@ public void testMutateRowAttemptsTagValues() throws InterruptedException { MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME); Attributes expected1 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "UNAVAILABLE") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, "global") @@ -570,7 +575,7 @@ public void testMutateRowAttemptsTagValues() throws InterruptedException { .build(); Attributes expected2 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -598,7 +603,7 @@ public void testMutateRowsPartialError() throws InterruptedException { MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME); Attributes expected = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -626,7 +631,7 @@ public void testMutateRowsRpcError() { MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME); Attributes expected = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "NOT_FOUND") .put(TABLE_ID_KEY, BAD_TABLE_ID) .put(ZONE_ID_KEY, "global") @@ -646,7 +651,7 @@ public void testReadRowsAttemptsTagValues() { MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME); Attributes expected1 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "UNAVAILABLE") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, "global") @@ -657,7 +662,7 @@ public void testReadRowsAttemptsTagValues() { .build(); Attributes expected2 = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -686,7 +691,7 @@ public void testBatchBlockingLatencies() throws InterruptedException { MetricData applicationLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME); Attributes expectedAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) .put(CLUSTER_ID_KEY, CLUSTER) @@ -712,7 +717,7 @@ public void testQueuedOnChannelServerStreamLatencies() throws Exception { MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME); Attributes attributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(CLUSTER_ID_KEY, CLUSTER) .put(ZONE_ID_KEY, ZONE) @@ -739,7 +744,7 @@ public void testQueuedOnChannelUnaryLatencies() throws Exception { MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME); Attributes attributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(TABLE_ID_KEY, TABLE) .put(CLUSTER_ID_KEY, CLUSTER) .put(ZONE_ID_KEY, ZONE) @@ -765,7 +770,7 @@ public void testPermanentFailure() { MetricData attemptLatency = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME); Attributes expected = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "NOT_FOUND") .put(TABLE_ID_KEY, BAD_TABLE_ID) .put(CLUSTER_ID_KEY, "") @@ -787,7 +792,7 @@ public void testRemainingDeadline() { MetricData deadlineMetric = getMetricData(metricReader, REMAINING_DEADLINE_NAME); Attributes retryAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "UNAVAILABLE") .put(TABLE_ID_KEY, TABLE) .put(METHOD_KEY, "Bigtable.ReadRows") @@ -807,7 +812,7 @@ public void testRemainingDeadline() { assertThat(retryRemainingDeadline).isEqualTo(9000); Attributes okAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(STATUS_KEY, "OK") .put(TABLE_ID_KEY, TABLE) .put(ZONE_ID_KEY, ZONE) @@ -840,14 +845,14 @@ public void testBatchWriteFlowControlTargetQpsIncreased() throws InterruptedExce MetricData targetQpsMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME); Attributes targetQpsAttributes = - baseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); + expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes); double expected_qps = 12; assertThat(expected_qps).isEqualTo(actual_qps); MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME); Attributes factorAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(METHOD_KEY, "Bigtable.MutateRows") .put(APPLIED_KEY, true) .put(STATUS_KEY, "OK") @@ -870,14 +875,14 @@ public void testBatchWriteFlowControlTargetQpsDecreased() throws InterruptedExce MetricData targetQpsMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME); Attributes targetQpsAttributes = - baseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); + expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes); double expected_qps = 8.0; assertThat(expected_qps).isEqualTo(actual_qps); MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME); Attributes factorAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(METHOD_KEY, "Bigtable.MutateRows") .put(APPLIED_KEY, true) .put(STATUS_KEY, "OK") @@ -900,7 +905,7 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMaxFactor() throws Interru MetricData targetQpsMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME); Attributes targetQpsAttributes = - baseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); + expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes); // Factor is 1.8 but capped at 1.3 so updated QPS is 13. double expected_qps = 13; @@ -908,7 +913,7 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMaxFactor() throws Interru MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME); Attributes factorAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(METHOD_KEY, "Bigtable.MutateRows") .put(APPLIED_KEY, true) .put(STATUS_KEY, "OK") @@ -932,7 +937,7 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMinFactor() throws Interru MetricData targetQpsMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME); Attributes targetQpsAttributes = - baseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); + expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes); // Factor is 0.5 but capped at 0.7 so updated QPS is 7. double expected_qps = 7; @@ -940,7 +945,7 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMinFactor() throws Interru MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME); Attributes factorAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(METHOD_KEY, "Bigtable.MutateRows") .put(APPLIED_KEY, true) .put(STATUS_KEY, "OK") @@ -965,7 +970,7 @@ public void testBatchWriteFlowControlTargetQpsDecreasedForError() throws Interru MetricData targetQpsMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME); Attributes targetQpsAttributes = - baseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); + expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build(); double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes); // On error, min factor is applied. double expected_qps = 7; @@ -973,7 +978,7 @@ public void testBatchWriteFlowControlTargetQpsDecreasedForError() throws Interru MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME); Attributes factorAttributes = - baseAttributes.toBuilder() + expectedBaseAttributes.toBuilder() .put(METHOD_KEY, "Bigtable.MutateRows") .put(APPLIED_KEY, true) .put(STATUS_KEY, "UNAVAILABLE") From a915fb74df199b2ba4db130a5cc342ab041c0aa5 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 25 Feb 2026 12:50:28 -0500 Subject: [PATCH 15/33] chore: internalize converters in the metrics exporter (#2804) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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://togithub.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) - [ ] Rollback plan is reviewed and LGTMed - [ ] All new data plane features have a completed end to end testing plan Fixes # ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). --- .../data/v2/stub/BigtableClientContext.java | 3 +-- .../BigtableCloudMonitoringExporter.java | 11 ++++++++- .../stub/metrics/BigtableExporterUtils.java | 15 ++++++------ .../bigtable/data/v2/stub/metrics/Util.java | 23 ++++--------------- 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index 511dd61c70..6f005a0408 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -124,8 +124,7 @@ public static BigtableClientContext create( if (settings.areInternalMetricsEnabled()) { builtinOtel = Util.createBuiltinOtel( - InstanceName.of(settings.getProjectId(), settings.getInstanceId()), - settings.getAppProfileId(), + clientInfo, credentials, settings.getMetricsEndpoint(), universeDomain, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 9043a351ab..67cc5ba134 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -39,11 +39,13 @@ import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.rpc.PermissionDeniedException; import com.google.auth.Credentials; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -112,10 +114,10 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { private final AtomicBoolean exportFailureLogged = new AtomicBoolean(false); static BigtableCloudMonitoringExporter create( + ClientInfo clientInfo, @Nullable Credentials credentials, @Nullable String endpoint, String universeDomain, - List converters, @Nullable ScheduledExecutorService executorService) throws IOException { Preconditions.checkNotNull(universeDomain); @@ -152,6 +154,13 @@ static BigtableCloudMonitoringExporter create( // it as not retried for now. settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout); + ImmutableList converters = + ImmutableList.of( + new PublicTimeSeriesConverter(), + new InternalTimeSeriesConverter( + Suppliers.memoize( + () -> BigtableExporterUtils.createInternalMonitoredResource(clientInfo)))); + return new BigtableCloudMonitoringExporter( MetricServiceClient.create(settingsBuilder.build()), converters); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 3b95ed1819..f27c2b56f8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -38,8 +38,8 @@ import com.google.api.Distribution; import com.google.api.Metric; import com.google.api.MonitoredResource; -import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.opentelemetry.detection.AttributeKeys; import com.google.cloud.opentelemetry.detection.DetectedPlatform; import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; @@ -182,10 +182,9 @@ static List convertToApplicationResourceTimeSeries( } @Nullable - static MonitoredResource createInternalMonitoredResource( - InstanceName instanceName, String appProfileId) { + static MonitoredResource createInternalMonitoredResource(ClientInfo clientInfo) { try { - MonitoredResource monitoredResource = detectResource(instanceName, appProfileId); + MonitoredResource monitoredResource = detectResource(clientInfo); logger.log(Level.FINE, "Internal metrics monitored resource: %s", monitoredResource); return monitoredResource; } catch (Exception e) { @@ -198,7 +197,7 @@ static MonitoredResource createInternalMonitoredResource( } @Nullable - private static MonitoredResource detectResource(InstanceName instanceName, String appProfileId) { + private static MonitoredResource detectResource(ClientInfo clientInfo) { GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE; DetectedPlatform detectedPlatform = detector.detectPlatform(); @@ -245,9 +244,9 @@ private static MonitoredResource detectResource(InstanceName instanceName, Strin return MonitoredResource.newBuilder() .setType("bigtable_client") - .putLabels("project_id", instanceName.getProject()) - .putLabels("instance", instanceName.getInstance()) - .putLabels("app_profile", appProfileId) + .putLabels("project_id", clientInfo.getInstanceName().getProject()) + .putLabels("instance", clientInfo.getInstanceName().getInstance()) + .putLabels("app_profile", clientInfo.getAppProfileId()) .putLabels("client_project", detectedPlatform.getProjectId()) .putLabels("region", region) .putLabels("cloud_platform", cloud_platform) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index 862e288a4a..f37a4191b1 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -27,7 +27,6 @@ import com.google.bigtable.v2.AuthorizedViewName; import com.google.bigtable.v2.CheckAndMutateRowRequest; import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest; -import com.google.bigtable.v2.InstanceName; import com.google.bigtable.v2.MaterializedViewName; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowsRequest; @@ -42,8 +41,6 @@ import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; import io.grpc.Status; @@ -162,8 +159,7 @@ static Map> createStatsHeaders(ApiCallContext apiCallContex } public static OpenTelemetrySdk createBuiltinOtel( - InstanceName instanceName, - String appProfileId, + ClientInfo clientInfo, @Nullable Credentials defaultCredentials, @Nullable String metricsEndpoint, String universeDomain, @@ -189,17 +185,7 @@ public static OpenTelemetrySdk createBuiltinOtel( MetricExporter publicExporter = BigtableCloudMonitoringExporter.create( - credentials, - metricsEndpoint, - universeDomain, - ImmutableList.of( - new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(), - new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( - Suppliers.memoize( - () -> - BigtableExporterUtils.createInternalMonitoredResource( - instanceName, appProfileId)))), - executor); + clientInfo, credentials, metricsEndpoint, universeDomain, executor); PeriodicMetricReaderBuilder readerBuilder = PeriodicMetricReader.builder(publicExporter).setExecutor(executor); meterProvider.registerMetricReader(readerBuilder.build()); @@ -274,7 +260,8 @@ public static ApiTracerFactory createOCMetricsFactory( } public static BuiltinMetricsTracerFactory createOtelMetricsFactory( - OpenTelemetry otel, ClientInfo clientInfo) { - return new BuiltinMetricsTracerFactory(otel, clientInfo); + OpenTelemetry otel, ClientInfo clientInfo) throws IOException { + + return BuiltinMetricsTracerFactory.create(otel, clientInfo); } } From be51db4ba3b817aa7416a64de6d5f2625244b29c Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 25 Feb 2026 14:30:29 -0500 Subject: [PATCH 16/33] chore: clean up Util to return Status.Code instead of string (#2805) This in prep for migrating to the strongly typed metrics --- .../v2/stub/metrics/BuiltinMetricsTracer.java | 27 ++++++------- .../data/v2/stub/metrics/MetricsTracer.java | 4 +- .../bigtable/data/v2/stub/metrics/Util.java | 38 +++++++++---------- .../data/v2/stub/metrics/UtilTest.java | 4 +- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index 546ea41c9f..74d09f5834 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -40,6 +40,7 @@ import com.google.common.base.Stopwatch; import com.google.common.math.IntMath; import io.grpc.Deadline; +import io.grpc.Status; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleGauge; import io.opentelemetry.api.metrics.DoubleHistogram; @@ -336,9 +337,9 @@ public void disableFlowControl() { flowControlIsDisabled = true; } - private void recordOperationCompletion(@Nullable Throwable status) { + private void recordOperationCompletion(@Nullable Throwable throwable) { if (operationFinishedEarly.get()) { - status = null; // force an ok + throwable = null; // force an ok } if (!opFinished.compareAndSet(false, true)) { @@ -347,7 +348,7 @@ private void recordOperationCompletion(@Nullable Throwable status) { long operationLatencyNano = operationTimer.elapsed(TimeUnit.NANOSECONDS); boolean isStreaming = operationType == OperationType.ServerStreaming; - String statusStr = extractStatus(status); + Status.Code code = extractStatus(throwable); // Publish metric data with all the attributes. The attributes get filtered in // BuiltinMetricsConstants when we construct the views. @@ -359,7 +360,7 @@ private void recordOperationCompletion(@Nullable Throwable status) { .put(METHOD_KEY, spanName.toString()) .put(CLIENT_NAME_KEY, NAME) .put(STREAMING_KEY, isStreaming) - .put(STATUS_KEY, statusStr) + .put(STATUS_KEY, code.name()) .build(); // Only record when retry count is greater than 0 so the retry @@ -381,9 +382,9 @@ private void recordOperationCompletion(@Nullable Throwable status) { } } - private void recordAttemptCompletion(@Nullable Throwable status) { + private void recordAttemptCompletion(@Nullable Throwable throwable) { if (operationFinishedEarly.get()) { - status = null; // force an ok + throwable = null; // force an ok } // If the attempt failed, the time spent in retry should be counted in application latency. // Stop the stopwatch and decrement requestLeft. @@ -397,14 +398,14 @@ private void recordAttemptCompletion(@Nullable Throwable status) { boolean isStreaming = operationType == OperationType.ServerStreaming; - // Patch the status until it's fixed in gax. When an attempt failed, + // Patch the throwable until it's fixed in gax. When an attempt failed, // it'll throw a ServerStreamingAttemptException. Unwrap the exception // so it could get processed by extractStatus - if (status instanceof ServerStreamingAttemptException) { - status = status.getCause(); + if (throwable instanceof ServerStreamingAttemptException) { + throwable = throwable.getCause(); } - String statusStr = extractStatus(status); + Status.Code code = extractStatus(throwable); Attributes attributes = baseAttributes.toBuilder() @@ -414,7 +415,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) { .put(METHOD_KEY, spanName.toString()) .put(CLIENT_NAME_KEY, NAME) .put(STREAMING_KEY, isStreaming) - .put(STATUS_KEY, statusStr) + .put(STATUS_KEY, code.name()) .build(); totalClientBlockingTime.addAndGet(grpcMessageSentDelay.get()); @@ -477,11 +478,11 @@ public void setBatchWriteFlowControlTargetQps(double targetQps) { @Override public void addBatchWriteFlowControlFactor( - double factor, @Nullable Throwable status, boolean applied) { + double factor, @Nullable Throwable throwable, boolean applied) { Attributes attributes = baseAttributes.toBuilder() .put(METHOD_KEY, spanName.toString()) - .put(STATUS_KEY, extractStatus(status)) + .put(STATUS_KEY, extractStatus(throwable).name()) .put(APPLIED_KEY, applied) .build(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java index 53b4ca87a8..73f54ad810 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java @@ -133,7 +133,7 @@ private void recordOperationCompletion(@Nullable Throwable throwable) { newTagCtxBuilder() .putLocal( RpcMeasureConstants.BIGTABLE_STATUS, - TagValue.create(Util.extractStatus(throwable))); + TagValue.create(Util.extractStatus(throwable).name())); measures.record(tagCtx.build()); } @@ -216,7 +216,7 @@ private void recordAttemptCompletion(@Nullable Throwable throwable) { newTagCtxBuilder() .putLocal( RpcMeasureConstants.BIGTABLE_STATUS, - TagValue.create(Util.extractStatus(throwable))); + TagValue.create(Util.extractStatus(throwable).name())); measures.record(tagCtx.build()); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index f37a4191b1..dc6155f88a 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -17,10 +17,9 @@ import com.google.api.core.InternalApi; import com.google.api.gax.grpc.GaxGrpcProperties; +import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiException; -import com.google.api.gax.rpc.StatusCode; -import com.google.api.gax.rpc.StatusCode.Code; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.OpencensusTracerFactory; import com.google.auth.Credentials; @@ -44,8 +43,6 @@ import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; import io.grpc.Status; -import io.grpc.StatusException; -import io.grpc.StatusRuntimeException; import io.opencensus.stats.StatsRecorder; import io.opencensus.tags.TagKey; import io.opencensus.tags.TagValue; @@ -79,25 +76,26 @@ public class Util { static final Metadata.Key ATTEMPT_EPOCH_KEY = Metadata.Key.of("bigtable-client-attempt-epoch-usec", Metadata.ASCII_STRING_MARSHALLER); - /** Convert an exception into a value that can be used to create an OpenCensus tag value. */ - public static String extractStatus(@Nullable Throwable error) { - final String statusString; - + public static Status.Code extractStatus(@Nullable Throwable error) { if (error == null) { - return StatusCode.Code.OK.toString(); - } else if (error instanceof CancellationException) { - statusString = Status.Code.CANCELLED.toString(); - } else if (error instanceof ApiException) { - statusString = ((ApiException) error).getStatusCode().getCode().toString(); - } else if (error instanceof StatusRuntimeException) { - statusString = ((StatusRuntimeException) error).getStatus().getCode().toString(); - } else if (error instanceof StatusException) { - statusString = ((StatusException) error).getStatus().getCode().toString(); - } else { - statusString = Code.UNKNOWN.toString(); + return Status.Code.OK; + } + // Handle java CancellationException as if it was a gax CancelledException + if (error instanceof CancellationException) { + return Status.Code.CANCELLED; + } + if (error instanceof ApiException) { + ApiException apiException = (ApiException) error; + if (apiException.getStatusCode() instanceof GrpcStatusCode) { + return ((GrpcStatusCode) apiException.getStatusCode()).getTransportCode(); + } } - return statusString; + Status s = Status.fromThrowable(error); + if (s != null) { + return s.getCode(); + } + return Status.Code.UNKNOWN; } static String extractTableId(Object request) { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java index 824d8be307..f1e98e03a4 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java @@ -29,7 +29,7 @@ public class UtilTest { @Test public void testOk() { - TagValue tagValue = TagValue.create(Util.extractStatus((Throwable) null)); + TagValue tagValue = TagValue.create(Util.extractStatus(null).name()); assertThat(tagValue.asString()).isEqualTo("OK"); } @@ -38,7 +38,7 @@ public void testError() { DeadlineExceededException error = new DeadlineExceededException( "Deadline exceeded", null, GrpcStatusCode.of(Status.Code.DEADLINE_EXCEEDED), true); - TagValue tagValue = TagValue.create(Util.extractStatus(error)); + TagValue tagValue = TagValue.create(Util.extractStatus(error).name()); assertThat(tagValue.asString()).isEqualTo("DEADLINE_EXCEEDED"); } } From b84ff59f361c6f3bf531ae41da0e7731bfad35fa Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 25 Feb 2026 20:22:36 -0500 Subject: [PATCH 17/33] =?UTF-8?q?chore:=20start=20pulling=20all=20metrics?= =?UTF-8?q?=20related=20things=20together=20under=20Metric=E2=80=A6=20(#28?= =?UTF-8?q?07)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …sImpl Change-Id: I78a5d0fed976381bd952ca405c36ce22e8c6178f 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://togithub.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) - [ ] Rollback plan is reviewed and LGTMed - [ ] All new data plane features have a completed end to end testing plan Fixes # ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). --- .../data/v2/internal/csm/Metrics.java | 38 +++ .../data/v2/internal/csm/MetricsImpl.java | 236 ++++++++++++++++++ .../data/v2/stub/BigtableClientContext.java | 132 +++------- .../BigtableCloudMonitoringExporter.java | 2 +- .../metrics/BuiltinMetricsTracerFactory.java | 3 +- .../bigtable/data/v2/stub/metrics/Util.java | 100 -------- 6 files changed, 308 insertions(+), 203 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java new file mode 100644 index 0000000000..d5e1dbf5b3 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java @@ -0,0 +1,38 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm; + +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; +import io.grpc.ManagedChannelBuilder; +import java.io.Closeable; +import java.io.IOException; +import javax.annotation.Nullable; + +public interface Metrics extends Closeable { + ApiTracerFactory createTracerFactory(ClientInfo clientInfo) throws IOException; + + > T configureGrpcChannel(T channelBuilder); + + @Nullable + ChannelPoolMetricsTracer getChannelPoolMetricsTracer(); + + void start(); + + @Override + void close(); +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java new file mode 100644 index 0000000000..139ea6727e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -0,0 +1,236 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm; + +import com.google.api.gax.grpc.GaxGrpcProperties; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.OpencensusTracerFactory; +import com.google.auth.Credentials; +import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; +import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.grpc.ManagedChannelBuilder; +import io.grpc.opentelemetry.GrpcOpenTelemetry; +import io.opencensus.stats.StatsRecorder; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import javax.annotation.Nullable; + +public class MetricsImpl implements Metrics, Closeable { + private final ApiTracerFactory userTracerFactory; + private final @Nullable OpenTelemetrySdk internalOtel; + private final @Nullable OpenTelemetry userOtel; + private final ScheduledExecutorService executor; + private final Tagger ocTagger; + private final StatsRecorder ocRecorder; + + @Nullable private final GrpcOpenTelemetry grpcOtel; + @Nullable private final ChannelPoolMetricsTracer channelPoolMetricsTracer; + private final List> tasks = new ArrayList<>(); + + public MetricsImpl( + ApiTracerFactory userTracerFactory, + @Nullable OpenTelemetrySdk internalOtel, + @Nullable OpenTelemetry userOtel, + Tagger ocTagger, + StatsRecorder ocRecorder, + ScheduledExecutorService executor) { + this.userTracerFactory = Preconditions.checkNotNull(userTracerFactory); + + this.internalOtel = internalOtel; + this.userOtel = userOtel; + + this.ocTagger = ocTagger; + this.ocRecorder = ocRecorder; + + this.executor = executor; + + if (internalOtel != null) { + this.grpcOtel = + GrpcOpenTelemetry.newBuilder() + .sdk(internalOtel) + .addOptionalLabel("grpc.lb.locality") + // Disable default grpc metrics + .disableAllMetrics() + // Enable specific grpc metrics + .enableMetrics(BuiltinMetricsConstants.GRPC_METRICS.keySet()) + .build(); + } else { + this.grpcOtel = null; + } + + if (internalOtel != null) { + this.channelPoolMetricsTracer = new ChannelPoolMetricsTracer(internalOtel); + } else { + this.channelPoolMetricsTracer = null; + } + } + + @Override + public void close() { + for (ScheduledFuture task : tasks) { + task.cancel(false); + } + if (internalOtel != null) { + internalOtel.close(); + } + } + + @Override + public void start() { + if (channelPoolMetricsTracer != null) { + tasks.add(channelPoolMetricsTracer.start(executor)); + } + } + + @Override + public > T configureGrpcChannel(T channelBuilder) { + if (grpcOtel == null) { + return channelBuilder; + } + grpcOtel.configureChannelBuilder(channelBuilder); + return channelBuilder; + } + + @Override + public ApiTracerFactory createTracerFactory(ClientInfo clientInfo) { + ImmutableList.Builder tracerFactories = ImmutableList.builder(); + tracerFactories + .add(createOCTracingFactory(clientInfo)) + .add(createOCMetricsFactory(clientInfo, ocTagger, ocRecorder)) + .add(userTracerFactory); + + if (internalOtel != null) { + tracerFactories.add(createOtelMetricsFactory(internalOtel, clientInfo)); + } + if (userOtel != null) { + tracerFactories.add(createOtelMetricsFactory(userOtel, clientInfo)); + } + + return new CompositeTracerFactory(tracerFactories.build()); + } + + @Override + @Nullable + public ChannelPoolMetricsTracer getChannelPoolMetricsTracer() { + return channelPoolMetricsTracer; + } + + public static OpenTelemetrySdk createBuiltinOtel( + ClientInfo clientInfo, + @Nullable Credentials defaultCredentials, + @Nullable String metricsEndpoint, + String universeDomain, + ScheduledExecutorService executor) + throws IOException { + + Credentials credentials = + BigtableDataSettings.getMetricsCredentials() != null + ? BigtableDataSettings.getMetricsCredentials() + : defaultCredentials; + + SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); + + for (Map.Entry entry : + BuiltinMetricsConstants.getAllViews().entrySet()) { + meterProvider.registerView(entry.getKey(), entry.getValue()); + } + + for (Map.Entry e : + BuiltinMetricsConstants.getInternalViews().entrySet()) { + meterProvider.registerView(e.getKey(), e.getValue()); + } + + MetricExporter publicExporter = + BigtableCloudMonitoringExporter.create( + clientInfo, credentials, metricsEndpoint, universeDomain, executor); + PeriodicMetricReaderBuilder readerBuilder = + PeriodicMetricReader.builder(publicExporter).setExecutor(executor); + meterProvider.registerMetricReader(readerBuilder.build()); + + return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + } + + private static ApiTracerFactory createOCTracingFactory(ClientInfo clientInfo) { + return new OpencensusTracerFactory( + ImmutableMap.builder() + // Annotate traces with the same tags as metrics + .put( + RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), + clientInfo.getInstanceName().getProject()) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), + clientInfo.getInstanceName().getInstance()) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), clientInfo.getAppProfileId()) + // Also annotate traces with library versions + .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) + .put("grpc", GaxGrpcProperties.getGrpcVersion()) + .put("gapic", Version.VERSION) + .build()); + } + + private static ApiTracerFactory createOCMetricsFactory( + ClientInfo clientInfo, Tagger tagger, StatsRecorder stats) { + + ImmutableMap attributes = + ImmutableMap.builder() + .put( + RpcMeasureConstants.BIGTABLE_PROJECT_ID, + TagValue.create(clientInfo.getInstanceName().getProject())) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID, + TagValue.create(clientInfo.getInstanceName().getInstance())) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, + TagValue.create(clientInfo.getAppProfileId())) + .build(); + return MetricsTracerFactory.create(tagger, stats, attributes); + } + + private static BuiltinMetricsTracerFactory createOtelMetricsFactory( + OpenTelemetry otel, ClientInfo clientInfo) { + + return BuiltinMetricsTracerFactory.create(otel, clientInfo); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index 6f005a0408..46474118b9 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -24,22 +24,17 @@ import com.google.api.gax.core.FixedExecutorProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.ClientContext; -import com.google.api.gax.tracing.ApiTracerFactory; import com.google.auth.Credentials; import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience; +import com.google.cloud.bigtable.data.v2.internal.csm.Metrics; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricsImpl; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; -import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; -import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; -import com.google.cloud.bigtable.data.v2.stub.metrics.Util; import com.google.cloud.bigtable.gaxx.grpc.BigtableTransportChannelProvider; import com.google.cloud.bigtable.gaxx.grpc.ChannelPrimer; -import com.google.common.collect.ImmutableList; import io.grpc.ManagedChannelBuilder; -import io.grpc.opentelemetry.GrpcOpenTelemetry; import io.opencensus.stats.Stats; import io.opencensus.stats.StatsRecorder; import io.opencensus.tags.Tagger; @@ -65,15 +60,11 @@ public class BigtableClientContext { private final boolean isChild; private final ClientInfo clientInfo; - private final ApiTracerFactory userTracerFactory; - @Nullable private final OpenTelemetrySdk builtinOpenTelemetry; - @Nullable private final OpenTelemetry userOpenTelemetry; + private final Metrics metrics; private final ClientContext clientContext; // the background executor shared for OTEL instances and monitoring client and all other // background tasks private final ExecutorProvider backgroundExecutorProvider; - private final Tagger ocTagger; - private final StatsRecorder ocRecorder; public static BigtableClientContext create(EnhancedBigtableStubSettings settings) throws IOException { @@ -110,8 +101,6 @@ public static BigtableClientContext create( FixedExecutorProvider.create(backgroundExecutor, shouldAutoClose); builder.setBackgroundExecutorProvider(executorProvider); - ApiTracerFactory userTracerFactory = settings.getTracerFactory(); - // Set up OpenTelemetry @Nullable OpenTelemetry userOtel = null; if (settings.getMetricsProvider() instanceof CustomOpenTelemetryMetricsProvider) { @@ -123,7 +112,7 @@ public static BigtableClientContext create( try { if (settings.areInternalMetricsEnabled()) { builtinOtel = - Util.createBuiltinOtel( + MetricsImpl.createBuiltinOtel( clientInfo, credentials, settings.getMetricsEndpoint(), @@ -134,26 +123,24 @@ public static BigtableClientContext create( logger.log(Level.WARNING, "Failed to get OTEL, will skip exporting client side metrics", t); } + Metrics metrics = + new MetricsImpl( + settings.getTracerFactory(), + builtinOtel, + userOtel, + ocTagger, + ocRecorder, + backgroundExecutor); + // Set up channel InstantiatingGrpcChannelProvider.Builder transportProvider = builder.getTransportChannelProvider() instanceof InstantiatingGrpcChannelProvider ? ((InstantiatingGrpcChannelProvider) builder.getTransportChannelProvider()).toBuilder() : null; - @Nullable ChannelPoolMetricsTracer channelPoolMetricsTracer = null; - // Internal metrics are scoped to the connections, so we need a mutable transportProvider, - // otherwise there is - // no reason to build the internal OtelProvider if (transportProvider != null) { - if (builtinOtel != null) { - channelPoolMetricsTracer = new ChannelPoolMetricsTracer(builtinOtel); - - // Configure grpc metrics - configureGrpcOtel(transportProvider, builtinOtel); - } - } + configureGrpcOtel(transportProvider, metrics); - if (transportProvider != null) { setupCookieHolder(transportProvider); ChannelPrimer channelPrimer = NoOpChannelPrimer.create(); @@ -173,42 +160,25 @@ public static BigtableClientContext create( BigtableTransportChannelProvider.create( transportProvider.build(), channelPrimer, - channelPoolMetricsTracer, + metrics.getChannelPoolMetricsTracer(), backgroundExecutor); builder.setTransportChannelProvider(btTransportProvider); } ClientContext clientContext = ClientContext.create(builder.build()); - if (channelPoolMetricsTracer != null) { - channelPoolMetricsTracer.start(clientContext.getExecutor()); - } - return new BigtableClientContext( - false, - clientInfo, - clientContext, - userTracerFactory, - builtinOtel, - userOtel, - ocTagger, - ocRecorder, - executorProvider); + metrics.start(); + try { + return new BigtableClientContext(false, clientInfo, clientContext, metrics, executorProvider); + } catch (IOException | RuntimeException t) { + metrics.close(); + throw t; + } } private static void configureGrpcOtel( - InstantiatingGrpcChannelProvider.Builder transportProvider, OpenTelemetrySdk otel) { - - GrpcOpenTelemetry grpcOtel = - GrpcOpenTelemetry.newBuilder() - .sdk(otel) - .addOptionalLabel("grpc.lb.locality") - // Disable default grpc metrics - .disableAllMetrics() - // Enable specific grpc metrics - .enableMetrics(BuiltinMetricsConstants.GRPC_METRICS.keySet()) - .build(); - + InstantiatingGrpcChannelProvider.Builder transportProvider, Metrics metrics) { @SuppressWarnings("rawtypes") ApiFunction oldConfigurator = transportProvider.getChannelConfigurator(); @@ -218,8 +188,7 @@ private static void configureGrpcOtel( if (oldConfigurator != null) { b = oldConfigurator.apply(b); } - grpcOtel.configureChannelBuilder(b); - return b; + return metrics.configureGrpcChannel(b); }); } @@ -227,54 +196,25 @@ private BigtableClientContext( boolean isChild, ClientInfo clientInfo, ClientContext clientContext, - ApiTracerFactory userTracerFactory, - @Nullable OpenTelemetrySdk builtinOtel, - @Nullable OpenTelemetry userOtel, - Tagger ocTagger, - StatsRecorder ocRecorder, + Metrics metrics, ExecutorProvider backgroundExecutorProvider) throws IOException { this.isChild = isChild; this.clientInfo = clientInfo; - this.userTracerFactory = userTracerFactory; - this.builtinOpenTelemetry = builtinOtel; - this.userOpenTelemetry = userOtel; - this.ocTagger = ocTagger; - this.ocRecorder = ocRecorder; + this.metrics = metrics; this.backgroundExecutorProvider = backgroundExecutorProvider; - ImmutableList.Builder tracerFactories = ImmutableList.builder(); - tracerFactories - .add(Util.createOCTracingFactory(clientInfo)) - .add(Util.createOCMetricsFactory(clientInfo, ocTagger, ocRecorder)) - .add(userTracerFactory); - - if (builtinOtel != null) { - tracerFactories.add(Util.createOtelMetricsFactory(builtinOtel, clientInfo)); - } - if (userOtel != null) { - tracerFactories.add(Util.createOtelMetricsFactory(userOtel, clientInfo)); - } - this.clientContext = - clientContext.toBuilder() - .setTracerFactory(new CompositeTracerFactory(tracerFactories.build())) - .build(); + clientContext.toBuilder().setTracerFactory(metrics.createTracerFactory(clientInfo)).build(); } public ClientInfo getClientInfo() { return clientInfo; } - @Nullable - public OpenTelemetrySdk getBuiltinOpenTelemetry() { - return builtinOpenTelemetry; - } - - @Nullable - public OpenTelemetry getUserOpenTelemetry() { - return this.userOpenTelemetry; + public Metrics getMetrics() { + return metrics; } public ClientContext getClientContext() { @@ -287,11 +227,7 @@ public BigtableClientContext createChild(InstanceName instanceName, String appPr true, clientInfo.toBuilder().setInstanceName(instanceName).setAppProfileId(appProfileId).build(), clientContext, - userTracerFactory, - builtinOpenTelemetry, - userOpenTelemetry, - ocTagger, - ocRecorder, + metrics, backgroundExecutorProvider); } @@ -303,12 +239,8 @@ public void close() throws Exception { for (BackgroundResource resource : clientContext.getBackgroundResources()) { resource.close(); } - if (builtinOpenTelemetry != null) { - builtinOpenTelemetry.close(); - } - if (builtinOpenTelemetry != null) { - builtinOpenTelemetry.close(); - } + metrics.close(); + if (backgroundExecutorProvider.shouldAutoClose()) { backgroundExecutorProvider.getExecutor().shutdown(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 67cc5ba134..2aba290aff 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -113,7 +113,7 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { private final AtomicBoolean exportFailureLogged = new AtomicBoolean(false); - static BigtableCloudMonitoringExporter create( + public static BigtableCloudMonitoringExporter create( ClientInfo clientInfo, @Nullable Credentials credentials, @Nullable String endpoint, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index 3d83659a28..c59c145f7f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -46,7 +46,6 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; -import java.io.IOException; /** * {@link ApiTracerFactory} that will generate OpenTelemetry metrics by using the {@link ApiTracer} @@ -74,7 +73,7 @@ public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { private final DoubleHistogram batchWriteFlowControlFactorHistogram; public static BuiltinMetricsTracerFactory create( - OpenTelemetry openTelemetry, ClientInfo clientInfo) throws IOException { + OpenTelemetry openTelemetry, ClientInfo clientInfo) { return new BuiltinMetricsTracerFactory(openTelemetry, clientInfo); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index dc6155f88a..7381b220e2 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -16,13 +16,9 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.core.InternalApi; -import com.google.api.gax.grpc.GaxGrpcProperties; import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiException; -import com.google.api.gax.tracing.ApiTracerFactory; -import com.google.api.gax.tracing.OpencensusTracerFactory; -import com.google.auth.Credentials; import com.google.bigtable.v2.AuthorizedViewName; import com.google.bigtable.v2.CheckAndMutateRowRequest; import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest; @@ -36,27 +32,10 @@ import com.google.bigtable.v2.ResponseParams; import com.google.bigtable.v2.SampleRowKeysRequest; import com.google.bigtable.v2.TableName; -import com.google.cloud.bigtable.Version; -import com.google.cloud.bigtable.data.v2.BigtableDataSettings; -import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; import io.grpc.Status; -import io.opencensus.stats.StatsRecorder; -import io.opencensus.tags.TagKey; -import io.opencensus.tags.TagValue; -import io.opencensus.tags.Tagger; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; -import java.io.IOException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -65,7 +44,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CancellationException; -import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; /** Utilities to help integrating with OpenCensus. */ @@ -156,41 +134,6 @@ static Map> createStatsHeaders(ApiCallContext apiCallContex return headers.build(); } - public static OpenTelemetrySdk createBuiltinOtel( - ClientInfo clientInfo, - @Nullable Credentials defaultCredentials, - @Nullable String metricsEndpoint, - String universeDomain, - ScheduledExecutorService executor) - throws IOException { - - Credentials credentials = - BigtableDataSettings.getMetricsCredentials() != null - ? BigtableDataSettings.getMetricsCredentials() - : defaultCredentials; - - SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); - - for (Map.Entry entry : - BuiltinMetricsConstants.getAllViews().entrySet()) { - meterProvider.registerView(entry.getKey(), entry.getValue()); - } - - for (Map.Entry e : - BuiltinMetricsConstants.getInternalViews().entrySet()) { - meterProvider.registerView(e.getKey(), e.getValue()); - } - - MetricExporter publicExporter = - BigtableCloudMonitoringExporter.create( - clientInfo, credentials, metricsEndpoint, universeDomain, executor); - PeriodicMetricReaderBuilder readerBuilder = - PeriodicMetricReader.builder(publicExporter).setExecutor(executor); - meterProvider.registerMetricReader(readerBuilder.build()); - - return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - } - public static String formatTransportTypeMetricLabel( MetadataExtractorInterceptor.SidebandData sidebandData) { return Optional.ofNullable(sidebandData) @@ -219,47 +162,4 @@ public static String formatZoneIdMetricLabel( .filter(s -> !s.isEmpty()) .orElse("global"); } - - public static ApiTracerFactory createOCTracingFactory(ClientInfo clientInfo) { - return new OpencensusTracerFactory( - ImmutableMap.builder() - // Annotate traces with the same tags as metrics - .put( - RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), - clientInfo.getInstanceName().getProject()) - .put( - RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), - clientInfo.getInstanceName().getInstance()) - .put( - RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), clientInfo.getAppProfileId()) - // Also annotate traces with library versions - .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) - .put("grpc", GaxGrpcProperties.getGrpcVersion()) - .put("gapic", Version.VERSION) - .build()); - } - - public static ApiTracerFactory createOCMetricsFactory( - ClientInfo clientInfo, Tagger tagger, StatsRecorder stats) { - - ImmutableMap attributes = - ImmutableMap.builder() - .put( - RpcMeasureConstants.BIGTABLE_PROJECT_ID, - TagValue.create(clientInfo.getInstanceName().getProject())) - .put( - RpcMeasureConstants.BIGTABLE_INSTANCE_ID, - TagValue.create(clientInfo.getInstanceName().getInstance())) - .put( - RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, - TagValue.create(clientInfo.getAppProfileId())) - .build(); - return MetricsTracerFactory.create(tagger, stats, attributes); - } - - public static BuiltinMetricsTracerFactory createOtelMetricsFactory( - OpenTelemetry otel, ClientInfo clientInfo) throws IOException { - - return BuiltinMetricsTracerFactory.create(otel, clientInfo); - } } From ed4ad83e9324c87324f8612a2b9b57064b3237fb Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 25 Feb 2026 21:30:30 -0500 Subject: [PATCH 18/33] chore: wire up the new typesafe metrics (#2808) --- .../data/v2/internal/csm/MetricsImpl.java | 21 +- .../data/v2/internal/csm/attributes/Util.java | 43 ++- .../TableApplicationBlockingLatency.java | 3 +- .../csm/metrics/TableAttemptLatency.java | 3 +- .../csm/metrics/TableAttemptLatency2.java | 13 +- .../metrics/TableClientBlockingLatency.java | 3 +- .../metrics/TableConnectivityErrorCount.java | 3 +- .../csm/metrics/TableDebugTagCount.java | 3 +- .../metrics/TableFirstResponseLatency.java | 3 +- .../csm/metrics/TableOperationLatency.java | 3 +- .../internal/csm/metrics/TableRetryCount.java | 3 +- .../csm/metrics/TableServerLatency.java | 3 +- .../v2/internal/csm/schema/TableSchema.java | 8 +- .../data/v2/stub/BigtableClientContext.java | 1 + .../v2/stub/MetadataExtractorInterceptor.java | 11 +- .../v2/stub/metrics/BuiltinMetricsTracer.java | 258 +++++++----------- .../metrics/BuiltinMetricsTracerFactory.java | 178 +----------- .../metrics/ChannelPoolMetricsTracer.java | 85 ++---- .../data/v2/stub/metrics/MetricsTracer.java | 2 +- .../bigtable/data/v2/stub/metrics/Util.java | 34 --- .../BigtableTransportChannelProvider.java | 2 +- .../metrics/BuiltinMetricsTracerTest.java | 7 +- .../metrics/ChannelPoolMetricsTracerTest.java | 19 +- 23 files changed, 243 insertions(+), 466 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java index 139ea6727e..3ae54c5313 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -21,6 +21,7 @@ import com.google.auth.Credentials; import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; @@ -57,6 +58,8 @@ import javax.annotation.Nullable; public class MetricsImpl implements Metrics, Closeable { + private final MetricRegistry metricRegistry; + private final ApiTracerFactory userTracerFactory; private final @Nullable OpenTelemetrySdk internalOtel; private final @Nullable OpenTelemetry userOtel; @@ -69,12 +72,14 @@ public class MetricsImpl implements Metrics, Closeable { private final List> tasks = new ArrayList<>(); public MetricsImpl( + ClientInfo clientInfo, ApiTracerFactory userTracerFactory, @Nullable OpenTelemetrySdk internalOtel, @Nullable OpenTelemetry userOtel, Tagger ocTagger, StatsRecorder ocRecorder, ScheduledExecutorService executor) { + metricRegistry = new MetricRegistry(); this.userTracerFactory = Preconditions.checkNotNull(userTracerFactory); this.internalOtel = internalOtel; @@ -100,7 +105,9 @@ public MetricsImpl( } if (internalOtel != null) { - this.channelPoolMetricsTracer = new ChannelPoolMetricsTracer(internalOtel); + this.channelPoolMetricsTracer = + new ChannelPoolMetricsTracer( + metricRegistry.newRecorderRegistry(internalOtel.getMeterProvider()), clientInfo); } else { this.channelPoolMetricsTracer = null; } @@ -141,10 +148,14 @@ public ApiTracerFactory createTracerFactory(ClientInfo clientInfo) { .add(userTracerFactory); if (internalOtel != null) { - tracerFactories.add(createOtelMetricsFactory(internalOtel, clientInfo)); + tracerFactories.add( + createOtelMetricsFactory( + metricRegistry.newRecorderRegistry(internalOtel.getMeterProvider()), clientInfo)); } if (userOtel != null) { - tracerFactories.add(createOtelMetricsFactory(userOtel, clientInfo)); + tracerFactories.add( + createOtelMetricsFactory( + metricRegistry.newRecorderRegistry(userOtel.getMeterProvider()), clientInfo)); } return new CompositeTracerFactory(tracerFactories.build()); @@ -229,8 +240,8 @@ private static ApiTracerFactory createOCMetricsFactory( } private static BuiltinMetricsTracerFactory createOtelMetricsFactory( - OpenTelemetry otel, ClientInfo clientInfo) { + RecorderRegistry recorder, ClientInfo clientInfo) { - return BuiltinMetricsTracerFactory.create(otel, clientInfo); + return BuiltinMetricsTracerFactory.create(recorder, clientInfo); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java index cf9c2a114e..9379f4726d 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java @@ -16,26 +16,37 @@ package com.google.cloud.bigtable.data.v2.internal.csm.attributes; +import com.google.bigtable.v2.PeerInfo; import com.google.bigtable.v2.PeerInfo.TransportType; -import com.google.common.base.Preconditions; +import com.google.bigtable.v2.ResponseParams; import java.util.Locale; +import java.util.Optional; +import javax.annotation.Nullable; public class Util { static final String TRANSPORT_TYPE_PREFIX = "TRANSPORT_TYPE_"; - public static String transportTypeToString(TransportType transportType) { + public static String formatTransportZone(@Nullable PeerInfo peerInfo) { + return Optional.ofNullable(peerInfo).map(PeerInfo::getApplicationFrontendZone).orElse(""); + } - Preconditions.checkArgument( - transportType.name().startsWith(TRANSPORT_TYPE_PREFIX) - || transportType == TransportType.UNRECOGNIZED, - "TransportType values must start with %s", - TRANSPORT_TYPE_PREFIX); + public static String formatTransportSubzone(@Nullable PeerInfo peerInfo) { + return Optional.ofNullable(peerInfo).map(PeerInfo::getApplicationFrontendSubzone).orElse(""); + } + + public static String formatTransportType(@Nullable PeerInfo peerInfo) { + return transportTypeToString( + Optional.ofNullable(peerInfo) + .map(PeerInfo::getTransportType) + .orElse(TransportType.TRANSPORT_TYPE_UNKNOWN)); + } + public static String transportTypeToString(TransportType transportType) { if (transportType == TransportType.TRANSPORT_TYPE_UNKNOWN) { - return "session_none"; + return "none"; } if (transportType == TransportType.UNRECOGNIZED) { - return "session_unrecognized"; + return "unrecognized"; } return transportType @@ -43,4 +54,18 @@ public static String transportTypeToString(TransportType transportType) { .substring(TRANSPORT_TYPE_PREFIX.length()) .toLowerCase(Locale.ENGLISH); } + + public static String formatClusterIdMetricLabel(@Nullable ResponseParams clusterInfo) { + return Optional.ofNullable(clusterInfo) + .map(ResponseParams::getClusterId) + .filter(s -> !s.isEmpty()) + .orElse(""); + } + + public static String formatZoneIdMetricLabel(@Nullable ResponseParams clusterInfo) { + return Optional.ofNullable(clusterInfo) + .map(ResponseParams::getZoneId) + .filter(s -> !s.isEmpty()) + .orElse("global"); + } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java index 90e390304e..05fdefd0be 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java @@ -29,6 +29,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableApplicationBlockingLatency extends MetricWrapper { private static final String NAME = @@ -69,7 +70,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Duration duration) { Attributes attributes = getSchema() diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java index 2ba86e89c9..530498fa9a 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java @@ -30,6 +30,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableAttemptLatency extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies"; @@ -67,7 +68,7 @@ private Recorder(Meter meter) { public void record( ClientInfo clientInfo, String tableId, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, MethodInfo methodInfo, Status.Code code, Duration latency) { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java index 0570559610..63cb2aa929 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java @@ -31,6 +31,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableAttemptLatency2 extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies2"; @@ -68,8 +69,8 @@ private Recorder(Meter meter) { public void record( ClientInfo clientInfo, String tableId, - PeerInfo peerInfo, - ResponseParams clusterInfo, + @Nullable PeerInfo peerInfo, + @Nullable ResponseParams clusterInfo, MethodInfo methodInfo, Status.Code code, Duration latency) { @@ -77,14 +78,12 @@ public void record( Attributes attributes = getSchema() .createResourceAttrs(clientInfo, tableId, clusterInfo) - .put( - MetricLabels.TRANSPORT_TYPE, - Util.transportTypeToString(peerInfo.getTransportType())) + .put(MetricLabels.TRANSPORT_TYPE, Util.formatTransportType(peerInfo)) .put(MetricLabels.STATUS_KEY, code.name()) .put(MetricLabels.TRANSPORT_REGION, "") // To maintain backwards compat CLIENT_UID is set using sideband data in the exporter - .put(MetricLabels.TRANSPORT_ZONE, peerInfo.getApplicationFrontendZone()) - .put(MetricLabels.TRANSPORT_SUBZONE, peerInfo.getApplicationFrontendSubzone()) + .put(MetricLabels.TRANSPORT_ZONE, Util.formatTransportZone(peerInfo)) + .put(MetricLabels.TRANSPORT_SUBZONE, Util.formatTransportSubzone(peerInfo)) .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName()) .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId()) .put(MetricLabels.METHOD_KEY, methodInfo.getName()) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java index 1d8deca639..7f9a584a69 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java @@ -29,6 +29,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableClientBlockingLatency extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/throttling_latencies"; @@ -69,7 +70,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Duration duration) { Attributes attributes = getSchema() diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java index 95d8fca949..0233b8adef 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java @@ -28,6 +28,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; +import javax.annotation.Nullable; public class TableConnectivityErrorCount extends MetricWrapper { private static final String NAME = @@ -68,7 +69,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Status.Code code, long count) { Attributes attributes = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java index f8bfc25fb5..5d9dbc8536 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableDebugTagCount.java @@ -26,6 +26,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; +import javax.annotation.Nullable; public class TableDebugTagCount extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/debug_tags"; @@ -63,7 +64,7 @@ public void record( ClientInfo clientInfo, String tableId, String tag, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, long amount) { Attributes attributes = getSchema() diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java index af5909c054..bde5009f68 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java @@ -30,6 +30,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableFirstResponseLatency extends MetricWrapper { private static final String NAME = @@ -72,7 +73,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Status.Code code, Duration duration) { Attributes attributes = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java index b6323cce8b..4a30d66f20 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java @@ -30,6 +30,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableOperationLatency extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/operation_latencies"; @@ -70,7 +71,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Status.Code code, Duration duration) { Attributes attributes = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java index de7b608b4e..a81a4bf903 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java @@ -28,6 +28,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; +import javax.annotation.Nullable; public class TableRetryCount extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/retry_count"; @@ -65,7 +66,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Status.Code code, long amount) { Attributes attributes = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java index b759591113..0d8dc0197b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java @@ -30,6 +30,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import java.time.Duration; +import javax.annotation.Nullable; public class TableServerLatency extends MetricWrapper { private static final String NAME = "bigtable.googleapis.com/internal/client/server_latencies"; @@ -70,7 +71,7 @@ public void record( ClientInfo clientInfo, String tableId, MethodInfo methodInfo, - ResponseParams clusterInfo, + @Nullable ResponseParams clusterInfo, Status.Code code, Duration duration) { Attributes attributes = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java index f536f73837..618551bb87 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java @@ -19,11 +19,13 @@ import com.google.bigtable.v2.ResponseParams; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util; import com.google.common.collect.ImmutableList; import com.google.monitoring.v3.ProjectName; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import javax.annotation.Nullable; public final class TableSchema extends Schema { // This implements the `bigtable_client_raw` resource defined in @@ -52,12 +54,12 @@ public ProjectName extractProjectName(Attributes attrs, EnvInfo envInfo, ClientI } public AttributesBuilder createResourceAttrs( - ClientInfo clientInfo, String tableId, ResponseParams clusterInfo) { + ClientInfo clientInfo, String tableId, @Nullable ResponseParams clusterInfo) { return Attributes.builder() .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject()) .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance()) .put(TABLE_ID_KEY, tableId) - .put(CLUSTER_ID_KEY, clusterInfo.getClusterId()) - .put(ZONE_ID_KEY, clusterInfo.getZoneId()); + .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(clusterInfo)) + .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(clusterInfo)); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index 46474118b9..c82b0a8d02 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -125,6 +125,7 @@ public static BigtableClientContext create( Metrics metrics = new MetricsImpl( + clientInfo, settings.getTracerFactory(), builtinOtel, userOtel, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java index 5b43f57527..14ad73131f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MetadataExtractorInterceptor.java @@ -33,6 +33,7 @@ import io.grpc.MethodDescriptor; import io.grpc.Status; import io.grpc.alts.AltsContextUtil; +import java.time.Duration; import java.util.Base64; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -98,7 +99,7 @@ public static class SidebandData { @Nullable private volatile ResponseParams responseParams; @Nullable private volatile PeerInfo peerInfo; - @Nullable private volatile Long gfeTiming; + @Nullable private volatile Duration gfeTiming; @Nullable public ResponseParams getResponseParams() { @@ -111,7 +112,7 @@ public PeerInfo getPeerInfo() { } @Nullable - public Long getGfeTiming() { + public Duration getGfeTiming() { return gfeTiming; } @@ -134,7 +135,7 @@ void onClose(Status status, Metadata trailers) { } @Nullable - private static Long extractGfeLatency(Metadata metadata) { + private static Duration extractGfeLatency(Metadata metadata) { String serverTiming = metadata.get(SERVER_TIMING_HEADER_KEY); if (serverTiming == null) { return null; @@ -142,14 +143,14 @@ private static Long extractGfeLatency(Metadata metadata) { Matcher matcher = SERVER_TIMING_HEADER_PATTERN.matcher(serverTiming); // this should always be true if (matcher.find()) { - return Long.parseLong(matcher.group("dur")); + return Duration.ofMillis(Long.parseLong(matcher.group("dur"))); } return null; } @Nullable private static PeerInfo extractPeerInfo( - Metadata metadata, Long gfeTiming, Attributes attributes) { + Metadata metadata, Duration gfeTiming, Attributes attributes) { String encodedStr = metadata.get(PEER_INFO_KEY); if (Strings.isNullOrEmpty(encodedStr)) { return null; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index 74d09f5834..57181faa34 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -15,38 +15,22 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.api.gax.tracing.ApiTracerFactory.OperationType; import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLIED_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TRANSPORT_REGION; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TRANSPORT_SUBZONE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TRANSPORT_TYPE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TRANSPORT_ZONE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY; import static com.google.cloud.bigtable.data.v2.stub.metrics.Util.extractStatus; import com.google.api.core.ObsoleteApi; import com.google.api.gax.retrying.ServerStreamingAttemptException; -import com.google.api.gax.tracing.SpanName; -import com.google.bigtable.v2.PeerInfo; -import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; +import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor.SidebandData; import com.google.common.base.Stopwatch; +import com.google.common.collect.Comparators; import com.google.common.math.IntMath; import io.grpc.Deadline; import io.grpc.Status; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.DoubleGauge; -import io.opentelemetry.api.metrics.DoubleHistogram; -import io.opentelemetry.api.metrics.LongCounter; import java.time.Duration; -import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -59,9 +43,11 @@ * bigtable.googleapis.com/client namespace */ class BuiltinMetricsTracer extends BigtableTracer { - private static final String NAME = "java-bigtable/" + Version.VERSION; - private final OperationType operationType; - private final SpanName spanName; + private static final MethodInfo READ_ROWS = + MethodInfo.builder().setName("Bigtable.ReadRows").setStreaming(true).build(); + private final MetricRegistry.RecorderRegistry recorder; + private final ClientInfo clientInfo; + private final MethodInfo methodInfo; // Operation level metrics private final AtomicBoolean operationFinishedEarly = new AtomicBoolean(); @@ -91,67 +77,19 @@ class BuiltinMetricsTracer extends BigtableTracer { private final AtomicLong totalClientBlockingTime = new AtomicLong(0); - private final Attributes baseAttributes; - private final AtomicLong grpcMessageSentDelay = new AtomicLong(0); private Deadline operationDeadline = null; - private volatile long remainingDeadlineAtAttemptStart = 0; - - // TODO: ensure that this is never null and remove all of the checks - // Sideband data wrapper itself should never be null unless a callable chain forgets to - // add BigtableTracer{Streaming,Unary}Callable. Which would be considered a bug. - @Nullable private volatile MetadataExtractorInterceptor.SidebandData sidebandData = null; - - // OpenCensus (and server) histogram buckets use [start, end), however OpenTelemetry uses (start, - // end]. To work around this, we measure all the latencies in nanoseconds and convert them - // to milliseconds and use DoubleHistogram. This should minimize the chance of a data - // point fall on the bucket boundary that causes off by one errors. - private final DoubleHistogram operationLatenciesHistogram; - private final DoubleHistogram attemptLatenciesHistogram; - private final DoubleHistogram attemptLatencies2Histogram; - private final DoubleHistogram serverLatenciesHistogram; - private final DoubleHistogram firstResponseLatenciesHistogram; - private final DoubleHistogram clientBlockingLatenciesHistogram; - private final DoubleHistogram applicationBlockingLatenciesHistogram; - private final DoubleHistogram remainingDeadlineHistogram; - private final LongCounter connectivityErrorCounter; - private final LongCounter retryCounter; - private final DoubleGauge batchWriteFlowControlTargetQps; - private final DoubleHistogram batchWriteFlowControlFactorHistogram; + private volatile Duration remainingDeadlineAtAttemptStart = Duration.ZERO; + + private volatile MetadataExtractorInterceptor.SidebandData sidebandData = new SidebandData(); BuiltinMetricsTracer( - OperationType operationType, - SpanName spanName, - Attributes attributes, - DoubleHistogram operationLatenciesHistogram, - DoubleHistogram attemptLatenciesHistogram, - DoubleHistogram attemptLatencies2Histogram, - DoubleHistogram serverLatenciesHistogram, - DoubleHistogram firstResponseLatenciesHistogram, - DoubleHistogram clientBlockingLatenciesHistogram, - DoubleHistogram applicationBlockingLatenciesHistogram, - DoubleHistogram deadlineHistogram, - LongCounter connectivityErrorCounter, - LongCounter retryCounter, - DoubleGauge batchWriteFlowControlTargetQps, - DoubleHistogram batchWriteFlowControlFactorHistogram) { - this.operationType = operationType; - this.spanName = spanName; - this.baseAttributes = attributes; - - this.operationLatenciesHistogram = operationLatenciesHistogram; - this.attemptLatenciesHistogram = attemptLatenciesHistogram; - this.attemptLatencies2Histogram = attemptLatencies2Histogram; - this.serverLatenciesHistogram = serverLatenciesHistogram; - this.firstResponseLatenciesHistogram = firstResponseLatenciesHistogram; - this.clientBlockingLatenciesHistogram = clientBlockingLatenciesHistogram; - this.applicationBlockingLatenciesHistogram = applicationBlockingLatenciesHistogram; - this.remainingDeadlineHistogram = deadlineHistogram; - this.connectivityErrorCounter = connectivityErrorCounter; - this.retryCounter = retryCounter; - this.batchWriteFlowControlTargetQps = batchWriteFlowControlTargetQps; - this.batchWriteFlowControlFactorHistogram = batchWriteFlowControlFactorHistogram; + MetricRegistry.RecorderRegistry recorder, ClientInfo clientInfo, MethodInfo methodInfo) { + + this.recorder = recorder; + this.clientInfo = clientInfo; + this.methodInfo = methodInfo; } @Override @@ -195,7 +133,8 @@ public void attemptStarted(Object request, int attemptNumber) { attemptCount++; attemptTimer = Stopwatch.createStarted(); if (operationDeadline != null) { - remainingDeadlineAtAttemptStart = operationDeadline.timeRemaining(TimeUnit.MILLISECONDS); + remainingDeadlineAtAttemptStart = + Duration.ofMillis(operationDeadline.timeRemaining(TimeUnit.MILLISECONDS)); } if (request != null) { this.tableId = Util.extractTableId(request); @@ -328,7 +267,7 @@ public void setTotalTimeoutDuration(java.time.Duration totalTimeoutDuration) { if (operationDeadline == null && !totalTimeoutDuration.isZero()) { this.operationDeadline = Deadline.after(totalTimeoutDuration.toMillis(), TimeUnit.MILLISECONDS); - this.remainingDeadlineAtAttemptStart = totalTimeoutDuration.toMillis(); + this.remainingDeadlineAtAttemptStart = totalTimeoutDuration; } } @@ -347,38 +286,45 @@ private void recordOperationCompletion(@Nullable Throwable throwable) { } long operationLatencyNano = operationTimer.elapsed(TimeUnit.NANOSECONDS); - boolean isStreaming = operationType == OperationType.ServerStreaming; Status.Code code = extractStatus(throwable); - // Publish metric data with all the attributes. The attributes get filtered in - // BuiltinMetricsConstants when we construct the views. - Attributes attributes = - baseAttributes.toBuilder() - .put(TABLE_ID_KEY, tableId) - .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(sidebandData)) - .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(sidebandData)) - .put(METHOD_KEY, spanName.toString()) - .put(CLIENT_NAME_KEY, NAME) - .put(STREAMING_KEY, isStreaming) - .put(STATUS_KEY, code.name()) - .build(); - // Only record when retry count is greater than 0 so the retry // graph will be less confusing if (attemptCount > 1) { - retryCounter.add(attemptCount - 1, attributes); + recorder.retryCount.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + code, + attemptCount - 1); } - operationLatenciesHistogram.record(convertToMs(operationLatencyNano), attributes); + recorder.operationLatency.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + code, + Duration.ofNanos(operationLatencyNano)); // serverLatencyTimer should already be stopped in recordAttemptCompletion long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get(); - applicationBlockingLatenciesHistogram.record(convertToMs(applicationLatencyNano), attributes); - - if (operationType == OperationType.ServerStreaming - && spanName.getMethodName().equals("ReadRows")) { - firstResponseLatenciesHistogram.record( - convertToMs(firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS)), attributes); + recorder.applicationBlockingLatency.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + Duration.ofNanos(applicationLatencyNano)); + + if (methodInfo.equals(READ_ROWS)) { + recorder.firstResponseLantency.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + code, + firstResponsePerOpTimer.elapsed()); } } @@ -396,8 +342,6 @@ private void recordAttemptCompletion(@Nullable Throwable throwable) { } } - boolean isStreaming = operationType == OperationType.ServerStreaming; - // Patch the throwable until it's fixed in gax. When an attempt failed, // it'll throw a ServerStreamingAttemptException. Unwrap the exception // so it could get processed by extractStatus @@ -407,60 +351,56 @@ private void recordAttemptCompletion(@Nullable Throwable throwable) { Status.Code code = extractStatus(throwable); - Attributes attributes = - baseAttributes.toBuilder() - .put(TABLE_ID_KEY, tableId) - .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(sidebandData)) - .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(sidebandData)) - .put(METHOD_KEY, spanName.toString()) - .put(CLIENT_NAME_KEY, NAME) - .put(STREAMING_KEY, isStreaming) - .put(STATUS_KEY, code.name()) - .build(); - totalClientBlockingTime.addAndGet(grpcMessageSentDelay.get()); - clientBlockingLatenciesHistogram.record(convertToMs(totalClientBlockingTime.get()), attributes); - - attemptLatenciesHistogram.record( - convertToMs(attemptTimer.elapsed(TimeUnit.NANOSECONDS)), attributes); - - String transportTypeStr = "cloudpath"; - String transportRegion = ""; - String transportZone = ""; - String transportSubzone = ""; - - if (sidebandData != null) { - transportTypeStr = Util.formatTransportTypeMetricLabel(sidebandData); - transportZone = - Optional.ofNullable(sidebandData.getPeerInfo()) - .map(PeerInfo::getApplicationFrontendZone) - .orElse(""); - transportSubzone = - Optional.ofNullable(sidebandData.getPeerInfo()) - .map(PeerInfo::getApplicationFrontendSubzone) - .orElse(""); - } - - attemptLatencies2Histogram.record( - convertToMs(attemptTimer.elapsed(TimeUnit.NANOSECONDS)), - attributes.toBuilder() - .put(TRANSPORT_TYPE, transportTypeStr) - .put(TRANSPORT_REGION, transportRegion) - .put(TRANSPORT_ZONE, transportZone) - .put(TRANSPORT_SUBZONE, transportSubzone) - .build()); + recorder.clientBlockingLatency.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + Duration.ofNanos(totalClientBlockingTime.get())); + + recorder.attemptLatency.record( + clientInfo, + tableId, + sidebandData.getResponseParams(), + methodInfo, + code, + attemptTimer.elapsed()); + + recorder.attemptLatency2.record( + clientInfo, + tableId, + sidebandData.getPeerInfo(), + sidebandData.getResponseParams(), + methodInfo, + code, + attemptTimer.elapsed()); // When operationDeadline is set, it's possible that the deadline is passed by the time we send // a new attempt. In this case we'll record 0. if (operationDeadline != null) { - remainingDeadlineHistogram.record(Math.max(0, remainingDeadlineAtAttemptStart), attributes); + recorder.remainingDeadline.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + code, + Comparators.max(remainingDeadlineAtAttemptStart, Duration.ZERO)); } - if (sidebandData != null && sidebandData.getGfeTiming() != null) { - serverLatenciesHistogram.record(sidebandData.getGfeTiming(), attributes); - connectivityErrorCounter.add(0, attributes); + if (sidebandData.getGfeTiming() != null) { + recorder.serverLatency.record( + clientInfo, + tableId, + methodInfo, + sidebandData.getResponseParams(), + code, + sidebandData.getGfeTiming()); + recorder.connectivityErrorCount.record( + clientInfo, tableId, methodInfo, sidebandData.getResponseParams(), code, 0); } else { - connectivityErrorCounter.add(1, attributes); + recorder.connectivityErrorCount.record( + clientInfo, tableId, methodInfo, sidebandData.getResponseParams(), code, 1); } } @@ -471,21 +411,13 @@ private static double convertToMs(long nanoSeconds) { @Override public void setBatchWriteFlowControlTargetQps(double targetQps) { - Attributes attributes = baseAttributes.toBuilder().put(METHOD_KEY, spanName.toString()).build(); - - batchWriteFlowControlTargetQps.set(targetQps, attributes); + recorder.batchWriteFlowControlTargetQps.record(clientInfo, methodInfo, targetQps); } @Override public void addBatchWriteFlowControlFactor( double factor, @Nullable Throwable throwable, boolean applied) { - Attributes attributes = - baseAttributes.toBuilder() - .put(METHOD_KEY, spanName.toString()) - .put(STATUS_KEY, extractStatus(throwable).name()) - .put(APPLIED_KEY, applied) - .build(); - - batchWriteFlowControlFactorHistogram.record(factor, attributes); + recorder.batchWriteFlowControlFactor.record( + clientInfo, extractStatus(throwable), applied, methodInfo, factor); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index c59c145f7f..0355160b67 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -15,37 +15,14 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES2_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.REMAINING_DEADLINE_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; - import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; import com.google.api.gax.tracing.SpanName; -import com.google.cloud.bigtable.Version; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.DoubleGauge; -import io.opentelemetry.api.metrics.DoubleHistogram; -import io.opentelemetry.api.metrics.LongCounter; -import io.opentelemetry.api.metrics.Meter; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo; /** * {@link ApiTracerFactory} that will generate OpenTelemetry metrics by using the {@link ApiTracer} @@ -54,151 +31,26 @@ @InternalApi("For internal use only") public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { - private final Attributes attributes; - - private static final String MILLISECOND = "ms"; - private static final String COUNT = "1"; - - private final DoubleHistogram operationLatenciesHistogram; - private final DoubleHistogram attemptLatenciesHistogram; - private final DoubleHistogram attemptLatencies2Histogram; - private final DoubleHistogram serverLatenciesHistogram; - private final DoubleHistogram firstResponseLatenciesHistogram; - private final DoubleHistogram clientBlockingLatenciesHistogram; - private final DoubleHistogram applicationBlockingLatenciesHistogram; - private final DoubleHistogram remainingDeadlineHistogram; - private final LongCounter connectivityErrorCounter; - private final LongCounter retryCounter; - private final DoubleGauge batchWriteFlowControlTargetQps; - private final DoubleHistogram batchWriteFlowControlFactorHistogram; + private final MetricRegistry.RecorderRegistry recorder; + private final ClientInfo clientInfo; public static BuiltinMetricsTracerFactory create( - OpenTelemetry openTelemetry, ClientInfo clientInfo) { - return new BuiltinMetricsTracerFactory(openTelemetry, clientInfo); + MetricRegistry.RecorderRegistry recorder, ClientInfo clientInfo) { + return new BuiltinMetricsTracerFactory(recorder, clientInfo); } - BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, ClientInfo clientInfo) { - Meter meter = openTelemetry.getMeter(METER_NAME); - - this.attributes = - Attributes.of( - BIGTABLE_PROJECT_ID_KEY, - clientInfo.getInstanceName().getProject(), - INSTANCE_ID_KEY, - clientInfo.getInstanceName().getInstance(), - APP_PROFILE_KEY, - clientInfo.getAppProfileId(), - CLIENT_NAME_KEY, - "bigtable-java/" + Version.VERSION); - - operationLatenciesHistogram = - meter - .histogramBuilder(OPERATION_LATENCIES_NAME) - .setDescription( - "Total time until final operation success or failure, including retries and" - + " backoff.") - .setUnit(MILLISECOND) - .build(); - attemptLatenciesHistogram = - meter - .histogramBuilder(ATTEMPT_LATENCIES_NAME) - .setDescription("Client observed latency per RPC attempt.") - .setUnit(MILLISECOND) - .build(); - attemptLatencies2Histogram = - meter - .histogramBuilder(ATTEMPT_LATENCIES2_NAME) - .setDescription("Client observed latency per RPC attempt with transport labels.") - .setUnit(MILLISECOND) - .build(); - serverLatenciesHistogram = - meter - .histogramBuilder(SERVER_LATENCIES_NAME) - .setDescription( - "The latency measured from the moment that the RPC entered the Google data center" - + " until the RPC was completed.") - .setUnit(MILLISECOND) - .build(); - firstResponseLatenciesHistogram = - meter - .histogramBuilder(FIRST_RESPONSE_LATENCIES_NAME) - .setDescription( - "Latency from operation start until the response headers were received. The" - + " publishing of the measurement will be delayed until the attempt response" - + " has been received.") - .setUnit(MILLISECOND) - .build(); - clientBlockingLatenciesHistogram = - meter - .histogramBuilder(CLIENT_BLOCKING_LATENCIES_NAME) - .setDescription( - "The artificial latency introduced by the client to limit the number of outstanding" - + " requests. The publishing of the measurement will be delayed until the" - + " attempt trailers have been received.") - .setUnit(MILLISECOND) - .build(); - applicationBlockingLatenciesHistogram = - meter - .histogramBuilder(APPLICATION_BLOCKING_LATENCIES_NAME) - .setDescription( - "The latency of the client application consuming available response data.") - .setUnit(MILLISECOND) - .build(); - remainingDeadlineHistogram = - meter - .histogramBuilder(REMAINING_DEADLINE_NAME) - .setDescription( - "The remaining deadline when the request is sent to grpc. This will either be the" - + " operation timeout, or the remaining deadline from operation timeout after" - + " retries and back offs.") - .setUnit(MILLISECOND) - .build(); - connectivityErrorCounter = - meter - .counterBuilder(CONNECTIVITY_ERROR_COUNT_NAME) - .setDescription( - "Number of requests that failed to reach the Google datacenter. (Requests without" - + " google response headers") - .setUnit(COUNT) - .build(); - retryCounter = - meter - .counterBuilder(RETRY_COUNT_NAME) - .setDescription("The number of additional RPCs sent after the initial attempt.") - .setUnit(COUNT) - .build(); - batchWriteFlowControlTargetQps = - meter - .gaugeBuilder(BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME) - .setDescription("The current target QPS of the client under batch write flow control.") - .setUnit("1") - .build(); - batchWriteFlowControlFactorHistogram = - meter - .histogramBuilder(BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME) - .setDescription( - "The distribution of batch write flow control factors received from the server.") - .setUnit("1") - .build(); + BuiltinMetricsTracerFactory(MetricRegistry.RecorderRegistry recorder, ClientInfo clientInfo) { + this.recorder = recorder; + this.clientInfo = clientInfo; } @Override public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { - return new BuiltinMetricsTracer( - operationType, - spanName, - attributes, - operationLatenciesHistogram, - attemptLatenciesHistogram, - attemptLatencies2Histogram, - serverLatenciesHistogram, - firstResponseLatenciesHistogram, - clientBlockingLatenciesHistogram, - applicationBlockingLatenciesHistogram, - remainingDeadlineHistogram, - connectivityErrorCounter, - retryCounter, - batchWriteFlowControlTargetQps, - batchWriteFlowControlFactorHistogram); + MethodInfo methodInfo = + MethodInfo.builder() + .setName(spanName.toString()) + .setStreaming(operationType == OperationType.ServerStreaming) + .build(); + return new BuiltinMetricsTracer(recorder, clientInfo, methodInfo); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java index ea849cf8ce..67adfe78d3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java @@ -15,17 +15,13 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OUTSTANDING_RPCS_PER_CHANNEL_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME; - import com.google.api.core.InternalApi; +import com.google.bigtable.v2.PeerInfo.TransportType; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelObserver; import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolObserver; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongHistogram; -import io.opentelemetry.api.metrics.Meter; +import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolSettings.LoadBalancingStrategy; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -38,34 +34,19 @@ public class ChannelPoolMetricsTracer implements Runnable { private static final Logger logger = Logger.getLogger(ChannelPoolMetricsTracer.class.getName()); private static final int SAMPLING_PERIOD_SECONDS = 60; - private final LongHistogram outstandingRpcsHistogram; - private final LongHistogram perConnectionErrorCountHistogram; + private final MetricRegistry.RecorderRegistry recorder; + private final ClientInfo clientInfo; private final AtomicReference bigtableChannelInsightsProviderRef = new AtomicReference<>(); - private final AtomicReference lbPolicyRef = new AtomicReference<>("ROUND_ROBIN"); + private final AtomicReference lbPolicyRef = + new AtomicReference<>(LoadBalancingStrategy.ROUND_ROBIN); // Attributes for unary and streaming RPCs, built on demand in run() - public ChannelPoolMetricsTracer(OpenTelemetry openTelemetry) { - Meter meter = openTelemetry.getMeter(METER_NAME); - this.outstandingRpcsHistogram = - meter - .histogramBuilder(OUTSTANDING_RPCS_PER_CHANNEL_NAME) - .ofLongs() - .setDescription( - "A distribution of the number of outstanding RPCs per connection in the client" - + " pool, sampled periodically.") - .setUnit("1") - .build(); - - this.perConnectionErrorCountHistogram = - meter - .histogramBuilder(PER_CONNECTION_ERROR_COUNT_NAME) - .ofLongs() - .setDescription("Distribution of counts of channels per 'error count per minute'.") - .setUnit("1") - .build(); + public ChannelPoolMetricsTracer(MetricRegistry.RecorderRegistry recorder, ClientInfo clientInfo) { + this.recorder = recorder; + this.clientInfo = clientInfo; } /** @@ -77,7 +58,7 @@ public void registerChannelInsightsProvider(BigtableChannelPoolObserver channelI } /** Register the current lb policy * */ - public void registerLoadBalancingStrategy(String lbPolicy) { + public void registerLoadBalancingStrategy(LoadBalancingStrategy lbPolicy) { this.lbPolicyRef.set(lbPolicy); } @@ -100,45 +81,25 @@ public void run() { return; } - String lbPolicy = lbPolicyRef.get(); - - Attributes dpUnaryAttrs = - Attributes.builder() - .put("transport_type", "directpath") - .put("streaming", false) - .put("lb_policy", lbPolicy) - .build(); - Attributes dpStreamingAttrs = - Attributes.builder() - .put("transport_type", "directpath") - .put("streaming", true) - .put("lb_policy", lbPolicy) - .build(); - Attributes cpUnaryAttrs = - Attributes.builder() - .put("transport_type", "cloudpath") - .put("streaming", false) - .put("lb_policy", lbPolicy) - .build(); - Attributes cpStreamingAttrs = - Attributes.builder() - .put("transport_type", "cloudpath") - .put("streaming", true) - .put("lb_policy", lbPolicy) - .build(); + LoadBalancingStrategy lbPolicy = lbPolicyRef.get(); for (BigtableChannelObserver info : channelInsights) { - Attributes unaryAttrs = info.isAltsChannel() ? dpUnaryAttrs : cpUnaryAttrs; - Attributes streamingAttrs = info.isAltsChannel() ? dpStreamingAttrs : cpStreamingAttrs; + TransportType transportType = + info.isAltsChannel() + ? TransportType.TRANSPORT_TYPE_DIRECT_ACCESS + : TransportType.TRANSPORT_TYPE_CLOUD_PATH; long currentOutstandingUnaryRpcs = info.getOutstandingUnaryRpcs(); long currentOutstandingStreamingRpcs = info.getOutstandingStreamingRpcs(); - outstandingRpcsHistogram.record(currentOutstandingUnaryRpcs, unaryAttrs); - outstandingRpcsHistogram.record(currentOutstandingStreamingRpcs, streamingAttrs); + + recorder.channelPoolOutstandingRpcs.record( + clientInfo, transportType, lbPolicy, false, currentOutstandingUnaryRpcs); + recorder.channelPoolOutstandingRpcs.record( + clientInfo, transportType, lbPolicy, true, currentOutstandingStreamingRpcs); long errors = info.getAndResetErrorCount(); // Record errors with empty attributes. - perConnectionErrorCountHistogram.record(errors, Attributes.empty()); + recorder.perConnectionErrorCount.record(clientInfo, errors); } } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java index 73f54ad810..448d8b442b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java @@ -191,7 +191,7 @@ private void recordAttemptCompletion(@Nullable Throwable throwable) { if (sidebandData != null && sidebandData.getGfeTiming() != null) { measures - .put(RpcMeasureConstants.BIGTABLE_GFE_LATENCY, sidebandData.getGfeTiming()) + .put(RpcMeasureConstants.BIGTABLE_GFE_LATENCY, sidebandData.getGfeTiming().toMillis()) .put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 0L); } else { measures.put(RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT, 1L); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java index 7381b220e2..4af8abb869 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java @@ -25,14 +25,11 @@ import com.google.bigtable.v2.MaterializedViewName; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowsRequest; -import com.google.bigtable.v2.PeerInfo; import com.google.bigtable.v2.ReadChangeStreamRequest; import com.google.bigtable.v2.ReadModifyWriteRowRequest; import com.google.bigtable.v2.ReadRowsRequest; -import com.google.bigtable.v2.ResponseParams; import com.google.bigtable.v2.SampleRowKeysRequest; import com.google.bigtable.v2.TableName; -import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; import io.grpc.Status; @@ -40,9 +37,7 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.concurrent.CancellationException; import javax.annotation.Nullable; @@ -133,33 +128,4 @@ static Map> createStatsHeaders(ApiCallContext apiCallContex } return headers.build(); } - - public static String formatTransportTypeMetricLabel( - MetadataExtractorInterceptor.SidebandData sidebandData) { - return Optional.ofNullable(sidebandData) - .flatMap(s -> Optional.ofNullable(s.getPeerInfo())) - .map(PeerInfo::getTransportType) - .orElse(PeerInfo.TransportType.TRANSPORT_TYPE_UNKNOWN) - .name() - .replace("TRANSPORT_TYPE_", "") - .toLowerCase(Locale.ENGLISH); - } - - public static String formatClusterIdMetricLabel( - @Nullable MetadataExtractorInterceptor.SidebandData sidebandData) { - return Optional.ofNullable(sidebandData) - .flatMap(d -> Optional.ofNullable(d.getResponseParams())) - .map(ResponseParams::getClusterId) - .filter(s -> !s.isEmpty()) - .orElse(""); - } - - public static String formatZoneIdMetricLabel( - @Nullable MetadataExtractorInterceptor.SidebandData sidebandData) { - return Optional.ofNullable(sidebandData) - .flatMap(d -> Optional.ofNullable(d.getResponseParams())) - .map(ResponseParams::getZoneId) - .filter(s -> !s.isEmpty()) - .orElse("global"); - } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java index e21c100c9c..3c71da79c6 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java @@ -166,7 +166,7 @@ public TransportChannel getTransportChannel() throws IOException { if (channelPoolMetricsTracer != null) { channelPoolMetricsTracer.registerChannelInsightsProvider(btChannelPool::getChannelInfos); channelPoolMetricsTracer.registerLoadBalancingStrategy( - btPoolSettings.getLoadBalancingStrategy().name()); + btPoolSettings.getLoadBalancingStrategy()); } return GrpcTransportChannel.create(btChannelPool); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 2aaea4a5e5..47d1078b9d 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -65,6 +65,7 @@ import com.google.cloud.bigtable.Version; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.FakeServiceBuilder; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId; import com.google.cloud.bigtable.data.v2.models.Query; @@ -197,7 +198,11 @@ public void setUp() throws Exception { OpenTelemetrySdk otel = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - BuiltinMetricsTracerFactory facotry = new BuiltinMetricsTracerFactory(otel, clientInfo); + MetricRegistry mr = new MetricRegistry(); + + BuiltinMetricsTracerFactory facotry = + new BuiltinMetricsTracerFactory( + mr.newRecorderRegistry(otel.getMeterProvider()), clientInfo); // Add an interceptor to add server-timing in headers ServerInterceptor trailersInterceptor = diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java index 855709503e..e33ffe37e3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java @@ -22,8 +22,12 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; +import com.google.bigtable.v2.InstanceName; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelObserver; import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolObserver; +import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolSettings.LoadBalancingStrategy; import com.google.common.collect.ImmutableList; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; @@ -68,12 +72,21 @@ public class ChannelPoolMetricsTracerTest { @Before public void setUp() { metricReader = InMemoryMetricReader.create(); + ClientInfo clientInfo = + ClientInfo.builder() + .setInstanceName(InstanceName.of("fake-project", "fake-instance")) + .setAppProfileId("fake-profile") + .build(); SdkMeterProvider meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader).build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); - tracker = new ChannelPoolMetricsTracer(openTelemetry); + MetricRegistry mr = new MetricRegistry(); + + tracker = + new ChannelPoolMetricsTracer( + mr.newRecorderRegistry(openTelemetry.getMeterProvider()), clientInfo); runnableCaptor = ArgumentCaptor.forClass(Runnable.class); // Configure mockScheduler to capture the runnable when tracker.start() is called @@ -147,7 +160,7 @@ private static Attributes getExpectedAttributes(String lbPolicy, boolean streami public void testSingleRun() { // Arrange tracker.registerChannelInsightsProvider(mockInsightsProvider); - tracker.registerLoadBalancingStrategy("LEAST_IN_FLIGHT"); + tracker.registerLoadBalancingStrategy(LoadBalancingStrategy.LEAST_IN_FLIGHT); tracker.start(mockScheduler); // Outstanding RPCs @@ -205,7 +218,7 @@ public void testSingleRun() { public void testMultipleRuns() { // Arrange tracker.registerChannelInsightsProvider(mockInsightsProvider); - tracker.registerLoadBalancingStrategy("ROUND_ROBIN"); + tracker.registerLoadBalancingStrategy(LoadBalancingStrategy.ROUND_ROBIN); tracker.start(mockScheduler); // First run From a0a042bdef1dbab462753584023d59aac60c684f Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 26 Feb 2026 13:51:22 -0500 Subject: [PATCH 19/33] chore: align transport type with previous labels (#2809) Change-Id: Ia98cb0f3987472d5a1751591ecb2df848348b63b --- .../data/v2/internal/csm/attributes/Util.java | 48 +++++++++++++++---- .../v2/internal/csm/attributes/UtilTest.java | 7 ++- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java index 9379f4726d..818e0b8859 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java @@ -19,6 +19,7 @@ import com.google.bigtable.v2.PeerInfo; import com.google.bigtable.v2.PeerInfo.TransportType; import com.google.bigtable.v2.ResponseParams; +import com.google.common.annotations.VisibleForTesting; import java.util.Locale; import java.util.Optional; import javax.annotation.Nullable; @@ -42,17 +43,48 @@ public static String formatTransportType(@Nullable PeerInfo peerInfo) { } public static String transportTypeToString(TransportType transportType) { - if (transportType == TransportType.TRANSPORT_TYPE_UNKNOWN) { - return "none"; + String label = transportTypeToStringWithoutFallback(transportType); + if (label != null) { + return label; } - if (transportType == TransportType.UNRECOGNIZED) { - return "unrecognized"; + // In case the client is running with a newer version of protos + if (transportType.name().startsWith(TRANSPORT_TYPE_PREFIX)) { + return transportType + .name() + .substring(TRANSPORT_TYPE_PREFIX.length()) + .toLowerCase(Locale.ENGLISH); + } else { + return transportType.name(); } + } - return transportType - .name() - .substring(TRANSPORT_TYPE_PREFIX.length()) - .toLowerCase(Locale.ENGLISH); + @VisibleForTesting + static String transportTypeToStringWithoutFallback(TransportType transportType) { + if (transportType == null) { + return "null"; + } + switch (transportType) { + case TRANSPORT_TYPE_UNKNOWN: + return "unknown"; + case TRANSPORT_TYPE_EXTERNAL: + return "external"; + case TRANSPORT_TYPE_CLOUD_PATH: + return "cloudpath"; + case TRANSPORT_TYPE_DIRECT_ACCESS: + return "directpath"; + case TRANSPORT_TYPE_SESSION_UNKNOWN: + return "session_unknown"; + case TRANSPORT_TYPE_SESSION_EXTERNAL: + return "session_external"; + case TRANSPORT_TYPE_SESSION_CLOUD_PATH: + return "session_cloudpath"; + case TRANSPORT_TYPE_SESSION_DIRECT_ACCESS: + return "session_directpath"; + case UNRECOGNIZED: + return "unrecognized"; + default: + return null; + } } public static String formatClusterIdMetricLabel(@Nullable ResponseParams clusterInfo) { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java index f75bb81727..78b6c18b8b 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java @@ -16,6 +16,8 @@ package com.google.cloud.bigtable.data.v2.internal.csm.attributes; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.bigtable.v2.PeerInfo.TransportType; import org.junit.jupiter.api.Test; @@ -23,8 +25,9 @@ class UtilTest { @Test void ensureAllTransportTypeHaveExpectedPrefix() { for (TransportType type : TransportType.values()) { - // Ensure that no variant throws an error - Util.transportTypeToString(type); + assertWithMessage("%s should have a mapping", type) + .that(Util.transportTypeToStringWithoutFallback(type)) + .isNotNull(); } } } From 335414dd0803e7edb5e7c01b8c462513efd652df Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 26 Feb 2026 15:52:31 -0500 Subject: [PATCH 20/33] chore: update the exporter to use MetricRegistry (#2810) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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://togithub.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) - [ ] Rollback plan is reviewed and LGTMed - [ ] All new data plane features have a completed end to end testing plan Fixes # ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). --- .../data/v2/internal/csm/MetricRegistry.java | 2 +- .../data/v2/internal/csm/MetricsImpl.java | 12 +- .../internal/csm/attributes/ClientInfo.java | 2 +- .../v2/internal/csm/attributes/EnvInfo.java | 2 +- .../data/v2/stub/BigtableClientContext.java | 4 + .../BigtableCloudMonitoringExporter.java | 328 ++++++------------ .../data/v2/stub/metrics/Converter.java | 218 ++++++++++++ .../BigtableCloudMonitoringExporterTest.java | 62 ++-- 8 files changed, 372 insertions(+), 258 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java index f485e79e4f..266ac7bc13 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java @@ -165,7 +165,7 @@ List getGrpcMetricNames() { return ImmutableList.copyOf(grpcMetricNames); } - MetricWrapper getMetric(String name) { + public MetricWrapper getMetric(String name) { return metrics.get(name); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java index 3ae54c5313..c7bf859431 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -23,6 +23,7 @@ import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; @@ -72,6 +73,7 @@ public class MetricsImpl implements Metrics, Closeable { private final List> tasks = new ArrayList<>(); public MetricsImpl( + MetricRegistry metricRegistry, ClientInfo clientInfo, ApiTracerFactory userTracerFactory, @Nullable OpenTelemetrySdk internalOtel, @@ -79,7 +81,7 @@ public MetricsImpl( Tagger ocTagger, StatsRecorder ocRecorder, ScheduledExecutorService executor) { - metricRegistry = new MetricRegistry(); + this.metricRegistry = metricRegistry; this.userTracerFactory = Preconditions.checkNotNull(userTracerFactory); this.internalOtel = internalOtel; @@ -168,6 +170,7 @@ public ChannelPoolMetricsTracer getChannelPoolMetricsTracer() { } public static OpenTelemetrySdk createBuiltinOtel( + MetricRegistry metricRegistry, ClientInfo clientInfo, @Nullable Credentials defaultCredentials, @Nullable String metricsEndpoint, @@ -194,7 +197,12 @@ public static OpenTelemetrySdk createBuiltinOtel( MetricExporter publicExporter = BigtableCloudMonitoringExporter.create( - clientInfo, credentials, metricsEndpoint, universeDomain, executor); + metricRegistry, + EnvInfo::detect, + clientInfo, + credentials, + metricsEndpoint, + universeDomain); PeriodicMetricReaderBuilder readerBuilder = PeriodicMetricReader.builder(publicExporter).setExecutor(executor); meterProvider.registerMetricReader(readerBuilder.build()); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java index 0d4717dfe9..64c4b211b2 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java @@ -42,7 +42,7 @@ public static Builder builder() { @AutoValue.Builder public abstract static class Builder { - protected abstract Builder setClientName(String name); + public abstract Builder setClientName(String name); public abstract Builder setInstanceName(InstanceName name); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java index cfc9182881..b7afb73ee9 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/EnvInfo.java @@ -77,7 +77,7 @@ public static Builder builder() { @AutoValue.Builder public abstract static class Builder { - protected abstract Builder setUid(String uid); + public abstract Builder setUid(String uid); public abstract Builder setPlatform(String platform); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java index c82b0a8d02..c4bef24798 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java @@ -28,6 +28,7 @@ import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials; import com.google.bigtable.v2.InstanceName; import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.Metrics; import com.google.cloud.bigtable.data.v2.internal.csm.MetricsImpl; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; @@ -101,6 +102,7 @@ public static BigtableClientContext create( FixedExecutorProvider.create(backgroundExecutor, shouldAutoClose); builder.setBackgroundExecutorProvider(executorProvider); + MetricRegistry metricRegistry = new MetricRegistry(); // Set up OpenTelemetry @Nullable OpenTelemetry userOtel = null; if (settings.getMetricsProvider() instanceof CustomOpenTelemetryMetricsProvider) { @@ -113,6 +115,7 @@ public static BigtableClientContext create( if (settings.areInternalMetricsEnabled()) { builtinOtel = MetricsImpl.createBuiltinOtel( + metricRegistry, clientInfo, credentials, settings.getMetricsEndpoint(), @@ -125,6 +128,7 @@ public static BigtableClientContext create( Metrics metrics = new MetricsImpl( + metricRegistry, clientInfo, settings.getTracerFactory(), builtinOtel, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 2aba290aff..3bec1fc1e7 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -15,40 +15,20 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES2_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.REMAINING_DEADLINE_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; - -import com.google.api.MonitoredResource; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; -import com.google.api.core.InternalApi; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; -import com.google.api.gax.core.FixedExecutorProvider; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.rpc.PermissionDeniedException; import com.google.auth.Credentials; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.MoreExecutors; import com.google.monitoring.v3.CreateTimeSeriesRequest; @@ -65,28 +45,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import javax.annotation.Nullable; -/** - * Bigtable Cloud Monitoring OpenTelemetry Exporter. - * - *

    The exporter will look for all bigtable owned metrics under bigtable.googleapis.com - * instrumentation scope and upload it via the Google Cloud Monitoring API. - */ -@InternalApi -public final class BigtableCloudMonitoringExporter implements MetricExporter { - - private static final Logger logger = +public class BigtableCloudMonitoringExporter implements MetricExporter { + private static final Logger LOGGER = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); // This system property can be used to override the monitoring endpoint @@ -97,54 +68,51 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { private static final String MONITORING_ENDPOINT_OVERRIDE_SYS_PROP = System.getProperty("bigtable.test-monitoring-endpoint"); - private static final String APPLICATION_RESOURCE_PROJECT_ID = "project_id"; - // This the quota limit from Cloud Monitoring. More details in // https://cloud.google.com/monitoring/quotas#custom_metrics_quotas. private static final int EXPORT_BATCH_SIZE_LIMIT = 200; + private final Supplier envInfo; + private final ClientInfo clientInfo; + private final MetricRegistry metricRegistry; private final MetricServiceClient client; - private final List timeSeriesConverters; - - private final AtomicBoolean isShutdown = new AtomicBoolean(false); - + private final AtomicReference state; private CompletableResultCode lastExportCode; - private final AtomicBoolean exportFailureLogged = new AtomicBoolean(false); + private enum State { + Running, + Closing, + Closed + } + public static BigtableCloudMonitoringExporter create( + MetricRegistry metricRegistry, + Supplier envInfo, ClientInfo clientInfo, @Nullable Credentials credentials, @Nullable String endpoint, - String universeDomain, - @Nullable ScheduledExecutorService executorService) + String universeDomain) throws IOException { + Preconditions.checkNotNull(universeDomain); - MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); - CredentialsProvider credentialsProvider = - Optional.ofNullable(credentials) - .map(FixedCredentialsProvider::create) - .orElse(NoCredentialsProvider.create()); - settingsBuilder.setCredentialsProvider(credentialsProvider); - - settingsBuilder.setUniverseDomain(universeDomain); - - // If background executor is not null, use it for the monitoring client. This allows us to - // share the same background executor with the data client. When it's null, the monitoring - // client will create a new executor service from InstantiatingExecutorProvider. It could be - // null if someone uses a CustomOpenTelemetryMetricsProvider#setupSdkMeterProvider without - // the executor. - if (executorService != null) { - settingsBuilder.setBackgroundExecutorProvider(FixedExecutorProvider.create(executorService)); - } + + MetricServiceSettings.Builder settingsBuilder = + MetricServiceSettings.newBuilder() + .setUniverseDomain(universeDomain) + .setCredentialsProvider( + Optional.ofNullable(credentials) + .map(FixedCredentialsProvider::create) + .orElse(NoCredentialsProvider.create())); if (MONITORING_ENDPOINT_OVERRIDE_SYS_PROP != null) { - logger.warning( + LOGGER.warning( "Setting the monitoring endpoint through system variable will be removed in future" + " versions"); settingsBuilder.setEndpoint(MONITORING_ENDPOINT_OVERRIDE_SYS_PROP); } + if (endpoint != null) { settingsBuilder.setEndpoint(endpoint); } @@ -154,105 +122,95 @@ public static BigtableCloudMonitoringExporter create( // it as not retried for now. settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout); - ImmutableList converters = - ImmutableList.of( - new PublicTimeSeriesConverter(), - new InternalTimeSeriesConverter( - Suppliers.memoize( - () -> BigtableExporterUtils.createInternalMonitoredResource(clientInfo)))); - return new BigtableCloudMonitoringExporter( - MetricServiceClient.create(settingsBuilder.build()), converters); + metricRegistry, envInfo, clientInfo, MetricServiceClient.create(settingsBuilder.build())); } - @VisibleForTesting BigtableCloudMonitoringExporter( - MetricServiceClient client, List converters) { + MetricRegistry metricRegistry, + Supplier envInfo, + ClientInfo clientInfo, + MetricServiceClient client) { + this.metricRegistry = metricRegistry; + this.envInfo = envInfo; + this.clientInfo = clientInfo; this.client = client; - this.timeSeriesConverters = ImmutableList.copyOf(converters); + this.state = new AtomicReference<>(State.Running); + } + + public void close() { + client.close(); } @Override public CompletableResultCode export(Collection metricData) { - Preconditions.checkState(!isShutdown.get(), "Exporter is shutting down"); + Preconditions.checkState(state.get() != State.Closed, "Exporter is closed"); lastExportCode = doExport(metricData); return lastExportCode; } - /** Export metrics associated with a BigtableTable resource. */ private CompletableResultCode doExport(Collection metricData) { - Map> bigtableTimeSeries = new HashMap<>(); - - List results = new ArrayList<>(); - - for (TimeSeriesConverter c : timeSeriesConverters) { - try { - for (Map.Entry> e : c.convert(metricData).entrySet()) { - bigtableTimeSeries - .computeIfAbsent(e.getKey(), (k) -> new ArrayList<>()) - .addAll(e.getValue()); - } - results.add(CompletableResultCode.ofSuccess()); - } catch (Throwable t) { - logger.log( - Level.WARNING, - String.format( - "Failed to convert %s metric data to cloud monitoring timeseries.", c.name), - t); - results.add(CompletableResultCode.ofExceptionalFailure(t)); + Map> converted; + + try { + converted = new Converter(metricRegistry, envInfo.get(), clientInfo).convertAll(metricData); + } catch (Throwable t) { + if (exportFailureLogged.compareAndSet(false, true)) { + LOGGER.log(Level.WARNING, "Failed to compose metrics for export", t); } - } - CompletableResultCode overall = CompletableResultCode.ofAll(results); - if (!overall.isSuccess()) { - return overall; + + return CompletableResultCode.ofExceptionalFailure(t); } - // Skips exporting if there's none - if (bigtableTimeSeries.isEmpty()) { - return CompletableResultCode.ofSuccess(); + List> futures = new ArrayList<>(); + + for (Entry> e : converted.entrySet()) { + futures.addAll(exportTimeSeries(e.getKey(), e.getValue())); } CompletableResultCode exportCode = new CompletableResultCode(); - bigtableTimeSeries.forEach( - (projectName, ts) -> { - ApiFuture> future = exportTimeSeries(projectName, ts); - ApiFutures.addCallback( - future, - new ApiFutureCallback>() { - @Override - public void onFailure(Throwable throwable) { - if (exportFailureLogged.compareAndSet(false, true)) { - String msg = "createServiceTimeSeries request failed"; - if (throwable instanceof PermissionDeniedException) { - msg += - String.format( - " Need monitoring metric writer permission on project=%s. Follow" - + " https://cloud.google.com/bigtable/docs/client-side-metrics-setup" - + " to set up permissions.", - projectName.getProject()); - } - logger.log(Level.WARNING, msg, throwable); - } - exportCode.fail(); - } - - @Override - public void onSuccess(List emptyList) { - // When an export succeeded reset the export failure flag to false so if there's a - // transient failure it'll be logged. - exportFailureLogged.set(false); - exportCode.succeed(); - } - }, - MoreExecutors.directExecutor()); - }); + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + ApiFutures.addCallback( + ApiFutures.allAsList(futures), + new ApiFutureCallback>() { + @Override + public void onFailure(Throwable throwable) { + if (exportFailureLogged.compareAndSet(false, true)) { + String msg = "createServiceTimeSeries request failed"; + if (throwable instanceof PermissionDeniedException) { + msg += + String.format( + " Need monitoring metric writer permission on project=%s. Follow" + + " https://cloud.google.com/bigtable/docs/client-side-metrics-setup" + + " to set up permissions.", + clientInfo.getInstanceName().getProject()); + } + RuntimeException asyncWrapper = new RuntimeException("export failed", throwable); + asyncWrapper.setStackTrace(stackTrace); + + if (state.get() != State.Closing || state.get() != State.Closed) { + // ignore the export warning when client is shutting down + LOGGER.log(Level.WARNING, msg, asyncWrapper); + } + } + exportCode.fail(); + } + + @Override + public void onSuccess(List objects) { + exportFailureLogged.set(false); + exportCode.succeed(); + } + }, + MoreExecutors.directExecutor()); return exportCode; } - private ApiFuture> exportTimeSeries( - ProjectName projectName, List timeSeries) { + private List> exportTimeSeries( + ProjectName projectName, Collection timeSeries) { List> batchResults = new ArrayList<>(); for (List batch : Iterables.partition(timeSeries, EXPORT_BATCH_SIZE_LIMIT)) { @@ -265,7 +223,7 @@ private ApiFuture> exportTimeSeries( batchResults.add(f); } - return ApiFutures.allAsList(batchResults); + return batchResults; } @Override @@ -278,8 +236,9 @@ public CompletableResultCode flush() { @Override public CompletableResultCode shutdown() { - if (!isShutdown.compareAndSet(false, true)) { - logger.log(Level.WARNING, "shutdown is called multiple times"); + State prevState = state.getAndSet(State.Closed); + if (prevState == State.Closed) { + LOGGER.log(Level.WARNING, "shutdown is called multiple times"); return CompletableResultCode.ofSuccess(); } CompletableResultCode flushResult = flush(); @@ -290,7 +249,7 @@ public CompletableResultCode shutdown() { try { client.shutdown(); } catch (Throwable e) { - logger.log(Level.WARNING, "failed to shutdown the monitoring client", e); + LOGGER.log(Level.WARNING, "failed to shutdown the monitoring client", e); throwable = e; } if (throwable != null) { @@ -299,97 +258,16 @@ public CompletableResultCode shutdown() { shutdownResult.succeed(); } }); + return CompletableResultCode.ofAll(Arrays.asList(flushResult, shutdownResult)); } - /** - * For Google Cloud Monitoring always return CUMULATIVE to keep track of the cumulative value of a - * metric over time. - */ @Override public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { return AggregationTemporality.CUMULATIVE; } - abstract static class TimeSeriesConverter { - private final String name; - - TimeSeriesConverter(String name) { - this.name = name; - } - - abstract Map> convert(Collection metricData); - } - - static class PublicTimeSeriesConverter extends TimeSeriesConverter { - private static final ImmutableList BIGTABLE_TABLE_METRICS = - ImmutableSet.of( - OPERATION_LATENCIES_NAME, - ATTEMPT_LATENCIES_NAME, - ATTEMPT_LATENCIES2_NAME, - SERVER_LATENCIES_NAME, - FIRST_RESPONSE_LATENCIES_NAME, - CLIENT_BLOCKING_LATENCIES_NAME, - APPLICATION_BLOCKING_LATENCIES_NAME, - RETRY_COUNT_NAME, - CONNECTIVITY_ERROR_COUNT_NAME, - REMAINING_DEADLINE_NAME) - .stream() - .map(m -> METER_NAME + m) - .collect(ImmutableList.toImmutableList()); - - private static final AtomicLong nextTaskIdSuffix = new AtomicLong(); - private final String taskId; - - PublicTimeSeriesConverter() { - this( - BigtableExporterUtils.DEFAULT_TASK_VALUE.get() - + "-" - + nextTaskIdSuffix.getAndIncrement()); - } - - PublicTimeSeriesConverter(String taskId) { - super("table metrics"); - this.taskId = taskId; - } - - @Override - public Map> convert(Collection metricData) { - List relevantData = - metricData.stream() - .filter(md -> BIGTABLE_TABLE_METRICS.contains(md.getName())) - .collect(Collectors.toList()); - if (relevantData.isEmpty()) { - return ImmutableMap.of(); - } - return BigtableExporterUtils.convertToBigtableTimeSeries(relevantData, taskId); - } - } - - static class InternalTimeSeriesConverter extends TimeSeriesConverter { - private static final ImmutableList APPLICATION_METRICS = - ImmutableSet.of(PER_CONNECTION_ERROR_COUNT_NAME).stream() - .map(m -> METER_NAME + m) - .collect(ImmutableList.toImmutableList()); - - private final Supplier monitoredResource; - - InternalTimeSeriesConverter(Supplier monitoredResource) { - super("client metrics"); - this.monitoredResource = monitoredResource; - } - - @Override - public Map> convert(Collection metricData) { - MonitoredResource monitoredResource = this.monitoredResource.get(); - if (monitoredResource == null) { - return ImmutableMap.of(); - } - - return ImmutableMap.of( - ProjectName.of(monitoredResource.getLabelsOrThrow(APPLICATION_RESOURCE_PROJECT_ID)), - BigtableExporterUtils.convertToApplicationResourceTimeSeries( - metricData, monitoredResource)); - } + public void prepareForShutdown() { + state.compareAndSet(State.Running, State.Closing); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java new file mode 100644 index 0000000000..4a2ca946f1 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java @@ -0,0 +1,218 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.api.MetricDescriptor.MetricKind.CUMULATIVE; +import static com.google.api.MetricDescriptor.MetricKind.GAUGE; +import static com.google.api.MetricDescriptor.MetricKind.UNRECOGNIZED; +import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION; +import static com.google.api.MetricDescriptor.ValueType.DOUBLE; +import static com.google.api.MetricDescriptor.ValueType.INT64; + +import com.google.api.Distribution; +import com.google.api.Distribution.BucketOptions; +import com.google.api.Distribution.BucketOptions.Explicit; +import com.google.api.Metric; +import com.google.api.MetricDescriptor.MetricKind; +import com.google.api.MetricDescriptor.ValueType; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.MetricWrapper; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.monitoring.v3.Point; +import com.google.monitoring.v3.ProjectName; +import com.google.monitoring.v3.TimeInterval; +import com.google.monitoring.v3.TimeSeries; +import com.google.monitoring.v3.TypedValue; +import com.google.protobuf.util.Timestamps; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.DoublePointData; +import io.opentelemetry.sdk.metrics.data.HistogramData; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.metrics.data.SumData; +import java.util.Collection; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Helper for exporting metrics from Opentelemetry to Cloud Monitoring. + * + *

    Takes collection {@link MetricData} and uses the {@link MetricWrapper}s defined in {@link + * MetricRegistry} to compose both the {@link com.google.api.MonitoredResource} and {@link Point}. + */ +class Converter { + private static final Logger LOGGER = Logger.getLogger(Converter.class.getName()); + + private final MetricRegistry metricRegistry; + private final EnvInfo envInfo; + private final ClientInfo clientInfo; + + Converter(MetricRegistry metricRegistry, EnvInfo envInfo, ClientInfo clientInfo) { + this.metricRegistry = metricRegistry; + this.envInfo = envInfo; + this.clientInfo = clientInfo; + } + + Map> convertAll(Collection otelMetrics) { + ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); + + for (MetricData metricData : otelMetrics) { + Multimap perProject = convertMetricData(metricData); + builder.putAll(perProject); + } + return builder.build().asMap(); + } + + private Multimap convertMetricData(MetricData metricData) { + MetricWrapper metricDef = metricRegistry.getMetric(metricData.getName()); + if (metricDef == null) { + LOGGER.log(Level.FINE, "Skipping unexpected metric: {}", metricData.getName()); + return ImmutableListMultimap.of(); + } + + ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); + + for (PointData pd : metricData.getData().getPoints()) { + ProjectName projectName = + metricDef.getSchema().extractProjectName(pd.getAttributes(), envInfo, clientInfo); + + TimeSeries timeSeries = + TimeSeries.newBuilder() + .setMetricKind(convertMetricKind(metricData)) + .setValueType(convertValueType(metricData.getType())) + .setResource( + metricDef + .getSchema() + .extractMonitoredResource(pd.getAttributes(), envInfo, clientInfo)) + .setMetric( + Metric.newBuilder() + .setType(metricDef.getExternalName()) + .putAllLabels( + metricDef.extractMetricLabels(pd.getAttributes(), envInfo, clientInfo))) + .addPoints(convertPointData(metricData.getType(), pd)) + .build(); + + builder.put(projectName, timeSeries); + } + return builder.build(); + } + + private Point convertPointData(MetricDataType type, PointData pointData) { + TimeInterval timeInterval = + TimeInterval.newBuilder() + .setStartTime(Timestamps.fromNanos(pointData.getStartEpochNanos())) + .setEndTime(Timestamps.fromNanos(pointData.getEpochNanos())) + .build(); + + Point.Builder builder = Point.newBuilder().setInterval(timeInterval); + switch (type) { + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return builder + .setValue( + TypedValue.newBuilder() + .setDistributionValue(convertHistogramData((HistogramPointData) pointData)) + .build()) + .build(); + case DOUBLE_GAUGE: + case DOUBLE_SUM: + return builder + .setValue( + TypedValue.newBuilder() + .setDoubleValue(((DoublePointData) pointData).getValue()) + .build()) + .build(); + case LONG_GAUGE: + case LONG_SUM: + return builder + .setValue(TypedValue.newBuilder().setInt64Value(((LongPointData) pointData).getValue())) + .build(); + default: + LOGGER.log(Level.WARNING, "unsupported metric type %s", type); + return builder.build(); + } + } + + private static Distribution convertHistogramData(HistogramPointData pointData) { + return Distribution.newBuilder() + .setCount(pointData.getCount()) + .setMean(pointData.getCount() == 0L ? 0.0D : pointData.getSum() / pointData.getCount()) + .setBucketOptions( + BucketOptions.newBuilder() + .setExplicitBuckets(Explicit.newBuilder().addAllBounds(pointData.getBoundaries()))) + .addAllBucketCounts(pointData.getCounts()) + .build(); + } + + private static MetricKind convertMetricKind(MetricData metricData) { + switch (metricData.getType()) { + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return convertHistogramType(metricData.getHistogramData()); + case LONG_GAUGE: + case DOUBLE_GAUGE: + return GAUGE; + case LONG_SUM: + return convertSumDataType(metricData.getLongSumData()); + case DOUBLE_SUM: + return convertSumDataType(metricData.getDoubleSumData()); + default: + return UNRECOGNIZED; + } + } + + private static MetricKind convertHistogramType(HistogramData histogramData) { + if (histogramData.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { + return CUMULATIVE; + } + return UNRECOGNIZED; + } + + private static MetricKind convertSumDataType(SumData sum) { + if (!sum.isMonotonic()) { + return GAUGE; + } + if (sum.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { + return CUMULATIVE; + } + return UNRECOGNIZED; + } + + private static ValueType convertValueType(MetricDataType metricDataType) { + switch (metricDataType) { + case LONG_GAUGE: + case LONG_SUM: + return INT64; + case DOUBLE_GAUGE: + case DOUBLE_SUM: + return DOUBLE; + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return DISTRIBUTION; + default: + return ValueType.UNRECOGNIZED; + } + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 285206e949..d9ccad187e 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -31,10 +31,13 @@ import static org.mockito.Mockito.when; import com.google.api.Distribution; -import com.google.api.MonitoredResource; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.api.gax.rpc.UnaryCallable; +import com.google.bigtable.v2.InstanceName; +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.stub.MetricServiceStub; import com.google.common.base.Suppliers; @@ -90,15 +93,32 @@ public class BigtableCloudMonitoringExporterTest { private Resource resource; private InstrumentationScopeInfo scope; + private EnvInfo envInfo = + EnvInfo.builder() + .setProject("client-project") + .setPlatform("gce_instance") + .setRegion("cleint-region") + .setHostName("harold") + .setHostId("1234567890") + .setUid(taskId) + .build(); + private ClientInfo clientInfo = + ClientInfo.builder() + .setInstanceName(InstanceName.of(projectId, instanceId)) + .setAppProfileId(appProfileId) + .setClientName(clientName) + .build(); + @Before public void setUp() { fakeMetricServiceClient = new FakeMetricServiceClient(mockMetricServiceStub); exporter = new BigtableCloudMonitoringExporter( - fakeMetricServiceClient, - ImmutableList.of( - new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(taskId))); + new MetricRegistry(), + Suppliers.ofInstance(envInfo), + clientInfo, + fakeMetricServiceClient); attributes = Attributes.builder() @@ -307,26 +327,12 @@ public void testExportingSumDataInBatches() { @Test public void testTimeSeriesForMetricWithGceOrGkeResource() { - String gceProjectId = "fake-gce-project"; BigtableCloudMonitoringExporter exporter = new BigtableCloudMonitoringExporter( - fakeMetricServiceClient, - ImmutableList.of( - new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( - Suppliers.ofInstance( - MonitoredResource.newBuilder() - .setType("bigtable_client") - .putLabels("project_id", gceProjectId) - .putLabels("instance", "resource-instance") - .putLabels("app_profile", "resource-app-profile") - .putLabels("client_project", "client-project") - .putLabels("region", "cleint-region") - .putLabels("cloud_platform", "gce_instance") - .putLabels("host_id", "1234567890") - .putLabels("host_name", "harold") - .putLabels("client_name", "java/1234") - .putLabels("uuid", "something") - .build())))); + new MetricRegistry(), + Suppliers.ofInstance(envInfo), + clientInfo, + fakeMetricServiceClient); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CreateTimeSeriesRequest.class); @@ -372,7 +378,7 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() { CreateTimeSeriesRequest request = argumentCaptor.getValue(); - assertThat(request.getName()).isEqualTo("projects/" + gceProjectId); + assertThat(request.getName()).isEqualTo("projects/" + projectId); assertThat(request.getTimeSeriesList()).hasSize(1); com.google.monitoring.v3.TimeSeries timeSeries = request.getTimeSeriesList().get(0); @@ -380,16 +386,16 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() { assertThat(timeSeries.getResource().getLabelsMap()) .isEqualTo( ImmutableMap.builder() - .put("project_id", gceProjectId) - .put("instance", "resource-instance") - .put("app_profile", "resource-app-profile") + .put("project_id", projectId) + .put("instance", instanceId) + .put("app_profile", appProfileId) .put("client_project", "client-project") .put("region", "cleint-region") .put("cloud_platform", "gce_instance") .put("host_id", "1234567890") .put("host_name", "harold") - .put("client_name", "java/1234") - .put("uuid", "something") + .put("client_name", clientName) + .put("uuid", taskId) .build()); assertThat(timeSeries.getMetric().getLabelsMap()) From a60b50dfa05d024030aa5a1c68194afb5f7fcdb3 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Thu, 26 Feb 2026 16:48:23 -0500 Subject: [PATCH 21/33] chore: update references to point to new csm constants (#2811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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://togithub.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) - [ ] Rollback plan is reviewed and LGTMed - [ ] All new data plane features have a completed end to end testing plan Fixes # ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). --- .../data/v2/internal/csm/MetricRegistry.java | 2 +- .../data/v2/internal/csm/MetricsImpl.java | 16 +- .../ClientBatchWriteFlowControlFactor.java | 2 +- .../ClientBatchWriteFlowControlTargetQps.java | 2 +- .../ClientChannelPoolOutstandingRpcs.java | 2 +- .../ClientPerConnectionErrorCount.java | 2 +- .../v2/internal/csm/metrics/Constants.java | 2 +- .../TableApplicationBlockingLatency.java | 3 +- .../csm/metrics/TableAttemptLatency.java | 2 +- .../csm/metrics/TableAttemptLatency2.java | 2 +- .../metrics/TableClientBlockingLatency.java | 2 +- .../metrics/TableConnectivityErrorCount.java | 2 +- .../metrics/TableFirstResponseLatency.java | 2 +- .../csm/metrics/TableOperationLatency.java | 2 +- .../csm/metrics/TableRemainingDeadline.java | 2 +- .../internal/csm/metrics/TableRetryCount.java | 2 +- .../csm/metrics/TableServerLatency.java | 2 +- .../stub/metrics/BigtableExporterUtils.java | 471 ------------------ .../stub/metrics/BuiltinMetricsConstants.java | 395 --------------- .../v2/stub/metrics/BuiltinMetricsView.java | 34 +- .../CustomOpenTelemetryMetricsProvider.java | 50 +- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 4 +- .../bigtable/data/v2/it/MetricsITUtils.java | 6 +- .../v2/it/StreamingMetricsMetadataIT.java | 19 +- .../data/v2/it/UnaryMetricsMetadataIT.java | 23 +- .../BigtableCloudMonitoringExporterTest.java | 125 ++--- .../stub/metrics/BuiltinMetricsTestUtils.java | 2 +- .../metrics/BuiltinMetricsTracerTest.java | 430 ++++++++-------- .../metrics/ChannelPoolMetricsTracerTest.java | 16 +- 29 files changed, 353 insertions(+), 1271 deletions(-) delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java index 266ac7bc13..b4caed95ee 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java @@ -53,7 +53,7 @@ * */ public class MetricRegistry { - static final String METER_NAME = "bigtable.googleapis.com/internal/client/"; + public static final String METER_NAME = "bigtable.googleapis.com/internal/client/"; final TableOperationLatency operationLatencyMetric; final TableAttemptLatency attemptLatencyMetric; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java index c7bf859431..67f0ef5c6e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -25,7 +25,6 @@ import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer; import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; @@ -42,10 +41,8 @@ import io.opencensus.tags.Tagger; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; @@ -53,7 +50,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import javax.annotation.Nullable; @@ -100,7 +96,7 @@ public MetricsImpl( // Disable default grpc metrics .disableAllMetrics() // Enable specific grpc metrics - .enableMetrics(BuiltinMetricsConstants.GRPC_METRICS.keySet()) + .enableMetrics(metricRegistry.getGrpcMetricNames()) .build(); } else { this.grpcOtel = null; @@ -185,16 +181,6 @@ public static OpenTelemetrySdk createBuiltinOtel( SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); - for (Map.Entry entry : - BuiltinMetricsConstants.getAllViews().entrySet()) { - meterProvider.registerView(entry.getKey(), entry.getValue()); - } - - for (Map.Entry e : - BuiltinMetricsConstants.getInternalViews().entrySet()) { - meterProvider.registerView(e.getKey(), e.getValue()); - } - MetricExporter publicExporter = BigtableCloudMonitoringExporter.create( metricRegistry, diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java index 2c5a989d51..c4c6d97118 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlFactor.java @@ -25,7 +25,7 @@ import io.opentelemetry.api.metrics.Meter; public class ClientBatchWriteFlowControlFactor extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/batch_write_flow_control_factor"; public ClientBatchWriteFlowControlFactor() { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java index fb6f55894f..a15189aa4a 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientBatchWriteFlowControlTargetQps.java @@ -24,7 +24,7 @@ import io.opentelemetry.api.metrics.Meter; public class ClientBatchWriteFlowControlTargetQps extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/batch_write_flow_control_target_qps"; public ClientBatchWriteFlowControlTargetQps() { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java index addd28a533..c5c1589c4f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientChannelPoolOutstandingRpcs.java @@ -28,7 +28,7 @@ import java.util.stream.Collectors; public class ClientChannelPoolOutstandingRpcs extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/connection_pool/outstanding_rpcs"; private static final List BUCKETS = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java index 25ede477fb..a6b2e89aaf 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java @@ -32,7 +32,7 @@ import java.util.Set; public class ClientPerConnectionErrorCount extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/per_connection_error_count"; static final List BUCKETS = diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java index 768f451e0e..f0f1a7c839 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java @@ -54,7 +54,7 @@ private MetricLabels() {} AttributeKey.stringKey("app_profile"); public static final AttributeKey DEBUG_TAG_KEY = AttributeKey.stringKey("tag"); - static final AttributeKey APPLIED_KEY = AttributeKey.booleanKey("applied"); + public static final AttributeKey APPLIED_KEY = AttributeKey.booleanKey("applied"); static final AttributeKey CHANNEL_POOL_LB_POLICY = AttributeKey.stringKey("lb_policy"); static final AttributeKey DP_REASON_KEY = AttributeKey.stringKey("reason"); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java index 05fdefd0be..9fd5561d0f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableApplicationBlockingLatency.java @@ -32,8 +32,7 @@ import javax.annotation.Nullable; public class TableApplicationBlockingLatency extends MetricWrapper { - private static final String NAME = - "bigtable.googleapis.com/internal/client/application_latencies"; + public static final String NAME = "bigtable.googleapis.com/internal/client/application_latencies"; public TableApplicationBlockingLatency() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java index 530498fa9a..e792cb8eb8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency.java @@ -33,7 +33,7 @@ import javax.annotation.Nullable; public class TableAttemptLatency extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies"; + public static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies"; public TableAttemptLatency() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java index 63cb2aa929..ca895e0e1b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableAttemptLatency2.java @@ -34,7 +34,7 @@ import javax.annotation.Nullable; public class TableAttemptLatency2 extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies2"; + public static final String NAME = "bigtable.googleapis.com/internal/client/attempt_latencies2"; public TableAttemptLatency2() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java index 7f9a584a69..7fc46c5559 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableClientBlockingLatency.java @@ -32,7 +32,7 @@ import javax.annotation.Nullable; public class TableClientBlockingLatency extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/throttling_latencies"; + public static final String NAME = "bigtable.googleapis.com/internal/client/throttling_latencies"; public TableClientBlockingLatency() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java index 0233b8adef..3f99f90248 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableConnectivityErrorCount.java @@ -31,7 +31,7 @@ import javax.annotation.Nullable; public class TableConnectivityErrorCount extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/connectivity_error_count"; public TableConnectivityErrorCount() { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java index bde5009f68..6ad09e7798 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableFirstResponseLatency.java @@ -33,7 +33,7 @@ import javax.annotation.Nullable; public class TableFirstResponseLatency extends MetricWrapper { - private static final String NAME = + public static final String NAME = "bigtable.googleapis.com/internal/client/first_response_latencies"; public TableFirstResponseLatency() { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java index 4a30d66f20..781501100f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableOperationLatency.java @@ -33,7 +33,7 @@ import javax.annotation.Nullable; public class TableOperationLatency extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/operation_latencies"; + public static final String NAME = "bigtable.googleapis.com/internal/client/operation_latencies"; public TableOperationLatency() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java index 3e911d42e6..314f9874c8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRemainingDeadline.java @@ -32,7 +32,7 @@ import java.time.Duration; public class TableRemainingDeadline extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/remaining_deadline"; + public static final String NAME = "bigtable.googleapis.com/internal/client/remaining_deadline"; public TableRemainingDeadline() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java index a81a4bf903..205bf83962 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableRetryCount.java @@ -31,7 +31,7 @@ import javax.annotation.Nullable; public class TableRetryCount extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/retry_count"; + public static final String NAME = "bigtable.googleapis.com/internal/client/retry_count"; public TableRetryCount() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java index 0d8dc0197b..cafc0c245e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/TableServerLatency.java @@ -33,7 +33,7 @@ import javax.annotation.Nullable; public class TableServerLatency extends MetricWrapper { - private static final String NAME = "bigtable.googleapis.com/internal/client/server_latencies"; + public static final String NAME = "bigtable.googleapis.com/internal/client/server_latencies"; public TableServerLatency() { super(TableSchema.INSTANCE, NAME); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java deleted file mode 100644 index f27c2b56f8..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import static com.google.api.Distribution.BucketOptions; -import static com.google.api.Distribution.BucketOptions.Explicit; -import static com.google.api.MetricDescriptor.MetricKind; -import static com.google.api.MetricDescriptor.MetricKind.CUMULATIVE; -import static com.google.api.MetricDescriptor.MetricKind.GAUGE; -import static com.google.api.MetricDescriptor.MetricKind.UNRECOGNIZED; -import static com.google.api.MetricDescriptor.ValueType; -import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION; -import static com.google.api.MetricDescriptor.ValueType.DOUBLE; -import static com.google.api.MetricDescriptor.ValueType.INT64; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.GRPC_METRICS; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INTERNAL_METRICS; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY; - -import com.google.api.Distribution; -import com.google.api.Metric; -import com.google.api.MonitoredResource; -import com.google.cloud.bigtable.Version; -import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; -import com.google.cloud.opentelemetry.detection.AttributeKeys; -import com.google.cloud.opentelemetry.detection.DetectedPlatform; -import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.monitoring.v3.Point; -import com.google.monitoring.v3.ProjectName; -import com.google.monitoring.v3.TimeInterval; -import com.google.monitoring.v3.TimeSeries; -import com.google.monitoring.v3.TypedValue; -import com.google.protobuf.Timestamp; -import com.google.protobuf.util.Timestamps; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.metrics.data.AggregationTemporality; -import io.opentelemetry.sdk.metrics.data.DoublePointData; -import io.opentelemetry.sdk.metrics.data.HistogramData; -import io.opentelemetry.sdk.metrics.data.HistogramPointData; -import io.opentelemetry.sdk.metrics.data.LongPointData; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.metrics.data.MetricDataType; -import io.opentelemetry.sdk.metrics.data.PointData; -import io.opentelemetry.sdk.metrics.data.SumData; -import java.lang.management.ManagementFactory; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.annotation.Nullable; - -/** Utils to convert OpenTelemetry types to Google Cloud Monitoring types. */ -class BigtableExporterUtils { - private static final String CLIENT_NAME = "java-bigtable/" + Version.VERSION; - - private static final Logger logger = Logger.getLogger(BigtableExporterUtils.class.getName()); - - private static final String BIGTABLE_RESOURCE_TYPE = "bigtable_client_raw"; - - // These metric labels will be promoted to the bigtable_table monitored resource fields - private static final Set> BIGTABLE_PROMOTED_RESOURCE_LABELS = - ImmutableSet.of( - BIGTABLE_PROJECT_ID_KEY, INSTANCE_ID_KEY, TABLE_ID_KEY, CLUSTER_ID_KEY, ZONE_ID_KEY); - - private static final Map SUPPORTED_PLATFORM_MAP = - ImmutableMap.of( - GCPPlatformDetector.SupportedPlatform.GOOGLE_COMPUTE_ENGINE, "gcp_compute_engine", - GCPPlatformDetector.SupportedPlatform.GOOGLE_KUBERNETES_ENGINE, "gcp_kubernetes_engine"); - - private static final AtomicLong nextUuidSuffix = new AtomicLong(); - - private BigtableExporterUtils() {} - - /** - * In most cases this should look like java-${UUID}@${hostname}. The hostname will be retrieved - * from the jvm name and fallback to the local hostname. - */ - private static String defaultTaskValue = null; - - static final Supplier DEFAULT_TASK_VALUE = - Suppliers.memoize(BigtableExporterUtils::computeDefaultTaskValue); - - private static String computeDefaultTaskValue() { - if (defaultTaskValue != null) { - return defaultTaskValue; - } - // Something like '@' - final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); - // If jvm doesn't have the expected format, fallback to the local hostname - if (jvmName.indexOf('@') < 1) { - String hostname = "localhost"; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - logger.log(Level.INFO, "Unable to get the hostname.", e); - } - // Generate a random number and use the same format "random_number@hostname". - return "java-" + UUID.randomUUID() + "@" + hostname; - } - return "java-" + UUID.randomUUID() + jvmName; - } - - static ProjectName getProjectName(PointData pointData) { - return ProjectName.of(pointData.getAttributes().get(BIGTABLE_PROJECT_ID_KEY)); - } - - // Returns a list of timeseries by project name - static Map> convertToBigtableTimeSeries( - Collection collection, String taskId) { - Map> allTimeSeries = new HashMap<>(); - - for (MetricData metricData : collection) { - if (!metricData.getInstrumentationScopeInfo().getName().equals(METER_NAME)) { - // Filter out metric data for instruments that are not part of the bigtable builtin metrics - continue; - } - - for (PointData pd : metricData.getData().getPoints()) { - ProjectName projectName = getProjectName(pd); - List current = - allTimeSeries.computeIfAbsent(projectName, ignored -> new ArrayList<>()); - current.add(convertPointToBigtableTimeSeries(metricData, pd, taskId)); - allTimeSeries.put(projectName, current); - } - } - - return allTimeSeries; - } - - static List convertToApplicationResourceTimeSeries( - Collection collection, MonitoredResource applicationResource) { - Preconditions.checkNotNull( - applicationResource, - "convert application metrics is called when the supported resource is not detected"); - List allTimeSeries = new ArrayList<>(); - for (MetricData metricData : collection) { - metricData.getData().getPoints().stream() - .map( - pointData -> - createInternalMetricsTimeSeries(metricData, pointData, applicationResource)) - .filter(Optional::isPresent) - .forEach(ts -> ts.ifPresent(allTimeSeries::add)); - } - return allTimeSeries; - } - - @Nullable - static MonitoredResource createInternalMonitoredResource(ClientInfo clientInfo) { - try { - MonitoredResource monitoredResource = detectResource(clientInfo); - logger.log(Level.FINE, "Internal metrics monitored resource: %s", monitoredResource); - return monitoredResource; - } catch (Exception e) { - logger.log( - Level.WARNING, - "Failed to detect resource, will skip exporting application level metrics ", - e); - return null; - } - } - - @Nullable - private static MonitoredResource detectResource(ClientInfo clientInfo) { - GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE; - DetectedPlatform detectedPlatform = detector.detectPlatform(); - - @Nullable - String cloud_platform = SUPPORTED_PLATFORM_MAP.get(detectedPlatform.getSupportedPlatform()); - if (cloud_platform == null) { - return null; - } - - Map attrs = detectedPlatform.getAttributes(); - ImmutableList locationKeys = - ImmutableList.of( - AttributeKeys.GCE_CLOUD_REGION, - AttributeKeys.GCE_AVAILABILITY_ZONE, - AttributeKeys.GKE_LOCATION_TYPE_REGION, - AttributeKeys.GKE_CLUSTER_LOCATION); - - String region = - locationKeys.stream().map(attrs::get).filter(Objects::nonNull).findFirst().orElse("global"); - - // Deal with possibility of a zone. Zones are of the form us-east1-c, but we want a region - // which, which is us-east1. - region = Arrays.stream(region.split("-")).limit(2).collect(Collectors.joining("-")); - - String hostname = attrs.get(AttributeKeys.GCE_INSTANCE_HOSTNAME); - // if (hostname == null) { - // hostname = attrs.get(AttributeKeys.SERVERLESS_COMPUTE_NAME); - // } - // if (hostname == null) { - // hostname = attrs.get(AttributeKeys.GAE_MODULE_NAME); - // } - if (hostname == null) { - hostname = System.getenv("HOSTNAME"); - } - if (hostname == null) { - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException ignored) { - } - } - if (hostname == null) { - hostname = ""; - } - - return MonitoredResource.newBuilder() - .setType("bigtable_client") - .putLabels("project_id", clientInfo.getInstanceName().getProject()) - .putLabels("instance", clientInfo.getInstanceName().getInstance()) - .putLabels("app_profile", clientInfo.getAppProfileId()) - .putLabels("client_project", detectedPlatform.getProjectId()) - .putLabels("region", region) - .putLabels("cloud_platform", cloud_platform) - .putLabels("host_id", attrs.get(AttributeKeys.GKE_HOST_ID)) - .putLabels("host_name", hostname) - .putLabels("client_name", CLIENT_NAME) - .putLabels("uuid", DEFAULT_TASK_VALUE.get() + "-" + nextUuidSuffix.getAndIncrement()) - .build(); - } - - private static TimeSeries convertPointToBigtableTimeSeries( - MetricData metricData, PointData pointData, String taskId) { - TimeSeries.Builder builder = - TimeSeries.newBuilder() - .setMetricKind(convertMetricKind(metricData)) - .setValueType(convertValueType(metricData.getType())); - Metric.Builder metricBuilder = Metric.newBuilder().setType(metricData.getName()); - - Attributes attributes = pointData.getAttributes(); - MonitoredResource.Builder monitoredResourceBuilder = - MonitoredResource.newBuilder().setType(BIGTABLE_RESOURCE_TYPE); - - for (AttributeKey key : attributes.asMap().keySet()) { - if (BIGTABLE_PROMOTED_RESOURCE_LABELS.contains(key)) { - monitoredResourceBuilder.putLabels(key.getKey(), String.valueOf(attributes.get(key))); - } else { - metricBuilder.putLabels(key.getKey(), String.valueOf(attributes.get(key))); - } - } - - builder.setResource(monitoredResourceBuilder.build()); - - metricBuilder.putLabels(CLIENT_UID_KEY.getKey(), taskId); - builder.setMetric(metricBuilder.build()); - - MetricKind kind = convertMetricKind(metricData); - - Timestamp endTimestamp = Timestamps.fromNanos(pointData.getEpochNanos()); - Timestamp startTimestamp; - - if (kind == GAUGE) { - // GAUGE metrics must have start_time equal to end_time. - startTimestamp = endTimestamp; - } else { - startTimestamp = Timestamps.fromNanos(pointData.getStartEpochNanos()); - } - TimeInterval timeInterval = - TimeInterval.newBuilder().setStartTime(startTimestamp).setEndTime(endTimestamp).build(); - - builder.addPoints(createPoint(metricData.getType(), pointData, timeInterval)); - - return builder.build(); - } - - private static Optional createInternalMetricsTimeSeries( - MetricData metricData, PointData pointData, MonitoredResource applicationResource) { - MetricKind kind = convertMetricKind(metricData); - TimeSeries.Builder builder = - TimeSeries.newBuilder() - .setMetricKind(kind) - .setValueType(convertValueType(metricData.getType())) - .setResource(applicationResource); - - final Metric.Builder metricBuilder; - // TODO: clean this up - // Internal metrics are based on views that include the metric prefix - // gRPC metrics dont have views and are dot encoded - // To unify these: - // - the useless views should be removed - // - internal metrics should use relative metric names w/o the prefix - if (INTERNAL_METRICS.contains(metricData.getName())) { - metricBuilder = newApplicationMetricBuilder(metricData.getName(), pointData.getAttributes()); - } else if (GRPC_METRICS.containsKey(metricData.getName())) { - metricBuilder = newGrpcMetricBuilder(metricData.getName(), pointData.getAttributes()); - } else { - logger.fine("Skipping unexpected internal metric: " + metricData.getName()); - return Optional.empty(); - } - - builder.setMetric(metricBuilder.build()); - - Timestamp endTimestamp = Timestamps.fromNanos(pointData.getEpochNanos()); - Timestamp startTimestamp; - if (kind == GAUGE) { - startTimestamp = endTimestamp; - } else { - startTimestamp = Timestamps.fromNanos(pointData.getStartEpochNanos()); - } - TimeInterval timeInterval = - TimeInterval.newBuilder().setStartTime(startTimestamp).setEndTime(endTimestamp).build(); - - builder.addPoints(createPoint(metricData.getType(), pointData, timeInterval)); - return Optional.of(builder.build()); - } - - private static Metric.Builder newApplicationMetricBuilder( - String metricName, Attributes attributes) { - // TODO: unify handling of metric prefixes - Metric.Builder metricBuilder = Metric.newBuilder().setType(metricName); - for (Map.Entry, Object> e : attributes.asMap().entrySet()) { - metricBuilder.putLabels(e.getKey().getKey(), String.valueOf(e.getValue())); - } - return metricBuilder; - } - - private static Metric.Builder newGrpcMetricBuilder(String grpcMetricName, Attributes attributes) { - Set allowedAttrs = GRPC_METRICS.get(grpcMetricName); - - Metric.Builder metricBuilder = - Metric.newBuilder() - .setType("bigtable.googleapis.com/internal/client/" + grpcMetricName.replace('.', '/')); - for (Map.Entry, Object> e : attributes.asMap().entrySet()) { - String attrKey = e.getKey().getKey(); - Object attrValue = e.getValue(); - - // gRPC metrics are experimental and can change attribute names, to avoid incompatibility with - // the predefined - // metric schemas in stackdriver, filter out unknown keys - if (!allowedAttrs.contains(attrKey)) { - continue; - } - // translate grpc key format to be compatible with cloud monitoring: - // grpc.xds_client.server_failure -> grpc_xds_client_server_failure - String normalizedKey = attrKey.replace('.', '_'); - metricBuilder.putLabels(normalizedKey, String.valueOf(attrValue)); - } - - return metricBuilder; - } - - private static MetricKind convertMetricKind(MetricData metricData) { - switch (metricData.getType()) { - case HISTOGRAM: - case EXPONENTIAL_HISTOGRAM: - return convertHistogramType(metricData.getHistogramData()); - case LONG_GAUGE: - case DOUBLE_GAUGE: - return GAUGE; - case LONG_SUM: - return convertSumDataType(metricData.getLongSumData()); - case DOUBLE_SUM: - return convertSumDataType(metricData.getDoubleSumData()); - default: - return UNRECOGNIZED; - } - } - - private static MetricKind convertHistogramType(HistogramData histogramData) { - if (histogramData.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { - return CUMULATIVE; - } - return UNRECOGNIZED; - } - - private static MetricKind convertSumDataType(SumData sum) { - if (!sum.isMonotonic()) { - return GAUGE; - } - if (sum.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { - return CUMULATIVE; - } - return UNRECOGNIZED; - } - - private static ValueType convertValueType(MetricDataType metricDataType) { - switch (metricDataType) { - case LONG_GAUGE: - case LONG_SUM: - return INT64; - case DOUBLE_GAUGE: - case DOUBLE_SUM: - return DOUBLE; - case HISTOGRAM: - case EXPONENTIAL_HISTOGRAM: - return DISTRIBUTION; - default: - return ValueType.UNRECOGNIZED; - } - } - - private static Point createPoint( - MetricDataType type, PointData pointData, TimeInterval timeInterval) { - Point.Builder builder = Point.newBuilder().setInterval(timeInterval); - switch (type) { - case HISTOGRAM: - case EXPONENTIAL_HISTOGRAM: - return builder - .setValue( - TypedValue.newBuilder() - .setDistributionValue(convertHistogramData((HistogramPointData) pointData)) - .build()) - .build(); - case DOUBLE_GAUGE: - case DOUBLE_SUM: - return builder - .setValue( - TypedValue.newBuilder() - .setDoubleValue(((DoublePointData) pointData).getValue()) - .build()) - .build(); - case LONG_GAUGE: - case LONG_SUM: - return builder - .setValue(TypedValue.newBuilder().setInt64Value(((LongPointData) pointData).getValue())) - .build(); - default: - logger.log(Level.WARNING, "unsupported metric type"); - return builder.build(); - } - } - - private static Distribution convertHistogramData(HistogramPointData pointData) { - return Distribution.newBuilder() - .setCount(pointData.getCount()) - .setMean(pointData.getCount() == 0L ? 0.0D : pointData.getSum() / pointData.getCount()) - .setBucketOptions( - BucketOptions.newBuilder() - .setExplicitBuckets(Explicit.newBuilder().addAllBounds(pointData.getBoundaries()))) - .addAllBucketCounts(pointData.getCounts()) - .build(); - } -} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java deleted file mode 100644 index 810d555de2..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import com.google.api.core.InternalApi; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.sdk.metrics.Aggregation; -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.InstrumentType; -import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.ViewBuilder; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nullable; - -/** Defining Bigtable builit-in metrics scope, attributes, metric names and views. */ -@InternalApi -public class BuiltinMetricsConstants { - - // Metric attribute keys for monitored resource - public static final AttributeKey BIGTABLE_PROJECT_ID_KEY = - AttributeKey.stringKey("project_id"); - public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); - public static final AttributeKey TABLE_ID_KEY = AttributeKey.stringKey("table"); - public static final AttributeKey CLUSTER_ID_KEY = AttributeKey.stringKey("cluster"); - public static final AttributeKey ZONE_ID_KEY = AttributeKey.stringKey("zone"); - - // Metric attribute keys for labels - // We need to access APP_PROFILE_KEY in EnhancedBigtableStubSettings and STREAMING_KEY in - // IT tests, so they're public. - public static final AttributeKey APP_PROFILE_KEY = AttributeKey.stringKey("app_profile"); - public static final AttributeKey STREAMING_KEY = AttributeKey.booleanKey("streaming"); - public static final AttributeKey CLIENT_NAME_KEY = AttributeKey.stringKey("client_name"); - static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method"); - static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); - static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); - static final AttributeKey APPLIED_KEY = AttributeKey.booleanKey("applied"); - - static final AttributeKey TRANSPORT_TYPE = AttributeKey.stringKey("transport_type"); - static final AttributeKey TRANSPORT_REGION = AttributeKey.stringKey("transport_region"); - static final AttributeKey TRANSPORT_ZONE = AttributeKey.stringKey("transport_zone"); - static final AttributeKey TRANSPORT_SUBZONE = AttributeKey.stringKey("transport_subzone"); - - // gRPC attribute keys - // Note that these attributes keys from transformed from - // A.B.C to A_B_C before exporting to Cloud Monitoring. - static final AttributeKey GRPC_LB_BACKEND_SERVICE_KEY = - AttributeKey.stringKey("grpc.lb.backend_service"); - static final AttributeKey GRPC_DISCONNECT_ERROR_KEY = - AttributeKey.stringKey("grpc.disconnect_error"); - static final AttributeKey GRPC_LB_LOCALITY_KEY = - AttributeKey.stringKey("grpc.lb.locality"); - static final AttributeKey GRPC_TARGET_KEY = AttributeKey.stringKey("grpc.target"); - static final AttributeKey GRPC_SECURITY_LEVEL_KEY = - AttributeKey.stringKey("grpc.security_level"); - static final AttributeKey GRPC_METHOD_KEY = AttributeKey.stringKey("grpc.method"); - static final AttributeKey GRPC_STATUS_KEY = AttributeKey.stringKey("grpc.status"); - static final AttributeKey GRPC_LB_RLS_DATA_PLANE_TARGET_KEY = - AttributeKey.stringKey("grpc.lb.rls.data_plane_target"); - static final AttributeKey GRPC_LB_PICK_RESULT_KEY = - AttributeKey.stringKey("grpc.lb.pick_result"); - static final AttributeKey GRPC_LB_RLS_SERVER_TARGET_KEY = - AttributeKey.stringKey("grpc.lb.rls.server_target"); - static final AttributeKey GRPC_XDS_SERVER_KEY = AttributeKey.stringKey("grpc.xds.server"); - static final AttributeKey GRPC_XDS_RESOURCE_TYPE_KEY = - AttributeKey.stringKey("grpc.xds.resource_type"); - - public static final String METER_NAME = "bigtable.googleapis.com/internal/client/"; - - // Metric names - public static final String OPERATION_LATENCIES_NAME = "operation_latencies"; - public static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; - // Temporary workaround for not being able to add new labels to ATTEMPT_LATENCIES_NAME - public static final String ATTEMPT_LATENCIES2_NAME = "attempt_latencies2"; - static final String RETRY_COUNT_NAME = "retry_count"; - static final String CONNECTIVITY_ERROR_COUNT_NAME = "connectivity_error_count"; - static final String SERVER_LATENCIES_NAME = "server_latencies"; - static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies"; - static final String APPLICATION_BLOCKING_LATENCIES_NAME = "application_latencies"; - static final String REMAINING_DEADLINE_NAME = "remaining_deadline"; - static final String CLIENT_BLOCKING_LATENCIES_NAME = "throttling_latencies"; - static final String PER_CONNECTION_ERROR_COUNT_NAME = "per_connection_error_count"; - static final String OUTSTANDING_RPCS_PER_CHANNEL_NAME = "connection_pool/outstanding_rpcs"; - static final String BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME = - "batch_write_flow_control_target_qps"; - static final String BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME = "batch_write_flow_control_factor"; - - // Start allow list of metrics that will be exported as internal - public static final Map> GRPC_METRICS = - ImmutableMap.>builder() - .put( - "grpc.client.attempt.duration", - ImmutableSet.of( - GRPC_LB_LOCALITY_KEY.getKey(), - GRPC_METHOD_KEY.getKey(), - GRPC_TARGET_KEY.getKey(), - GRPC_STATUS_KEY.getKey())) - .put( - "grpc.lb.rls.default_target_picks", - ImmutableSet.of( - GRPC_LB_RLS_DATA_PLANE_TARGET_KEY.getKey(), GRPC_LB_PICK_RESULT_KEY.getKey())) - .put( - "grpc.lb.rls.target_picks", - ImmutableSet.of( - GRPC_TARGET_KEY.getKey(), - GRPC_LB_RLS_SERVER_TARGET_KEY.getKey(), - GRPC_LB_RLS_DATA_PLANE_TARGET_KEY.getKey(), - GRPC_LB_PICK_RESULT_KEY.getKey())) - .put( - "grpc.lb.rls.failed_picks", - ImmutableSet.of(GRPC_TARGET_KEY.getKey(), GRPC_LB_RLS_SERVER_TARGET_KEY.getKey())) - // TODO: "grpc.xds_client.connected" - .put( - "grpc.xds_client.server_failure", - ImmutableSet.of(GRPC_TARGET_KEY.getKey(), GRPC_XDS_SERVER_KEY.getKey())) - // TODO: "grpc.xds_client.resource_updates_valid", - .put( - "grpc.xds_client.resource_updates_invalid", - ImmutableSet.of( - GRPC_TARGET_KEY.getKey(), - GRPC_XDS_SERVER_KEY.getKey(), - GRPC_XDS_RESOURCE_TYPE_KEY.getKey())) - // TODO: "grpc.xds_client.resources" - // gRPC subchannel metrics - .put( - "grpc.subchannel.disconnections", - ImmutableSet.of( - GRPC_LB_BACKEND_SERVICE_KEY.getKey(), - GRPC_DISCONNECT_ERROR_KEY.getKey(), - GRPC_LB_LOCALITY_KEY.getKey(), - GRPC_TARGET_KEY.getKey())) - .put( - "grpc.subchannel.connection_attempts_succeeded", - ImmutableSet.of( - GRPC_LB_BACKEND_SERVICE_KEY.getKey(), - GRPC_LB_LOCALITY_KEY.getKey(), - GRPC_TARGET_KEY.getKey())) - .put( - "grpc.subchannel.connection_attempts_failed", - ImmutableSet.of( - GRPC_LB_BACKEND_SERVICE_KEY.getKey(), - GRPC_LB_LOCALITY_KEY.getKey(), - GRPC_TARGET_KEY.getKey())) - .put( - "grpc.subchannel.open_connections", - ImmutableSet.of( - GRPC_LB_BACKEND_SERVICE_KEY.getKey(), - GRPC_LB_LOCALITY_KEY.getKey(), - GRPC_SECURITY_LEVEL_KEY.getKey(), - GRPC_TARGET_KEY.getKey())) - .build(); - - public static final Set INTERNAL_METRICS = - ImmutableSet.of(PER_CONNECTION_ERROR_COUNT_NAME, OUTSTANDING_RPCS_PER_CHANNEL_NAME).stream() - .map(m -> METER_NAME + m) - .collect(ImmutableSet.toImmutableSet()); - // End allow list of metrics that will be exported - - // Buckets under 100,000 are identical to buckets for server side metrics handler_latencies. - // Extending client side bucket to up to 3,200,000. - private static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM = - Aggregation.explicitBucketHistogram( - ImmutableList.of( - 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0, - 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, - 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, 100000.0, 200000.0, - 400000.0, 800000.0, 1600000.0, 3200000.0)); // max is 53.3 minutes - - private static final Aggregation AGGREGATION_PER_CONNECTION_ERROR_COUNT_HISTOGRAM = - Aggregation.explicitBucketHistogram( - ImmutableList.of( - 1.0, - 2.0, - 4.0, - 8.0, - 16.0, - 32.0, - 64.0, - 125.0, - 250.0, - 500.0, - 1_000.0, - 2_000.0, - 4_000.0, - 8_000.0, - 16_000.0, - 32_000.0, - 64_000.0, - 128_000.0, - 250_000.0, - 500_000.0, - 1_000_000.0)); - - // Buckets for outstanding RPCs per channel, max ~100 - private static final Aggregation AGGREGATION_OUTSTANDING_RPCS_HISTOGRAM = - Aggregation.explicitBucketHistogram( - ImmutableList.of( - 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, - 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, - 135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0, 170.0, 175.0, 180.0, 185.0, 190.0, - 195.0, 200.0)); - private static final Aggregation AGGREGATION_BATCH_WRITE_FLOW_CONTROL_FACTOR_HISTOGRAM = - Aggregation.explicitBucketHistogram(ImmutableList.of(0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3)); - - static final Set COMMON_ATTRIBUTES = - ImmutableSet.of( - BIGTABLE_PROJECT_ID_KEY, - INSTANCE_ID_KEY, - TABLE_ID_KEY, - APP_PROFILE_KEY, - CLUSTER_ID_KEY, - ZONE_ID_KEY, - METHOD_KEY, - CLIENT_NAME_KEY); - - static void defineView( - ImmutableMap.Builder viewMap, - String id, - @Nullable Aggregation aggregation, - InstrumentType type, - String unit, - Set attributes) { - InstrumentSelector selector = - InstrumentSelector.builder() - .setName(id) - .setMeterName(METER_NAME) - .setType(type) - .setUnit(unit) - .build(); - Set attributesFilter = - ImmutableSet.builder() - .addAll( - COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) - .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) - .build(); - ViewBuilder viewBuilder = - View.builder().setName(METER_NAME + id).setAttributeFilter(attributesFilter); - if (aggregation != null) { - viewBuilder.setAggregation(aggregation); - } - viewMap.put(selector, viewBuilder.build()); - } - - // uses cloud.BigtableClient schema - public static Map getInternalViews() { - ImmutableMap.Builder views = ImmutableMap.builder(); - defineView( - views, - PER_CONNECTION_ERROR_COUNT_NAME, - AGGREGATION_PER_CONNECTION_ERROR_COUNT_HISTOGRAM, - InstrumentType.HISTOGRAM, - "1", - ImmutableSet.builder() - .add(BIGTABLE_PROJECT_ID_KEY, INSTANCE_ID_KEY, APP_PROFILE_KEY, CLIENT_NAME_KEY) - .build()); - defineView( - views, - OUTSTANDING_RPCS_PER_CHANNEL_NAME, - AGGREGATION_OUTSTANDING_RPCS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "1", - ImmutableSet.builder() - .add(BIGTABLE_PROJECT_ID_KEY, INSTANCE_ID_KEY, APP_PROFILE_KEY, CLIENT_NAME_KEY) - .build()); - return views.build(); - } - - public static Map getAllViews() { - ImmutableMap.Builder views = ImmutableMap.builder(); - - defineView( - views, - OPERATION_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(STREAMING_KEY, STATUS_KEY) - .build()); - defineView( - views, - ATTEMPT_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(STREAMING_KEY, STATUS_KEY) - .build()); - defineView( - views, - ATTEMPT_LATENCIES2_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add( - STREAMING_KEY, - STATUS_KEY, - TRANSPORT_TYPE, - TRANSPORT_REGION, - TRANSPORT_ZONE, - TRANSPORT_SUBZONE) - .build()); - defineView( - views, - SERVER_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).add(STATUS_KEY).build()); - defineView( - views, - FIRST_RESPONSE_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).add(STATUS_KEY).build()); - defineView( - views, - APPLICATION_BLOCKING_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).build()); - defineView( - views, - CLIENT_BLOCKING_LATENCIES_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).build()); - defineView( - views, - RETRY_COUNT_NAME, - Aggregation.sum(), - InstrumentType.COUNTER, - "1", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).add(STATUS_KEY).build()); - defineView( - views, - CONNECTIVITY_ERROR_COUNT_NAME, - Aggregation.sum(), - InstrumentType.COUNTER, - "1", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).add(STATUS_KEY).build()); - defineView( - views, - REMAINING_DEADLINE_NAME, - AGGREGATION_WITH_MILLIS_HISTOGRAM, - InstrumentType.HISTOGRAM, - "ms", - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(STREAMING_KEY, STATUS_KEY) - .build()); - defineView( - views, - BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME, - null, - InstrumentType.GAUGE, - "1", - ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).build()); - defineView( - views, - BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME, - AGGREGATION_BATCH_WRITE_FLOW_CONTROL_FACTOR_HISTOGRAM, - InstrumentType.HISTOGRAM, - "1", - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(STATUS_KEY, APPLIED_KEY) - .build()); - return views.build(); - } -} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index edca9bd53f..cec15f6221 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -16,11 +16,8 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.auth.Credentials; -import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; import java.io.IOException; -import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; @@ -37,24 +34,15 @@ private BuiltinMetricsView() {} @Deprecated public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) - throws IOException { - registerBuiltinMetrics(builder); - } + throws IOException {} @Deprecated - public static void registerBuiltinMetrics(SdkMeterProviderBuilder builder) throws IOException { - for (Map.Entry entry : - BuiltinMetricsConstants.getAllViews().entrySet()) { - builder.registerView(entry.getKey(), entry.getValue()); - } - } + public static void registerBuiltinMetrics(SdkMeterProviderBuilder builder) throws IOException {} @Deprecated public static void registerBuiltinMetrics( String projectId, @Nullable Credentials credentials, SdkMeterProviderBuilder builder) - throws IOException { - registerBuiltinMetrics(builder); - } + throws IOException {} @Deprecated public static void registerBuiltinMetrics( @@ -62,16 +50,12 @@ public static void registerBuiltinMetrics( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @Nullable String endpoint) - throws IOException { - registerBuiltinMetrics(credentials, builder, endpoint); - } + throws IOException {} @Deprecated public static void registerBuiltinMetrics( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @Nullable String endpoint) - throws IOException { - registerBuiltinMetrics(builder); - } + throws IOException {} @Deprecated public static void registerBuiltinMetrics( @@ -79,9 +63,7 @@ public static void registerBuiltinMetrics( SdkMeterProviderBuilder builder, @Nullable String endpoint, @Nullable ScheduledExecutorService executorService) - throws IOException { - registerBuiltinMetrics(builder); - } + throws IOException {} @Deprecated static void registerBuiltinMetricsWithUniverseDomain( @@ -90,7 +72,5 @@ static void registerBuiltinMetricsWithUniverseDomain( @Nullable String endpoint, String universeDomain, @Nullable ScheduledExecutorService executorService) - throws IOException { - registerBuiltinMetrics(builder); - } + throws IOException {} } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java index 66f4e25a17..66041e8aca 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java @@ -18,11 +18,8 @@ import com.google.auth.Credentials; import com.google.common.base.MoreObjects; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; import java.io.IOException; -import java.util.Map; import java.util.concurrent.ScheduledExecutorService; /** @@ -33,19 +30,6 @@ *

    {@code
      * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
      *
    - * // Set up SdkMeterProvider for client side metrics
    - * CustomOpenTelemetryMetricsProvider.setupSdkMeterProvider(sdkMeterProvider);
    - *
    - * // register other metrics reader and views
    - * sdkMeterProvider.registerMetricReader(..);
    - * sdkMeterProvider.registerView(..);
    - *
    - * // create the OTEL instance
    - * OpenTelemetry openTelemetry = OpenTelemetrySdk
    - *     .builder()
    - *     .setMeterProvider(sdkMeterProvider.build())
    - *     .build();
    - *
      * // Override MetricsProvider in BigtableDataSettings
      * BigtableDataSettings settings = BigtableDataSettings.newBuilder()
      *   .setProjectId("my-project")
    @@ -71,45 +55,35 @@ public OpenTelemetry getOpenTelemetry() {
       }
     
       /**
    -   * Convenient method to set up SdkMeterProviderBuilder with the default credential and endpoint.
    +   * @deprecated this is no longer needed and is now a no-op
        */
    -  public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder) throws IOException {
    -    for (Map.Entry entry :
    -        BuiltinMetricsConstants.getAllViews().entrySet()) {
    -      builder.registerView(entry.getKey(), entry.getValue());
    -    }
    -  }
    +  @Deprecated
    +  public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder) throws IOException {}
     
       /**
    -   * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)}
    +   * @deprecated this is no longer needed and is now a no-op
        */
       @Deprecated
       public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, Credentials credentials)
    -      throws IOException {
    -    setupSdkMeterProvider(builder);
    -  }
    +      throws IOException {}
     
       /**
    -   * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)}
    +   * @deprecated this is no longer needed and is now a no-op
        */
       @Deprecated
       public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, String endpoint)
    -      throws IOException {
    -    setupSdkMeterProvider(builder);
    -  }
    +      throws IOException {}
     
       /**
    -   * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)}
    +   * @deprecated this is no longer needed and is now a no-op
        */
       @Deprecated
       public static void setupSdkMeterProvider(
           SdkMeterProviderBuilder builder, Credentials credentials, String endpoint)
    -      throws IOException {
    -    setupSdkMeterProvider(builder);
    -  }
    +      throws IOException {}
     
       /**
    -   * @deprecated Please use {@link #setupSdkMeterProvider(SdkMeterProviderBuilder)}
    +   * @deprecated this is no longer needed and is now a no-op
        */
       @Deprecated
       public static void setupSdkMeterProvider(
    @@ -117,9 +91,7 @@ public static void setupSdkMeterProvider(
           Credentials credentials,
           String endpoint,
           ScheduledExecutorService executor)
    -      throws IOException {
    -    setupSdkMeterProvider(builder);
    -  }
    +      throws IOException {}
     
       @Override
       public String toString() {
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
    index 20555520f6..b8e5df4487 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
    @@ -29,10 +29,10 @@
     import com.google.cloud.bigtable.admin.v2.models.Table;
     import com.google.cloud.bigtable.data.v2.BigtableDataClient;
     import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels;
     import com.google.cloud.bigtable.data.v2.models.Query;
     import com.google.cloud.bigtable.data.v2.models.Row;
     import com.google.cloud.bigtable.data.v2.models.RowMutation;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants;
     import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider;
     import com.google.cloud.bigtable.test_helpers.env.CloudEnv;
     import com.google.cloud.bigtable.test_helpers.env.PrefixGenerator;
    @@ -339,7 +339,7 @@ private void verifyMetricsWithMetricsReader(
                   .putAll(ts.getMetric().getLabelsMap())
                   .build();
           AttributesBuilder attributesBuilder = Attributes.builder();
    -      String streamingKey = BuiltinMetricsConstants.STREAMING_KEY.getKey();
    +      String streamingKey = MetricLabels.STREAMING_KEY.getKey();
           attributesMap.forEach(
               (k, v) -> {
                 if (!k.equals(streamingKey)) {
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MetricsITUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MetricsITUtils.java
    index 56f6bfa476..5e56d36e72 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MetricsITUtils.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MetricsITUtils.java
    @@ -15,7 +15,7 @@
      */
     package com.google.cloud.bigtable.data.v2.it;
     
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
     import com.google.common.truth.Correspondence;
     import io.opentelemetry.sdk.metrics.data.MetricData;
     import io.opentelemetry.sdk.metrics.data.PointData;
    @@ -27,11 +27,11 @@ public class MetricsITUtils {
     
       static final Correspondence POINT_DATA_CLUSTER_ID_CONTAINS =
           Correspondence.from(
    -          (pd, s) -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY).contains(s),
    +          (pd, s) -> pd.getAttributes().get(TableSchema.CLUSTER_ID_KEY).contains(s),
               "contains attributes");
     
       static final Correspondence POINT_DATA_ZONE_ID_CONTAINS =
           Correspondence.from(
    -          (pd, s) -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY).contains(s),
    +          (pd, s) -> pd.getAttributes().get(TableSchema.ZONE_ID_KEY).contains(s),
               "contains attributes");
     }
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
    index 1c9245ba39..03d9c156c3 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
    @@ -26,9 +26,10 @@
     import com.google.cloud.bigtable.admin.v2.models.Cluster;
     import com.google.cloud.bigtable.data.v2.BigtableDataClient;
     import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableOperationLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
     import com.google.cloud.bigtable.data.v2.models.Query;
     import com.google.cloud.bigtable.data.v2.models.Row;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants;
     import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider;
     import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
     import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
    @@ -105,23 +106,23 @@ public void testSuccess() throws Exception {
         Collection allMetricData = metricReader.collectAllMetrics();
         List metrics =
             metricReader.collectAllMetrics().stream()
    -            .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME))
    +            .filter(m -> m.getName().contains(TableOperationLatency.NAME))
                 .collect(Collectors.toList());
     
         assertThat(allMetricData)
             .comparingElementsUsing(METRIC_DATA_NAME_CONTAINS)
    -        .contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME);
    +        .contains(TableOperationLatency.NAME);
         assertThat(metrics).hasSize(1);
     
         MetricData metricData = metrics.get(0);
         List pointData = new ArrayList<>(metricData.getData().getPoints());
         List clusterAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.CLUSTER_ID_KEY))
                 .collect(Collectors.toList());
         List zoneAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.ZONE_ID_KEY))
                 .collect(Collectors.toList());
     
         assertThat(pointData)
    @@ -146,23 +147,23 @@ public void testFailure() {
         Collection allMetricData = metricReader.collectAllMetrics();
         List metrics =
             metricReader.collectAllMetrics().stream()
    -            .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME))
    +            .filter(m -> m.getName().contains(TableOperationLatency.NAME))
                 .collect(Collectors.toList());
     
         assertThat(allMetricData)
             .comparingElementsUsing(METRIC_DATA_NAME_CONTAINS)
    -        .contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME);
    +        .contains(TableOperationLatency.NAME);
         assertThat(metrics).hasSize(1);
     
         MetricData metricData = metrics.get(0);
         List pointData = new ArrayList<>(metricData.getData().getPoints());
         List clusterAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.CLUSTER_ID_KEY))
                 .collect(Collectors.toList());
         List zoneAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.ZONE_ID_KEY))
                 .collect(Collectors.toList());
     
         assertThat(pointData)
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
    index 0196614299..50ff7ea6ad 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
    @@ -26,8 +26,10 @@
     import com.google.cloud.bigtable.admin.v2.models.Cluster;
     import com.google.cloud.bigtable.data.v2.BigtableDataClient;
     import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableAttemptLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableOperationLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
     import com.google.cloud.bigtable.data.v2.models.RowMutation;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants;
     import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider;
     import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
     import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
    @@ -110,23 +112,23 @@ public void testSuccess() throws Exception {
         Collection allMetricData = metricReader.collectAllMetrics();
         List metrics =
             allMetricData.stream()
    -            .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME))
    +            .filter(m -> m.getName().contains(TableOperationLatency.NAME))
                 .collect(Collectors.toList());
     
         assertThat(allMetricData)
             .comparingElementsUsing(METRIC_DATA_NAME_CONTAINS)
    -        .contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME);
    +        .contains(TableOperationLatency.NAME);
         assertThat(metrics).hasSize(1);
     
         MetricData metricData = metrics.get(0);
         List pointData = new ArrayList<>(metricData.getData().getPoints());
         List clusterAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.CLUSTER_ID_KEY))
                 .collect(Collectors.toList());
         List zoneAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.ZONE_ID_KEY))
                 .collect(Collectors.toList());
     
         assertThat(pointData)
    @@ -163,10 +165,7 @@ public void testFailure() throws Exception {
         Collection allMetricData = metricReader.collectAllMetrics();
         MetricData metricData = null;
         for (MetricData md : allMetricData) {
    -      if (md.getName()
    -          .equals(
    -              BuiltinMetricsConstants.METER_NAME
    -                  + BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME)) {
    +      if (md.getName().equals(TableAttemptLatency.NAME)) {
             metricData = md;
             break;
           }
    @@ -174,7 +173,7 @@ public void testFailure() throws Exception {
     
         assertThat(allMetricData)
             .comparingElementsUsing(METRIC_DATA_NAME_CONTAINS)
    -        .contains(BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME);
    +        .contains(TableAttemptLatency.NAME);
         assertThat(metricData).isNotNull();
     
         List pointData = new ArrayList<>(metricData.getData().getPoints());
    @@ -185,11 +184,11 @@ public void testFailure() throws Exception {
         assertThat(pointData).comparingElementsUsing(POINT_DATA_ZONE_ID_CONTAINS).contains("global");
         List clusterAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.CLUSTER_ID_KEY))
                 .collect(Collectors.toList());
         List zoneAttributes =
             pointData.stream()
    -            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
    +            .map(pd -> pd.getAttributes().get(TableSchema.ZONE_ID_KEY))
                 .collect(Collectors.toList());
     
         assertThat(clusterAttributes).contains("");
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
    index d9ccad187e..7df30aa330 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
    @@ -15,14 +15,6 @@
      */
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
     import static com.google.common.truth.Truth.assertThat;
     import static org.mockito.ArgumentMatchers.any;
     import static org.mockito.Mockito.mock;
    @@ -38,6 +30,9 @@
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
     import com.google.cloud.monitoring.v3.MetricServiceClient;
     import com.google.cloud.monitoring.v3.stub.MetricServiceStub;
     import com.google.common.base.Suppliers;
    @@ -122,17 +117,17 @@ public void setUp() {
     
         attributes =
             Attributes.builder()
    -            .put(BIGTABLE_PROJECT_ID_KEY, projectId)
    -            .put(INSTANCE_ID_KEY, instanceId)
    -            .put(TABLE_ID_KEY, tableId)
    -            .put(CLUSTER_ID_KEY, cluster)
    -            .put(ZONE_ID_KEY, zone)
    -            .put(APP_PROFILE_KEY, appProfileId)
    +            .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, projectId)
    +            .put(TableSchema.INSTANCE_ID_KEY, instanceId)
    +            .put(TableSchema.TABLE_ID_KEY, tableId)
    +            .put(TableSchema.CLUSTER_ID_KEY, cluster)
    +            .put(TableSchema.ZONE_ID_KEY, zone)
    +            .put(MetricLabels.APP_PROFILE_KEY, appProfileId)
                 .build();
     
         resource = Resource.create(Attributes.empty());
     
    -    scope = InstrumentationScopeInfo.create(BuiltinMetricsConstants.METER_NAME);
    +    scope = InstrumentationScopeInfo.create(MetricRegistry.METER_NAME);
       }
     
       @After
    @@ -175,15 +170,19 @@ public void testExportingSumData() {
     
         assertThat(timeSeries.getResource().getLabelsMap())
             .containsExactly(
    -            BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    -            INSTANCE_ID_KEY.getKey(), instanceId,
    -            TABLE_ID_KEY.getKey(), tableId,
    -            CLUSTER_ID_KEY.getKey(), cluster,
    -            ZONE_ID_KEY.getKey(), zone);
    +            TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    +            TableSchema.INSTANCE_ID_KEY.getKey(), instanceId,
    +            TableSchema.TABLE_ID_KEY.getKey(), tableId,
    +            TableSchema.CLUSTER_ID_KEY.getKey(), cluster,
    +            TableSchema.ZONE_ID_KEY.getKey(), zone);
     
         assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
         assertThat(timeSeries.getMetric().getLabelsMap())
    -        .containsAtLeast(APP_PROFILE_KEY.getKey(), appProfileId, CLIENT_UID_KEY.getKey(), taskId);
    +        .containsAtLeast(
    +            MetricLabels.APP_PROFILE_KEY.getKey(),
    +            appProfileId,
    +            MetricLabels.CLIENT_UID.getKey(),
    +            taskId);
         assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(fakeValue);
         assertThat(timeSeries.getPoints(0).getInterval().getStartTime().getNanos())
             .isEqualTo(startEpoch);
    @@ -235,15 +234,19 @@ public void testExportingHistogramData() {
     
         assertThat(timeSeries.getResource().getLabelsMap())
             .containsExactly(
    -            BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    -            INSTANCE_ID_KEY.getKey(), instanceId,
    -            TABLE_ID_KEY.getKey(), tableId,
    -            CLUSTER_ID_KEY.getKey(), cluster,
    -            ZONE_ID_KEY.getKey(), zone);
    +            TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    +            TableSchema.INSTANCE_ID_KEY.getKey(), instanceId,
    +            TableSchema.TABLE_ID_KEY.getKey(), tableId,
    +            TableSchema.CLUSTER_ID_KEY.getKey(), cluster,
    +            TableSchema.ZONE_ID_KEY.getKey(), zone);
     
         assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
         assertThat(timeSeries.getMetric().getLabelsMap())
    -        .containsAtLeast(APP_PROFILE_KEY.getKey(), appProfileId, CLIENT_UID_KEY.getKey(), taskId);
    +        .containsAtLeast(
    +            MetricLabels.APP_PROFILE_KEY.getKey(),
    +            appProfileId,
    +            MetricLabels.CLIENT_UID.getKey(),
    +            taskId);
         Distribution distribution = timeSeries.getPoints(0).getValue().getDistributionValue();
         assertThat(distribution.getCount()).isEqualTo(3);
         assertThat(timeSeries.getPoints(0).getInterval().getStartTime().getNanos())
    @@ -268,12 +271,12 @@ public void testExportingSumDataInBatches() {
         for (int i = 0; i < 250; i++) {
           Attributes testAttributes =
               Attributes.builder()
    -              .put(BIGTABLE_PROJECT_ID_KEY, projectId)
    -              .put(INSTANCE_ID_KEY, instanceId)
    -              .put(TABLE_ID_KEY, tableId + i)
    -              .put(CLUSTER_ID_KEY, cluster)
    -              .put(ZONE_ID_KEY, zone)
    -              .put(APP_PROFILE_KEY, appProfileId)
    +              .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, projectId)
    +              .put(TableSchema.INSTANCE_ID_KEY, instanceId)
    +              .put(TableSchema.TABLE_ID_KEY, tableId + i)
    +              .put(TableSchema.CLUSTER_ID_KEY, cluster)
    +              .put(TableSchema.ZONE_ID_KEY, zone)
    +              .put(MetricLabels.APP_PROFILE_KEY, appProfileId)
                   .build();
           LongPointData longPointData =
               ImmutableLongPointData.create(startEpoch, endEpoch, testAttributes, i);
    @@ -309,15 +312,19 @@ public void testExportingSumDataInBatches() {
     
           assertThat(timeSeries.getResource().getLabelsMap())
               .containsExactly(
    -              BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    -              INSTANCE_ID_KEY.getKey(), instanceId,
    -              TABLE_ID_KEY.getKey(), tableId + i,
    -              CLUSTER_ID_KEY.getKey(), cluster,
    -              ZONE_ID_KEY.getKey(), zone);
    +              TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), projectId,
    +              TableSchema.INSTANCE_ID_KEY.getKey(), instanceId,
    +              TableSchema.TABLE_ID_KEY.getKey(), tableId + i,
    +              TableSchema.CLUSTER_ID_KEY.getKey(), cluster,
    +              TableSchema.ZONE_ID_KEY.getKey(), zone);
     
           assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
           assertThat(timeSeries.getMetric().getLabelsMap())
    -          .containsAtLeast(APP_PROFILE_KEY.getKey(), appProfileId, CLIENT_UID_KEY.getKey(), taskId);
    +          .containsAtLeast(
    +              MetricLabels.APP_PROFILE_KEY.getKey(),
    +              appProfileId,
    +              MetricLabels.CLIENT_UID.getKey(),
    +              taskId);
           assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(i);
           assertThat(timeSeries.getPoints(0).getInterval().getStartTime().getNanos())
               .isEqualTo(startEpoch);
    @@ -348,13 +355,13 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() {
                 startEpoch,
                 endEpoch,
                 Attributes.of(
    -                BIGTABLE_PROJECT_ID_KEY,
    +                ClientSchema.BIGTABLE_PROJECT_ID_KEY,
                     projectId,
    -                INSTANCE_ID_KEY,
    +                ClientSchema.INSTANCE_ID_KEY,
                     instanceId,
    -                APP_PROFILE_KEY,
    +                ClientSchema.APP_PROFILE_KEY,
                     appProfileId,
    -                CLIENT_NAME_KEY,
    +                ClientSchema.CLIENT_NAME,
                     clientName),
                 3d,
                 true,
    @@ -401,10 +408,10 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() {
         assertThat(timeSeries.getMetric().getLabelsMap())
             .isEqualTo(
                 ImmutableMap.builder()
    -                .put(BIGTABLE_PROJECT_ID_KEY.getKey(), projectId)
    -                .put(INSTANCE_ID_KEY.getKey(), instanceId)
    -                .put(APP_PROFILE_KEY.getKey(), appProfileId)
    -                .put(CLIENT_NAME_KEY.getKey(), clientName)
    +                .put(ClientSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), projectId)
    +                .put(ClientSchema.INSTANCE_ID_KEY.getKey(), instanceId)
    +                .put(ClientSchema.APP_PROFILE_KEY.getKey(), appProfileId)
    +                .put(ClientSchema.CLIENT_NAME.getKey(), clientName)
                     .build());
       }
     
    @@ -447,7 +454,9 @@ public void testExportingToMultipleProjects() {
             ImmutableHistogramPointData.create(
                 startEpoch,
                 endEpoch,
    -            attributes.toBuilder().put(BIGTABLE_PROJECT_ID_KEY, "another-project").build(),
    +            attributes.toBuilder()
    +                .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, "another-project")
    +                .build(),
                 50d,
                 true,
                 5d, // min
    @@ -492,26 +501,26 @@ public void testExportingToMultipleProjects() {
         assertThat(labelsMap)
             .containsExactly(
                 ImmutableMap.of(
    -                BIGTABLE_PROJECT_ID_KEY.getKey(),
    +                TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(),
                     projectId,
    -                INSTANCE_ID_KEY.getKey(),
    +                TableSchema.INSTANCE_ID_KEY.getKey(),
                     instanceId,
    -                TABLE_ID_KEY.getKey(),
    +                TableSchema.TABLE_ID_KEY.getKey(),
                     tableId,
    -                CLUSTER_ID_KEY.getKey(),
    +                TableSchema.CLUSTER_ID_KEY.getKey(),
                     cluster,
    -                ZONE_ID_KEY.getKey(),
    +                TableSchema.ZONE_ID_KEY.getKey(),
                     zone),
                 ImmutableMap.of(
    -                BIGTABLE_PROJECT_ID_KEY.getKey(),
    +                TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(),
                     "another-project",
    -                INSTANCE_ID_KEY.getKey(),
    +                TableSchema.INSTANCE_ID_KEY.getKey(),
                     instanceId,
    -                TABLE_ID_KEY.getKey(),
    +                TableSchema.TABLE_ID_KEY.getKey(),
                     tableId,
    -                CLUSTER_ID_KEY.getKey(),
    +                TableSchema.CLUSTER_ID_KEY.getKey(),
                     cluster,
    -                ZONE_ID_KEY.getKey(),
    +                TableSchema.ZONE_ID_KEY.getKey(),
                     zone));
         assertThat(counts).containsExactly(3l, 15l);
       }
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
    index 32453efd7f..8eee324317 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
    @@ -42,7 +42,7 @@ public class BuiltinMetricsTestUtils {
       private BuiltinMetricsTestUtils() {}
     
       public static MetricData getMetricData(InMemoryMetricReader reader, String metricName) {
    -    String fullMetricName = BuiltinMetricsConstants.METER_NAME + metricName;
    +    String fullMetricName = metricName;
         Collection allMetricData = Collections.emptyList();
     
         // Fetch the MetricData with retries
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
    index 47d1078b9d..b6afa75226 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
    @@ -15,25 +15,6 @@
      */
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLIED_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.REMAINING_DEADLINE_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
     import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedDoubleValue;
     import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedValue;
     import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getMetricData;
    @@ -67,6 +48,19 @@
     import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientBatchWriteFlowControlFactor;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientBatchWriteFlowControlTargetQps;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableApplicationBlockingLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableAttemptLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableClientBlockingLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableConnectivityErrorCount;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableFirstResponseLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableOperationLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableRemainingDeadline;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableRetryCount;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.TableServerLatency;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
     import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
     import com.google.cloud.bigtable.data.v2.models.Query;
     import com.google.cloud.bigtable.data.v2.models.Row;
    @@ -103,10 +97,8 @@
     import io.grpc.stub.StreamObserver;
     import io.opentelemetry.api.common.Attributes;
     import io.opentelemetry.sdk.OpenTelemetrySdk;
    -import io.opentelemetry.sdk.metrics.InstrumentSelector;
     import io.opentelemetry.sdk.metrics.SdkMeterProvider;
     import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
    -import io.opentelemetry.sdk.metrics.View;
     import io.opentelemetry.sdk.metrics.data.HistogramPointData;
     import io.opentelemetry.sdk.metrics.data.MetricData;
     import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
    @@ -120,7 +112,6 @@
     import java.util.Collections;
     import java.util.Iterator;
     import java.util.List;
    -import java.util.Map;
     import java.util.concurrent.TimeUnit;
     import java.util.concurrent.atomic.AtomicBoolean;
     import java.util.concurrent.atomic.AtomicInteger;
    @@ -173,9 +164,9 @@ public class BuiltinMetricsTracerTest {
               .build();
       private Attributes expectedBaseAttributes =
           Attributes.builder()
    -          .put(BuiltinMetricsConstants.BIGTABLE_PROJECT_ID_KEY, PROJECT_ID)
    -          .put(BuiltinMetricsConstants.INSTANCE_ID_KEY, INSTANCE_ID)
    -          .put(BuiltinMetricsConstants.APP_PROFILE_KEY, APP_PROFILE_ID)
    +          .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, PROJECT_ID)
    +          .put(TableSchema.INSTANCE_ID_KEY, INSTANCE_ID)
    +          .put(MetricLabels.APP_PROFILE_KEY, APP_PROFILE_ID)
               .build();
     
       private InMemoryMetricReader metricReader;
    @@ -191,11 +182,6 @@ public void setUp() throws Exception {
         SdkMeterProviderBuilder meterProvider =
             SdkMeterProvider.builder().registerMetricReader(metricReader);
     
    -    for (Map.Entry entry :
    -        BuiltinMetricsConstants.getAllViews().entrySet()) {
    -      meterProvider.registerView(entry.getKey(), entry.getValue());
    -    }
    -
         OpenTelemetrySdk otel =
             OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
         MetricRegistry mr = new MetricRegistry();
    @@ -313,16 +299,16 @@ public void testReadRowsOperationLatencies() {
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(STREAMING_KEY, true)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.STREAMING_KEY, true)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
    -    MetricData metricData = getMetricData(metricReader, OPERATION_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableOperationLatency.NAME);
     
         long value = getAggregatedValue(metricData, expectedAttributes);
         assertThat(value).isIn(Range.closed(SERVER_LATENCY, elapsed));
    @@ -338,16 +324,16 @@ public void testReadRowsOperationLatenciesOnAuthorizedView() {
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(STREAMING_KEY, true)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.STREAMING_KEY, true)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
    -    MetricData metricData = getMetricData(metricReader, OPERATION_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableOperationLatency.NAME);
         long value = getAggregatedValue(metricData, expectedAttributes);
         assertThat(value).isIn(Range.closed(SERVER_LATENCY, elapsed));
       }
    @@ -383,15 +369,15 @@ public void onComplete() {}
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, FIRST_RESPONSE_TABLE_ID)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, FIRST_RESPONSE_TABLE_ID)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
    -    MetricData metricData = getMetricData(metricReader, FIRST_RESPONSE_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableFirstResponseLatency.NAME);
     
         long value = getAggregatedValue(metricData, expectedAttributes);
         assertThat(value).isAtMost(firstResponseTimer.elapsed(TimeUnit.MILLISECONDS));
    @@ -403,38 +389,38 @@ public void testGfeMetrics() {
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
                 .build();
     
    -    MetricData serverLatenciesMetricData = getMetricData(metricReader, SERVER_LATENCIES_NAME);
    +    MetricData serverLatenciesMetricData = getMetricData(metricReader, TableServerLatency.NAME);
     
         long serverLatencies = getAggregatedValue(serverLatenciesMetricData, expectedAttributes);
         assertThat(serverLatencies).isEqualTo(FAKE_SERVER_TIMING);
     
         MetricData connectivityErrorCountMetricData =
    -        getMetricData(metricReader, CONNECTIVITY_ERROR_COUNT_NAME);
    +        getMetricData(metricReader, TableConnectivityErrorCount.NAME);
         Attributes expected1 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "UNAVAILABLE")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, "global")
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "UNAVAILABLE")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
         Attributes expected2 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
         verifyAttributes(connectivityErrorCountMetricData, expected1);
    @@ -480,25 +466,28 @@ public void onComplete() {
         assertThat(counter.get()).isEqualTo(fakeService.getResponseCounter().get());
     
         MetricData applicationLatency =
    -        getMetricData(metricReader, APPLICATION_BLOCKING_LATENCIES_NAME);
    +        getMetricData(metricReader, TableApplicationBlockingLatency.NAME);
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
                 .build();
         long value = getAggregatedValue(applicationLatency, expectedAttributes);
     
         assertThat(value).isAtLeast((APPLICATION_LATENCY - SLEEP_VARIABILITY) * counter.get());
     
    -    MetricData operationLatency = getMetricData(metricReader, OPERATION_LATENCIES_NAME);
    +    MetricData operationLatency = getMetricData(metricReader, TableOperationLatency.NAME);
         long operationLatencyValue =
             getAggregatedValue(
                 operationLatency,
    -            expectedAttributes.toBuilder().put(STATUS_KEY, "OK").put(STREAMING_KEY, true).build());
    +            expectedAttributes.toBuilder()
    +                .put(MetricLabels.STATUS_KEY, "OK")
    +                .put(MetricLabels.STREAMING_KEY, true)
    +                .build());
         assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY);
       }
     
    @@ -515,15 +504,15 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti
         }
     
         MetricData applicationLatency =
    -        getMetricData(metricReader, APPLICATION_BLOCKING_LATENCIES_NAME);
    +        getMetricData(metricReader, TableApplicationBlockingLatency.NAME);
     
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
                 .build();
     
         long value = getAggregatedValue(applicationLatency, expectedAttributes);
    @@ -532,11 +521,14 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti
         assertThat(counter).isEqualTo(fakeService.getResponseCounter().get());
         assertThat(value).isAtLeast(APPLICATION_LATENCY * (counter - 1) - SERVER_LATENCY);
     
    -    MetricData operationLatency = getMetricData(metricReader, OPERATION_LATENCIES_NAME);
    +    MetricData operationLatency = getMetricData(metricReader, TableOperationLatency.NAME);
         long operationLatencyValue =
             getAggregatedValue(
                 operationLatency,
    -            expectedAttributes.toBuilder().put(STATUS_KEY, "OK").put(STREAMING_KEY, true).build());
    +            expectedAttributes.toBuilder()
    +                .put(MetricLabels.STATUS_KEY, "OK")
    +                .put(MetricLabels.STREAMING_KEY, true)
    +                .build());
         assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY);
       }
     
    @@ -545,15 +537,15 @@ public void testRetryCount() throws InterruptedException {
         stub.mutateRowCallable()
             .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value"));
     
    -    MetricData metricData = getMetricData(metricReader, RETRY_COUNT_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableRetryCount.NAME);
         Attributes expectedAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(METHOD_KEY, "Bigtable.MutateRow")
    -            .put(STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRow")
    +            .put(MetricLabels.STATUS_KEY, "OK")
                 .build();
     
         long value = getAggregatedValue(metricData, expectedAttributes);
    @@ -566,28 +558,28 @@ public void testMutateRowAttemptsTagValues() throws InterruptedException {
             .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value"));
     
         outstandingRpcCounter.waitUntilRpcsDone();
    -    MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableAttemptLatency.NAME);
     
         Attributes expected1 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "UNAVAILABLE")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, "global")
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(METHOD_KEY, "Bigtable.MutateRow")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, false)
    +            .put(MetricLabels.STATUS_KEY, "UNAVAILABLE")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRow")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, false)
                 .build();
     
         Attributes expected2 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.MutateRow")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, false)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRow")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, false)
                 .build();
     
         verifyAttributes(metricData, expected1);
    @@ -605,17 +597,17 @@ public void testMutateRowsPartialError() throws InterruptedException {
     
         Assert.assertThrows(BatchingException.class, batcher::close);
     
    -    MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableAttemptLatency.NAME);
     
         Attributes expected =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.MutateRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, false)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, false)
                 .build();
     
         verifyAttributes(metricData, expected);
    @@ -633,17 +625,17 @@ public void testMutateRowsRpcError() {
     
         Assert.assertThrows(BatchingException.class, batcher::close);
     
    -    MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableAttemptLatency.NAME);
     
         Attributes expected =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "NOT_FOUND")
    -            .put(TABLE_ID_KEY, BAD_TABLE_ID)
    -            .put(ZONE_ID_KEY, "global")
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(METHOD_KEY, "Bigtable.MutateRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, false)
    +            .put(MetricLabels.STATUS_KEY, "NOT_FOUND")
    +            .put(TableSchema.TABLE_ID_KEY, BAD_TABLE_ID)
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, false)
                 .build();
     
         verifyAttributes(metricData, expected);
    @@ -653,28 +645,28 @@ public void testMutateRowsRpcError() {
       public void testReadRowsAttemptsTagValues() {
         Lists.newArrayList(stub.readRowsCallable().call(Query.create("fake-table")).iterator());
     
    -    MetricData metricData = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME);
    +    MetricData metricData = getMetricData(metricReader, TableAttemptLatency.NAME);
     
         Attributes expected1 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "UNAVAILABLE")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, "global")
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, true)
    +            .put(MetricLabels.STATUS_KEY, "UNAVAILABLE")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, true)
                 .build();
     
         Attributes expected2 =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    -            .put(STREAMING_KEY, true)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
    +            .put(MetricLabels.STREAMING_KEY, true)
                 .build();
     
         verifyAttributes(metricData, expected1);
    @@ -693,15 +685,15 @@ public void testBatchBlockingLatencies() throws InterruptedException {
     
           int expectedNumRequests = 6 / batchElementCount;
     
    -      MetricData applicationLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME);
    +      MetricData applicationLatency = getMetricData(metricReader, TableClientBlockingLatency.NAME);
     
           Attributes expectedAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(TABLE_ID_KEY, TABLE)
    -              .put(ZONE_ID_KEY, ZONE)
    -              .put(CLUSTER_ID_KEY, CLUSTER)
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +              .put(TableSchema.TABLE_ID_KEY, TABLE)
    +              .put(TableSchema.ZONE_ID_KEY, ZONE)
    +              .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                   .build();
     
           long value = getAggregatedValue(applicationLatency, expectedAttributes);
    @@ -719,15 +711,15 @@ public void testQueuedOnChannelServerStreamLatencies() throws Exception {
         Duration proxyDelayPriorTest = delayProxyDetector.getCurrentDelayUsed();
         f.get();
     
    -    MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME);
    +    MetricData clientLatency = getMetricData(metricReader, TableClientBlockingLatency.NAME);
     
         Attributes attributes =
             expectedBaseAttributes.toBuilder()
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
         assertThat(Duration.ofMillis(getAggregatedValue(clientLatency, attributes)))
    @@ -746,15 +738,15 @@ public void testQueuedOnChannelUnaryLatencies() throws Exception {
         f.get();
     
         outstandingRpcCounter.waitUntilRpcsDone();
    -    MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME);
    +    MetricData clientLatency = getMetricData(metricReader, TableClientBlockingLatency.NAME);
     
         Attributes attributes =
             expectedBaseAttributes.toBuilder()
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(METHOD_KEY, "Bigtable.MutateRow")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRow")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
         assertThat(Duration.ofMillis(getAggregatedValue(clientLatency, attributes)))
    @@ -772,39 +764,39 @@ public void testPermanentFailure() {
         } catch (NotFoundException e) {
         }
     
    -    MetricData attemptLatency = getMetricData(metricReader, ATTEMPT_LATENCIES_NAME);
    +    MetricData attemptLatency = getMetricData(metricReader, TableAttemptLatency.NAME);
     
         Attributes expected =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "NOT_FOUND")
    -            .put(TABLE_ID_KEY, BAD_TABLE_ID)
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(ZONE_ID_KEY, "global")
    -            .put(STREAMING_KEY, true)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "NOT_FOUND")
    +            .put(TableSchema.TABLE_ID_KEY, BAD_TABLE_ID)
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(MetricLabels.STREAMING_KEY, true)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
     
         verifyAttributes(attemptLatency, expected);
     
    -    MetricData opLatency = getMetricData(metricReader, OPERATION_LATENCIES_NAME);
    +    MetricData opLatency = getMetricData(metricReader, TableOperationLatency.NAME);
         verifyAttributes(opLatency, expected);
       }
     
       @Test
       public void testRemainingDeadline() {
         stub.readRowsCallable().all().call(Query.create(TABLE));
    -    MetricData deadlineMetric = getMetricData(metricReader, REMAINING_DEADLINE_NAME);
    +    MetricData deadlineMetric = getMetricData(metricReader, TableRemainingDeadline.NAME);
     
         Attributes retryAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "UNAVAILABLE")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(ZONE_ID_KEY, "global")
    -            .put(CLUSTER_ID_KEY, "")
    -            .put(STREAMING_KEY, true)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "UNAVAILABLE")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(TableSchema.ZONE_ID_KEY, "global")
    +            .put(TableSchema.CLUSTER_ID_KEY, "")
    +            .put(MetricLabels.STREAMING_KEY, true)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
         HistogramPointData retryHistogramPointData =
             deadlineMetric.getHistogramData().getPoints().stream()
    @@ -818,13 +810,13 @@ public void testRemainingDeadline() {
     
         Attributes okAttributes =
             expectedBaseAttributes.toBuilder()
    -            .put(STATUS_KEY, "OK")
    -            .put(TABLE_ID_KEY, TABLE)
    -            .put(ZONE_ID_KEY, ZONE)
    -            .put(CLUSTER_ID_KEY, CLUSTER)
    -            .put(METHOD_KEY, "Bigtable.ReadRows")
    -            .put(STREAMING_KEY, true)
    -            .put(CLIENT_NAME_KEY, CLIENT_NAME)
    +            .put(MetricLabels.STATUS_KEY, "OK")
    +            .put(TableSchema.TABLE_ID_KEY, TABLE)
    +            .put(TableSchema.ZONE_ID_KEY, ZONE)
    +            .put(TableSchema.CLUSTER_ID_KEY, CLUSTER)
    +            .put(MetricLabels.METHOD_KEY, "Bigtable.ReadRows")
    +            .put(MetricLabels.STREAMING_KEY, true)
    +            .put(MetricLabels.CLIENT_NAME, CLIENT_NAME)
                 .build();
         HistogramPointData okHistogramPointData =
             deadlineMetric.getHistogramData().getPoints().stream()
    @@ -848,19 +840,21 @@ public void testBatchWriteFlowControlTargetQpsIncreased() throws InterruptedExce
           batcher.close();
     
           MetricData targetQpsMetric =
    -          getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME);
    +          getMetricData(metricReader, ClientBatchWriteFlowControlTargetQps.NAME);
           Attributes targetQpsAttributes =
    -          expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build();
    +          expectedBaseAttributes.toBuilder()
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .build();
           double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes);
           double expected_qps = 12;
           assertThat(expected_qps).isEqualTo(actual_qps);
     
    -      MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME);
    +      MetricData factorMetric = getMetricData(metricReader, ClientBatchWriteFlowControlFactor.NAME);
           Attributes factorAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(APPLIED_KEY, true)
    -              .put(STATUS_KEY, "OK")
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.APPLIED_KEY, true)
    +              .put(MetricLabels.STATUS_KEY, "OK")
                   .build();
           double actual_factor_mean = getAggregatedDoubleValue(factorMetric, factorAttributes);
           double expected_factor_mean = 1.2;
    @@ -878,19 +872,21 @@ public void testBatchWriteFlowControlTargetQpsDecreased() throws InterruptedExce
           batcher.close();
     
           MetricData targetQpsMetric =
    -          getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME);
    +          getMetricData(metricReader, ClientBatchWriteFlowControlTargetQps.NAME);
           Attributes targetQpsAttributes =
    -          expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build();
    +          expectedBaseAttributes.toBuilder()
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .build();
           double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes);
           double expected_qps = 8.0;
           assertThat(expected_qps).isEqualTo(actual_qps);
     
    -      MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME);
    +      MetricData factorMetric = getMetricData(metricReader, ClientBatchWriteFlowControlFactor.NAME);
           Attributes factorAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(APPLIED_KEY, true)
    -              .put(STATUS_KEY, "OK")
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.APPLIED_KEY, true)
    +              .put(MetricLabels.STATUS_KEY, "OK")
                   .build();
           double actual_factor_mean = getAggregatedDoubleValue(factorMetric, factorAttributes);
           double expected_factor_mean = 0.8;
    @@ -908,20 +904,22 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMaxFactor() throws Interru
           batcher.close();
     
           MetricData targetQpsMetric =
    -          getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME);
    +          getMetricData(metricReader, ClientBatchWriteFlowControlTargetQps.NAME);
           Attributes targetQpsAttributes =
    -          expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build();
    +          expectedBaseAttributes.toBuilder()
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .build();
           double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes);
           // Factor is 1.8 but capped at 1.3 so updated QPS is 13.
           double expected_qps = 13;
           assertThat(expected_qps).isEqualTo(actual_qps);
     
    -      MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME);
    +      MetricData factorMetric = getMetricData(metricReader, ClientBatchWriteFlowControlFactor.NAME);
           Attributes factorAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(APPLIED_KEY, true)
    -              .put(STATUS_KEY, "OK")
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.APPLIED_KEY, true)
    +              .put(MetricLabels.STATUS_KEY, "OK")
                   .build();
           double actual_factor_mean = getAggregatedDoubleValue(factorMetric, factorAttributes);
           // Factor is 1.8 but capped at 1.3
    @@ -940,20 +938,22 @@ public void testBatchWriteFlowControlTargetQpsCappedOnMinFactor() throws Interru
           batcher.close();
     
           MetricData targetQpsMetric =
    -          getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME);
    +          getMetricData(metricReader, ClientBatchWriteFlowControlTargetQps.NAME);
           Attributes targetQpsAttributes =
    -          expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build();
    +          expectedBaseAttributes.toBuilder()
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .build();
           double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes);
           // Factor is 0.5 but capped at 0.7 so updated QPS is 7.
           double expected_qps = 7;
           assertThat(expected_qps).isEqualTo(actual_qps);
     
    -      MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME);
    +      MetricData factorMetric = getMetricData(metricReader, ClientBatchWriteFlowControlFactor.NAME);
           Attributes factorAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(APPLIED_KEY, true)
    -              .put(STATUS_KEY, "OK")
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.APPLIED_KEY, true)
    +              .put(MetricLabels.STATUS_KEY, "OK")
                   .build();
           double actual_factor_mean = getAggregatedDoubleValue(factorMetric, factorAttributes);
           // Factor is 0.5 but capped at 0.7
    @@ -973,20 +973,22 @@ public void testBatchWriteFlowControlTargetQpsDecreasedForError() throws Interru
           batcher.close();
     
           MetricData targetQpsMetric =
    -          getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_TARGET_QPS_NAME);
    +          getMetricData(metricReader, ClientBatchWriteFlowControlTargetQps.NAME);
           Attributes targetQpsAttributes =
    -          expectedBaseAttributes.toBuilder().put(METHOD_KEY, "Bigtable.MutateRows").build();
    +          expectedBaseAttributes.toBuilder()
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .build();
           double actual_qps = getAggregatedDoubleValue(targetQpsMetric, targetQpsAttributes);
           // On error, min factor is applied.
           double expected_qps = 7;
           assertThat(expected_qps).isEqualTo(actual_qps);
     
    -      MetricData factorMetric = getMetricData(metricReader, BATCH_WRITE_FLOW_CONTROL_FACTOR_NAME);
    +      MetricData factorMetric = getMetricData(metricReader, ClientBatchWriteFlowControlFactor.NAME);
           Attributes factorAttributes =
               expectedBaseAttributes.toBuilder()
    -              .put(METHOD_KEY, "Bigtable.MutateRows")
    -              .put(APPLIED_KEY, true)
    -              .put(STATUS_KEY, "UNAVAILABLE")
    +              .put(MetricLabels.METHOD_KEY, "Bigtable.MutateRows")
    +              .put(MetricLabels.APPLIED_KEY, true)
    +              .put(MetricLabels.STATUS_KEY, "UNAVAILABLE")
                   .build();
           double actual_factor_mean = getAggregatedDoubleValue(factorMetric, factorAttributes);
           // On error, min factor is applied.
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java
    index e33ffe37e3..a4da359abd 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java
    @@ -15,8 +15,6 @@
      */
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OUTSTANDING_RPCS_PER_CHANNEL_NAME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME;
     import static com.google.common.truth.Truth.assertThat;
     import static org.mockito.ArgumentMatchers.any;
     import static org.mockito.ArgumentMatchers.anyLong;
    @@ -25,6 +23,8 @@
     import com.google.bigtable.v2.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientChannelPoolOutstandingRpcs;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientPerConnectionErrorCount;
     import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelObserver;
     import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolObserver;
     import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolSettings.LoadBalancingStrategy;
    @@ -182,7 +182,7 @@ public void testSingleRun() {
     
         // Assert Outstanding RPCs metric
         Optional rpcMetricDataOpt =
    -        getMetricData(metrics, OUTSTANDING_RPCS_PER_CHANNEL_NAME);
    +        getMetricData(metrics, ClientChannelPoolOutstandingRpcs.NAME);
         assertThat(rpcMetricDataOpt.isPresent()).isTrue();
         MetricData rpcMetricData = rpcMetricDataOpt.get();
         Collection rpcPoints = rpcMetricData.getHistogramData().getPoints();
    @@ -202,7 +202,7 @@ public void testSingleRun() {
     
         // Assert Error Count metric
         Optional errorMetricDataOpt =
    -        getMetricData(metrics, PER_CONNECTION_ERROR_COUNT_NAME);
    +        getMetricData(metrics, ClientPerConnectionErrorCount.NAME);
         assertThat(errorMetricDataOpt.isPresent()).isTrue();
         MetricData errorMetricData = errorMetricDataOpt.get();
         Collection errorPoints = errorMetricData.getHistogramData().getPoints();
    @@ -249,7 +249,7 @@ public void testMultipleRuns() {
     
         // Assert Outstanding RPCs
         Optional rpcMetricDataOpt =
    -        getMetricData(metrics, OUTSTANDING_RPCS_PER_CHANNEL_NAME);
    +        getMetricData(metrics, ClientChannelPoolOutstandingRpcs.NAME);
         assertThat(rpcMetricDataOpt.isPresent()).isTrue();
         Collection rpcPoints =
             rpcMetricDataOpt.get().getHistogramData().getPoints();
    @@ -265,7 +265,7 @@ public void testMultipleRuns() {
     
         // Assert Error Counts
         Optional errorMetricDataOpt =
    -        getMetricData(metrics, PER_CONNECTION_ERROR_COUNT_NAME);
    +        getMetricData(metrics, ClientPerConnectionErrorCount.NAME);
         assertThat(errorMetricDataOpt.isPresent()).isTrue();
         Collection errorPoints =
             errorMetricDataOpt.get().getHistogramData().getPoints();
    @@ -294,7 +294,7 @@ public void testErrorMetricsOnlyRecordedForAllChannels() {
     
         Collection metrics = metricReader.collectAllMetrics();
         Optional errorMetricDataOpt =
    -        getMetricData(metrics, PER_CONNECTION_ERROR_COUNT_NAME);
    +        getMetricData(metrics, ClientPerConnectionErrorCount.NAME);
         assertThat(errorMetricDataOpt.isPresent()).isTrue();
         Collection errorPoints =
             errorMetricDataOpt.get().getHistogramData().getPoints();
    @@ -315,7 +315,7 @@ public void testDefaultLbPolicy() {
     
         Collection metrics = metricReader.collectAllMetrics();
         Optional rpcMetricDataOpt =
    -        getMetricData(metrics, OUTSTANDING_RPCS_PER_CHANNEL_NAME);
    +        getMetricData(metrics, ClientChannelPoolOutstandingRpcs.NAME);
         assertThat(rpcMetricDataOpt.isPresent()).isTrue();
         Collection points = rpcMetricDataOpt.get().getHistogramData().getPoints();
     
    
    From e526a240938bf1cc9a3bf59fcd5e45be865630e1 Mon Sep 17 00:00:00 2001
    From: Igor Bernstein 
    Date: Thu, 26 Feb 2026 16:53:59 -0500
    Subject: [PATCH 22/33] chore: move non-public opencensus impl into
     internal.metrics (#2813)
    
    * chore: remove stale metrics code
    
    Change-Id: Ib98c15b0531f70d1c0bd65ffbd2161b3e605448a
    
    * chore: move non-public opencensus impl to internal.metrics
    
    Change-Id: I9e12571f8f361c0a1345d8cdbd5deb3a44be7f51
    
    # Conflicts:
    #	google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    #	google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java
    ---
     .../data/v2/internal/csm/MetricsImpl.java     |  4 +-
     .../data/v2/internal/csm/attributes/Util.java | 26 ++++++++++
     .../csm/opencensus}/MetricsTracer.java        |  4 +-
     .../csm/opencensus}/MetricsTracerFactory.java |  2 +-
     .../csm/opencensus}/RpcMeasureConstants.java  |  2 +-
     .../csm/opencensus}/RpcViewConstants.java     | 47 +++++++++++++------
     .../RateLimitingServerStreamingCallable.java  |  2 +-
     .../v2/stub/metrics/BuiltinMetricsTracer.java |  2 +-
     .../data/v2/stub/metrics/RpcViews.java        | 28 ++++-------
     .../bigtable/data/v2/stub/metrics/Util.java   | 27 -----------
     .../v2/internal/csm/attributes/UtilTest.java  | 23 +++++++++
     .../BigtableTracerCallableTest.java           |  4 +-
     .../csm/opencensus}/MetricsTracerTest.java    |  4 +-
     .../csm/opencensus}/SimpleStatsComponent.java |  2 +-
     .../csm/opencensus}/StatsTestUtils.java       |  2 +-
     .../data/v2/stub/metrics/UtilTest.java        | 44 -----------------
     16 files changed, 106 insertions(+), 117 deletions(-)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/MetricsTracer.java (97%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/MetricsTracerFactory.java (96%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/RpcMeasureConstants.java (98%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/RpcViewConstants.java (73%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/BigtableTracerCallableTest.java (98%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/MetricsTracerTest.java (98%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/SimpleStatsComponent.java (93%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/opencensus}/StatsTestUtils.java (99%)
     delete mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java
    
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    index 67f0ef5c6e..c149ecf30c 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    @@ -24,12 +24,12 @@
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.MetricsTracerFactory;
    +import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants;
     import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter;
     import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory;
     import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer;
     import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants;
     import com.google.common.base.Preconditions;
     import com.google.common.collect.ImmutableList;
     import com.google.common.collect.ImmutableMap;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    index 818e0b8859..493abf8acb 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    @@ -16,12 +16,16 @@
     
     package com.google.cloud.bigtable.data.v2.internal.csm.attributes;
     
    +import com.google.api.gax.grpc.GrpcStatusCode;
    +import com.google.api.gax.rpc.ApiException;
     import com.google.bigtable.v2.PeerInfo;
     import com.google.bigtable.v2.PeerInfo.TransportType;
     import com.google.bigtable.v2.ResponseParams;
     import com.google.common.annotations.VisibleForTesting;
    +import io.grpc.Status;
     import java.util.Locale;
     import java.util.Optional;
    +import java.util.concurrent.CancellationException;
     import javax.annotation.Nullable;
     
     public class Util {
    @@ -100,4 +104,26 @@ public static String formatZoneIdMetricLabel(@Nullable ResponseParams clusterInf
             .filter(s -> !s.isEmpty())
             .orElse("global");
       }
    +
    +  public static Status.Code extractStatus(@Nullable Throwable error) {
    +    if (error == null) {
    +      return Status.Code.OK;
    +    }
    +    // Handle java CancellationException as if it was a gax CancelledException
    +    if (error instanceof CancellationException) {
    +      return Status.Code.CANCELLED;
    +    }
    +    if (error instanceof ApiException) {
    +      ApiException apiException = (ApiException) error;
    +      if (apiException.getStatusCode() instanceof GrpcStatusCode) {
    +        return ((GrpcStatusCode) apiException.getStatusCode()).getTransportCode();
    +      }
    +    }
    +
    +    Status s = Status.fromThrowable(error);
    +    if (s != null) {
    +      return s.getCode();
    +    }
    +    return Status.Code.UNKNOWN;
    +  }
     }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracer.java
    similarity index 97%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracer.java
    index 448d8b442b..921d0329ad 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracer.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
     
    @@ -21,7 +21,9 @@
     import com.google.api.gax.retrying.ServerStreamingAttemptException;
     import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
     import com.google.api.gax.tracing.SpanName;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.common.base.Stopwatch;
     import io.opencensus.stats.MeasureMap;
     import io.opencensus.stats.StatsRecorder;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerFactory.java
    similarity index 96%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerFactory.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerFactory.java
    index e0c173a2be..0f557e6536 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerFactory.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerFactory.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import com.google.api.core.InternalApi;
     import com.google.api.gax.tracing.ApiTracer;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcMeasureConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcMeasureConstants.java
    similarity index 98%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcMeasureConstants.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcMeasureConstants.java
    index 560bb084bf..39c9bb0e99 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcMeasureConstants.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcMeasureConstants.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import com.google.api.core.InternalApi;
     import io.opencensus.stats.Measure.MeasureLong;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViewConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcViewConstants.java
    similarity index 73%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViewConstants.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcViewConstants.java
    index 4e21eaf785..51af4269ad 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViewConstants.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/RpcViewConstants.java
    @@ -13,22 +13,24 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    -
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_ATTEMPT_LATENCY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_BATCH_THROTTLED_TIME;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_GFE_LATENCY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_INSTANCE_ID;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_OP;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_OP_ATTEMPT_COUNT;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_OP_LATENCY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_PROJECT_ID;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_READ_ROWS_FIRST_ROW_LATENCY;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants.BIGTABLE_STATUS;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_ATTEMPT_LATENCY;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_BATCH_THROTTLED_TIME;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_GFE_LATENCY;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_INSTANCE_ID;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_OP;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_OP_ATTEMPT_COUNT;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_OP_LATENCY;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_PROJECT_ID;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_READ_ROWS_FIRST_ROW_LATENCY;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants.BIGTABLE_STATUS;
    +
    +import com.google.common.annotations.VisibleForTesting;
     import com.google.common.collect.ImmutableList;
    +import com.google.common.collect.ImmutableSet;
     import io.opencensus.stats.Aggregation;
     import io.opencensus.stats.Aggregation.Count;
     import io.opencensus.stats.Aggregation.Distribution;
    @@ -37,7 +39,7 @@
     import io.opencensus.stats.View;
     import java.util.Arrays;
     
    -class RpcViewConstants {
    +public class RpcViewConstants {
       // Aggregations
       private static final Aggregation COUNT = Count.create();
       private static final Aggregation SUM = Sum.create();
    @@ -167,4 +169,19 @@ class RpcViewConstants {
               AGGREGATION_WITH_MILLIS_HISTOGRAM,
               ImmutableList.of(
                   BIGTABLE_INSTANCE_ID, BIGTABLE_PROJECT_ID, BIGTABLE_APP_PROFILE_ID, BIGTABLE_OP));
    +
    +  @VisibleForTesting
    +  public static final ImmutableSet BIGTABLE_CLIENT_VIEWS_SET =
    +      ImmutableSet.of(
    +          RpcViewConstants.BIGTABLE_OP_LATENCY_VIEW,
    +          RpcViewConstants.BIGTABLE_COMPLETED_OP_VIEW,
    +          RpcViewConstants.BIGTABLE_READ_ROWS_FIRST_ROW_LATENCY_VIEW,
    +          RpcViewConstants.BIGTABLE_ATTEMPT_LATENCY_VIEW,
    +          RpcViewConstants.BIGTABLE_ATTEMPTS_PER_OP_VIEW,
    +          RpcViewConstants.BIGTABLE_BATCH_THROTTLED_TIME_VIEW);
    +
    +  public static final ImmutableSet GFE_VIEW_SET =
    +      ImmutableSet.of(
    +          RpcViewConstants.BIGTABLE_GFE_LATENCY_VIEW,
    +          RpcViewConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT_VIEW);
     }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
    index c9f9ba06c1..4f4f788aac 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
    @@ -15,7 +15,7 @@
      */
     package com.google.cloud.bigtable.data.v2.stub;
     
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.Util.extractStatus;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util.extractStatus;
     
     import com.google.api.gax.rpc.ApiCallContext;
     import com.google.api.gax.rpc.DeadlineExceededException;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
    index 57181faa34..44034523ab 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
    @@ -16,7 +16,7 @@
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
     import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
    -import static com.google.cloud.bigtable.data.v2.stub.metrics.Util.extractStatus;
    +import static com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util.extractStatus;
     
     import com.google.api.core.ObsoleteApi;
     import com.google.api.gax.retrying.ServerStreamingAttemptException;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViews.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViews.java
    index e8902108aa..c4948a20bf 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViews.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/RpcViews.java
    @@ -15,29 +15,15 @@
      */
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
    +import com.google.api.core.InternalApi;
    +import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcViewConstants;
     import com.google.common.annotations.VisibleForTesting;
    -import com.google.common.collect.ImmutableSet;
     import io.opencensus.stats.Stats;
     import io.opencensus.stats.View;
     import io.opencensus.stats.ViewManager;
     
     @Deprecated
     public class RpcViews {
    -  @VisibleForTesting
    -  private static final ImmutableSet BIGTABLE_CLIENT_VIEWS_SET =
    -      ImmutableSet.of(
    -          RpcViewConstants.BIGTABLE_OP_LATENCY_VIEW,
    -          RpcViewConstants.BIGTABLE_COMPLETED_OP_VIEW,
    -          RpcViewConstants.BIGTABLE_READ_ROWS_FIRST_ROW_LATENCY_VIEW,
    -          RpcViewConstants.BIGTABLE_ATTEMPT_LATENCY_VIEW,
    -          RpcViewConstants.BIGTABLE_ATTEMPTS_PER_OP_VIEW,
    -          RpcViewConstants.BIGTABLE_BATCH_THROTTLED_TIME_VIEW);
    -
    -  private static final ImmutableSet GFE_VIEW_SET =
    -      ImmutableSet.of(
    -          RpcViewConstants.BIGTABLE_GFE_LATENCY_VIEW,
    -          RpcViewConstants.BIGTABLE_GFE_HEADER_MISSING_COUNT_VIEW);
    -
       private static boolean gfeMetricsRegistered = false;
     
       /** Registers all Bigtable specific views. */
    @@ -55,16 +41,18 @@ public static void registerBigtableClientGfeViews() {
         registerBigtableClientGfeViews(Stats.getViewManager());
       }
     
    +  @InternalApi
       @VisibleForTesting
    -  static void registerBigtableClientViews(ViewManager viewManager) {
    -    for (View view : BIGTABLE_CLIENT_VIEWS_SET) {
    +  public static void registerBigtableClientViews(ViewManager viewManager) {
    +    for (View view : RpcViewConstants.BIGTABLE_CLIENT_VIEWS_SET) {
           viewManager.registerView(view);
         }
       }
     
    +  @InternalApi
       @VisibleForTesting
    -  static void registerBigtableClientGfeViews(ViewManager viewManager) {
    -    for (View view : GFE_VIEW_SET) {
    +  public static void registerBigtableClientGfeViews(ViewManager viewManager) {
    +    for (View view : RpcViewConstants.GFE_VIEW_SET) {
           viewManager.registerView(view);
         }
         gfeMetricsRegistered = true;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    index 4af8abb869..db739567e8 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    @@ -16,9 +16,7 @@
     package com.google.cloud.bigtable.data.v2.stub.metrics;
     
     import com.google.api.core.InternalApi;
    -import com.google.api.gax.grpc.GrpcStatusCode;
     import com.google.api.gax.rpc.ApiCallContext;
    -import com.google.api.gax.rpc.ApiException;
     import com.google.bigtable.v2.AuthorizedViewName;
     import com.google.bigtable.v2.CheckAndMutateRowRequest;
     import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest;
    @@ -32,14 +30,11 @@
     import com.google.bigtable.v2.TableName;
     import com.google.common.collect.ImmutableMap;
     import io.grpc.Metadata;
    -import io.grpc.Status;
     import java.time.Instant;
     import java.time.temporal.ChronoUnit;
     import java.util.Arrays;
     import java.util.List;
     import java.util.Map;
    -import java.util.concurrent.CancellationException;
    -import javax.annotation.Nullable;
     
     /** Utilities to help integrating with OpenCensus. */
     @InternalApi("For internal use only")
    @@ -49,28 +44,6 @@ public class Util {
       static final Metadata.Key ATTEMPT_EPOCH_KEY =
           Metadata.Key.of("bigtable-client-attempt-epoch-usec", Metadata.ASCII_STRING_MARSHALLER);
     
    -  public static Status.Code extractStatus(@Nullable Throwable error) {
    -    if (error == null) {
    -      return Status.Code.OK;
    -    }
    -    // Handle java CancellationException as if it was a gax CancelledException
    -    if (error instanceof CancellationException) {
    -      return Status.Code.CANCELLED;
    -    }
    -    if (error instanceof ApiException) {
    -      ApiException apiException = (ApiException) error;
    -      if (apiException.getStatusCode() instanceof GrpcStatusCode) {
    -        return ((GrpcStatusCode) apiException.getStatusCode()).getTransportCode();
    -      }
    -    }
    -
    -    Status s = Status.fromThrowable(error);
    -    if (s != null) {
    -      return s.getCode();
    -    }
    -    return Status.Code.UNKNOWN;
    -  }
    -
       static String extractTableId(Object request) {
         String tableName = null;
         String authorizedViewName = null;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java
    index 78b6c18b8b..782b04928e 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/UtilTest.java
    @@ -16,9 +16,14 @@
     
     package com.google.cloud.bigtable.data.v2.internal.csm.attributes;
     
    +import static com.google.common.truth.Truth.assertThat;
     import static com.google.common.truth.Truth.assertWithMessage;
     
    +import com.google.api.gax.grpc.GrpcStatusCode;
    +import com.google.api.gax.rpc.DeadlineExceededException;
     import com.google.bigtable.v2.PeerInfo.TransportType;
    +import io.grpc.Status;
    +import io.opencensus.tags.TagValue;
     import org.junit.jupiter.api.Test;
     
     class UtilTest {
    @@ -30,4 +35,22 @@ void ensureAllTransportTypeHaveExpectedPrefix() {
               .isNotNull();
         }
       }
    +
    +  @Test
    +  public void testOk() {
    +    TagValue tagValue =
    +        TagValue.create(
    +            com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util.extractStatus(null)
    +                .name());
    +    assertThat(tagValue.asString()).isEqualTo("OK");
    +  }
    +
    +  @Test
    +  public void testError() {
    +    DeadlineExceededException error =
    +        new DeadlineExceededException(
    +            "Deadline exceeded", null, GrpcStatusCode.of(Status.Code.DEADLINE_EXCEEDED), true);
    +    TagValue tagValue = TagValue.create(Util.extractStatus(error).name());
    +    assertThat(tagValue.asString()).isEqualTo("DEADLINE_EXCEEDED");
    +  }
     }
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/BigtableTracerCallableTest.java
    similarity index 98%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/BigtableTracerCallableTest.java
    index f9b0e56ac5..4eec40a696 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/BigtableTracerCallableTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import static com.google.common.truth.Truth.assertThat;
     import static org.junit.Assert.fail;
    @@ -45,6 +45,8 @@
     import com.google.cloud.bigtable.data.v2.models.TableId;
     import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext;
     import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.RpcViews;
     import com.google.common.collect.ImmutableMap;
     import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
     import io.grpc.Metadata;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerTest.java
    similarity index 98%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerTest.java
    index da864bf495..cadd777983 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/MetricsTracerTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import static com.google.common.truth.Truth.assertThat;
     import static org.mockito.ArgumentMatchers.any;
    @@ -39,6 +39,8 @@
     import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
     import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext;
     import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.RpcViews;
     import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor;
     import com.google.common.base.Stopwatch;
     import com.google.common.collect.ImmutableMap;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/SimpleStatsComponent.java
    similarity index 93%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/SimpleStatsComponent.java
    index 99aed9c3b4..bf867989d1 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/SimpleStatsComponent.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import io.opencensus.implcore.common.MillisClock;
     import io.opencensus.implcore.internal.SimpleEventQueue;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsTestUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/StatsTestUtils.java
    similarity index 99%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsTestUtils.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/StatsTestUtils.java
    index e808af8a84..db86a027fc 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsTestUtils.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/opencensus/StatsTestUtils.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.opencensus;
     
     import static com.google.common.base.Preconditions.checkNotNull;
     
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java
    deleted file mode 100644
    index f1e98e03a4..0000000000
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/UtilTest.java
    +++ /dev/null
    @@ -1,44 +0,0 @@
    -/*
    - * Copyright 2019 Google LLC
    - *
    - * Licensed under the Apache License, Version 2.0 (the "License");
    - * you may not use this file except in compliance with the License.
    - * You may obtain a copy of the License at
    - *
    - *     https://www.apache.org/licenses/LICENSE-2.0
    - *
    - * Unless required by applicable law or agreed to in writing, software
    - * distributed under the License is distributed on an "AS IS" BASIS,
    - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    - * See the License for the specific language governing permissions and
    - * limitations under the License.
    - */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    -
    -import static com.google.common.truth.Truth.assertThat;
    -
    -import com.google.api.gax.grpc.GrpcStatusCode;
    -import com.google.api.gax.rpc.DeadlineExceededException;
    -import io.grpc.Status;
    -import io.opencensus.tags.TagValue;
    -import org.junit.Test;
    -import org.junit.runner.RunWith;
    -import org.junit.runners.JUnit4;
    -
    -@RunWith(JUnit4.class)
    -public class UtilTest {
    -  @Test
    -  public void testOk() {
    -    TagValue tagValue = TagValue.create(Util.extractStatus(null).name());
    -    assertThat(tagValue.asString()).isEqualTo("OK");
    -  }
    -
    -  @Test
    -  public void testError() {
    -    DeadlineExceededException error =
    -        new DeadlineExceededException(
    -            "Deadline exceeded", null, GrpcStatusCode.of(Status.Code.DEADLINE_EXCEEDED), true);
    -    TagValue tagValue = TagValue.create(Util.extractStatus(error).name());
    -    assertThat(tagValue.asString()).isEqualTo("DEADLINE_EXCEEDED");
    -  }
    -}
    
    From 571aec7bab0c2de0e22af2aa5ffb62c40ccd84ed Mon Sep 17 00:00:00 2001
    From: Igor Bernstein 
    Date: Thu, 26 Feb 2026 17:51:59 -0500
    Subject: [PATCH 23/33] chore: move all moveable metrics impl to new home
     (#2815)
    
    * chore: move all moveable metrics impl to new home
    
    Change-Id: I95394f940d2fc6b1d46569a4ef572312e32cb7a0
    
    * oops
    
    Change-Id: I5ce66ebcd47ad9a46e0aceb19e4e561f05dbe43b
    ---
     .../data/v2/internal/csm/Metrics.java         |  2 +-
     .../data/v2/internal/csm/MetricsImpl.java     |  8 +--
     .../data/v2/internal/csm/attributes/Util.java | 52 +++++++++++++++++++
     .../BigtableCloudMonitoringExporter.java      |  6 ++-
     .../csm/exporter}/Converter.java              |  2 +-
     .../tracers}/BigtableGrpcStreamTracer.java    |  5 +-
     .../BigtableTracerStreamingCallable.java      |  3 +-
     .../tracers}/BigtableTracerUnaryCallable.java |  3 +-
     .../csm/tracers}/BuiltinMetricsTracer.java    |  4 +-
     .../tracers}/BuiltinMetricsTracerFactory.java |  2 +-
     .../tracers}/ChannelPoolMetricsTracer.java    |  2 +-
     .../csm/tracers}/CompositeTracer.java         |  3 +-
     .../csm/tracers}/CompositeTracerFactory.java  |  2 +-
     .../tracers}/TracedBatcherUnaryCallable.java  |  3 +-
     .../data/v2/stub/EnhancedBigtableStub.java    |  6 +--
     .../data/v2/stub/metrics/BigtableTracer.java  |  4 ++
     .../v2/stub/metrics/BuiltinMetricsView.java   |  7 +--
     .../v2/stub/metrics/NoopMetricsProvider.java  |  4 +-
     .../bigtable/data/v2/stub/metrics/Util.java   | 52 -------------------
     .../BigtableTransportChannelProvider.java     |  2 +-
     .../BigtableCloudMonitoringExporterTest.java  |  2 +-
     .../tracers}/BuiltinMetricsTracerTest.java    |  2 +-
     .../ChannelPoolMetricsTracerTest.java         |  2 +-
     .../csm/tracers}/CompositeTracerTest.java     |  3 +-
     24 files changed, 96 insertions(+), 85 deletions(-)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/exporter}/BigtableCloudMonitoringExporter.java (98%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/exporter}/Converter.java (99%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BigtableGrpcStreamTracer.java (89%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BigtableTracerStreamingCallable.java (97%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BigtableTracerUnaryCallable.java (94%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BuiltinMetricsTracer.java (98%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BuiltinMetricsTracerFactory.java (97%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/ChannelPoolMetricsTracer.java (98%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/CompositeTracer.java (98%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/CompositeTracerFactory.java (96%)
     rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/TracedBatcherUnaryCallable.java (93%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/exporter}/BigtableCloudMonitoringExporterTest.java (99%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/BuiltinMetricsTracerTest.java (99%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/ChannelPoolMetricsTracerTest.java (99%)
     rename google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/{stub/metrics => internal/csm/tracers}/CompositeTracerTest.java (98%)
    
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java
    index d5e1dbf5b3..7df665c673 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Metrics.java
    @@ -17,7 +17,7 @@
     
     import com.google.api.gax.tracing.ApiTracerFactory;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.ChannelPoolMetricsTracer;
     import io.grpc.ManagedChannelBuilder;
     import java.io.Closeable;
     import java.io.IOException;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    index c149ecf30c..db389030d8 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    @@ -24,12 +24,12 @@
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.exporter.BigtableCloudMonitoringExporter;
     import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.MetricsTracerFactory;
     import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BuiltinMetricsTracerFactory;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.ChannelPoolMetricsTracer;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.CompositeTracerFactory;
     import com.google.common.base.Preconditions;
     import com.google.common.collect.ImmutableList;
     import com.google.common.collect.ImmutableMap;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    index 493abf8acb..221452537d 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/Util.java
    @@ -18,9 +18,20 @@
     
     import com.google.api.gax.grpc.GrpcStatusCode;
     import com.google.api.gax.rpc.ApiException;
    +import com.google.bigtable.v2.AuthorizedViewName;
    +import com.google.bigtable.v2.CheckAndMutateRowRequest;
    +import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest;
    +import com.google.bigtable.v2.MaterializedViewName;
    +import com.google.bigtable.v2.MutateRowRequest;
    +import com.google.bigtable.v2.MutateRowsRequest;
     import com.google.bigtable.v2.PeerInfo;
     import com.google.bigtable.v2.PeerInfo.TransportType;
    +import com.google.bigtable.v2.ReadChangeStreamRequest;
    +import com.google.bigtable.v2.ReadModifyWriteRowRequest;
    +import com.google.bigtable.v2.ReadRowsRequest;
     import com.google.bigtable.v2.ResponseParams;
    +import com.google.bigtable.v2.SampleRowKeysRequest;
    +import com.google.bigtable.v2.TableName;
     import com.google.common.annotations.VisibleForTesting;
     import io.grpc.Status;
     import java.util.Locale;
    @@ -126,4 +137,45 @@ public static Status.Code extractStatus(@Nullable Throwable error) {
         }
         return Status.Code.UNKNOWN;
       }
    +
    +  public static String extractTableId(Object request) {
    +    String tableName = null;
    +    String authorizedViewName = null;
    +    String materializedViewName = null;
    +    if (request instanceof ReadRowsRequest) {
    +      tableName = ((ReadRowsRequest) request).getTableName();
    +      authorizedViewName = ((ReadRowsRequest) request).getAuthorizedViewName();
    +      materializedViewName = ((ReadRowsRequest) request).getMaterializedViewName();
    +    } else if (request instanceof MutateRowsRequest) {
    +      tableName = ((MutateRowsRequest) request).getTableName();
    +      authorizedViewName = ((MutateRowsRequest) request).getAuthorizedViewName();
    +    } else if (request instanceof MutateRowRequest) {
    +      tableName = ((MutateRowRequest) request).getTableName();
    +      authorizedViewName = ((MutateRowRequest) request).getAuthorizedViewName();
    +    } else if (request instanceof SampleRowKeysRequest) {
    +      tableName = ((SampleRowKeysRequest) request).getTableName();
    +      authorizedViewName = ((SampleRowKeysRequest) request).getAuthorizedViewName();
    +      materializedViewName = ((SampleRowKeysRequest) request).getMaterializedViewName();
    +    } else if (request instanceof CheckAndMutateRowRequest) {
    +      tableName = ((CheckAndMutateRowRequest) request).getTableName();
    +      authorizedViewName = ((CheckAndMutateRowRequest) request).getAuthorizedViewName();
    +    } else if (request instanceof ReadModifyWriteRowRequest) {
    +      tableName = ((ReadModifyWriteRowRequest) request).getTableName();
    +      authorizedViewName = ((ReadModifyWriteRowRequest) request).getAuthorizedViewName();
    +    } else if (request instanceof GenerateInitialChangeStreamPartitionsRequest) {
    +      tableName = ((GenerateInitialChangeStreamPartitionsRequest) request).getTableName();
    +    } else if (request instanceof ReadChangeStreamRequest) {
    +      tableName = ((ReadChangeStreamRequest) request).getTableName();
    +    }
    +    if (tableName != null && !tableName.isEmpty()) {
    +      return TableName.parse(tableName).getTable();
    +    }
    +    if (authorizedViewName != null && !authorizedViewName.isEmpty()) {
    +      return AuthorizedViewName.parse(authorizedViewName).getTable();
    +    }
    +    if (materializedViewName != null && !materializedViewName.isEmpty()) {
    +      return MaterializedViewName.parse(materializedViewName).getMaterializedView();
    +    }
    +    return "";
    +  }
     }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    similarity index 98%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    index 3bec1fc1e7..99a740f387 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.exporter;
     
     import com.google.api.core.ApiFuture;
     import com.google.api.core.ApiFutureCallback;
    @@ -28,6 +28,7 @@
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
     import com.google.cloud.monitoring.v3.MetricServiceClient;
     import com.google.cloud.monitoring.v3.MetricServiceSettings;
    +import com.google.common.annotations.VisibleForTesting;
     import com.google.common.base.Preconditions;
     import com.google.common.collect.Iterables;
     import com.google.common.util.concurrent.MoreExecutors;
    @@ -126,7 +127,8 @@ public static BigtableCloudMonitoringExporter create(
             metricRegistry, envInfo, clientInfo, MetricServiceClient.create(settingsBuilder.build()));
       }
     
    -  BigtableCloudMonitoringExporter(
    +  @VisibleForTesting
    +  public BigtableCloudMonitoringExporter(
           MetricRegistry metricRegistry,
           Supplier envInfo,
           ClientInfo clientInfo,
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java
    similarity index 99%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java
    index 4a2ca946f1..68b4536a32 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Converter.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java
    @@ -14,7 +14,7 @@
      * limitations under the License.
      */
     
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.exporter;
     
     import static com.google.api.MetricDescriptor.MetricKind.CUMULATIVE;
     import static com.google.api.MetricDescriptor.MetricKind.GAUGE;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableGrpcStreamTracer.java
    similarity index 89%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableGrpcStreamTracer.java
    index 9b220c1de3..99a184b5e3 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableGrpcStreamTracer.java
    @@ -13,8 +13,9 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import io.grpc.ClientStreamTracer;
     import io.grpc.Metadata;
     
    @@ -26,7 +27,7 @@
     class BigtableGrpcStreamTracer extends ClientStreamTracer {
       private final BigtableTracer tracer;
     
    -  public BigtableGrpcStreamTracer(BigtableTracer tracer) {
    +  private BigtableGrpcStreamTracer(BigtableTracer tracer) {
         this.tracer = tracer;
       }
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerStreamingCallable.java
    similarity index 97%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerStreamingCallable.java
    index 3cdcdc374e..562305798f 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerStreamingCallable.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerStreamingCallable.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.InternalApi;
     import com.google.api.gax.grpc.GrpcCallContext;
    @@ -23,6 +23,7 @@
     import com.google.api.gax.rpc.StreamController;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
     import com.google.cloud.bigtable.data.v2.stub.SafeResponseObserver;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.common.base.Preconditions;
     import com.google.common.base.Stopwatch;
     import java.util.concurrent.TimeUnit;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerUnaryCallable.java
    similarity index 94%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerUnaryCallable.java
    index 363a69af3d..443ee17345 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerUnaryCallable.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BigtableTracerUnaryCallable.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.ApiFuture;
     import com.google.api.core.InternalApi;
    @@ -21,6 +21,7 @@
     import com.google.api.gax.rpc.ApiCallContext;
     import com.google.api.gax.rpc.UnaryCallable;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.common.base.Preconditions;
     import javax.annotation.Nonnull;
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracer.java
    similarity index 98%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracer.java
    index 44034523ab..88dd39c0dc 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracer.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
     import static com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util.extractStatus;
    @@ -23,8 +23,10 @@
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.Util;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor.SidebandData;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.common.base.Stopwatch;
     import com.google.common.collect.Comparators;
     import com.google.common.math.IntMath;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerFactory.java
    similarity index 97%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerFactory.java
    index 0355160b67..8a41fd339e 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerFactory.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.InternalApi;
     import com.google.api.gax.tracing.ApiTracer;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracer.java
    similarity index 98%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracer.java
    index 67adfe78d3..0eb9242b77 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracer.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.InternalApi;
     import com.google.bigtable.v2.PeerInfo.TransportType;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracer.java
    similarity index 98%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracer.java
    index fad00a6d91..d9362acb48 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracer.java
    @@ -13,13 +13,14 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
     
     import com.google.api.core.ObsoleteApi;
     import com.google.api.gax.tracing.ApiTracer;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.common.collect.ImmutableList;
     import java.util.ArrayList;
     import java.util.List;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerFactory.java
    similarity index 96%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerFactory.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerFactory.java
    index 2d9256a5ea..8b2606e955 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerFactory.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerFactory.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.InternalApi;
     import com.google.api.gax.tracing.ApiTracer;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/TracedBatcherUnaryCallable.java
    similarity index 93%
    rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java
    rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/TracedBatcherUnaryCallable.java
    index 44ba688d55..9b1b9764ab 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/TracedBatcherUnaryCallable.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import com.google.api.core.ApiFuture;
     import com.google.api.core.InternalApi;
    @@ -21,6 +21,7 @@
     import com.google.api.gax.rpc.ApiCallContext;
     import com.google.api.gax.rpc.UnaryCallable;
     import com.google.api.gax.tracing.ApiTracer;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     
     /**
      * This callable will extract total throttled time from {@link ApiCallContext} and add it to {@link
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
    index d28d41ecbc..ec223c470e 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
    @@ -61,6 +61,9 @@
     import com.google.cloud.bigtable.data.v2.internal.PrepareResponse;
     import com.google.cloud.bigtable.data.v2.internal.RequestContext;
     import com.google.cloud.bigtable.data.v2.internal.SqlRow;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BigtableTracerStreamingCallable;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BigtableTracerUnaryCallable;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.TracedBatcherUnaryCallable;
     import com.google.cloud.bigtable.data.v2.models.BulkMutation;
     import com.google.cloud.bigtable.data.v2.models.ChangeStreamMutation;
     import com.google.cloud.bigtable.data.v2.models.ChangeStreamRecord;
    @@ -85,11 +88,8 @@
     import com.google.cloud.bigtable.data.v2.stub.changestream.GenerateInitialChangeStreamPartitionsUserCallable;
     import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamResumptionStrategy;
     import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamUserCallable;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerStreamingCallable;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerUnaryCallable;
     import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersServerStreamingCallable;
     import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersUnaryCallable;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.TracedBatcherUnaryCallable;
     import com.google.cloud.bigtable.data.v2.stub.mutaterows.BulkMutateRowsUserFacingCallable;
     import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsAttemptResult;
     import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java
    index a1a53b6089..df27fbd842 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracer.java
    @@ -20,6 +20,8 @@
     import com.google.api.gax.rpc.ApiCallContext;
     import com.google.api.gax.tracing.ApiTracer;
     import com.google.api.gax.tracing.BaseApiTracer;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BigtableTracerStreamingCallable;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BigtableTracerUnaryCallable;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
     import java.time.Duration;
     import javax.annotation.Nullable;
    @@ -28,6 +30,8 @@
      * A Bigtable specific {@link ApiTracer} that includes additional contexts. This class is a base
      * implementation that does nothing.
      */
    +// NOTE: this class was part of the public surface so can't move to
    +// com.google.cloud.bigtable.data.v2.internal.csm with the rest of the metrics.
     @BetaApi("This surface is not stable yet it might be removed in the future.")
     public class BigtableTracer extends BaseApiTracer {
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
    index cec15f6221..2ec4fdfed4 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
    @@ -22,11 +22,8 @@
     import javax.annotation.Nullable;
     
     /**
    - * A util class to register built-in metrics on a custom OpenTelemetry instance. This is for
    - * advanced usage, and is only necessary when wanting to write built-in metrics to cloud monitoring
    - * and custom sinks.
    - *
    - * @deprecated Use methods in {@link CustomOpenTelemetryMetricsProvider} instead.
    + * @deprecated this class is no longer used and is empty. It only exists because it's symbols were
    + *     part of the public surface.
      */
     @Deprecated
     public class BuiltinMetricsView {
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
    index 9a00ddb135..2ccb64a890 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
    @@ -20,8 +20,8 @@
     /**
      * Set {@link
      * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)},
    - * to {@link this#INSTANCE} to disable collecting and export client side metrics
    - * https://cloud.google.com/bigtable/docs/client-side-metrics.
    + * to {@link NoopMetricsProvider#INSTANCE} to disable collecting and export of client side metrics.
      */
     public final class NoopMetricsProvider implements MetricsProvider {
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    index db739567e8..a5e3ebea68 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
    @@ -17,17 +17,6 @@
     
     import com.google.api.core.InternalApi;
     import com.google.api.gax.rpc.ApiCallContext;
    -import com.google.bigtable.v2.AuthorizedViewName;
    -import com.google.bigtable.v2.CheckAndMutateRowRequest;
    -import com.google.bigtable.v2.GenerateInitialChangeStreamPartitionsRequest;
    -import com.google.bigtable.v2.MaterializedViewName;
    -import com.google.bigtable.v2.MutateRowRequest;
    -import com.google.bigtable.v2.MutateRowsRequest;
    -import com.google.bigtable.v2.ReadChangeStreamRequest;
    -import com.google.bigtable.v2.ReadModifyWriteRowRequest;
    -import com.google.bigtable.v2.ReadRowsRequest;
    -import com.google.bigtable.v2.SampleRowKeysRequest;
    -import com.google.bigtable.v2.TableName;
     import com.google.common.collect.ImmutableMap;
     import io.grpc.Metadata;
     import java.time.Instant;
    @@ -44,47 +33,6 @@ public class Util {
       static final Metadata.Key ATTEMPT_EPOCH_KEY =
           Metadata.Key.of("bigtable-client-attempt-epoch-usec", Metadata.ASCII_STRING_MARSHALLER);
     
    -  static String extractTableId(Object request) {
    -    String tableName = null;
    -    String authorizedViewName = null;
    -    String materializedViewName = null;
    -    if (request instanceof ReadRowsRequest) {
    -      tableName = ((ReadRowsRequest) request).getTableName();
    -      authorizedViewName = ((ReadRowsRequest) request).getAuthorizedViewName();
    -      materializedViewName = ((ReadRowsRequest) request).getMaterializedViewName();
    -    } else if (request instanceof MutateRowsRequest) {
    -      tableName = ((MutateRowsRequest) request).getTableName();
    -      authorizedViewName = ((MutateRowsRequest) request).getAuthorizedViewName();
    -    } else if (request instanceof MutateRowRequest) {
    -      tableName = ((MutateRowRequest) request).getTableName();
    -      authorizedViewName = ((MutateRowRequest) request).getAuthorizedViewName();
    -    } else if (request instanceof SampleRowKeysRequest) {
    -      tableName = ((SampleRowKeysRequest) request).getTableName();
    -      authorizedViewName = ((SampleRowKeysRequest) request).getAuthorizedViewName();
    -      materializedViewName = ((SampleRowKeysRequest) request).getMaterializedViewName();
    -    } else if (request instanceof CheckAndMutateRowRequest) {
    -      tableName = ((CheckAndMutateRowRequest) request).getTableName();
    -      authorizedViewName = ((CheckAndMutateRowRequest) request).getAuthorizedViewName();
    -    } else if (request instanceof ReadModifyWriteRowRequest) {
    -      tableName = ((ReadModifyWriteRowRequest) request).getTableName();
    -      authorizedViewName = ((ReadModifyWriteRowRequest) request).getAuthorizedViewName();
    -    } else if (request instanceof GenerateInitialChangeStreamPartitionsRequest) {
    -      tableName = ((GenerateInitialChangeStreamPartitionsRequest) request).getTableName();
    -    } else if (request instanceof ReadChangeStreamRequest) {
    -      tableName = ((ReadChangeStreamRequest) request).getTableName();
    -    }
    -    if (tableName != null && !tableName.isEmpty()) {
    -      return TableName.parse(tableName).getTable();
    -    }
    -    if (authorizedViewName != null && !authorizedViewName.isEmpty()) {
    -      return AuthorizedViewName.parse(authorizedViewName).getTable();
    -    }
    -    if (materializedViewName != null && !materializedViewName.isEmpty()) {
    -      return MaterializedViewName.parse(materializedViewName).getMaterializedView();
    -    }
    -    return "";
    -  }
    -
       /**
        * Add attempt number and client timestamp from api call context to request headers. Attempt
        * number starts from 0.
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java
    index 3c71da79c6..a893ba8218 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java
    @@ -23,7 +23,7 @@
     import com.google.api.gax.rpc.TransportChannel;
     import com.google.api.gax.rpc.TransportChannelProvider;
     import com.google.auth.Credentials;
    -import com.google.cloud.bigtable.data.v2.stub.metrics.ChannelPoolMetricsTracer;
    +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.ChannelPoolMetricsTracer;
     import com.google.common.base.Preconditions;
     import io.grpc.ManagedChannel;
     import java.io.IOException;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    similarity index 99%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    index 7df30aa330..b352eb1660 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.exporter;
     
     import static com.google.common.truth.Truth.assertThat;
     import static org.mockito.ArgumentMatchers.any;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    similarity index 99%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    index b6afa75226..9ef3ce3c9d 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedDoubleValue;
     import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedValue;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    similarity index 99%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    index a4da359abd..a15c0f53c4 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ChannelPoolMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import static com.google.common.truth.Truth.assertThat;
     import static org.mockito.ArgumentMatchers.any;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerTest.java
    similarity index 98%
    rename from google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java
    rename to google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerTest.java
    index 62c343f16c..c77f3e1e50 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/CompositeTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/CompositeTracerTest.java
    @@ -13,7 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -package com.google.cloud.bigtable.data.v2.stub.metrics;
    +package com.google.cloud.bigtable.data.v2.internal.csm.tracers;
     
     import static com.google.api.gax.util.TimeConversionUtils.toThreetenDuration;
     import static com.google.common.truth.Truth.assertThat;
    @@ -26,6 +26,7 @@
     import com.google.api.gax.tracing.ApiTracer.Scope;
     import com.google.bigtable.v2.ReadRowsRequest;
     import com.google.cloud.bigtable.data.v2.stub.MetadataExtractorInterceptor;
    +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracer;
     import com.google.cloud.bigtable.misc_utilities.MethodComparator;
     import com.google.common.collect.ImmutableList;
     import java.lang.reflect.Method;
    
    From 279776dde6a71217c8d86ee81dcab3cfb2f5f4c7 Mon Sep 17 00:00:00 2001
    From: Igor Bernstein 
    Date: Thu, 26 Feb 2026 19:44:23 -0500
    Subject: [PATCH 24/33] chore: port a couple of tests for csm2 (#2816)
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    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://togithub.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)
    - [ ] Rollback plan is reviewed and LGTMed
    - [ ] All new data plane features have a completed end to end testing plan
    
    Fixes # ☕️
    
    If you write sample code, please follow the [samples format](
    https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
    ---
     google-cloud-bigtable/pom.xml                 |   5 +
     .../internal/csm/attributes/MethodInfo.java   |   4 +
     .../v2/internal/csm/metrics/Constants.java    |   3 +-
     .../csm/MetricRegistryExportTest.java         | 694 ++++++++++++++++++
     .../BigtableCloudMonitoringExporterTest2.java | 560 ++++++++++++++
     5 files changed, 1265 insertions(+), 1 deletion(-)
     create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
     create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    
    diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml
    index 76285f5dfa..a8e0dbeb52 100644
    --- a/google-cloud-bigtable/pom.xml
    +++ b/google-cloud-bigtable/pom.xml
    @@ -255,6 +255,11 @@
         
     
         
    +    
    +      com.google.api.grpc
    +      grpc-google-cloud-monitoring-v3
    +      test
    +    
         
           com.google.api
           gax
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java
    index 122e5fe5ba..4312392afa 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/MethodInfo.java
    @@ -28,6 +28,10 @@ public abstract class MethodInfo {
       /** If the method is streaming (ie a scan). */
       public abstract boolean getStreaming();
     
    +  public static MethodInfo of(String name, boolean streaming) {
    +    return builder().setName(name).setStreaming(streaming).build();
    +  }
    +
       public static Builder builder() {
         return new AutoValue_MethodInfo.Builder();
       }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java
    index f0f1a7c839..3478fd2e42 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/Constants.java
    @@ -58,7 +58,8 @@ private MetricLabels() {}
     
         static final AttributeKey CHANNEL_POOL_LB_POLICY = AttributeKey.stringKey("lb_policy");
         static final AttributeKey DP_REASON_KEY = AttributeKey.stringKey("reason");
    -    static final AttributeKey DP_IP_PREFERENCE_KEY = AttributeKey.stringKey("reason");
    +    static final AttributeKey DP_IP_PREFERENCE_KEY =
    +        AttributeKey.stringKey("ip_preference");
     
         public static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
     
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
    new file mode 100644
    index 0000000000..e31e2e3047
    --- /dev/null
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
    @@ -0,0 +1,694 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.csm;
    +
    +import static com.google.common.truth.Truth.assertThat;
    +import static com.google.common.truth.Truth.assertWithMessage;
    +import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
    +
    +import com.google.api.Distribution;
    +import com.google.api.MonitoredResource;
    +import com.google.api.gax.core.NoCredentialsProvider;
    +import com.google.api.gax.grpc.GrpcTransportChannel;
    +import com.google.api.gax.rpc.FixedTransportChannelProvider;
    +import com.google.bigtable.v2.InstanceName;
    +import com.google.bigtable.v2.PeerInfo;
    +import com.google.bigtable.v2.PeerInfo.TransportType;
    +import com.google.bigtable.v2.ResponseParams;
    +import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
    +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.MethodInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.exporter.BigtableCloudMonitoringExporter;
    +import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelPoolSettings.LoadBalancingStrategy;
    +import com.google.cloud.monitoring.v3.MetricServiceClient;
    +import com.google.cloud.monitoring.v3.MetricServiceSettings;
    +import com.google.common.base.Function;
    +import com.google.common.collect.ImmutableMap;
    +import com.google.common.truth.Correspondence;
    +import com.google.common.truth.Truth;
    +import com.google.monitoring.v3.CreateTimeSeriesRequest;
    +import com.google.monitoring.v3.MetricServiceGrpc.MetricServiceImplBase;
    +import com.google.monitoring.v3.Point;
    +import com.google.monitoring.v3.TimeSeries;
    +import com.google.monitoring.v3.TypedValue;
    +import com.google.protobuf.Empty;
    +import io.grpc.ManagedChannel;
    +import io.grpc.ManagedChannelBuilder;
    +import io.grpc.Server;
    +import io.grpc.Status;
    +import io.grpc.Status.Code;
    +import io.grpc.stub.StreamObserver;
    +import io.opentelemetry.sdk.metrics.SdkMeterProvider;
    +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
    +import java.time.Duration;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.concurrent.BlockingDeque;
    +import java.util.concurrent.LinkedBlockingDeque;
    +import java.util.concurrent.TimeUnit;
    +import java.util.stream.Collectors;
    +import org.junit.jupiter.api.AfterEach;
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +
    +public class MetricRegistryExportTest {
    +  private static final InstanceName INSTANCE_NAME = InstanceName.of("my-project", "my-instance");
    +  private static final String appProfileId = "my-app-profile";
    +  private static final String tableId = "my-table";
    +  private static final String clusterId = "my-cluster";
    +  private static final String clusterZone = "us-east1-b";
    +
    +  private Server server;
    +  private FakeMetricService metricService;
    +  private ManagedChannel fakeServiceChannel;
    +
    +  private PeriodicMetricReader metricReader;
    +  private SdkMeterProvider meterProvider;
    +  private MetricRegistry metricRegistry;
    +  private RecorderRegistry registry;
    +
    +  private EnvInfo envInfo;
    +  private ClientInfo clientInfo =
    +      ClientInfo.builder().setInstanceName(INSTANCE_NAME).setAppProfileId(appProfileId).build();
    +  private MethodInfo methodInfo;
    +  private ResponseParams clusterInfo;
    +  private PeerInfo peerInfo;
    +
    +  private MonitoredResource expectedTableMonitoredResource;
    +  private MonitoredResource expectedClientMonitoredResource;
    +
    +  @BeforeEach
    +  void setUp() throws Exception {
    +    metricService = new FakeMetricService();
    +    server = FakeServiceBuilder.create(metricService).start();
    +
    +    envInfo =
    +        EnvInfo.builder()
    +            .setPlatform("gcp_compute_engine")
    +            .setProject("my-client-project")
    +            .setRegion("us-east1")
    +            .setHostId("123456")
    +            .setHostName("my-vm")
    +            .build();
    +
    +    fakeServiceChannel =
    +        ManagedChannelBuilder.forAddress("localhost", server.getPort()).usePlaintext().build();
    +
    +    metricRegistry = new MetricRegistry();
    +
    +    MetricServiceClient metricClient =
    +        MetricServiceClient.create(
    +            MetricServiceSettings.newBuilder()
    +                .setTransportChannelProvider(
    +                    FixedTransportChannelProvider.create(
    +                        GrpcTransportChannel.create(fakeServiceChannel)))
    +                .setCredentialsProvider(NoCredentialsProvider.create())
    +                .build());
    +    BigtableCloudMonitoringExporter exporter =
    +        new BigtableCloudMonitoringExporter(
    +            metricRegistry, () -> envInfo, clientInfo, metricClient);
    +    metricReader = PeriodicMetricReader.create(exporter);
    +    meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader).build();
    +
    +    registry = metricRegistry.newRecorderRegistry(meterProvider);
    +
    +    methodInfo = MethodInfo.builder().setName("Bigtable.ReadRow").setStreaming(false).build();
    +
    +    clusterInfo =
    +        ResponseParams.newBuilder().setZoneId(clusterZone).setClusterId(clusterId).build();
    +    peerInfo =
    +        PeerInfo.newBuilder()
    +            .setTransportType(TransportType.TRANSPORT_TYPE_SESSION_CLOUD_PATH)
    +            .setGoogleFrontendId(123)
    +            .setApplicationFrontendZone("us-east1-c")
    +            .setApplicationFrontendSubzone("ab")
    +            .build();
    +
    +    expectedTableMonitoredResource =
    +        MonitoredResource.newBuilder()
    +            .setType("bigtable_client_raw")
    +            .putLabels("project_id", clientInfo.getInstanceName().getProject())
    +            .putLabels("instance", clientInfo.getInstanceName().getInstance())
    +            .putLabels("cluster", clusterInfo.getClusterId())
    +            .putLabels("table", tableId)
    +            .putLabels("zone", clusterInfo.getZoneId())
    +            .build();
    +
    +    expectedClientMonitoredResource =
    +        MonitoredResource.newBuilder()
    +            .setType("bigtable_client")
    +            .putLabels("project_id", clientInfo.getInstanceName().getProject())
    +            .putLabels("instance", clientInfo.getInstanceName().getInstance())
    +            .putLabels("app_profile", appProfileId)
    +            .putLabels("client_project", envInfo.getProject())
    +            .putLabels("region", envInfo.getRegion())
    +            .putLabels("cloud_platform", envInfo.getPlatform())
    +            .putLabels("host_id", envInfo.getHostId())
    +            .putLabels("host_name", envInfo.getHostName())
    +            .putLabels("client_name", clientInfo.getClientName())
    +            .putLabels("uuid", envInfo.getUid())
    +            .build();
    +  }
    +
    +  @AfterEach
    +  void tearDown() {
    +    meterProvider.close();
    +    fakeServiceChannel.shutdown();
    +    server.shutdownNow();
    +  }
    +
    +  @Test
    +  void testOpLatency() {
    +    registry.operationLatency.record(
    +        clientInfo,
    +        tableId,
    +        methodInfo,
    +        clusterInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/operation_latencies");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName(),
    +            "streaming", Boolean.toString(methodInfo.getStreaming()));
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testAttemptLatency() {
    +    registry.attemptLatency.record(
    +        clientInfo,
    +        tableId,
    +        clusterInfo,
    +        methodInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/attempt_latencies");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName(),
    +            "streaming", Boolean.toString(methodInfo.getStreaming()));
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testAttemptLatency2() {
    +    registry.attemptLatency2.record(
    +        clientInfo,
    +        tableId,
    +        peerInfo,
    +        clusterInfo,
    +        methodInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/attempt_latencies2");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "transport_type", "session_cloudpath",
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "transport_region", "",
    +            "transport_zone", peerInfo.getApplicationFrontendZone(),
    +            "transport_subzone", peerInfo.getApplicationFrontendSubzone(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName(),
    +            "streaming", Boolean.toString(methodInfo.getStreaming()));
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testRetryCount() {
    +    registry.retryCount.record(
    +        clientInfo, tableId, methodInfo, clusterInfo, Status.UNAVAILABLE.getCode(), 1);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/retry_count");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName());
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder().setValue(TypedValue.newBuilder().setInt64Value(1)).build());
    +  }
    +
    +  @Test
    +  void testFirstByteLatency() {
    +    registry.firstResponseLantency.record(
    +        clientInfo,
    +        tableId,
    +        methodInfo,
    +        clusterInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/first_response_latencies");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName());
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testServerLatencies() {
    +    registry.serverLatency.record(
    +        clientInfo,
    +        tableId,
    +        methodInfo,
    +        clusterInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/server_latencies");
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName(),
    +            "streaming", Boolean.toString(methodInfo.getStreaming()));
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testChannelPoolOutstandingRpcs() {
    +    registry.channelPoolOutstandingRpcs.record(
    +        clientInfo,
    +        peerInfo.getTransportType(),
    +        LoadBalancingStrategy.POWER_OF_TWO_LEAST_IN_FLIGHT,
    +        true,
    +        1);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/connection_pool/outstanding_rpcs");
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "transport_type", "session_cloudpath",
    +            "lb_policy", "POWER_OF_TWO_LEAST_IN_FLIGHT",
    +            "streaming", "true");
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(1)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testConnectivityErrors() {
    +    registry.connectivityErrorCount.record(
    +        clientInfo, tableId, methodInfo, clusterInfo, Status.UNAVAILABLE.getCode(), 1);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/connectivity_error_count");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName());
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder().setValue(TypedValue.newBuilder().setInt64Value(1)).build());
    +  }
    +
    +  @Test
    +  void testDpCompatGuage() {
    +    registry.dpCompatGuage.recordFailure(clientInfo, "something");
    +    registry.dpCompatGuage.recordSuccess(clientInfo, "ipv4");
    +
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    List timeSeriesList =
    +        metricService.findTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/direct_access/compatible");
    +
    +    assertThat(timeSeriesList).hasSize(2);
    +    for (TimeSeries timeSeries : timeSeriesList) {
    +      Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +    }
    +    assertThat(timeSeriesList)
    +        .comparingElementsUsing(
    +            Correspondence.transforming(
    +                (Function>)
    +                    input -> input.getMetric().getLabelsMap(),
    +                "metric labels"))
    +        .containsExactly(
    +            ImmutableMap.of(
    +                "reason", "",
    +                "ip_preference", "ipv4"),
    +            ImmutableMap.of(
    +                "reason", "something",
    +                "ip_preference", ""));
    +  }
    +
    +  @Test
    +  void testApplicationErrors() {
    +    registry.applicationBlockingLatency.record(
    +        clientInfo, tableId, methodInfo, clusterInfo, Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/application_latencies");
    +
    +    assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName());
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testClientBlocking() {
    +    registry.clientBlockingLatency.record(
    +        clientInfo, tableId, methodInfo, clusterInfo, Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/throttling_latencies");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName());
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testRemainingDeadline() {
    +    registry.remainingDeadline.record(
    +        clientInfo,
    +        tableId,
    +        methodInfo,
    +        clusterInfo,
    +        Status.UNAVAILABLE.getCode(),
    +        Duration.ofMillis(123));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/remaining_deadline");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedTableMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "UNAVAILABLE",
    +            "client_uid", envInfo.getUid(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId(),
    +            "method", methodInfo.getName(),
    +            "streaming", Boolean.toString(methodInfo.getStreaming()));
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(123.0)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testPerConnectionErrors() {
    +    registry.perConnectionErrorCount.record(clientInfo, 1);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/per_connection_error_count");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "project_id", clientInfo.getInstanceName().getProject(),
    +            "client_uid", envInfo.getUid(),
    +            "instance", clientInfo.getInstanceName().getInstance(),
    +            "client_name", clientInfo.getClientName(),
    +            "app_profile", clientInfo.getAppProfileId());
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(Distribution.newBuilder().setCount(1).setMean(1)))
    +                .build());
    +  }
    +
    +  @Test
    +  void testBatchWriteFactor() {
    +    registry.batchWriteFlowControlFactor.record(
    +        clientInfo, Code.DEADLINE_EXCEEDED, true, MethodInfo.of("Bigtable.MutateRows", false), 0.5);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/batch_write_flow_control_factor");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly(
    +            "status", "DEADLINE_EXCEEDED",
    +            "applied", "true",
    +            "method", "Bigtable.MutateRows");
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(TypedValue.newBuilder().setDoubleValue(0.5).build())
    +                .build());
    +  }
    +
    +  @Test
    +  void testBatchWriteQps() {
    +    registry.batchWriteFlowControlTargetQps.record(
    +        clientInfo, MethodInfo.of("Bigtable.MutateRows", false), 123);
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/batch_write_flow_control_target_qps");
    +
    +    Truth.assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsExactly("method", "Bigtable.MutateRows");
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(TypedValue.newBuilder().setDoubleValue(123.0).build())
    +                .build());
    +  }
    +
    +  @Test
    +  void testPacemaker() {
    +    registry.pacemakerDelay.record(clientInfo, "background", Duration.ofMillis(1));
    +    metricReader.forceFlush().join(1, TimeUnit.MINUTES);
    +
    +    TimeSeries timeSeries =
    +        metricService.getSingleTimeSeriesByName(
    +            "bigtable.googleapis.com/internal/client/pacemaker_delays");
    +
    +    assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap()).containsExactly("executor", "background");
    +
    +    assertThat(timeSeries.getPointsList())
    +        .comparingExpectedFieldsOnly()
    +        .containsExactly(
    +            Point.newBuilder()
    +                .setValue(
    +                    TypedValue.newBuilder()
    +                        .setDistributionValue(
    +                            Distribution.newBuilder().setCount(1).setMean(1000.0)))
    +                .build());
    +  }
    +
    +  private static class FakeMetricService extends MetricServiceImplBase {
    +    final BlockingDeque requests = new LinkedBlockingDeque<>();
    +
    +    @Override
    +    public void createServiceTimeSeries(
    +        CreateTimeSeriesRequest request, StreamObserver responseObserver) {
    +      requests.add(request);
    +      responseObserver.onNext(Empty.getDefaultInstance());
    +      responseObserver.onCompleted();
    +    }
    +
    +    List findTimeSeriesByName(String name) {
    +      return requests.stream()
    +          .flatMap(r -> r.getTimeSeriesList().stream())
    +          .filter(ts -> name.equals(ts.getMetric().getType()))
    +          .collect(Collectors.toList());
    +    }
    +
    +    TimeSeries getSingleTimeSeriesByName(String name) {
    +      List timeSeriesList = findTimeSeriesByName(name);
    +      assertWithMessage("Expected to have a single TimeSeries with the name %s", name)
    +          .that(timeSeriesList)
    +          .hasSize(1);
    +
    +      return timeSeriesList.get(0);
    +    }
    +  }
    +}
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    new file mode 100644
    index 0000000000..7fdde6d5ca
    --- /dev/null
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    @@ -0,0 +1,560 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.csm.exporter;
    +
    +import static com.google.common.truth.Truth.assertThat;
    +
    +import com.google.api.Distribution;
    +import com.google.api.core.ApiFuture;
    +import com.google.api.core.ApiFutures;
    +import com.google.api.gax.rpc.ApiCallContext;
    +import com.google.api.gax.rpc.UnaryCallable;
    +import com.google.bigtable.v2.InstanceName;
    +import com.google.bigtable.v2.TableName;
    +import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    +import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.MetricLabels;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema;
    +import com.google.cloud.bigtable.data.v2.internal.csm.schema.TableSchema;
    +import com.google.cloud.monitoring.v3.MetricServiceClient;
    +import com.google.cloud.monitoring.v3.stub.MetricServiceStub;
    +import com.google.common.base.Suppliers;
    +import com.google.common.collect.ImmutableList;
    +import com.google.common.collect.ImmutableMap;
    +import com.google.monitoring.v3.CreateTimeSeriesRequest;
    +import com.google.monitoring.v3.TimeSeries;
    +import com.google.protobuf.Empty;
    +import com.google.protobuf.util.Timestamps;
    +import io.opentelemetry.api.common.Attributes;
    +import io.opentelemetry.api.metrics.MeterProvider;
    +import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
    +import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
    +import io.opentelemetry.sdk.metrics.data.HistogramPointData;
    +import io.opentelemetry.sdk.metrics.data.LongPointData;
    +import io.opentelemetry.sdk.metrics.data.MetricData;
    +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramData;
    +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramPointData;
    +import io.opentelemetry.sdk.metrics.internal.data.ImmutableLongPointData;
    +import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData;
    +import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
    +import io.opentelemetry.sdk.resources.Resource;
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.Collection;
    +import java.util.Collections;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.concurrent.BlockingDeque;
    +import java.util.concurrent.LinkedBlockingDeque;
    +import java.util.concurrent.TimeUnit;
    +import org.junit.jupiter.api.AfterEach;
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.api.extension.ExtendWith;
    +import org.mockito.Answers;
    +import org.mockito.Mock;
    +import org.mockito.junit.jupiter.MockitoExtension;
    +
    +@ExtendWith(MockitoExtension.class)
    +public class BigtableCloudMonitoringExporterTest2 {
    +  private static final TableName tableName =
    +      TableName.of("fake-project", "fake-instance", "fake-table");
    +  private static final String appProfileId = "default";
    +  private static final String zone = "us-east-1";
    +  private static final String cluster = "cluster-1";
    +
    +  private ClientInfo clientInfo;
    +  private EnvInfo envInfo;
    +
    +  private FakeMetricServiceStub mockMetricServiceStub;
    +  private MetricServiceClient fakeMetricServiceClient;
    +  private BigtableCloudMonitoringExporter exporter;
    +
    +  private Attributes attributes;
    +  private Resource resource;
    +  private InstrumentationScopeInfo scope;
    +
    +  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    +  private MeterProvider meterProvider;
    +
    +  @BeforeEach
    +  public void setUp() {
    +    mockMetricServiceStub = new FakeMetricServiceStub();
    +    fakeMetricServiceClient = new FakeMetricServiceClient(mockMetricServiceStub);
    +
    +    envInfo =
    +        EnvInfo.builder()
    +            .setProject("client-project")
    +            .setPlatform("gce_instance")
    +            .setRegion("cleint-region")
    +            .setHostName("harold")
    +            .setHostId("1234567890")
    +            .build();
    +
    +    clientInfo =
    +        ClientInfo.builder()
    +            .setInstanceName(InstanceName.of(tableName.getProject(), tableName.getInstance()))
    +            .setAppProfileId(appProfileId)
    +            .build();
    +
    +    MetricRegistry metricRegistry = new MetricRegistry();
    +    exporter =
    +        new BigtableCloudMonitoringExporter(
    +            metricRegistry, () -> envInfo, clientInfo, fakeMetricServiceClient);
    +
    +    attributes =
    +        Attributes.builder()
    +            .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, tableName.getProject())
    +            .put(TableSchema.INSTANCE_ID_KEY, tableName.getInstance())
    +            .put(TableSchema.TABLE_ID_KEY, tableName.getTable())
    +            .put(TableSchema.CLUSTER_ID_KEY, cluster)
    +            .put(TableSchema.ZONE_ID_KEY, zone)
    +            .put(MetricLabels.APP_PROFILE_KEY, appProfileId)
    +            .build();
    +
    +    resource = Resource.create(Attributes.empty());
    +
    +    scope = InstrumentationScopeInfo.create(MetricRegistry.METER_NAME);
    +  }
    +
    +  @AfterEach
    +  public void tearDown() {}
    +
    +  @Test
    +  public void testExportingSumData() throws InterruptedException {
    +    long fakeValue = 11L;
    +
    +    long startEpoch = 10;
    +    long endEpoch = 15;
    +    LongPointData longPointData =
    +        ImmutableLongPointData.create(startEpoch, endEpoch, attributes, fakeValue);
    +
    +    MetricData longData =
    +        ImmutableMetricData.createLongSum(
    +            resource,
    +            scope,
    +            "bigtable.googleapis.com/internal/client/retry_count",
    +            "description",
    +            "1",
    +            ImmutableSumData.create(
    +                true, AggregationTemporality.CUMULATIVE, ImmutableList.of(longPointData)));
    +
    +    exporter.export(Collections.singletonList(longData));
    +
    +    CreateTimeSeriesRequest request = mockMetricServiceStub.requests.poll(1, TimeUnit.MINUTES);
    +
    +    assertThat(request.getTimeSeriesList()).hasSize(1);
    +
    +    TimeSeries timeSeries = request.getTimeSeriesList().get(0);
    +
    +    assertThat(timeSeries.getResource().getLabelsMap())
    +        .containsExactly(
    +            TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), tableName.getProject(),
    +            TableSchema.INSTANCE_ID_KEY.getKey(), tableName.getInstance(),
    +            TableSchema.TABLE_ID_KEY.getKey(), tableName.getTable(),
    +            TableSchema.CLUSTER_ID_KEY.getKey(), cluster,
    +            TableSchema.ZONE_ID_KEY.getKey(), zone);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsAtLeast(
    +            MetricLabels.APP_PROFILE_KEY.getKey(),
    +            appProfileId,
    +            MetricLabels.CLIENT_UID.getKey(),
    +            envInfo.getUid());
    +    assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(fakeValue);
    +    assertThat(timeSeries.getPoints(0).getInterval().getStartTime())
    +        .isEqualTo(Timestamps.fromNanos(startEpoch));
    +    assertThat(timeSeries.getPoints(0).getInterval().getEndTime())
    +        .isEqualTo(Timestamps.fromNanos(endEpoch));
    +  }
    +
    +  @Test
    +  public void testExportingHistogramData() throws InterruptedException {
    +    long startEpoch = 10;
    +    long endEpoch = 15;
    +    HistogramPointData histogramPointData =
    +        ImmutableHistogramPointData.create(
    +            startEpoch,
    +            endEpoch,
    +            attributes,
    +            3d,
    +            true,
    +            1d, // min
    +            true,
    +            2d, // max
    +            Collections.singletonList(1.0),
    +            Arrays.asList(1L, 2L));
    +
    +    MetricData histogramData =
    +        ImmutableMetricData.createDoubleHistogram(
    +            resource,
    +            scope,
    +            "bigtable.googleapis.com/internal/client/operation_latencies",
    +            "description",
    +            "ms",
    +            ImmutableHistogramData.create(
    +                AggregationTemporality.CUMULATIVE, ImmutableList.of(histogramPointData)));
    +
    +    exporter.export(Arrays.asList(histogramData));
    +
    +    CreateTimeSeriesRequest request = mockMetricServiceStub.requests.poll(1, TimeUnit.MINUTES);
    +
    +    assertThat(request.getTimeSeriesList()).hasSize(1);
    +
    +    TimeSeries timeSeries = request.getTimeSeriesList().get(0);
    +
    +    assertThat(timeSeries.getResource().getLabelsMap())
    +        .containsExactly(
    +            TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), tableName.getProject(),
    +            TableSchema.INSTANCE_ID_KEY.getKey(), tableName.getInstance(),
    +            TableSchema.TABLE_ID_KEY.getKey(), tableName.getTable(),
    +            TableSchema.CLUSTER_ID_KEY.getKey(), cluster,
    +            TableSchema.ZONE_ID_KEY.getKey(), zone);
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .containsAtLeast(
    +            MetricLabels.APP_PROFILE_KEY.getKey(),
    +            appProfileId,
    +            MetricLabels.CLIENT_UID.getKey(),
    +            this.envInfo.getUid());
    +    Distribution distribution = timeSeries.getPoints(0).getValue().getDistributionValue();
    +    assertThat(distribution.getCount()).isEqualTo(3);
    +    assertThat(timeSeries.getPoints(0).getInterval().getStartTime())
    +        .isEqualTo(Timestamps.fromNanos(startEpoch));
    +    assertThat(timeSeries.getPoints(0).getInterval().getEndTime())
    +        .isEqualTo(Timestamps.fromNanos(endEpoch));
    +  }
    +
    +  @Test
    +  public void testExportingSumDataInBatches() {
    +    long startEpoch = 10;
    +    long endEpoch = 15;
    +
    +    Collection toExport = new ArrayList<>();
    +    for (int i = 0; i < 250; i++) {
    +      Attributes testAttributes =
    +          Attributes.builder()
    +              .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, tableName.getProject())
    +              .put(TableSchema.INSTANCE_ID_KEY, tableName.getInstance())
    +              .put(TableSchema.TABLE_ID_KEY, tableName.getTable() + i)
    +              .put(TableSchema.CLUSTER_ID_KEY, cluster)
    +              .put(TableSchema.ZONE_ID_KEY, zone)
    +              .put(MetricLabels.APP_PROFILE_KEY, appProfileId)
    +              .build();
    +      LongPointData longPointData =
    +          ImmutableLongPointData.create(startEpoch, endEpoch, testAttributes, i);
    +
    +      MetricData longData =
    +          ImmutableMetricData.createLongSum(
    +              resource,
    +              scope,
    +              "bigtable.googleapis.com/internal/client/retry_count",
    +              "description",
    +              "1",
    +              ImmutableSumData.create(
    +                  true, AggregationTemporality.CUMULATIVE, ImmutableList.of(longPointData)));
    +      toExport.add(longData);
    +    }
    +
    +    exporter.export(toExport);
    +
    +    assertThat(mockMetricServiceStub.requests).hasSize(2);
    +    CreateTimeSeriesRequest firstRequest = mockMetricServiceStub.requests.poll();
    +    CreateTimeSeriesRequest secondRequest = mockMetricServiceStub.requests.poll();
    +
    +    assertThat(firstRequest.getTimeSeriesList()).hasSize(200);
    +    assertThat(secondRequest.getTimeSeriesList()).hasSize(50);
    +
    +    for (int i = 0; i < 250; i++) {
    +      TimeSeries timeSeries;
    +      if (i < 200) {
    +        timeSeries = firstRequest.getTimeSeriesList().get(i);
    +      } else {
    +        timeSeries = secondRequest.getTimeSeriesList().get(i - 200);
    +      }
    +
    +      assertThat(timeSeries.getResource().getLabelsMap())
    +          .containsExactly(
    +              TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(),
    +              tableName.getProject(),
    +              TableSchema.INSTANCE_ID_KEY.getKey(),
    +              tableName.getInstance(),
    +              TableSchema.TABLE_ID_KEY.getKey(),
    +              tableName.getTable() + i,
    +              TableSchema.CLUSTER_ID_KEY.getKey(),
    +              cluster,
    +              TableSchema.ZONE_ID_KEY.getKey(),
    +              zone);
    +
    +      assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
    +      assertThat(timeSeries.getMetric().getLabelsMap())
    +          .containsAtLeast(
    +              MetricLabels.APP_PROFILE_KEY.getKey(),
    +              appProfileId,
    +              MetricLabels.CLIENT_UID.getKey(),
    +              envInfo.getUid());
    +      assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(i);
    +      assertThat(timeSeries.getPoints(0).getInterval().getStartTime())
    +          .isEqualTo(Timestamps.fromNanos(startEpoch));
    +      assertThat(timeSeries.getPoints(0).getInterval().getEndTime())
    +          .isEqualTo(Timestamps.fromNanos(endEpoch));
    +    }
    +  }
    +
    +  @Test
    +  public void testTimeSeriesForMetricWithGceOrGkeResource() throws InterruptedException {
    +    String gceProjectId = "fake-gce-project";
    +    EnvInfo envInfo =
    +        EnvInfo.builder()
    +            .setPlatform("gce_instance")
    +            .setProject(gceProjectId)
    +            .setRegion("cleint-region")
    +            .setHostId("1234567890")
    +            .setHostName("harold")
    +            .build();
    +
    +    ClientInfo clientInfo =
    +        ClientInfo.builder()
    +            .setInstanceName(InstanceName.of(tableName.getProject(), tableName.getInstance()))
    +            .setAppProfileId(appProfileId)
    +            .build();
    +
    +    MetricRegistry metricRegistry = new MetricRegistry();
    +    BigtableCloudMonitoringExporter exporter =
    +        new BigtableCloudMonitoringExporter(
    +            metricRegistry, Suppliers.ofInstance(envInfo), clientInfo, fakeMetricServiceClient);
    +
    +    long startEpoch = 10;
    +    long endEpoch = 15;
    +    HistogramPointData histogramPointData =
    +        ImmutableHistogramPointData.create(
    +            startEpoch,
    +            endEpoch,
    +            Attributes.of(
    +                ClientSchema.BIGTABLE_PROJECT_ID_KEY,
    +                tableName.getProject(),
    +                ClientSchema.INSTANCE_ID_KEY,
    +                tableName.getInstance(),
    +                ClientSchema.APP_PROFILE_KEY,
    +                appProfileId,
    +                ClientSchema.CLIENT_NAME,
    +                clientInfo.getClientName()),
    +            3d,
    +            true,
    +            1d, // min
    +            true,
    +            2d, // max
    +            Arrays.asList(1.0),
    +            Arrays.asList(1L, 2L));
    +
    +    MetricData histogramData =
    +        ImmutableMetricData.createDoubleHistogram(
    +            resource,
    +            scope,
    +            "bigtable.googleapis.com/internal/client/per_connection_error_count",
    +            "description",
    +            "ms",
    +            ImmutableHistogramData.create(
    +                AggregationTemporality.CUMULATIVE, ImmutableList.of(histogramPointData)));
    +
    +    exporter.export(Collections.singletonList(histogramData));
    +
    +    CreateTimeSeriesRequest request = mockMetricServiceStub.requests.poll(1, TimeUnit.MINUTES);
    +
    +    assertThat(request.getName()).isEqualTo("projects/" + tableName.getProject());
    +    assertThat(request.getTimeSeriesList()).hasSize(1);
    +
    +    TimeSeries timeSeries = request.getTimeSeriesList().get(0);
    +
    +    assertThat(timeSeries.getResource().getLabelsMap())
    +        .isEqualTo(
    +            ImmutableMap.builder()
    +                .put("project_id", tableName.getProject())
    +                .put("instance", tableName.getInstance())
    +                .put("app_profile", appProfileId)
    +                .put("client_project", gceProjectId)
    +                .put("region", "cleint-region")
    +                .put("cloud_platform", "gce_instance")
    +                .put("host_id", "1234567890")
    +                .put("host_name", "harold")
    +                .put("client_name", clientInfo.getClientName())
    +                .put("uuid", envInfo.getUid())
    +                .build());
    +
    +    assertThat(timeSeries.getMetric().getLabelsMap())
    +        .isEqualTo(
    +            ImmutableMap.builder()
    +                .put(ClientSchema.BIGTABLE_PROJECT_ID_KEY.getKey(), tableName.getProject())
    +                .put(ClientSchema.INSTANCE_ID_KEY.getKey(), tableName.getInstance())
    +                .put(ClientSchema.APP_PROFILE_KEY.getKey(), appProfileId)
    +                .put(ClientSchema.CLIENT_NAME.getKey(), clientInfo.getClientName())
    +                .put(MetricLabels.CLIENT_UID.getKey(), envInfo.getUid())
    +                .build());
    +  }
    +
    +  @Test
    +  public void testExportingToMultipleProjects() throws InterruptedException {
    +    long startEpoch = 10;
    +    long endEpoch = 15;
    +    HistogramPointData histogramPointData1 =
    +        ImmutableHistogramPointData.create(
    +            startEpoch,
    +            endEpoch,
    +            attributes,
    +            3d,
    +            true,
    +            1d, // min
    +            true,
    +            2d, // max
    +            Arrays.asList(1.0),
    +            Arrays.asList(1L, 2L));
    +
    +    MetricData histogramData1 =
    +        ImmutableMetricData.createDoubleHistogram(
    +            resource,
    +            scope,
    +            "bigtable.googleapis.com/internal/client/operation_latencies",
    +            "description",
    +            "ms",
    +            ImmutableHistogramData.create(
    +                AggregationTemporality.CUMULATIVE, ImmutableList.of(histogramPointData1)));
    +
    +    HistogramPointData histogramPointData2 =
    +        ImmutableHistogramPointData.create(
    +            startEpoch,
    +            endEpoch,
    +            attributes.toBuilder()
    +                .put(TableSchema.BIGTABLE_PROJECT_ID_KEY, "another-project")
    +                .build(),
    +            50d,
    +            true,
    +            5d, // min
    +            true,
    +            30d, // max
    +            Arrays.asList(1.0),
    +            Arrays.asList(5L, 10L));
    +
    +    MetricData histogramData2 =
    +        ImmutableMetricData.createDoubleHistogram(
    +            resource,
    +            scope,
    +            "bigtable.googleapis.com/internal/client/operation_latencies",
    +            "description",
    +            "ms",
    +            ImmutableHistogramData.create(
    +                AggregationTemporality.CUMULATIVE, ImmutableList.of(histogramPointData2)));
    +
    +    exporter.export(Arrays.asList(histogramData1, histogramData2));
    +
    +    List allValues =
    +        Arrays.asList(
    +            mockMetricServiceStub.requests.poll(1, TimeUnit.MINUTES),
    +            mockMetricServiceStub.requests.poll(1, TimeUnit.MINUTES));
    +
    +    assertThat(allValues).hasSize(2);
    +
    +    List> labelsMap = new ArrayList<>();
    +    List counts = new ArrayList<>();
    +    allValues.forEach(
    +        value -> {
    +          labelsMap.add(value.getTimeSeriesList().get(0).getResource().getLabelsMap());
    +          counts.add(
    +              value
    +                  .getTimeSeriesList()
    +                  .get(0)
    +                  .getPoints(0)
    +                  .getValue()
    +                  .getDistributionValue()
    +                  .getCount());
    +        });
    +
    +    assertThat(labelsMap)
    +        .containsExactly(
    +            ImmutableMap.of(
    +                TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(),
    +                tableName.getProject(),
    +                TableSchema.INSTANCE_ID_KEY.getKey(),
    +                tableName.getInstance(),
    +                TableSchema.TABLE_ID_KEY.getKey(),
    +                tableName.getTable(),
    +                TableSchema.CLUSTER_ID_KEY.getKey(),
    +                cluster,
    +                TableSchema.ZONE_ID_KEY.getKey(),
    +                zone),
    +            ImmutableMap.of(
    +                TableSchema.BIGTABLE_PROJECT_ID_KEY.getKey(),
    +                "another-project",
    +                TableSchema.INSTANCE_ID_KEY.getKey(),
    +                tableName.getInstance(),
    +                TableSchema.TABLE_ID_KEY.getKey(),
    +                tableName.getTable(),
    +                TableSchema.CLUSTER_ID_KEY.getKey(),
    +                cluster,
    +                TableSchema.ZONE_ID_KEY.getKey(),
    +                zone));
    +    assertThat(counts).containsExactly(3l, 15l);
    +  }
    +
    +  private static class FakeMetricServiceClient extends MetricServiceClient {
    +    protected FakeMetricServiceClient(MetricServiceStub stub) {
    +      super(stub);
    +    }
    +  }
    +
    +  private static class FakeMetricServiceStub extends MetricServiceStub {
    +    private final BlockingDeque requests = new LinkedBlockingDeque<>();
    +
    +    @Override
    +    public UnaryCallable createServiceTimeSeriesCallable() {
    +      return new UnaryCallable() {
    +        @Override
    +        public ApiFuture futureCall(
    +            CreateTimeSeriesRequest createTimeSeriesRequest, ApiCallContext apiCallContext) {
    +          requests.add(createTimeSeriesRequest);
    +          return ApiFutures.immediateFuture(Empty.getDefaultInstance());
    +        }
    +      };
    +    }
    +
    +    @Override
    +    public void close() {}
    +
    +    @Override
    +    public void shutdown() {}
    +
    +    @Override
    +    public boolean isShutdown() {
    +      return false;
    +    }
    +
    +    @Override
    +    public boolean isTerminated() {
    +      return false;
    +    }
    +
    +    @Override
    +    public void shutdownNow() {}
    +
    +    @Override
    +    public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
    +      return false;
    +    }
    +  }
    +}
    
    From 07d346702a5934529173b3ad17ca05e7e499bdff Mon Sep 17 00:00:00 2001
    From: Igor Bernstein 
    Date: Thu, 26 Feb 2026 20:52:27 -0500
    Subject: [PATCH 25/33] chore: add non-generated typesafe names (#2817)
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    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://togithub.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)
    - [ ] Rollback plan is reviewed and LGTMed
    - [ ] All new data plane features have a completed end to end testing plan
    
    Fixes # ☕️
    
    If you write sample code, please follow the [samples format](
    https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
    ---
     .../data/v2/BigtableDataClientFactory.java    |  2 +-
     .../data/v2/internal/RequestContext.java      |  4 +-
     .../data/v2/internal/api/InstanceName.java    | 70 +++++++++++++++
     .../data/v2/internal/api/TableName.java       | 86 +++++++++++++++++++
     .../data/v2/internal/csm/MetricsImpl.java     |  8 +-
     .../internal/csm/attributes/ClientInfo.java   |  2 +-
     .../BigtableCloudMonitoringExporter.java      |  2 +-
     .../ClientPerConnectionErrorCount.java        |  5 +-
     .../v2/internal/csm/schema/ClientSchema.java  |  6 +-
     .../internal/csm/schema/GrpcClientSchema.java |  6 +-
     .../v2/internal/csm/schema/TableSchema.java   |  4 +-
     .../data/v2/stub/BigtableChannelPrimer.java   |  4 +-
     .../data/v2/stub/BigtableClientContext.java   |  2 +-
     .../v2/internal/api/InstanceNameTest.java     | 51 +++++++++++
     .../data/v2/internal/api/TableNameTest.java   | 62 +++++++++++++
     .../csm/MetricRegistryExportTest.java         | 14 +--
     .../csm/attributes/ClientInfoTest.java        |  2 +-
     .../BigtableCloudMonitoringExporterTest.java  |  2 +-
     .../BigtableCloudMonitoringExporterTest2.java |  2 +-
     .../csm/tracers/BuiltinMetricsTracerTest.java |  2 +-
     .../tracers/ChannelPoolMetricsTracerTest.java |  2 +-
     21 files changed, 304 insertions(+), 34 deletions(-)
     create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceName.java
     create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/TableName.java
     create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceNameTest.java
     create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/TableNameTest.java
    
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java
    index d529f02eb2..f19726e2a3 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactory.java
    @@ -16,7 +16,7 @@
     package com.google.cloud.bigtable.data.v2;
     
     import com.google.api.core.BetaApi;
    -import com.google.bigtable.v2.InstanceName;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.stub.BigtableClientContext;
     import com.google.cloud.bigtable.data.v2.stub.ClientOperationSettings;
     import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java
    index 2c3213d003..7058ae137c 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/RequestContext.java
    @@ -36,8 +36,8 @@ public abstract class RequestContext implements Serializable {
     
       public static RequestContext create(ClientInfo clientInfo) {
         return create(
    -        clientInfo.getInstanceName().getProject(),
    -        clientInfo.getInstanceName().getInstance(),
    +        clientInfo.getInstanceName().getProjectId(),
    +        clientInfo.getInstanceName().getInstanceId(),
             clientInfo.getAppProfileId());
       }
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceName.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceName.java
    new file mode 100644
    index 0000000000..01dfed2d72
    --- /dev/null
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceName.java
    @@ -0,0 +1,70 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.api;
    +
    +import com.google.auto.value.AutoValue;
    +import com.google.common.base.Preconditions;
    +import com.google.common.base.Splitter;
    +import java.util.List;
    +
    +@AutoValue
    +public abstract class InstanceName {
    +  public abstract String getProjectId();
    +
    +  public abstract String getInstanceId();
    +
    +  @Override
    +  public final String toString() {
    +    return String.format("projects/%s/instances/%s", getProjectId(), getInstanceId());
    +  }
    +
    +  public static InstanceName of(String projectId, String instanceId) {
    +    return InstanceName.builder().setProjectId(projectId).setInstanceId(instanceId).build();
    +  }
    +
    +  public static Builder builder() {
    +    return new AutoValue_InstanceName.Builder();
    +  }
    +
    +  public static InstanceName parse(String name) {
    +    List parts = Splitter.on('/').splitToList(name);
    +    Preconditions.checkArgument(parts.size() == 4, "Invalid instance name: %s", name);
    +    Preconditions.checkArgument(
    +        "projects".equals(parts.get(0)),
    +        "Invalid instance name: %s, must start with projects/",
    +        name);
    +    Preconditions.checkArgument(
    +        !parts.get(1).isEmpty(), "Invalid instance name %s, must have a project id", name);
    +    Preconditions.checkArgument(
    +        "instances".equals(parts.get(2)),
    +        "Invalid instance name: %s, must start with projects/$PROJECT_ID/instances/",
    +        name);
    +    Preconditions.checkArgument(
    +        !parts.get(3).isEmpty(), "Invalid instance name %s, must have an instance id", name);
    +
    +    return builder().setProjectId(parts.get(1)).setInstanceId(parts.get(3)).build();
    +  }
    +
    +  @AutoValue.Builder
    +  public abstract static class Builder {
    +    public abstract Builder setProjectId(String projectId);
    +
    +    public abstract Builder setInstanceId(String instanceId);
    +
    +    public abstract InstanceName build();
    +  }
    +}
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/TableName.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/TableName.java
    new file mode 100644
    index 0000000000..159c7b0b50
    --- /dev/null
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/api/TableName.java
    @@ -0,0 +1,86 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.api;
    +
    +import com.google.auto.value.AutoValue;
    +import com.google.common.base.Preconditions;
    +import com.google.common.base.Splitter;
    +import java.util.List;
    +
    +@AutoValue
    +public abstract class TableName {
    +  public abstract String getProjectId();
    +
    +  public abstract String getInstanceId();
    +
    +  public abstract String getTableId();
    +
    +  public InstanceName getInstanceName() {
    +    return InstanceName.builder()
    +        .setProjectId(getProjectId())
    +        .setInstanceId(getInstanceId())
    +        .build();
    +  }
    +
    +  @Override
    +  public final String toString() {
    +    return String.format("%s/tables/%s", getInstanceName(), getTableId());
    +  }
    +
    +  public static Builder builder() {
    +    return new AutoValue_TableName.Builder();
    +  }
    +
    +  public static TableName parse(String name) {
    +    List parts = Splitter.on('/').splitToList(name);
    +    Preconditions.checkArgument(parts.size() == 6, "Invalid table name: %s", name);
    +    Preconditions.checkArgument(
    +        "projects".equals(parts.get(0)), "Invalid table name: %s, must start with projects/", name);
    +    Preconditions.checkArgument(
    +        !parts.get(1).isEmpty(), "Invalid table name %s, must have a project id", name);
    +    Preconditions.checkArgument(
    +        "instances".equals(parts.get(2)),
    +        "Invalid table name: %s, must start with projects/$PROJECT_ID/instances/",
    +        name);
    +    Preconditions.checkArgument(
    +        !parts.get(3).isEmpty(), "Invalid table name %s, must have an instance id", name);
    +    Preconditions.checkArgument(
    +        "tables".equals(parts.get(4)),
    +        "Invalid table name: %s, must start with"
    +            + " projects/$PROJECT_ID/instances/$INSTANCE_ID/tables",
    +        name);
    +    Preconditions.checkArgument(
    +        !parts.get(5).isEmpty(), "Invalid table name %s, must have table id", name);
    +
    +    return builder()
    +        .setProjectId(parts.get(1))
    +        .setInstanceId(parts.get(3))
    +        .setTableId(parts.get(5))
    +        .build();
    +  }
    +
    +  @AutoValue.Builder
    +  public abstract static class Builder {
    +    public abstract Builder setProjectId(String projectId);
    +
    +    public abstract Builder setInstanceId(String instanceId);
    +
    +    public abstract Builder setTableId(String tableId);
    +
    +    public abstract TableName build();
    +  }
    +}
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    index db389030d8..51adb36ea4 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    @@ -202,10 +202,10 @@ private static ApiTracerFactory createOCTracingFactory(ClientInfo clientInfo) {
                 // Annotate traces with the same tags as metrics
                 .put(
                     RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(),
    -                clientInfo.getInstanceName().getProject())
    +                clientInfo.getInstanceName().getProjectId())
                 .put(
                     RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(),
    -                clientInfo.getInstanceName().getInstance())
    +                clientInfo.getInstanceName().getInstanceId())
                 .put(
                     RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), clientInfo.getAppProfileId())
                 // Also annotate traces with library versions
    @@ -222,10 +222,10 @@ private static ApiTracerFactory createOCMetricsFactory(
             ImmutableMap.builder()
                 .put(
                     RpcMeasureConstants.BIGTABLE_PROJECT_ID,
    -                TagValue.create(clientInfo.getInstanceName().getProject()))
    +                TagValue.create(clientInfo.getInstanceName().getProjectId()))
                 .put(
                     RpcMeasureConstants.BIGTABLE_INSTANCE_ID,
    -                TagValue.create(clientInfo.getInstanceName().getInstance()))
    +                TagValue.create(clientInfo.getInstanceName().getInstanceId()))
                 .put(
                     RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID,
                     TagValue.create(clientInfo.getAppProfileId()))
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java
    index 64c4b211b2..7122cb40c7 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfo.java
    @@ -17,8 +17,8 @@
     package com.google.cloud.bigtable.data.v2.internal.csm.attributes;
     
     import com.google.auto.value.AutoValue;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.cloud.bigtable.Version;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     
     /**
      * A value class to capture parameters that the client was instantiated with. These parameters will
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    index 99a740f387..2aa98c33ea 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporter.java
    @@ -188,7 +188,7 @@ public void onFailure(Throwable throwable) {
                             " Need monitoring metric writer permission on project=%s. Follow"
                                 + " https://cloud.google.com/bigtable/docs/client-side-metrics-setup"
                                 + " to set up permissions.",
    -                        clientInfo.getInstanceName().getProject());
    +                        clientInfo.getInstanceName().getProjectId());
                   }
                   RuntimeException asyncWrapper = new RuntimeException("export failed", throwable);
                   asyncWrapper.setStackTrace(stackTrace);
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java
    index a6b2e89aaf..dc07f6e0e9 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/metrics/ClientPerConnectionErrorCount.java
    @@ -99,8 +99,9 @@ public void record(ClientInfo clientInfo, long value) {
           Attributes attributes =
               getSchema()
                   .createResourceAttrs(clientInfo)
    -              .put(MetricLabels.BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject())
    -              .put(MetricLabels.INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance())
    +              .put(
    +                  MetricLabels.BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProjectId())
    +              .put(MetricLabels.INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstanceId())
                   .put(MetricLabels.CLIENT_NAME, clientInfo.getClientName())
                   .put(MetricLabels.APP_PROFILE_KEY, clientInfo.getAppProfileId())
                   .build();
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java
    index 11cf90c445..5ef030539d 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/ClientSchema.java
    @@ -64,13 +64,13 @@ public ClientSchema() {
     
       @Override
       public ProjectName extractProjectName(Attributes attrs, EnvInfo envInfo, ClientInfo clientInfo) {
    -    return ProjectName.of(clientInfo.getInstanceName().getProject());
    +    return ProjectName.of(clientInfo.getInstanceName().getProjectId());
       }
     
       public AttributesBuilder createResourceAttrs(ClientInfo clientInfo) {
         return Attributes.builder()
    -        .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject())
    -        .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance())
    +        .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProjectId())
    +        .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstanceId())
             .put(APP_PROFILE_KEY, clientInfo.getAppProfileId())
             .put(CLIENT_NAME, clientInfo.getClientName());
       }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java
    index 62a8df1d3c..0a5b3adeb2 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/GrpcClientSchema.java
    @@ -30,9 +30,9 @@
     public final class GrpcClientSchema extends Schema {
       // Unlike the normal ClientSchema, the bigtable resource ids must be injected during export time
       private static final DeferredAttr BIGTABLE_PROJECT_ID =
    -      DeferredAttr.fromClientInfo("project_id", ci -> ci.getInstanceName().getProject());
    +      DeferredAttr.fromClientInfo("project_id", ci -> ci.getInstanceName().getProjectId());
       private static final DeferredAttr INSTANCE_ID =
    -      DeferredAttr.fromClientInfo("instance", ci -> ci.getInstanceName().getInstance());
    +      DeferredAttr.fromClientInfo("instance", ci -> ci.getInstanceName().getInstanceId());
       private static final DeferredAttr APP_PROFILE_ID =
           DeferredAttr.fromClientInfo("app_profile", ClientInfo::getAppProfileId);
       private static final DeferredAttr CLIENT_NAME =
    @@ -73,6 +73,6 @@ private GrpcClientSchema() {
       @Override
       public ProjectName extractProjectName(
           Attributes ignored, EnvInfo ignored2, ClientInfo clientInfo) {
    -    return ProjectName.of(clientInfo.getInstanceName().getProject());
    +    return ProjectName.of(clientInfo.getInstanceName().getProjectId());
       }
     }
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java
    index 618551bb87..e333837d7a 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/schema/TableSchema.java
    @@ -56,8 +56,8 @@ public ProjectName extractProjectName(Attributes attrs, EnvInfo envInfo, ClientI
       public AttributesBuilder createResourceAttrs(
           ClientInfo clientInfo, String tableId, @Nullable ResponseParams clusterInfo) {
         return Attributes.builder()
    -        .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProject())
    -        .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstance())
    +        .put(BIGTABLE_PROJECT_ID_KEY, clientInfo.getInstanceName().getProjectId())
    +        .put(INSTANCE_ID_KEY, clientInfo.getInstanceName().getInstanceId())
             .put(TABLE_ID_KEY, tableId)
             .put(CLUSTER_ID_KEY, Util.formatClusterIdMetricLabel(clusterInfo))
             .put(ZONE_ID_KEY, Util.formatZoneIdMetricLabel(clusterInfo));
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimer.java
    index 97c6e364c8..3b2a169910 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimer.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableChannelPrimer.java
    @@ -20,9 +20,9 @@
     import com.google.api.core.SettableApiFuture;
     import com.google.auth.Credentials;
     import com.google.bigtable.v2.BigtableGrpc;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.bigtable.v2.PingAndWarmRequest;
     import com.google.bigtable.v2.PingAndWarmResponse;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.gaxx.grpc.ChannelPrimer;
     import io.grpc.CallCredentials;
     import io.grpc.CallOptions;
    @@ -80,7 +80,7 @@ static BigtableChannelPrimer create(
     
         request =
             PingAndWarmRequest.newBuilder()
    -            .setName(InstanceName.format(projectId, instanceId))
    +            .setName(InstanceName.of(projectId, instanceId).toString())
                 .setAppProfileId(appProfileId)
                 .build();
     
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java
    index c4bef24798..2828d67f43 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java
    @@ -26,8 +26,8 @@
     import com.google.api.gax.rpc.ClientContext;
     import com.google.auth.Credentials;
     import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.Metrics;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricsImpl;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceNameTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceNameTest.java
    new file mode 100644
    index 0000000000..09778bd46e
    --- /dev/null
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/InstanceNameTest.java
    @@ -0,0 +1,51 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.api;
    +
    +import static com.google.common.truth.Truth.assertThat;
    +import static org.junit.jupiter.api.Assertions.*;
    +
    +import org.junit.jupiter.api.Test;
    +
    +class InstanceNameTest {
    +
    +  @Test
    +  void testParseOk() {
    +    assertThat(InstanceName.parse("projects/my-project/instances/my-instance"))
    +        .isEqualTo(
    +            InstanceName.builder().setProjectId("my-project").setInstanceId("my-instance").build());
    +  }
    +
    +  @Test
    +  void testParseFail() {
    +    assertThrows(IllegalArgumentException.class, () -> InstanceName.parse(""));
    +    assertThrows(IllegalArgumentException.class, () -> InstanceName.parse("projects/my-project"));
    +    assertThrows(
    +        IllegalArgumentException.class, () -> TableName.parse("projects/my-project/instances"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> InstanceName.parse("projects/my-project/instances/my-instance/extra"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> InstanceName.parse("projects//instances/my-instance"));
    +    assertThrows(
    +        IllegalArgumentException.class, () -> InstanceName.parse("projects/my-project/instances/"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> InstanceName.parse("projects/my-project/instances//"));
    +  }
    +}
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/TableNameTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/TableNameTest.java
    new file mode 100644
    index 0000000000..fd8e8310a7
    --- /dev/null
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/api/TableNameTest.java
    @@ -0,0 +1,62 @@
    +/*
    + * Copyright 2025 Google LLC
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     https://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.bigtable.data.v2.internal.api;
    +
    +import static com.google.common.truth.Truth.assertThat;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +
    +import org.junit.jupiter.api.Test;
    +
    +class TableNameTest {
    +
    +  @Test
    +  void testParseOk() {
    +    assertThat(TableName.parse("projects/my-project/instances/my-instance/tables/my-table"))
    +        .isEqualTo(
    +            TableName.builder()
    +                .setProjectId("my-project")
    +                .setInstanceId("my-instance")
    +                .setTableId("my-table")
    +                .build());
    +  }
    +
    +  @Test
    +  void testParseFail() {
    +    assertThrows(IllegalArgumentException.class, () -> TableName.parse(""));
    +    assertThrows(IllegalArgumentException.class, () -> TableName.parse("projects/my-project"));
    +    assertThrows(
    +        IllegalArgumentException.class, () -> TableName.parse("projects/my-project/instances"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects/my-project/instances/my-instance"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects/my-project/instances/my-instance/tables"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects/my-project/instances/my-instance/tables/my-table/extra"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects//instances/my-instance/tables"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects/my-project/instances//tables/my-table"));
    +    assertThrows(
    +        IllegalArgumentException.class,
    +        () -> TableName.parse("projects/my-project/instances/my-instance/tables/"));
    +  }
    +}
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
    index e31e2e3047..974ac41868 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java
    @@ -25,11 +25,11 @@
     import com.google.api.gax.core.NoCredentialsProvider;
     import com.google.api.gax.grpc.GrpcTransportChannel;
     import com.google.api.gax.rpc.FixedTransportChannelProvider;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.bigtable.v2.PeerInfo;
     import com.google.bigtable.v2.PeerInfo.TransportType;
     import com.google.bigtable.v2.ResponseParams;
     import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    @@ -143,8 +143,8 @@ void setUp() throws Exception {
         expectedTableMonitoredResource =
             MonitoredResource.newBuilder()
                 .setType("bigtable_client_raw")
    -            .putLabels("project_id", clientInfo.getInstanceName().getProject())
    -            .putLabels("instance", clientInfo.getInstanceName().getInstance())
    +            .putLabels("project_id", clientInfo.getInstanceName().getProjectId())
    +            .putLabels("instance", clientInfo.getInstanceName().getInstanceId())
                 .putLabels("cluster", clusterInfo.getClusterId())
                 .putLabels("table", tableId)
                 .putLabels("zone", clusterInfo.getZoneId())
    @@ -153,8 +153,8 @@ void setUp() throws Exception {
         expectedClientMonitoredResource =
             MonitoredResource.newBuilder()
                 .setType("bigtable_client")
    -            .putLabels("project_id", clientInfo.getInstanceName().getProject())
    -            .putLabels("instance", clientInfo.getInstanceName().getInstance())
    +            .putLabels("project_id", clientInfo.getInstanceName().getProjectId())
    +            .putLabels("instance", clientInfo.getInstanceName().getInstanceId())
                 .putLabels("app_profile", appProfileId)
                 .putLabels("client_project", envInfo.getProject())
                 .putLabels("region", envInfo.getRegion())
    @@ -575,9 +575,9 @@ void testPerConnectionErrors() {
     
         assertThat(timeSeries.getMetric().getLabelsMap())
             .containsExactly(
    -            "project_id", clientInfo.getInstanceName().getProject(),
    +            "project_id", clientInfo.getInstanceName().getProjectId(),
                 "client_uid", envInfo.getUid(),
    -            "instance", clientInfo.getInstanceName().getInstance(),
    +            "instance", clientInfo.getInstanceName().getInstanceId(),
                 "client_name", clientInfo.getClientName(),
                 "app_profile", clientInfo.getAppProfileId());
     
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java
    index 283c26f514..e97cb1dd52 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/attributes/ClientInfoTest.java
    @@ -18,7 +18,7 @@
     
     import static com.google.common.truth.Truth.assertThat;
     
    -import com.google.bigtable.v2.InstanceName;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import org.junit.jupiter.api.Test;
     
     class ClientInfoTest {
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    index b352eb1660..e6c8c109fc 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest.java
    @@ -26,7 +26,7 @@
     import com.google.api.core.ApiFuture;
     import com.google.api.core.ApiFutures;
     import com.google.api.gax.rpc.UnaryCallable;
    -import com.google.bigtable.v2.InstanceName;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    index 7fdde6d5ca..adb52ef258 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtableCloudMonitoringExporterTest2.java
    @@ -23,8 +23,8 @@
     import com.google.api.core.ApiFutures;
     import com.google.api.gax.rpc.ApiCallContext;
     import com.google.api.gax.rpc.UnaryCallable;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.bigtable.v2.TableName;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    index 9ef3ce3c9d..17d55870ff 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/BuiltinMetricsTracerTest.java
    @@ -35,7 +35,6 @@
     import com.google.api.gax.rpc.ResponseObserver;
     import com.google.api.gax.rpc.StreamController;
     import com.google.bigtable.v2.BigtableGrpc;
    -import com.google.bigtable.v2.InstanceName;
     import com.google.bigtable.v2.MutateRowRequest;
     import com.google.bigtable.v2.MutateRowResponse;
     import com.google.bigtable.v2.MutateRowsRequest;
    @@ -46,6 +45,7 @@
     import com.google.cloud.bigtable.Version;
     import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
     import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientBatchWriteFlowControlFactor;
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    index a15c0f53c4..fec4f7956a 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/ChannelPoolMetricsTracerTest.java
    @@ -20,7 +20,7 @@
     import static org.mockito.ArgumentMatchers.anyLong;
     import static org.mockito.Mockito.when;
     
    -import com.google.bigtable.v2.InstanceName;
    +import com.google.cloud.bigtable.data.v2.internal.api.InstanceName;
     import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry;
     import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
     import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientChannelPoolOutstandingRpcs;
    
    From dde68fe0ee5c5a491a5ae5382babea57e901605c Mon Sep 17 00:00:00 2001
    From: Mend Renovate 
    Date: Fri, 27 Feb 2026 16:20:11 +0000
    Subject: [PATCH 26/33] deps: update shared dependencies (#2814)
    
    * deps: update shared dependencies
    
    * fix: mock ApiTracerFactory.withContext()
    
    ---------
    
    Co-authored-by: Diego Marquez 
    ---
     .github/workflows/unmanaged_dependency_check.yaml             | 2 +-
     .kokoro/presubmit/graalvm-native-a.cfg                        | 2 +-
     .kokoro/presubmit/graalvm-native-b.cfg                        | 2 +-
     .kokoro/presubmit/graalvm-native-c.cfg                        | 2 +-
     google-cloud-bigtable-bom/pom.xml                             | 2 +-
     google-cloud-bigtable-deps-bom/pom.xml                        | 4 ++--
     .../google/cloud/bigtable/data/v2/stub/SkipTrailersTest.java  | 1 +
     pom.xml                                                       | 2 +-
     8 files changed, 9 insertions(+), 8 deletions(-)
    
    diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
    index 716fd44a54..bb458025c6 100644
    --- a/.github/workflows/unmanaged_dependency_check.yaml
    +++ b/.github/workflows/unmanaged_dependency_check.yaml
    @@ -14,6 +14,6 @@ jobs:
           shell: bash
           run: .kokoro/build.sh
         - name: Unmanaged dependency check
    -      uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.56.1
    +      uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.57.0
           with:
             bom-path: google-cloud-bigtable-bom/pom.xml
    diff --git a/.kokoro/presubmit/graalvm-native-a.cfg b/.kokoro/presubmit/graalvm-native-a.cfg
    index af4115f37f..b113cf7c79 100644
    --- a/.kokoro/presubmit/graalvm-native-a.cfg
    +++ b/.kokoro/presubmit/graalvm-native-a.cfg
    @@ -3,7 +3,7 @@
     # Configure the docker image for kokoro-trampoline.
     env_vars: {
       key: "TRAMPOLINE_IMAGE"
    -  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.56.0" # {x-version-update:google-cloud-shared-dependencies:current}
    +  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.57.0" # {x-version-update:google-cloud-shared-dependencies:current}
     }
     
     env_vars: {
    diff --git a/.kokoro/presubmit/graalvm-native-b.cfg b/.kokoro/presubmit/graalvm-native-b.cfg
    index 33056bc067..1f91aa6783 100644
    --- a/.kokoro/presubmit/graalvm-native-b.cfg
    +++ b/.kokoro/presubmit/graalvm-native-b.cfg
    @@ -3,7 +3,7 @@
     # Configure the docker image for kokoro-trampoline.
     env_vars: {
       key: "TRAMPOLINE_IMAGE"
    -  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.56.0" # {x-version-update:google-cloud-shared-dependencies:current}
    +  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.57.0" # {x-version-update:google-cloud-shared-dependencies:current}
     }
     
     env_vars: {
    diff --git a/.kokoro/presubmit/graalvm-native-c.cfg b/.kokoro/presubmit/graalvm-native-c.cfg
    index 8a6c25f495..53a7b7d63b 100644
    --- a/.kokoro/presubmit/graalvm-native-c.cfg
    +++ b/.kokoro/presubmit/graalvm-native-c.cfg
    @@ -3,7 +3,7 @@
     # Configure the docker image for kokoro-trampoline.
     env_vars: {
       key: "TRAMPOLINE_IMAGE"
    -  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.56.0" # {x-version-update:google-cloud-shared-dependencies:current}
    +  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.57.0" # {x-version-update:google-cloud-shared-dependencies:current}
     }
     
     env_vars: {
    diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml
    index 58187f9673..a512f454d6 100644
    --- a/google-cloud-bigtable-bom/pom.xml
    +++ b/google-cloud-bigtable-bom/pom.xml
    @@ -8,7 +8,7 @@
         
             com.google.cloud
             sdk-platform-java-config
    -        3.56.1
    +        3.57.0
             
         
     
    diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml
    index 744079ceee..56fa2b756f 100644
    --- a/google-cloud-bigtable-deps-bom/pom.xml
    +++ b/google-cloud-bigtable-deps-bom/pom.xml
    @@ -7,7 +7,7 @@
       
         com.google.cloud
         sdk-platform-java-config
    -    3.56.1
    +    3.57.0
         
       
     
    @@ -68,7 +68,7 @@
           
             com.google.cloud
             gapic-libraries-bom
    -        1.77.0
    +        1.80.0
             pom
             import
           
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SkipTrailersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SkipTrailersTest.java
    index 9759f798c4..5dee789c19 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SkipTrailersTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SkipTrailersTest.java
    @@ -95,6 +95,7 @@ public void setUp() throws Exception {
         server = FakeServiceBuilder.create(hackedService).start();
     
         when(tracerFactory.newTracer(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(tracer);
    +    when(tracerFactory.withContext(Mockito.any())).thenReturn(tracerFactory);
     
         BigtableDataSettings.Builder clientBuilder =
             BigtableDataSettings.newBuilderForEmulator(server.getPort())
    diff --git a/pom.xml b/pom.xml
    index 42c2741202..91a827a4f6 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -14,7 +14,7 @@
         
             com.google.cloud
             sdk-platform-java-config
    -        3.56.1
    +        3.57.0
             
         
     
    
    From f05a1a3b0bb730e62c349dc8a7a1a82b0cf00fa7 Mon Sep 17 00:00:00 2001
    From: cloud-java-bot <122572305+cloud-java-bot@users.noreply.github.com>
    Date: Fri, 27 Feb 2026 11:41:01 -0500
    Subject: [PATCH 27/33] chore: Update generation configuration at Fri Feb 27
     03:03:33 UTC 2026 (#2768)
    
    * chore: Update generation configuration at Sat Jan 31 02:58:32 UTC 2026
    
    * chore: generate libraries at Sat Jan 31 02:59:04 UTC 2026
    
    * chore: Update generation configuration at Sun Feb  1 03:09:05 UTC 2026
    
    * chore: generate libraries at Sun Feb  1 03:09:38 UTC 2026
    
    * chore: Update generation configuration at Tue Feb  3 03:05:33 UTC 2026
    
    * chore: Update generation configuration at Wed Feb  4 03:03:29 UTC 2026
    
    * chore: Update generation configuration at Thu Feb  5 03:04:57 UTC 2026
    
    * chore: generate libraries at Thu Feb  5 03:05:30 UTC 2026
    
    * chore: Update generation configuration at Fri Feb  6 03:04:11 UTC 2026
    
    * chore: Update generation configuration at Sat Feb  7 03:00:24 UTC 2026
    
    * chore: Update generation configuration at Tue Feb 10 03:09:29 UTC 2026
    
    * chore: Update generation configuration at Wed Feb 11 03:09:22 UTC 2026
    
    * chore: Update generation configuration at Thu Feb 12 03:08:57 UTC 2026
    
    * chore: Update generation configuration at Fri Feb 13 03:09:18 UTC 2026
    
    * chore: generate libraries at Fri Feb 13 03:09:45 UTC 2026
    
    * chore: Update generation configuration at Sat Feb 14 03:02:51 UTC 2026
    
    * chore: Update generation configuration at Sun Feb 15 03:08:49 UTC 2026
    
    * chore: generate libraries at Sun Feb 15 03:09:21 UTC 2026
    
    * chore: Update generation configuration at Tue Feb 17 03:06:21 UTC 2026
    
    * chore: Update generation configuration at Wed Feb 18 03:07:45 UTC 2026
    
    * chore: Update generation configuration at Thu Feb 19 03:06:42 UTC 2026
    
    * chore: Update generation configuration at Fri Feb 20 03:04:42 UTC 2026
    
    * chore: generate libraries at Fri Feb 20 03:05:14 UTC 2026
    
    * chore: Update generation configuration at Sat Feb 21 02:59:52 UTC 2026
    
    * chore: Update generation configuration at Tue Feb 24 03:06:30 UTC 2026
    
    * chore: Update generation configuration at Wed Feb 25 03:06:56 UTC 2026
    
    * chore: Update generation configuration at Thu Feb 26 03:04:22 UTC 2026
    
    * chore: Update generation configuration at Fri Feb 27 03:03:33 UTC 2026
    ---
     .../hermetic_library_generation.yaml          |   2 +-
     README.md                                     |   2 +-
     generation_config.yaml                        |   6 +-
     .../reflect-config.json                       |  36 +
     .../v2/BaseBigtableTableAdminClientTest.java  |  15 +
     .../com/google/bigtable/admin/v2/Table.java   | 383 ++++++++-
     .../bigtable/admin/v2/TableOrBuilder.java     |  55 ++
     .../google/bigtable/admin/v2/TableProto.java  | 103 ++-
     .../admin/v2/TieredStorageConfig.java         | 721 ++++++++++++++++
     .../v2/TieredStorageConfigOrBuilder.java      |  68 ++
     .../bigtable/admin/v2/TieredStorageRule.java  | 801 ++++++++++++++++++
     .../admin/v2/TieredStorageRuleOrBuilder.java  |  70 ++
     .../google/bigtable/admin/v2/table.proto      |  32 +-
     13 files changed, 2243 insertions(+), 51 deletions(-)
     create mode 100644 proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfig.java
     create mode 100644 proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfigOrBuilder.java
     create mode 100644 proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRule.java
     create mode 100644 proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRuleOrBuilder.java
    
    diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml
    index aab36acb20..cb086cb986 100644
    --- a/.github/workflows/hermetic_library_generation.yaml
    +++ b/.github/workflows/hermetic_library_generation.yaml
    @@ -37,7 +37,7 @@ jobs:
           with:
             fetch-depth: 0
             token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }}
    -    - uses: googleapis/sdk-platform-java/.github/scripts@v2.66.0
    +    - uses: googleapis/sdk-platform-java/.github/scripts@v2.67.0
           if: env.SHOULD_RUN == 'true'
           with:
             base_ref: ${{ github.base_ref }}
    diff --git a/README.md b/README.md
    index 0177e0de23..0a064637ee 100644
    --- a/README.md
    +++ b/README.md
    @@ -49,7 +49,7 @@ If you are using Maven without the BOM, add this to your dependencies:
     If you are using Gradle 5.x or later, add this to your dependencies:
     
     ```Groovy
    -implementation platform('com.google.cloud:libraries-bom:26.74.0')
    +implementation platform('com.google.cloud:libraries-bom:26.76.0')
     
     implementation 'com.google.cloud:google-cloud-bigtable'
     ```
    diff --git a/generation_config.yaml b/generation_config.yaml
    index 0f2a822e5c..bc0c86ab19 100644
    --- a/generation_config.yaml
    +++ b/generation_config.yaml
    @@ -1,6 +1,6 @@
    -gapic_generator_version: 2.66.0
    -googleapis_commitish: fa4dc54cf123a351f3215b384a7dc7c9f36005b7
    -libraries_bom_version: 26.74.0
    +gapic_generator_version: 2.67.0
    +googleapis_commitish: d420134ab324c0cbe0f4ae06ad9697dac77f26ad
    +libraries_bom_version: 26.76.0
     template_excludes:
       - .gitignore
       - .kokoro/presubmit/integration.cfg
    diff --git a/google-cloud-bigtable/src/main/resources/META-INF/native-image/com.google.cloud.bigtable.admin.v2/reflect-config.json b/google-cloud-bigtable/src/main/resources/META-INF/native-image/com.google.cloud.bigtable.admin.v2/reflect-config.json
    index a7f4f88e42..edfd3eed0c 100644
    --- a/google-cloud-bigtable/src/main/resources/META-INF/native-image/com.google.cloud.bigtable.admin.v2/reflect-config.json
    +++ b/google-cloud-bigtable/src/main/resources/META-INF/native-image/com.google.cloud.bigtable.admin.v2/reflect-config.json
    @@ -2654,6 +2654,42 @@
         "allDeclaredClasses": true,
         "allPublicClasses": true
       },
    +  {
    +    "name": "com.google.bigtable.admin.v2.TieredStorageConfig",
    +    "queryAllDeclaredConstructors": true,
    +    "queryAllPublicConstructors": true,
    +    "queryAllDeclaredMethods": true,
    +    "allPublicMethods": true,
    +    "allDeclaredClasses": true,
    +    "allPublicClasses": true
    +  },
    +  {
    +    "name": "com.google.bigtable.admin.v2.TieredStorageConfig$Builder",
    +    "queryAllDeclaredConstructors": true,
    +    "queryAllPublicConstructors": true,
    +    "queryAllDeclaredMethods": true,
    +    "allPublicMethods": true,
    +    "allDeclaredClasses": true,
    +    "allPublicClasses": true
    +  },
    +  {
    +    "name": "com.google.bigtable.admin.v2.TieredStorageRule",
    +    "queryAllDeclaredConstructors": true,
    +    "queryAllPublicConstructors": true,
    +    "queryAllDeclaredMethods": true,
    +    "allPublicMethods": true,
    +    "allDeclaredClasses": true,
    +    "allPublicClasses": true
    +  },
    +  {
    +    "name": "com.google.bigtable.admin.v2.TieredStorageRule$Builder",
    +    "queryAllDeclaredConstructors": true,
    +    "queryAllPublicConstructors": true,
    +    "queryAllDeclaredMethods": true,
    +    "allPublicMethods": true,
    +    "allDeclaredClasses": true,
    +    "allPublicClasses": true
    +  },
       {
         "name": "com.google.bigtable.admin.v2.Type",
         "queryAllDeclaredConstructors": true,
    diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BaseBigtableTableAdminClientTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BaseBigtableTableAdminClientTest.java
    index 3477fc053d..9f5a50c41e 100644
    --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BaseBigtableTableAdminClientTest.java
    +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BaseBigtableTableAdminClientTest.java
    @@ -81,6 +81,7 @@
     import com.google.bigtable.admin.v2.SnapshotTableRequest;
     import com.google.bigtable.admin.v2.Table;
     import com.google.bigtable.admin.v2.TableName;
    +import com.google.bigtable.admin.v2.TieredStorageConfig;
     import com.google.bigtable.admin.v2.Type;
     import com.google.bigtable.admin.v2.UndeleteTableRequest;
     import com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest;
    @@ -166,6 +167,7 @@ public void createTableTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -216,6 +218,7 @@ public void createTableTest2() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -266,6 +269,7 @@ public void createTableFromSnapshotTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -328,6 +332,7 @@ public void createTableFromSnapshotTest2() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -388,6 +393,7 @@ public void createTableFromSnapshotTest3() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -450,6 +456,7 @@ public void createTableFromSnapshotTest4() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -598,6 +605,7 @@ public void getTableTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -642,6 +650,7 @@ public void getTableTest2() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -686,6 +695,7 @@ public void updateTableTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -809,6 +819,7 @@ public void undeleteTableTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -861,6 +872,7 @@ public void undeleteTableTest2() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    @@ -1330,6 +1342,7 @@ public void modifyColumnFamiliesTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -1378,6 +1391,7 @@ public void modifyColumnFamiliesTest2() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         mockBigtableTableAdmin.addResponse(expectedResponse);
    @@ -2534,6 +2548,7 @@ public void restoreTableTest() throws Exception {
                 .setRestoreInfo(RestoreInfo.newBuilder().build())
                 .setChangeStreamConfig(ChangeStreamConfig.newBuilder().build())
                 .setDeletionProtection(true)
    +            .setTieredStorageConfig(TieredStorageConfig.newBuilder().build())
                 .setRowKeySchema(Type.Struct.newBuilder().build())
                 .build();
         Operation resultOperation =
    diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/Table.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/Table.java
    index e305d9af2b..caed2b904e 100644
    --- a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/Table.java
    +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/Table.java
    @@ -3829,6 +3829,78 @@ public com.google.bigtable.admin.v2.Table.AutomatedBackupPolicy getAutomatedBack
         return com.google.bigtable.admin.v2.Table.AutomatedBackupPolicy.getDefaultInstance();
       }
     
    +  public static final int TIERED_STORAGE_CONFIG_FIELD_NUMBER = 14;
    +  private com.google.bigtable.admin.v2.TieredStorageConfig tieredStorageConfig_;
    +
    +  /**
    +   *
    +   *
    +   * 
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return Whether the tieredStorageConfig field is set. + */ + @java.lang.Override + public boolean hasTieredStorageConfig() { + return ((bitField0_ & 0x00000004) != 0); + } + + /** + * + * + *
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return The tieredStorageConfig. + */ + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfig getTieredStorageConfig() { + return tieredStorageConfig_ == null + ? com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance() + : tieredStorageConfig_; + } + + /** + * + * + *
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder + getTieredStorageConfigOrBuilder() { + return tieredStorageConfig_ == null + ? com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance() + : tieredStorageConfig_; + } + public static final int ROW_KEY_SCHEMA_FIELD_NUMBER = 15; private com.google.bigtable.admin.v2.Type.Struct rowKeySchema_; @@ -3900,7 +3972,7 @@ public com.google.bigtable.admin.v2.Table.AutomatedBackupPolicy getAutomatedBack */ @java.lang.Override public boolean hasRowKeySchema() { - return ((bitField0_ & 0x00000004) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** @@ -4087,6 +4159,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io 13, (com.google.bigtable.admin.v2.Table.AutomatedBackupPolicy) automatedBackupConfig_); } if (((bitField0_ & 0x00000004) != 0)) { + output.writeMessage(14, getTieredStorageConfig()); + } + if (((bitField0_ & 0x00000008) != 0)) { output.writeMessage(15, getRowKeySchema()); } getUnknownFields().writeTo(output); @@ -4145,6 +4220,10 @@ public int getSerializedSize() { (com.google.bigtable.admin.v2.Table.AutomatedBackupPolicy) automatedBackupConfig_); } if (((bitField0_ & 0x00000004) != 0)) { + size += + com.google.protobuf.CodedOutputStream.computeMessageSize(14, getTieredStorageConfig()); + } + if (((bitField0_ & 0x00000008) != 0)) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(15, getRowKeySchema()); } size += getUnknownFields().getSerializedSize(); @@ -4175,6 +4254,10 @@ public boolean equals(final java.lang.Object obj) { if (!getChangeStreamConfig().equals(other.getChangeStreamConfig())) return false; } if (getDeletionProtection() != other.getDeletionProtection()) return false; + if (hasTieredStorageConfig() != other.hasTieredStorageConfig()) return false; + if (hasTieredStorageConfig()) { + if (!getTieredStorageConfig().equals(other.getTieredStorageConfig())) return false; + } if (hasRowKeySchema() != other.hasRowKeySchema()) return false; if (hasRowKeySchema()) { if (!getRowKeySchema().equals(other.getRowKeySchema())) return false; @@ -4220,6 +4303,10 @@ public int hashCode() { } hash = (37 * hash) + DELETION_PROTECTION_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getDeletionProtection()); + if (hasTieredStorageConfig()) { + hash = (37 * hash) + TIERED_STORAGE_CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getTieredStorageConfig().hashCode(); + } if (hasRowKeySchema()) { hash = (37 * hash) + ROW_KEY_SCHEMA_FIELD_NUMBER; hash = (53 * hash) + getRowKeySchema().hashCode(); @@ -4401,6 +4488,7 @@ private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { internalGetRestoreInfoFieldBuilder(); internalGetChangeStreamConfigFieldBuilder(); + internalGetTieredStorageConfigFieldBuilder(); internalGetRowKeySchemaFieldBuilder(); } } @@ -4427,6 +4515,11 @@ public Builder clear() { if (automatedBackupPolicyBuilder_ != null) { automatedBackupPolicyBuilder_.clear(); } + tieredStorageConfig_ = null; + if (tieredStorageConfigBuilder_ != null) { + tieredStorageConfigBuilder_.dispose(); + tieredStorageConfigBuilder_ = null; + } rowKeySchema_ = null; if (rowKeySchemaBuilder_ != null) { rowKeySchemaBuilder_.dispose(); @@ -4501,9 +4594,16 @@ private void buildPartial0(com.google.bigtable.admin.v2.Table result) { result.deletionProtection_ = deletionProtection_; } if (((from_bitField0_ & 0x00000100) != 0)) { + result.tieredStorageConfig_ = + tieredStorageConfigBuilder_ == null + ? tieredStorageConfig_ + : tieredStorageConfigBuilder_.build(); + to_bitField0_ |= 0x00000004; + } + if (((from_bitField0_ & 0x00000200) != 0)) { result.rowKeySchema_ = rowKeySchemaBuilder_ == null ? rowKeySchema_ : rowKeySchemaBuilder_.build(); - to_bitField0_ |= 0x00000004; + to_bitField0_ |= 0x00000008; } result.bitField0_ |= to_bitField0_; } @@ -4549,6 +4649,9 @@ public Builder mergeFrom(com.google.bigtable.admin.v2.Table other) { if (other.getDeletionProtection() != false) { setDeletionProtection(other.getDeletionProtection()); } + if (other.hasTieredStorageConfig()) { + mergeTieredStorageConfig(other.getTieredStorageConfig()); + } if (other.hasRowKeySchema()) { mergeRowKeySchema(other.getRowKeySchema()); } @@ -4656,11 +4759,18 @@ public Builder mergeFrom( automatedBackupConfigCase_ = 13; break; } // case 106 + case 114: + { + input.readMessage( + internalGetTieredStorageConfigFieldBuilder().getBuilder(), extensionRegistry); + bitField0_ |= 0x00000100; + break; + } // case 114 case 122: { input.readMessage( internalGetRowKeySchemaFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; break; } // case 122 default: @@ -6248,6 +6358,259 @@ public Builder clearAutomatedBackupPolicy() { return automatedBackupPolicyBuilder_; } + private com.google.bigtable.admin.v2.TieredStorageConfig tieredStorageConfig_; + private com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageConfig, + com.google.bigtable.admin.v2.TieredStorageConfig.Builder, + com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder> + tieredStorageConfigBuilder_; + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return Whether the tieredStorageConfig field is set. + */ + public boolean hasTieredStorageConfig() { + return ((bitField0_ & 0x00000100) != 0); + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return The tieredStorageConfig. + */ + public com.google.bigtable.admin.v2.TieredStorageConfig getTieredStorageConfig() { + if (tieredStorageConfigBuilder_ == null) { + return tieredStorageConfig_ == null + ? com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance() + : tieredStorageConfig_; + } else { + return tieredStorageConfigBuilder_.getMessage(); + } + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public Builder setTieredStorageConfig(com.google.bigtable.admin.v2.TieredStorageConfig value) { + if (tieredStorageConfigBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + tieredStorageConfig_ = value; + } else { + tieredStorageConfigBuilder_.setMessage(value); + } + bitField0_ |= 0x00000100; + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public Builder setTieredStorageConfig( + com.google.bigtable.admin.v2.TieredStorageConfig.Builder builderForValue) { + if (tieredStorageConfigBuilder_ == null) { + tieredStorageConfig_ = builderForValue.build(); + } else { + tieredStorageConfigBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000100; + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public Builder mergeTieredStorageConfig( + com.google.bigtable.admin.v2.TieredStorageConfig value) { + if (tieredStorageConfigBuilder_ == null) { + if (((bitField0_ & 0x00000100) != 0) + && tieredStorageConfig_ != null + && tieredStorageConfig_ + != com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance()) { + getTieredStorageConfigBuilder().mergeFrom(value); + } else { + tieredStorageConfig_ = value; + } + } else { + tieredStorageConfigBuilder_.mergeFrom(value); + } + if (tieredStorageConfig_ != null) { + bitField0_ |= 0x00000100; + onChanged(); + } + return this; + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public Builder clearTieredStorageConfig() { + bitField0_ = (bitField0_ & ~0x00000100); + tieredStorageConfig_ = null; + if (tieredStorageConfigBuilder_ != null) { + tieredStorageConfigBuilder_.dispose(); + tieredStorageConfigBuilder_ = null; + } + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public com.google.bigtable.admin.v2.TieredStorageConfig.Builder + getTieredStorageConfigBuilder() { + bitField0_ |= 0x00000100; + onChanged(); + return internalGetTieredStorageConfigFieldBuilder().getBuilder(); + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + public com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder + getTieredStorageConfigOrBuilder() { + if (tieredStorageConfigBuilder_ != null) { + return tieredStorageConfigBuilder_.getMessageOrBuilder(); + } else { + return tieredStorageConfig_ == null + ? com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance() + : tieredStorageConfig_; + } + } + + /** + * + * + *
    +     * Rules to specify what data is stored in each storage tier.
    +     * Different tiers store data differently, providing different trade-offs
    +     * between cost and performance. Different parts of a table can be stored
    +     * separately on different tiers.
    +     * If a config is specified, tiered storage is enabled for this table.
    +     * Otherwise, tiered storage is disabled.
    +     * Only SSD instances can configure tiered storage.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageConfig, + com.google.bigtable.admin.v2.TieredStorageConfig.Builder, + com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder> + internalGetTieredStorageConfigFieldBuilder() { + if (tieredStorageConfigBuilder_ == null) { + tieredStorageConfigBuilder_ = + new com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageConfig, + com.google.bigtable.admin.v2.TieredStorageConfig.Builder, + com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder>( + getTieredStorageConfig(), getParentForChildren(), isClean()); + tieredStorageConfig_ = null; + } + return tieredStorageConfigBuilder_; + } + private com.google.bigtable.admin.v2.Type.Struct rowKeySchema_; private com.google.protobuf.SingleFieldBuilder< com.google.bigtable.admin.v2.Type.Struct, @@ -6322,7 +6685,7 @@ public Builder clearAutomatedBackupPolicy() { * @return Whether the rowKeySchema field is set. */ public boolean hasRowKeySchema() { - return ((bitField0_ & 0x00000100) != 0); + return ((bitField0_ & 0x00000200) != 0); } /** @@ -6474,7 +6837,7 @@ public Builder setRowKeySchema(com.google.bigtable.admin.v2.Type.Struct value) { } else { rowKeySchemaBuilder_.setMessage(value); } - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); return this; } @@ -6550,7 +6913,7 @@ public Builder setRowKeySchema( } else { rowKeySchemaBuilder_.setMessage(builderForValue.build()); } - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); return this; } @@ -6621,7 +6984,7 @@ public Builder setRowKeySchema( */ public Builder mergeRowKeySchema(com.google.bigtable.admin.v2.Type.Struct value) { if (rowKeySchemaBuilder_ == null) { - if (((bitField0_ & 0x00000100) != 0) + if (((bitField0_ & 0x00000200) != 0) && rowKeySchema_ != null && rowKeySchema_ != com.google.bigtable.admin.v2.Type.Struct.getDefaultInstance()) { getRowKeySchemaBuilder().mergeFrom(value); @@ -6632,7 +6995,7 @@ public Builder mergeRowKeySchema(com.google.bigtable.admin.v2.Type.Struct value) rowKeySchemaBuilder_.mergeFrom(value); } if (rowKeySchema_ != null) { - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); } return this; @@ -6703,7 +7066,7 @@ public Builder mergeRowKeySchema(com.google.bigtable.admin.v2.Type.Struct value) * .google.bigtable.admin.v2.Type.Struct row_key_schema = 15; */ public Builder clearRowKeySchema() { - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); rowKeySchema_ = null; if (rowKeySchemaBuilder_ != null) { rowKeySchemaBuilder_.dispose(); @@ -6778,7 +7141,7 @@ public Builder clearRowKeySchema() { * .google.bigtable.admin.v2.Type.Struct row_key_schema = 15; */ public com.google.bigtable.admin.v2.Type.Struct.Builder getRowKeySchemaBuilder() { - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); return internalGetRowKeySchemaFieldBuilder().getBuilder(); } diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableOrBuilder.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableOrBuilder.java index 722205040e..f82f216bfa 100644 --- a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableOrBuilder.java +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableOrBuilder.java @@ -408,6 +408,61 @@ com.google.bigtable.admin.v2.ColumnFamily getColumnFamiliesOrDefault( com.google.bigtable.admin.v2.Table.AutomatedBackupPolicyOrBuilder getAutomatedBackupPolicyOrBuilder(); + /** + * + * + *
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return Whether the tieredStorageConfig field is set. + */ + boolean hasTieredStorageConfig(); + + /** + * + * + *
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + * + * @return The tieredStorageConfig. + */ + com.google.bigtable.admin.v2.TieredStorageConfig getTieredStorageConfig(); + + /** + * + * + *
    +   * Rules to specify what data is stored in each storage tier.
    +   * Different tiers store data differently, providing different trade-offs
    +   * between cost and performance. Different parts of a table can be stored
    +   * separately on different tiers.
    +   * If a config is specified, tiered storage is enabled for this table.
    +   * Otherwise, tiered storage is disabled.
    +   * Only SSD instances can configure tiered storage.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageConfig tiered_storage_config = 14; + */ + com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder getTieredStorageConfigOrBuilder(); + /** * * diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableProto.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableProto.java index 30caaee768..c013ea2ed5 100644 --- a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableProto.java +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TableProto.java @@ -116,6 +116,14 @@ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry r internal_static_google_bigtable_admin_v2_BackupInfo_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_google_bigtable_admin_v2_BackupInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor; + static final com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_google_bigtable_admin_v2_TieredStorageConfig_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor; + static final com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_google_bigtable_admin_v2_TieredStorageRule_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_google_bigtable_admin_v2_ProtoSchema_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable @@ -145,7 +153,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\013backup_info\030\002 \001(\0132$.google.bigtable.admin.v2.BackupInfoH\000B\r\n" + "\013source_info\"I\n" + "\022ChangeStreamConfig\0223\n" - + "\020retention_period\030\001 \001(\0132\031.google.protobuf.Duration\"\225\014\n" + + "\020retention_period\030\001 \001(\0132\031.google.protobuf.Duration\"\343\014\n" + "\005Table\022\014\n" + "\004name\030\001 \001(\t\022O\n" + "\016cluster_states\030\002" @@ -160,12 +168,14 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + " \001(\0132,.google.bigtable.admin.v2.ChangeStreamConfig\022\033\n" + "\023deletion_protection\030\t \001(\010\022X\n" + "\027automated_backup_policy\030\r" - + " \001(\01325.google.bigtable.admin.v2.Table.AutomatedBackupPolicyH\000\022=\n" + + " \001(\01325.google.bigtable.admin.v2.Table.AutomatedBackupPolicyH\000\022L\n" + + "\025tiered_storage_config\030\016 \001(\013" + + "2-.google.bigtable.admin.v2.TieredStorageConfig\022=\n" + "\016row_key_schema\030\017" + " \001(\0132%.google.bigtable.admin.v2.Type.Struct\032\306\002\n" + "\014ClusterState\022]\n" - + "\021replication_state\030\001 \001(\0162=.g" - + "oogle.bigtable.admin.v2.Table.ClusterState.ReplicationStateB\003\340A\003\022F\n" + + "\021replication_state\030\001 \001(\0162=.goo" + + "gle.bigtable.admin.v2.Table.ClusterState.ReplicationStateB\003\340A\003\022F\n" + "\017encryption_info\030\002" + " \003(\0132(.google.bigtable.admin.v2.EncryptionInfoB\003\340A\003\"\216\001\n" + "\020ReplicationState\022\023\n" @@ -196,13 +206,13 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\020REPLICATION_VIEW\020\003\022\023\n" + "\017ENCRYPTION_VIEW\020\005\022\010\n" + "\004FULL\020\004:_\352A\\\n" - + "\"bigtableadmin.googleapis.com/Tab" - + "le\0226projects/{project}/instances/{instance}/tables/{table}B\031\n" + + "\"bigtableadmin.googleapis.com/Table" + + "\0226projects/{project}/instances/{instance}/tables/{table}B\031\n" + "\027automated_backup_config\"\343\005\n" + "\016AuthorizedView\022\021\n" + "\004name\030\001 \001(\tB\003\340A\010\022J\n" - + "\013subset_view\030\002 \001(\01323.google.bigtab" - + "le.admin.v2.AuthorizedView.SubsetViewH\000\022\014\n" + + "\013subset_view\030\002" + + " \001(\01323.google.bigtable.admin.v2.AuthorizedView.SubsetViewH\000\022\014\n" + "\004etag\030\003 \001(\t\022\033\n" + "\023deletion_protection\030\004 \001(\010\032?\n\r" + "FamilySubsets\022\022\n\n" @@ -210,8 +220,8 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\022qualifier_prefixes\030\002 \003(\014\032\360\001\n\n" + "SubsetView\022\024\n" + "\014row_prefixes\030\001 \003(\014\022^\n" - + "\016family_subsets\030\002 \003(\0132F.google.bigtable.admin.v2.Auth" - + "orizedView.SubsetView.FamilySubsetsEntry\032l\n" + + "\016family_subsets\030\002 \003(\0132F.google.bigtable.admin.v2.Author" + + "izedView.SubsetView.FamilySubsetsEntry\032l\n" + "\022FamilySubsetsEntry\022\013\n" + "\003key\030\001 \001(\t\022E\n" + "\005value\030\002" @@ -221,9 +231,9 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\tNAME_ONLY\020\001\022\t\n" + "\005BASIC\020\002\022\010\n" + "\004FULL\020\003:\254\001\352A\250\001\n" - + "+bigtableadmin.googleapis.com/AuthorizedView\022Xprojects/{project}/instances/{ins" - + "tance}/tables/{table}/authorizedViews/{a" - + "uthorized_view}*\017authorizedViews2\016authorizedViewB\021\n" + + "+bigtableadmin.googleapis.com/AuthorizedView\022Xprojects/{project}/instances/{insta" + + "nce}/tables/{table}/authorizedViews/{aut" + + "horized_view}*\017authorizedViews2\016authorizedViewB\021\n" + "\017authorized_view\"u\n" + "\014ColumnFamily\0221\n" + "\007gc_rule\030\001 \001(\0132 .google.bigtable.admin.v2.GcRule\0222\n\n" @@ -256,15 +266,15 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\017data_size_bytes\030\003 \001(\003B\003\340A\003\0224\n" + "\013create_time\030\004 \001(\0132\032.google.protobuf.TimestampB\003\340A\003\022/\n" + "\013delete_time\030\005 \001(\0132\032.google.protobuf.Timestamp\022<\n" - + "\005state\030\006 \001(\0162(.goog" - + "le.bigtable.admin.v2.Snapshot.StateB\003\340A\003\022\023\n" + + "\005state\030\006 \001(\0162(.google" + + ".bigtable.admin.v2.Snapshot.StateB\003\340A\003\022\023\n" + "\013description\030\007 \001(\t\"5\n" + "\005State\022\023\n" + "\017STATE_NOT_KNOWN\020\000\022\t\n" + "\005READY\020\001\022\014\n" + "\010CREATING\020\002:{\352Ax\n" - + "%bigtableadmin.googleapis.com/Snapshot\022Oprojects/{project}/instances/{instance" - + "}/clusters/{cluster}/snapshots/{snapshot}\"\371\005\n" + + "%bigtableadmin.googleapis.com/Snapshot\022Oprojects/{project}/instances/{instance}/" + + "clusters/{cluster}/snapshots/{snapshot}\"\371\005\n" + "\006Backup\022\014\n" + "\004name\030\001 \001(\t\022\034\n" + "\014source_table\030\002 \001(\tB\006\340A\005\340A\002\022\032\n\r" @@ -276,10 +286,10 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\010end_time\030\005" + " \001(\0132\032.google.protobuf.TimestampB\003\340A\003\022\027\n\n" + "size_bytes\030\006 \001(\003B\003\340A\003\022:\n" - + "\005state\030\007 \001" - + "(\0162&.google.bigtable.admin.v2.Backup.StateB\003\340A\003\022F\n" - + "\017encryption_info\030\t \001(\0132(.googl" - + "e.bigtable.admin.v2.EncryptionInfoB\003\340A\003\022@\n" + + "\005state\030\007 \001(\016" + + "2&.google.bigtable.admin.v2.Backup.StateB\003\340A\003\022F\n" + + "\017encryption_info\030\t" + + " \001(\0132(.google.bigtable.admin.v2.EncryptionInfoB\003\340A\003\022@\n" + "\013backup_type\030\013 \001(\0162+.google.bigtable.admin.v2.Backup.BackupType\0228\n" + "\024hot_to_standard_time\030\014 \001(\0132\032.google.protobuf.Timestamp\"7\n" + "\005State\022\025\n" @@ -290,15 +300,21 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\027BACKUP_TYPE_UNSPECIFIED\020\000\022\014\n" + "\010STANDARD\020\001\022\007\n" + "\003HOT\020\002:u\352Ar\n" - + "#bigtableadmin.googleapis.com/Backup\022Kprojects/{project}/instances/{" - + "instance}/clusters/{cluster}/backups/{backup}\"\300\001\n\n" + + "#bigtableadmin.googleapis.com/Backup\022Kprojects/{project}/instances/{in" + + "stance}/clusters/{cluster}/backups/{backup}\"\300\001\n\n" + "BackupInfo\022\023\n" + "\006backup\030\001 \001(\tB\003\340A\003\0223\n\n" + "start_time\030\002 \001(\0132\032.google.protobuf.TimestampB\003\340A\003\0221\n" + "\010end_time\030\003 \001(\0132\032.google.protobuf.TimestampB\003\340A\003\022\031\n" + "\014source_table\030\004 \001(\tB\003\340A\003\022\032\n\r" + "source_backup\030\n" - + " \001(\tB\003\340A\003\"-\n" + + " \001(\tB\003\340A\003\"]\n" + + "\023TieredStorageConfig\022F\n" + + "\021infrequent_access\030\001" + + " \001(\0132+.google.bigtable.admin.v2.TieredStorageRule\"W\n" + + "\021TieredStorageRule\022:\n" + + "\025include_if_older_than\030\001 \001(\0132\031.google.protobuf.DurationH\000B\006\n" + + "\004rule\"-\n" + "\013ProtoSchema\022\036\n" + "\021proto_descriptors\030\002 \001(\014B\003\340A\002\"\240\002\n" + "\014SchemaBundle\022\021\n" @@ -306,21 +322,21 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "\014proto_schema\030\002" + " \001(\0132%.google.bigtable.admin.v2.ProtoSchemaH\000\022\021\n" + "\004etag\030\003 \001(\tB\003\340A\001:\242\001\352A\236\001\n" - + ")bigtableadmin.googleapis.com/SchemaBundle\022Tprojects/{project}/inst" - + "ances/{instance}/tables/{table}/schemaBundles/{schema_bundle}*\r" + + ")bigtableadmin.googleapis.com/SchemaBundle\022Tp" + + "rojects/{project}/instances/{instance}/tables/{table}/schemaBundles/{schema_bundle}*\r" + "schemaBundles2\014schemaBundleB\006\n" + "\004type*D\n" + "\021RestoreSourceType\022#\n" + "\037RESTORE_SOURCE_TYPE_UNSPECIFIED\020\000\022\n\n" + "\006BACKUP\020\001B\367\002\n" + "\034com.google.bigtable.admin.v2B\n" - + "TableProtoP\001Z8cloud.google.com/go/big" - + "table/admin/apiv2/adminpb;adminpb\252\002\036Goog" - + "le.Cloud.Bigtable.Admin.V2\312\002\036Google\\Clou" - + "d\\Bigtable\\Admin\\V2\352\002\"Google::Cloud::Bigtable::Admin::V2\352A\246\001\n" - + "(cloudkms.googleapis.com/CryptoKeyVersion\022zprojects/{projec" - + "t}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVer" - + "sions/{crypto_key_version}b\006proto3" + + "TableProtoP\001Z8cloud.google.com/go/bigtable/admin/apiv2/" + + "adminpb;adminpb\252\002\036Google.Cloud.Bigtable." + + "Admin.V2\312\002\036Google\\Cloud\\Bigtable\\Admin\\V" + + "2\352\002\"Google::Cloud::Bigtable::Admin::V2\352A\246\001\n" + + "(cloudkms.googleapis.com/CryptoKeyVersion\022zprojects/{project}/locations/{loca" + + "tion}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_" + + "version}b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( @@ -362,6 +378,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { "ChangeStreamConfig", "DeletionProtection", "AutomatedBackupPolicy", + "TieredStorageConfig", "RowKeySchema", "AutomatedBackupConfig", }); @@ -508,8 +525,24 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { new java.lang.String[] { "Backup", "StartTime", "EndTime", "SourceTable", "SourceBackup", }); - internal_static_google_bigtable_admin_v2_ProtoSchema_descriptor = + internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor = getDescriptor().getMessageType(10); + internal_static_google_bigtable_admin_v2_TieredStorageConfig_fieldAccessorTable = + new com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor, + new java.lang.String[] { + "InfrequentAccess", + }); + internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor = + getDescriptor().getMessageType(11); + internal_static_google_bigtable_admin_v2_TieredStorageRule_fieldAccessorTable = + new com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor, + new java.lang.String[] { + "IncludeIfOlderThan", "Rule", + }); + internal_static_google_bigtable_admin_v2_ProtoSchema_descriptor = + getDescriptor().getMessageType(12); internal_static_google_bigtable_admin_v2_ProtoSchema_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_google_bigtable_admin_v2_ProtoSchema_descriptor, @@ -517,7 +550,7 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { "ProtoDescriptors", }); internal_static_google_bigtable_admin_v2_SchemaBundle_descriptor = - getDescriptor().getMessageType(11); + getDescriptor().getMessageType(13); internal_static_google_bigtable_admin_v2_SchemaBundle_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_google_bigtable_admin_v2_SchemaBundle_descriptor, diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfig.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfig.java new file mode 100644 index 0000000000..e54e3440fb --- /dev/null +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfig.java @@ -0,0 +1,721 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: google/bigtable/admin/v2/table.proto +// Protobuf Java Version: 4.33.2 + +package com.google.bigtable.admin.v2; + +/** + * + * + *
    + * Config for tiered storage.
    + * A valid config must have a valid TieredStorageRule. Otherwise the whole
    + * TieredStorageConfig must be unset.
    + * By default all data is stored in the SSD tier (only SSD instances can
    + * configure tiered storage).
    + * 
    + * + * Protobuf type {@code google.bigtable.admin.v2.TieredStorageConfig} + */ +@com.google.protobuf.Generated +public final class TieredStorageConfig extends com.google.protobuf.GeneratedMessage + implements + // @@protoc_insertion_point(message_implements:google.bigtable.admin.v2.TieredStorageConfig) + TieredStorageConfigOrBuilder { + private static final long serialVersionUID = 0L; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 2, + /* suffix= */ "", + "TieredStorageConfig"); + } + + // Use TieredStorageConfig.newBuilder() to construct. + private TieredStorageConfig(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + + private TieredStorageConfig() {} + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.bigtable.admin.v2.TieredStorageConfig.class, + com.google.bigtable.admin.v2.TieredStorageConfig.Builder.class); + } + + private int bitField0_; + public static final int INFREQUENT_ACCESS_FIELD_NUMBER = 1; + private com.google.bigtable.admin.v2.TieredStorageRule infrequentAccess_; + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return Whether the infrequentAccess field is set. + */ + @java.lang.Override + public boolean hasInfrequentAccess() { + return ((bitField0_ & 0x00000001) != 0); + } + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return The infrequentAccess. + */ + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRule getInfrequentAccess() { + return infrequentAccess_ == null + ? com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance() + : infrequentAccess_; + } + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder getInfrequentAccessOrBuilder() { + return infrequentAccess_ == null + ? com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance() + : infrequentAccess_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(1, getInfrequentAccess()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(1, getInfrequentAccess()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.bigtable.admin.v2.TieredStorageConfig)) { + return super.equals(obj); + } + com.google.bigtable.admin.v2.TieredStorageConfig other = + (com.google.bigtable.admin.v2.TieredStorageConfig) obj; + + if (hasInfrequentAccess() != other.hasInfrequentAccess()) return false; + if (hasInfrequentAccess()) { + if (!getInfrequentAccess().equals(other.getInfrequentAccess())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasInfrequentAccess()) { + hash = (37 * hash) + INFREQUENT_ACCESS_FIELD_NUMBER; + hash = (53 * hash) + getInfrequentAccess().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom(java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.google.bigtable.admin.v2.TieredStorageConfig prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * + * + *
    +   * Config for tiered storage.
    +   * A valid config must have a valid TieredStorageRule. Otherwise the whole
    +   * TieredStorageConfig must be unset.
    +   * By default all data is stored in the SSD tier (only SSD instances can
    +   * configure tiered storage).
    +   * 
    + * + * Protobuf type {@code google.bigtable.admin.v2.TieredStorageConfig} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder + implements + // @@protoc_insertion_point(builder_implements:google.bigtable.admin.v2.TieredStorageConfig) + com.google.bigtable.admin.v2.TieredStorageConfigOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.bigtable.admin.v2.TieredStorageConfig.class, + com.google.bigtable.admin.v2.TieredStorageConfig.Builder.class); + } + + // Construct using com.google.bigtable.admin.v2.TieredStorageConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + internalGetInfrequentAccessFieldBuilder(); + } + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + infrequentAccess_ = null; + if (infrequentAccessBuilder_ != null) { + infrequentAccessBuilder_.dispose(); + infrequentAccessBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageConfig_descriptor; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfig getDefaultInstanceForType() { + return com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance(); + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfig build() { + com.google.bigtable.admin.v2.TieredStorageConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfig buildPartial() { + com.google.bigtable.admin.v2.TieredStorageConfig result = + new com.google.bigtable.admin.v2.TieredStorageConfig(this); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartial0(com.google.bigtable.admin.v2.TieredStorageConfig result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.infrequentAccess_ = + infrequentAccessBuilder_ == null ? infrequentAccess_ : infrequentAccessBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.bigtable.admin.v2.TieredStorageConfig) { + return mergeFrom((com.google.bigtable.admin.v2.TieredStorageConfig) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.bigtable.admin.v2.TieredStorageConfig other) { + if (other == com.google.bigtable.admin.v2.TieredStorageConfig.getDefaultInstance()) + return this; + if (other.hasInfrequentAccess()) { + mergeInfrequentAccess(other.getInfrequentAccess()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + input.readMessage( + internalGetInfrequentAccessFieldBuilder().getBuilder(), extensionRegistry); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private com.google.bigtable.admin.v2.TieredStorageRule infrequentAccess_; + private com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageRule, + com.google.bigtable.admin.v2.TieredStorageRule.Builder, + com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder> + infrequentAccessBuilder_; + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return Whether the infrequentAccess field is set. + */ + public boolean hasInfrequentAccess() { + return ((bitField0_ & 0x00000001) != 0); + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return The infrequentAccess. + */ + public com.google.bigtable.admin.v2.TieredStorageRule getInfrequentAccess() { + if (infrequentAccessBuilder_ == null) { + return infrequentAccess_ == null + ? com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance() + : infrequentAccess_; + } else { + return infrequentAccessBuilder_.getMessage(); + } + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public Builder setInfrequentAccess(com.google.bigtable.admin.v2.TieredStorageRule value) { + if (infrequentAccessBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + infrequentAccess_ = value; + } else { + infrequentAccessBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public Builder setInfrequentAccess( + com.google.bigtable.admin.v2.TieredStorageRule.Builder builderForValue) { + if (infrequentAccessBuilder_ == null) { + infrequentAccess_ = builderForValue.build(); + } else { + infrequentAccessBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public Builder mergeInfrequentAccess(com.google.bigtable.admin.v2.TieredStorageRule value) { + if (infrequentAccessBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0) + && infrequentAccess_ != null + && infrequentAccess_ + != com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance()) { + getInfrequentAccessBuilder().mergeFrom(value); + } else { + infrequentAccess_ = value; + } + } else { + infrequentAccessBuilder_.mergeFrom(value); + } + if (infrequentAccess_ != null) { + bitField0_ |= 0x00000001; + onChanged(); + } + return this; + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public Builder clearInfrequentAccess() { + bitField0_ = (bitField0_ & ~0x00000001); + infrequentAccess_ = null; + if (infrequentAccessBuilder_ != null) { + infrequentAccessBuilder_.dispose(); + infrequentAccessBuilder_ = null; + } + onChanged(); + return this; + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public com.google.bigtable.admin.v2.TieredStorageRule.Builder getInfrequentAccessBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return internalGetInfrequentAccessFieldBuilder().getBuilder(); + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + public com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder getInfrequentAccessOrBuilder() { + if (infrequentAccessBuilder_ != null) { + return infrequentAccessBuilder_.getMessageOrBuilder(); + } else { + return infrequentAccess_ == null + ? com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance() + : infrequentAccess_; + } + } + + /** + * + * + *
    +     * Rule to specify what data is stored in the infrequent access(IA) tier.
    +     * The IA tier allows storing more data per node with reduced performance.
    +     * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageRule, + com.google.bigtable.admin.v2.TieredStorageRule.Builder, + com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder> + internalGetInfrequentAccessFieldBuilder() { + if (infrequentAccessBuilder_ == null) { + infrequentAccessBuilder_ = + new com.google.protobuf.SingleFieldBuilder< + com.google.bigtable.admin.v2.TieredStorageRule, + com.google.bigtable.admin.v2.TieredStorageRule.Builder, + com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder>( + getInfrequentAccess(), getParentForChildren(), isClean()); + infrequentAccess_ = null; + } + return infrequentAccessBuilder_; + } + + // @@protoc_insertion_point(builder_scope:google.bigtable.admin.v2.TieredStorageConfig) + } + + // @@protoc_insertion_point(class_scope:google.bigtable.admin.v2.TieredStorageConfig) + private static final com.google.bigtable.admin.v2.TieredStorageConfig DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.bigtable.admin.v2.TieredStorageConfig(); + } + + public static com.google.bigtable.admin.v2.TieredStorageConfig getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TieredStorageConfig parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageConfig getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } +} diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfigOrBuilder.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfigOrBuilder.java new file mode 100644 index 0000000000..380f53080d --- /dev/null +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageConfigOrBuilder.java @@ -0,0 +1,68 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: google/bigtable/admin/v2/table.proto +// Protobuf Java Version: 4.33.2 + +package com.google.bigtable.admin.v2; + +@com.google.protobuf.Generated +public interface TieredStorageConfigOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.bigtable.admin.v2.TieredStorageConfig) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return Whether the infrequentAccess field is set. + */ + boolean hasInfrequentAccess(); + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + * + * @return The infrequentAccess. + */ + com.google.bigtable.admin.v2.TieredStorageRule getInfrequentAccess(); + + /** + * + * + *
    +   * Rule to specify what data is stored in the infrequent access(IA) tier.
    +   * The IA tier allows storing more data per node with reduced performance.
    +   * 
    + * + * .google.bigtable.admin.v2.TieredStorageRule infrequent_access = 1; + */ + com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder getInfrequentAccessOrBuilder(); +} diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRule.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRule.java new file mode 100644 index 0000000000..2a78390f88 --- /dev/null +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRule.java @@ -0,0 +1,801 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: google/bigtable/admin/v2/table.proto +// Protobuf Java Version: 4.33.2 + +package com.google.bigtable.admin.v2; + +/** + * + * + *
    + * Rule to specify what data is stored in a storage tier.
    + * 
    + * + * Protobuf type {@code google.bigtable.admin.v2.TieredStorageRule} + */ +@com.google.protobuf.Generated +public final class TieredStorageRule extends com.google.protobuf.GeneratedMessage + implements + // @@protoc_insertion_point(message_implements:google.bigtable.admin.v2.TieredStorageRule) + TieredStorageRuleOrBuilder { + private static final long serialVersionUID = 0L; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 2, + /* suffix= */ "", + "TieredStorageRule"); + } + + // Use TieredStorageRule.newBuilder() to construct. + private TieredStorageRule(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + + private TieredStorageRule() {} + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageRule_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.bigtable.admin.v2.TieredStorageRule.class, + com.google.bigtable.admin.v2.TieredStorageRule.Builder.class); + } + + private int ruleCase_ = 0; + + @SuppressWarnings("serial") + private java.lang.Object rule_; + + public enum RuleCase + implements + com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + INCLUDE_IF_OLDER_THAN(1), + RULE_NOT_SET(0); + private final int value; + + private RuleCase(int value) { + this.value = value; + } + + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static RuleCase valueOf(int value) { + return forNumber(value); + } + + public static RuleCase forNumber(int value) { + switch (value) { + case 1: + return INCLUDE_IF_OLDER_THAN; + case 0: + return RULE_NOT_SET; + default: + return null; + } + } + + public int getNumber() { + return this.value; + } + }; + + public RuleCase getRuleCase() { + return RuleCase.forNumber(ruleCase_); + } + + public static final int INCLUDE_IF_OLDER_THAN_FIELD_NUMBER = 1; + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return Whether the includeIfOlderThan field is set. + */ + @java.lang.Override + public boolean hasIncludeIfOlderThan() { + return ruleCase_ == 1; + } + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return The includeIfOlderThan. + */ + @java.lang.Override + public com.google.protobuf.Duration getIncludeIfOlderThan() { + if (ruleCase_ == 1) { + return (com.google.protobuf.Duration) rule_; + } + return com.google.protobuf.Duration.getDefaultInstance(); + } + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + @java.lang.Override + public com.google.protobuf.DurationOrBuilder getIncludeIfOlderThanOrBuilder() { + if (ruleCase_ == 1) { + return (com.google.protobuf.Duration) rule_; + } + return com.google.protobuf.Duration.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (ruleCase_ == 1) { + output.writeMessage(1, (com.google.protobuf.Duration) rule_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (ruleCase_ == 1) { + size += + com.google.protobuf.CodedOutputStream.computeMessageSize( + 1, (com.google.protobuf.Duration) rule_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.bigtable.admin.v2.TieredStorageRule)) { + return super.equals(obj); + } + com.google.bigtable.admin.v2.TieredStorageRule other = + (com.google.bigtable.admin.v2.TieredStorageRule) obj; + + if (!getRuleCase().equals(other.getRuleCase())) return false; + switch (ruleCase_) { + case 1: + if (!getIncludeIfOlderThan().equals(other.getIncludeIfOlderThan())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (ruleCase_) { + case 1: + hash = (37 * hash) + INCLUDE_IF_OLDER_THAN_FIELD_NUMBER; + hash = (53 * hash) + getIncludeIfOlderThan().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom(java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.google.bigtable.admin.v2.TieredStorageRule prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * + * + *
    +   * Rule to specify what data is stored in a storage tier.
    +   * 
    + * + * Protobuf type {@code google.bigtable.admin.v2.TieredStorageRule} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder + implements + // @@protoc_insertion_point(builder_implements:google.bigtable.admin.v2.TieredStorageRule) + com.google.bigtable.admin.v2.TieredStorageRuleOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageRule_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.bigtable.admin.v2.TieredStorageRule.class, + com.google.bigtable.admin.v2.TieredStorageRule.Builder.class); + } + + // Construct using com.google.bigtable.admin.v2.TieredStorageRule.newBuilder() + private Builder() {} + + private Builder(com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (includeIfOlderThanBuilder_ != null) { + includeIfOlderThanBuilder_.clear(); + } + ruleCase_ = 0; + rule_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.bigtable.admin.v2.TableProto + .internal_static_google_bigtable_admin_v2_TieredStorageRule_descriptor; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRule getDefaultInstanceForType() { + return com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance(); + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRule build() { + com.google.bigtable.admin.v2.TieredStorageRule result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRule buildPartial() { + com.google.bigtable.admin.v2.TieredStorageRule result = + new com.google.bigtable.admin.v2.TieredStorageRule(this); + if (bitField0_ != 0) { + buildPartial0(result); + } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(com.google.bigtable.admin.v2.TieredStorageRule result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(com.google.bigtable.admin.v2.TieredStorageRule result) { + result.ruleCase_ = ruleCase_; + result.rule_ = this.rule_; + if (ruleCase_ == 1 && includeIfOlderThanBuilder_ != null) { + result.rule_ = includeIfOlderThanBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.bigtable.admin.v2.TieredStorageRule) { + return mergeFrom((com.google.bigtable.admin.v2.TieredStorageRule) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.bigtable.admin.v2.TieredStorageRule other) { + if (other == com.google.bigtable.admin.v2.TieredStorageRule.getDefaultInstance()) return this; + switch (other.getRuleCase()) { + case INCLUDE_IF_OLDER_THAN: + { + mergeIncludeIfOlderThan(other.getIncludeIfOlderThan()); + break; + } + case RULE_NOT_SET: + { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + input.readMessage( + internalGetIncludeIfOlderThanFieldBuilder().getBuilder(), extensionRegistry); + ruleCase_ = 1; + break; + } // case 10 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int ruleCase_ = 0; + private java.lang.Object rule_; + + public RuleCase getRuleCase() { + return RuleCase.forNumber(ruleCase_); + } + + public Builder clearRule() { + ruleCase_ = 0; + rule_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder> + includeIfOlderThanBuilder_; + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return Whether the includeIfOlderThan field is set. + */ + @java.lang.Override + public boolean hasIncludeIfOlderThan() { + return ruleCase_ == 1; + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return The includeIfOlderThan. + */ + @java.lang.Override + public com.google.protobuf.Duration getIncludeIfOlderThan() { + if (includeIfOlderThanBuilder_ == null) { + if (ruleCase_ == 1) { + return (com.google.protobuf.Duration) rule_; + } + return com.google.protobuf.Duration.getDefaultInstance(); + } else { + if (ruleCase_ == 1) { + return includeIfOlderThanBuilder_.getMessage(); + } + return com.google.protobuf.Duration.getDefaultInstance(); + } + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + public Builder setIncludeIfOlderThan(com.google.protobuf.Duration value) { + if (includeIfOlderThanBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + rule_ = value; + onChanged(); + } else { + includeIfOlderThanBuilder_.setMessage(value); + } + ruleCase_ = 1; + return this; + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + public Builder setIncludeIfOlderThan(com.google.protobuf.Duration.Builder builderForValue) { + if (includeIfOlderThanBuilder_ == null) { + rule_ = builderForValue.build(); + onChanged(); + } else { + includeIfOlderThanBuilder_.setMessage(builderForValue.build()); + } + ruleCase_ = 1; + return this; + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + public Builder mergeIncludeIfOlderThan(com.google.protobuf.Duration value) { + if (includeIfOlderThanBuilder_ == null) { + if (ruleCase_ == 1 && rule_ != com.google.protobuf.Duration.getDefaultInstance()) { + rule_ = + com.google.protobuf.Duration.newBuilder((com.google.protobuf.Duration) rule_) + .mergeFrom(value) + .buildPartial(); + } else { + rule_ = value; + } + onChanged(); + } else { + if (ruleCase_ == 1) { + includeIfOlderThanBuilder_.mergeFrom(value); + } else { + includeIfOlderThanBuilder_.setMessage(value); + } + } + ruleCase_ = 1; + return this; + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + public Builder clearIncludeIfOlderThan() { + if (includeIfOlderThanBuilder_ == null) { + if (ruleCase_ == 1) { + ruleCase_ = 0; + rule_ = null; + onChanged(); + } + } else { + if (ruleCase_ == 1) { + ruleCase_ = 0; + rule_ = null; + } + includeIfOlderThanBuilder_.clear(); + } + return this; + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + public com.google.protobuf.Duration.Builder getIncludeIfOlderThanBuilder() { + return internalGetIncludeIfOlderThanFieldBuilder().getBuilder(); + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + @java.lang.Override + public com.google.protobuf.DurationOrBuilder getIncludeIfOlderThanOrBuilder() { + if ((ruleCase_ == 1) && (includeIfOlderThanBuilder_ != null)) { + return includeIfOlderThanBuilder_.getMessageOrBuilder(); + } else { + if (ruleCase_ == 1) { + return (com.google.protobuf.Duration) rule_; + } + return com.google.protobuf.Duration.getDefaultInstance(); + } + } + + /** + * + * + *
    +     * Include cells older than the given age.
    +     * For the infrequent access tier, this value must be at least 30 days.
    +     * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder> + internalGetIncludeIfOlderThanFieldBuilder() { + if (includeIfOlderThanBuilder_ == null) { + if (!(ruleCase_ == 1)) { + rule_ = com.google.protobuf.Duration.getDefaultInstance(); + } + includeIfOlderThanBuilder_ = + new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder>( + (com.google.protobuf.Duration) rule_, getParentForChildren(), isClean()); + rule_ = null; + } + ruleCase_ = 1; + onChanged(); + return includeIfOlderThanBuilder_; + } + + // @@protoc_insertion_point(builder_scope:google.bigtable.admin.v2.TieredStorageRule) + } + + // @@protoc_insertion_point(class_scope:google.bigtable.admin.v2.TieredStorageRule) + private static final com.google.bigtable.admin.v2.TieredStorageRule DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.bigtable.admin.v2.TieredStorageRule(); + } + + public static com.google.bigtable.admin.v2.TieredStorageRule getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TieredStorageRule parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.bigtable.admin.v2.TieredStorageRule getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } +} diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRuleOrBuilder.java b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRuleOrBuilder.java new file mode 100644 index 0000000000..642ec6aac2 --- /dev/null +++ b/proto-google-cloud-bigtable-admin-v2/src/main/java/com/google/bigtable/admin/v2/TieredStorageRuleOrBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: google/bigtable/admin/v2/table.proto +// Protobuf Java Version: 4.33.2 + +package com.google.bigtable.admin.v2; + +@com.google.protobuf.Generated +public interface TieredStorageRuleOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.bigtable.admin.v2.TieredStorageRule) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return Whether the includeIfOlderThan field is set. + */ + boolean hasIncludeIfOlderThan(); + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + * + * @return The includeIfOlderThan. + */ + com.google.protobuf.Duration getIncludeIfOlderThan(); + + /** + * + * + *
    +   * Include cells older than the given age.
    +   * For the infrequent access tier, this value must be at least 30 days.
    +   * 
    + * + * .google.protobuf.Duration include_if_older_than = 1; + */ + com.google.protobuf.DurationOrBuilder getIncludeIfOlderThanOrBuilder(); + + com.google.bigtable.admin.v2.TieredStorageRule.RuleCase getRuleCase(); +} diff --git a/proto-google-cloud-bigtable-admin-v2/src/main/proto/google/bigtable/admin/v2/table.proto b/proto-google-cloud-bigtable-admin-v2/src/main/proto/google/bigtable/admin/v2/table.proto index 68913d057a..4ce692f860 100644 --- a/proto-google-cloud-bigtable-admin-v2/src/main/proto/google/bigtable/admin/v2/table.proto +++ b/proto-google-cloud-bigtable-admin-v2/src/main/proto/google/bigtable/admin/v2/table.proto @@ -1,4 +1,4 @@ -// Copyright 2025 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -205,6 +205,15 @@ message Table { AutomatedBackupPolicy automated_backup_policy = 13; } + // Rules to specify what data is stored in each storage tier. + // Different tiers store data differently, providing different trade-offs + // between cost and performance. Different parts of a table can be stored + // separately on different tiers. + // If a config is specified, tiered storage is enabled for this table. + // Otherwise, tiered storage is disabled. + // Only SSD instances can configure tiered storage. + TieredStorageConfig tiered_storage_config = 14; + // The row key schema for this table. The schema is used to decode the raw row // key bytes into a structured format. The order of field declarations in this // schema is important, as it reflects how the raw row key bytes are @@ -638,6 +647,27 @@ enum RestoreSourceType { BACKUP = 1; } +// Config for tiered storage. +// A valid config must have a valid TieredStorageRule. Otherwise the whole +// TieredStorageConfig must be unset. +// By default all data is stored in the SSD tier (only SSD instances can +// configure tiered storage). +message TieredStorageConfig { + // Rule to specify what data is stored in the infrequent access(IA) tier. + // The IA tier allows storing more data per node with reduced performance. + TieredStorageRule infrequent_access = 1; +} + +// Rule to specify what data is stored in a storage tier. +message TieredStorageRule { + // Rules to specify what data is stored in this tier. + oneof rule { + // Include cells older than the given age. + // For the infrequent access tier, this value must be at least 30 days. + google.protobuf.Duration include_if_older_than = 1; + } +} + // Represents a protobuf schema. message ProtoSchema { // Required. Contains a protobuf-serialized From c62071092d67f8ccfebe3166ca826fb001c76e28 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 27 Feb 2026 12:17:28 -0500 Subject: [PATCH 28/33] feat: expose generated GAPIC admin client and freeze legacy surface (#2806) * feat: Expose autogenerated GAPIC client via getNewApi() This change adds a `getNewApi()` method to `BigtableTableAdminClient` which returns the underlying `BaseBigtableTableAdminClient`. By using composition, we expose the fully generated GAPIC client to users, allowing them to access the newest features and proto-based methods. This maintains the legacy client as a backward-compatible facade without risking method signature collisions. This addresses the legacy client integration phase of the Admin API GAPIC upgrade. Tracking Bug: b/475818901 * refactor: Mark legacy handwritten methods as ObsoleteApi Marks all existing legacy veneer methods in BigtableTableAdminClient with @ObsoleteApi, directing users to the new getNewApi() surface. This freezes the legacy API surface as planned in the GAPIC modernization effort. Tracking Bug: b/475818901 * chore: generate libraries at Wed Feb 25 19:35:31 UTC 2026 * chore: add comments in the javadoc * chore: update the javadoc with a link * chore: PR comments --------- Co-authored-by: cloud-java-bot --- .../admin/v2/BigtableTableAdminClient.java | 524 ++++++++++++++---- 1 file changed, 429 insertions(+), 95 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java index b5ee9d90ea..0b7028c2ea 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.api.core.ObsoleteApi; import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.NotFoundException; @@ -103,13 +104,30 @@ * // One instance per application. * BigtableTableAdminClient client = BigtableTableAdminClient.create("[PROJECT]", "[INSTANCE]"); * - * CreateTableRequest request = - * CreateTableRequest.of("my-table") - * .addFamily("cf1") - * .addFamily("cf2", GCRULES.maxVersions(10)) - * .addSplit(ByteString.copyFromUtf8("b")) - * .addSplit(ByteString.copyFromUtf8("q")); - * client.createTable(request); + * com.google.bigtable.admin.v2.CreateTableRequest request = + * com.google.bigtable.admin.v2.CreateTableRequest.newBuilder() + * .setParent(InstanceName.of("[PROJECT]", "[INSTANCE]").toString()) + * .setTableId("my-table") + * .setTable( + * com.google.bigtable.admin.v2.Table.newBuilder() + * .putColumnFamilies("cf1", com.google.bigtable.admin.v2.ColumnFamily.getDefaultInstance()) + * .putColumnFamilies( + * "cf2", + * com.google.bigtable.admin.v2.ColumnFamily.newBuilder() + * .setGcRule(GcRuleBuilder.maxVersions(10)) + * .build()) + * .build()) + * .addInitialSplits( + * com.google.bigtable.admin.v2.CreateTableRequest.Split.newBuilder() + * .setKey(ByteString.copyFromUtf8("b")) + * .build()) + * .addInitialSplits( + * com.google.bigtable.admin.v2.CreateTableRequest.Split.newBuilder() + * .setKey(ByteString.copyFromUtf8("q")) + * .build()) + * .build(); + * + * client.getNewApi().createTable(request); * * // Cleanup during application shutdown. * client.close(); @@ -151,6 +169,7 @@ public final class BigtableTableAdminClient implements AutoCloseable { private final EnhancedBigtableTableAdminStub stub; private final String projectId; private final String instanceId; + private final BaseBigtableTableAdminClient newApi; /** Constructs an instance of BigtableTableAdminClient with the given project and instance IDs. */ public static BigtableTableAdminClient create( @@ -190,6 +209,7 @@ private BigtableTableAdminClient( this.projectId = projectId; this.instanceId = instanceId; this.stub = stub; + this.newApi = BaseBigtableTableAdminClient.create(stub); } /** Gets the project ID of the instance whose tables this client manages. */ @@ -202,13 +222,24 @@ public String getInstanceId() { return instanceId; } + /** + * Returns the modern autogenerated client. This provides access to the newest features and + * proto-based methods. + */ + public BaseBigtableTableAdminClient getNewApi() { + return newApi; + } + @Override public void close() { stub.close(); } /** - * Creates a new table with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createTable(com.google.bigtable.admin.v2.CreateTableRequest)}. + * + *

    Creates a new table with the specified configuration. * *

    Sample code: * @@ -232,12 +263,16 @@ public void close() { * @see CreateTableRequest for available options. * @see GCRules for the documentation on available garbage collection rules. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Table createTable(CreateTableRequest request) { return ApiExceptions.callAndTranslateApiException(createTableAsync(request)); } /** - * Asynchronously creates a new table with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createTableCallable()}. + * + *

    Asynchronously creates a new table with the specified configuration. * *

    Sample code: * @@ -276,13 +311,17 @@ public Table createTable(CreateTableRequest request) { * @see GCRules for the documentation on available garbage collection rules. */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture createTableAsync(CreateTableRequest request) { return transformToTableResponse( this.stub.createTableCallable().futureCall(request.toProto(projectId, instanceId))); } /** - * Update a table with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateTable(com.google.bigtable.admin.v2.UpdateTableRequest)}. + * + *

    Update a table with the specified configuration. * *

    Sample code: * @@ -302,12 +341,16 @@ public ApiFuture

    createTableAsync(CreateTableRequest request) { * * @see UpdateTableRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Table updateTable(UpdateTableRequest request) { return ApiExceptions.callAndTranslateApiException(updateTableAsync(request)); } /** - * Asynchronously update a table with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateTableOperationCallable()}. + * + *

    Asynchronously update a table with the specified configuration. * *

    Sample code: * @@ -335,6 +378,7 @@ public Table updateTable(UpdateTableRequest request) { * * @see UpdateTableRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture

    updateTableAsync(UpdateTableRequest request) { return ApiFutures.transform( stub.updateTableOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -348,7 +392,10 @@ public Table apply(com.google.bigtable.admin.v2.Table tableProto) { } /** - * Creates, updates and drops column families as specified in the request. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#modifyColumnFamilies(com.google.bigtable.admin.v2.ModifyColumnFamiliesRequest)}. + * + *

    Creates, updates and drops column families as specified in the request. * *

    Sample code: * @@ -381,12 +428,16 @@ public Table apply(com.google.bigtable.admin.v2.Table tableProto) { * * @see ModifyColumnFamiliesRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Table modifyFamilies(ModifyColumnFamiliesRequest request) { return ApiExceptions.callAndTranslateApiException(modifyFamiliesAsync(request)); } /** - * Asynchronously creates, updates, and drops column families as specified in the request. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#modifyColumnFamiliesCallable()}. + * + *

    Asynchronously creates, updates, and drops column families as specified in the request. * *

    Sample code: * @@ -433,6 +484,7 @@ public Table modifyFamilies(ModifyColumnFamiliesRequest request) { * @see ModifyColumnFamiliesRequest for available options. */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture

    modifyFamiliesAsync(ModifyColumnFamiliesRequest request) { return transformToTableResponse( this.stub @@ -441,7 +493,10 @@ public ApiFuture
    modifyFamiliesAsync(ModifyColumnFamiliesRequest request) } /** - * Deletes the table specified by the table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteTable(com.google.bigtable.admin.v2.DeleteTableRequest)}. + * + *

    Deletes the table specified by the table ID. * *

    Sample code: * @@ -449,12 +504,16 @@ public ApiFuture

    modifyFamiliesAsync(ModifyColumnFamiliesRequest request) * client.deleteTable("my-table"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void deleteTable(String tableId) { ApiExceptions.callAndTranslateApiException(deleteTableAsync(tableId)); } /** - * Asynchronously deletes the table specified by the table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteTableCallable()}. + * + *

    Asynchronously deletes the table specified by the table ID. * *

    Sample code: * @@ -477,6 +536,7 @@ public void deleteTable(String tableId) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture deleteTableAsync(String tableId) { DeleteTableRequest request = DeleteTableRequest.newBuilder().setName(getTableName(tableId)).build(); @@ -485,7 +545,10 @@ public ApiFuture deleteTableAsync(String tableId) { } /** - * Checks if the table specified by the table ID exists. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTable(com.google.bigtable.admin.v2.GetTableRequest)}. + * + *

    Checks if the table specified by the table ID exists. * *

    Sample code: * @@ -495,12 +558,16 @@ public ApiFuture deleteTableAsync(String tableId) { * } * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public boolean exists(String tableId) { return ApiExceptions.callAndTranslateApiException(existsAsync(tableId)); } /** - * Asynchronously checks if the table specified by the table ID exists. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTableCallable()}. + * + *

    Asynchronously checks if the table specified by the table ID exists. * *

    Sample code: * @@ -526,6 +593,7 @@ public boolean exists(String tableId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture existsAsync(String tableId) { ApiFuture

    protoFuture = @@ -555,7 +623,10 @@ public Boolean apply(NotFoundException ignored) { } /** - * Gets the table metadata by table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTable(com.google.bigtable.admin.v2.GetTableRequest)}. + * + *

    Gets the table metadata by table ID. * *

    Sample code: * @@ -570,12 +641,16 @@ public Boolean apply(NotFoundException ignored) { * } * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Table getTable(String tableId) { return ApiExceptions.callAndTranslateApiException(getTableAsync(tableId)); } /** - * Asynchronously gets the table metadata by table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTableCallable()}. + * + *

    Asynchronously gets the table metadata by table ID. * *

    Sample code: * @@ -602,6 +677,7 @@ public Table getTable(String tableId) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture

    getTableAsync(String tableId) { return getTableAsync(tableId, com.google.bigtable.admin.v2.Table.View.SCHEMA_VIEW); } @@ -615,21 +691,29 @@ private ApiFuture
    getTableAsync( } /** - * Gets the current encryption info for the table across all of the clusters. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTable(com.google.bigtable.admin.v2.GetTableRequest)}. + * + *

    Gets the current encryption info for the table across all of the clusters. * *

    The returned Map will be keyed by cluster id and contain a status for all of the keys in * use. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Map> getEncryptionInfo(String tableId) { return ApiExceptions.callAndTranslateApiException(getEncryptionInfoAsync(tableId)); } /** - * Asynchronously gets the current encryption info for the table across all of the clusters. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getTableCallable()}. + * + *

    Asynchronously gets the current encryption info for the table across all of the clusters. * *

    The returned Map will be keyed by cluster id and contain a status for all of the keys in * use. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture>> getEncryptionInfoAsync(String tableId) { GetTableRequest request = GetTableRequest.newBuilder() @@ -661,7 +745,10 @@ public Map> apply(com.google.bigtable.admin.v2.Tabl } /** - * Lists all table IDs in the instance. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listTables(com.google.bigtable.admin.v2.ListTablesRequest)}. + * + *

    Lists all table IDs in the instance. * *

    Sample code: * @@ -673,12 +760,16 @@ public Map> apply(com.google.bigtable.admin.v2.Tabl * } * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List listTables() { return ApiExceptions.callAndTranslateApiException(listTablesAsync()); } /** - * Asynchronously lists all table IDs in the instance. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listTablesPagedCallable()}. + * + *

    Asynchronously lists all table IDs in the instance. * *

    Sample code: * @@ -703,6 +794,7 @@ public List listTables() { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> listTablesAsync() { ListTablesRequest request = ListTablesRequest.newBuilder() @@ -766,7 +858,10 @@ public List apply(List protos) { } /** - * Drops rows by the specified row key prefix and table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRange(com.google.bigtable.admin.v2.DropRowRangeRequest)}. + * + *

    Drops rows by the specified row key prefix and table ID. * *

    Please note that this method is considered part of the admin API and is rate limited. * @@ -776,12 +871,16 @@ public List apply(List protos) { * client.dropRowRange("my-table", "prefix"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void dropRowRange(String tableId, String rowKeyPrefix) { ApiExceptions.callAndTranslateApiException(dropRowRangeAsync(tableId, rowKeyPrefix)); } /** - * Asynchronously drops rows by the specified row key prefix and table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRangeCallable()}. + * + *

    Asynchronously drops rows by the specified row key prefix and table ID. * *

    Please note that this method is considered part of the admin API and is rate limited. * @@ -806,12 +905,16 @@ public void dropRowRange(String tableId, String rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture dropRowRangeAsync(String tableId, String rowKeyPrefix) { return dropRowRangeAsync(tableId, ByteString.copyFromUtf8(rowKeyPrefix)); } /** - * Drops rows by the specified row key prefix and table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRange(com.google.bigtable.admin.v2.DropRowRangeRequest)}. + * + *

    Drops rows by the specified row key prefix and table ID. * *

    Please note that this method is considered part of the admin API and is rate limited. * @@ -822,12 +925,16 @@ public ApiFuture dropRowRangeAsync(String tableId, String rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void dropRowRange(String tableId, ByteString rowKeyPrefix) { ApiExceptions.callAndTranslateApiException(dropRowRangeAsync(tableId, rowKeyPrefix)); } /** - * Asynchronously drops rows by the specified row key prefix and table ID. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRangeCallable()}. + * + *

    Asynchronously drops rows by the specified row key prefix and table ID. * *

    Please note that this method is considered part of the admin API and is rate limited. * @@ -852,6 +959,7 @@ public void dropRowRange(String tableId, ByteString rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture dropRowRangeAsync(String tableId, ByteString rowKeyPrefix) { DropRowRangeRequest request = DropRowRangeRequest.newBuilder() @@ -863,7 +971,10 @@ public ApiFuture dropRowRangeAsync(String tableId, ByteString rowKeyPrefix } /** - * Drops all data in the table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRange(com.google.bigtable.admin.v2.DropRowRangeRequest)}. + * + *

    Drops all data in the table. * *

    Sample code: * @@ -871,12 +982,16 @@ public ApiFuture dropRowRangeAsync(String tableId, ByteString rowKeyPrefix * client.dropAllRows("my-table"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void dropAllRows(String tableId) { ApiExceptions.callAndTranslateApiException(dropAllRowsAsync(tableId)); } /** - * Asynchronously drops all data in the table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#dropRowRangeCallable()}. + * + *

    Asynchronously drops all data in the table. * *

    Sample code: * @@ -899,6 +1014,7 @@ public void dropAllRows(String tableId) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture dropAllRowsAsync(String tableId) { DropRowRangeRequest request = DropRowRangeRequest.newBuilder() @@ -910,7 +1026,10 @@ public ApiFuture dropAllRowsAsync(String tableId) { } /** - * Blocks the current thread until replication has caught up to the point when this method was + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#checkConsistency(com.google.bigtable.admin.v2.CheckConsistencyRequest)}. + * + *

    Blocks the current thread until replication has caught up to the point when this method was * called. This allows callers to make sure that their mutations have been replicated across all * of their clusters. * @@ -922,6 +1041,7 @@ public ApiFuture dropAllRowsAsync(String tableId) { * * @throws com.google.api.gax.retrying.PollException when polling exceeds the total timeout */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void awaitReplication(String tableId) { // TODO(igorbernstein2): remove usage of typesafe names com.google.bigtable.admin.v2.TableName tableName = @@ -937,7 +1057,10 @@ public void awaitConsistency(ConsistencyRequest consistencyRequest) { } /** - * Creates a backup with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createBackup(com.google.bigtable.admin.v2.CreateBackupRequest)}. + * + *

    Creates a backup with the specified configuration. * *

    Sample code * @@ -949,12 +1072,16 @@ public void awaitConsistency(ConsistencyRequest consistencyRequest) { * Backup response = client.createBackup(request); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Backup createBackup(CreateBackupRequest request) { return ApiExceptions.callAndTranslateApiException(createBackupAsync(request)); } /** - * Creates a backup with the specified configuration asynchronously. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createBackupOperationCallable()}. + * + *

    Creates a backup with the specified configuration asynchronously. * *

    Sample code * @@ -980,6 +1107,7 @@ public Backup createBackup(CreateBackupRequest request) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture createBackupAsync(CreateBackupRequest request) { return ApiFutures.transform( stub.createBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -993,7 +1121,10 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { } /** - * Gets a backup with the specified backup ID in the specified cluster. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getBackup(com.google.bigtable.admin.v2.GetBackupRequest)}. + * + *

    Gets a backup with the specified backup ID in the specified cluster. * *

    Sample code * @@ -1001,12 +1132,16 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { * Backup backup = client.getBackup(clusterId, backupId); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Backup getBackup(String clusterId, String backupId) { return ApiExceptions.callAndTranslateApiException(getBackupAsync(clusterId, backupId)); } /** - * Gets a backup with the specified backup ID in the specified cluster asynchronously. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getBackupCallable()}. + * + *

    Gets a backup with the specified backup ID in the specified cluster asynchronously. * *

    Sample code * @@ -1028,6 +1163,7 @@ public Backup getBackup(String clusterId, String backupId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getBackupAsync(String clusterId, String backupId) { GetBackupRequest request = GetBackupRequest.newBuilder() @@ -1045,7 +1181,10 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backup) { } /** - * Lists backups in the specified cluster. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listBackups(com.google.bigtable.admin.v2.ListBackupsRequest)}. + * + *

    Lists backups in the specified cluster. * *

    Sample code * @@ -1053,12 +1192,16 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backup) { * List backups = client.listBackups(clusterId); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List listBackups(String clusterId) { return ApiExceptions.callAndTranslateApiException(listBackupsAsync(clusterId)); } /** - * Lists backups in the specified cluster asynchronously. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listBackupsPagedCallable()}. + * + *

    Lists backups in the specified cluster asynchronously. * *

    Sample code: * @@ -1083,6 +1226,7 @@ public List listBackups(String clusterId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> listBackupsAsync(String clusterId) { ListBackupsRequest request = ListBackupsRequest.newBuilder() @@ -1146,7 +1290,10 @@ public List apply(List protos) { } /** - * Deletes a backup with the specified backup ID in the specified cluster. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteBackup(com.google.bigtable.admin.v2.DeleteBackupRequest)}. + * + *

    Deletes a backup with the specified backup ID in the specified cluster. * *

    Sample code * @@ -1154,12 +1301,16 @@ public List apply(List protos) { * client.deleteBackup(clusterId, backupId); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void deleteBackup(String clusterId, String backupId) { ApiExceptions.callAndTranslateApiException(deleteBackupAsync(clusterId, backupId)); } /** - * Deletes a backup with the specified backup ID in the specified cluster asynchronously. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteBackupCallable()}. + * + *

    Deletes a backup with the specified backup ID in the specified cluster asynchronously. * *

    Sample code * @@ -1181,6 +1332,7 @@ public void deleteBackup(String clusterId, String backupId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture deleteBackupAsync(String clusterId, String backupId) { DeleteBackupRequest request = DeleteBackupRequest.newBuilder() @@ -1191,7 +1343,10 @@ public ApiFuture deleteBackupAsync(String clusterId, String backupId) { } /** - * Updates a backup with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateBackup(com.google.bigtable.admin.v2.UpdateBackupRequest)}. + * + *

    Updates a backup with the specified configuration. * *

    Sample code * @@ -1199,12 +1354,16 @@ public ApiFuture deleteBackupAsync(String clusterId, String backupId) { * Backup backup = client.updateBackup(clusterId, backupId); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Backup updateBackup(UpdateBackupRequest request) { return ApiExceptions.callAndTranslateApiException(updateBackupAsync(request)); } /** - * Updates a backup with the specified configuration asynchronously. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateBackupCallable()}. + * + *

    Updates a backup with the specified configuration asynchronously. * *

    Sample code * @@ -1226,6 +1385,7 @@ public Backup updateBackup(UpdateBackupRequest request) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture updateBackupAsync(UpdateBackupRequest request) { return ApiFutures.transform( stub.updateBackupCallable().futureCall(request.toProto(projectId, instanceId)), @@ -1239,7 +1399,10 @@ public Backup apply(com.google.bigtable.admin.v2.Backup proto) { } /** - * Restores a backup to a new table with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#restoreTable(com.google.bigtable.admin.v2.RestoreTableRequest)}. + * + *

    Restores a backup to a new table with the specified configuration. * *

    Sample code * @@ -1248,12 +1411,18 @@ public Backup apply(com.google.bigtable.admin.v2.Backup proto) { * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId)); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public RestoredTableResult restoreTable(RestoreTableRequest request) throws ExecutionException, InterruptedException { return ApiExceptions.callAndTranslateApiException(restoreTableAsync(request)); } - /** Restores a backup to a new table with the specified configuration asynchronously. + /** + *

    This method is obsolete. For the recommended proto-based approach, please see + * {@link com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#restoreTableAsync(com.google.bigtable.admin.v2.RestoreTableRequest)}. + * + *

    Restores a backup to a new table with the specified configuration asynchronously. + * *

    Sample code * *

    {@code
    @@ -1274,7 +1443,8 @@ public RestoredTableResult restoreTable(RestoreTableRequest request)
        *   MoreExecutors.directExecutor()
        * );
        * 
    - * */ + */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture restoreTableAsync(RestoreTableRequest request) { final OperationFuture future = this.stub @@ -1377,7 +1547,10 @@ public ApiFuture awaitOptimizeRestoredTableAsync( } /** - * Copy an existing backup to a new backup in a Cloud Bigtable cluster with the specified + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#copyBackup(com.google.bigtable.admin.v2.CopyBackupRequest)}. + * + *

    Copy an existing backup to a new backup in a Cloud Bigtable cluster with the specified * configuration. * *

    Sample code Note: You want to create the client with project and instance where you want the @@ -1414,12 +1587,16 @@ public ApiFuture awaitOptimizeRestoredTableAsync( * Backup response = client.copyBackup(request); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Backup copyBackup(CopyBackupRequest request) { return ApiExceptions.callAndTranslateApiException(copyBackupAsync(request)); } /** - * Creates a copy of a backup from an existing backup in a Cloud Bigtable cluster with the + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#copyBackupOperationCallable()}. + * + *

    Creates a copy of a backup from an existing backup in a Cloud Bigtable cluster with the * specified configuration asynchronously. * *

    Sample code @@ -1446,6 +1623,7 @@ public Backup copyBackup(CopyBackupRequest request) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture copyBackupAsync(CopyBackupRequest request) { return ApiFutures.transform( stub.copyBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -1459,9 +1637,12 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { } /** - * Returns a future that is resolved when replication has caught up to the point when this method - * was called. This allows callers to make sure that their mutations have been replicated across - * all of their clusters. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#generateConsistencyTokenCallable()}. + * + *

    Returns a future that is resolved when replication has caught up to the point when this + * method was called. This allows callers to make sure that their mutations have been replicated + * across all of their clusters. * *

    Sample code: * @@ -1485,6 +1666,7 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { * } */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture awaitReplicationAsync(final String tableId) { // TODO(igorbernstein2): remove usage of typesafe names com.google.bigtable.admin.v2.TableName tableName = @@ -1517,7 +1699,10 @@ public ApiFuture waitForConsistencyAsync(String tableId, String consistenc } /** - * Creates a new authorized view with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createAuthorizedView(com.google.bigtable.admin.v2.CreateAuthorizedViewRequest)}. + * + *

    Creates a new authorized view with the specified configuration. * *

    Sample code: * @@ -1536,12 +1721,16 @@ public ApiFuture waitForConsistencyAsync(String tableId, String consistenc * * @see CreateAuthorizedViewRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public AuthorizedView createAuthorizedView(CreateAuthorizedViewRequest request) { return ApiExceptions.callAndTranslateApiException(createAuthorizedViewAsync(request)); } /** - * Asynchronously creates a new authorized view with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createAuthorizedViewOperationCallable()}. + * + *

    Asynchronously creates a new authorized view with the specified configuration. * *

    Sample code: * @@ -1574,6 +1763,7 @@ public AuthorizedView createAuthorizedView(CreateAuthorizedViewRequest request) * * @see CreateAuthorizedViewRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture createAuthorizedViewAsync(CreateAuthorizedViewRequest request) { return ApiFutures.transform( stub.createAuthorizedViewOperationCallable() @@ -1589,7 +1779,10 @@ public AuthorizedView apply( } /** - * Updates an existing authorized view with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateAuthorizedView(com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest)}. + * + *

    Updates an existing authorized view with the specified configuration. * *

    Sample code: * @@ -1604,12 +1797,16 @@ public AuthorizedView apply( * * @see UpdateAuthorizedViewRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public AuthorizedView updateAuthorizedView(UpdateAuthorizedViewRequest request) { return ApiExceptions.callAndTranslateApiException(updateAuthorizedViewAsync(request)); } /** - * Asynchronously updates an existing authorized view with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateAuthorizedViewOperationCallable()}. + * + *

    Asynchronously updates an existing authorized view with the specified configuration. * *

    Sample code: * @@ -1638,6 +1835,7 @@ public AuthorizedView updateAuthorizedView(UpdateAuthorizedViewRequest request) * * @see UpdateAuthorizedViewRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture updateAuthorizedViewAsync(UpdateAuthorizedViewRequest request) { return ApiFutures.transform( stub.updateAuthorizedViewOperationCallable() @@ -1653,7 +1851,10 @@ public AuthorizedView apply( } /** - * Gets an authorized view with the specified authorized view ID in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getAuthorizedView(com.google.bigtable.admin.v2.GetAuthorizedViewRequest)}. + * + *

    Gets an authorized view with the specified authorized view ID in the specified table. * *

    Sample code: * @@ -1661,14 +1862,18 @@ public AuthorizedView apply( * AuthorizedView authorizedView = client.getAuthorizedView("my-table", "my-authorized-view"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public AuthorizedView getAuthorizedView(String tableId, String authorizedViewId) { return ApiExceptions.callAndTranslateApiException( getAuthorizedViewAsync(tableId, authorizedViewId)); } /** - * Asynchronously gets an authorized view with the specified authorized view ID in the specified - * table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getAuthorizedViewCallable()}. + * + *

    Asynchronously gets an authorized view with the specified authorized view ID in the + * specified table. * *

    Sample code: * @@ -1690,6 +1895,7 @@ public AuthorizedView getAuthorizedView(String tableId, String authorizedViewId) * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getAuthorizedViewAsync(String tableId, String authorizedViewId) { GetAuthorizedViewRequest request = GetAuthorizedViewRequest.newBuilder() @@ -1709,7 +1915,10 @@ public AuthorizedView apply( } /** - * Lists all authorized view IDs in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listAuthorizedViews(com.google.bigtable.admin.v2.ListAuthorizedViewsRequest)}. + * + *

    Lists all authorized view IDs in the specified table. * *

    Sample code: * @@ -1717,12 +1926,16 @@ public AuthorizedView apply( * List authorizedViews = client.listAuthorizedViews("my-table"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List listAuthorizedViews(String tableId) { return ApiExceptions.callAndTranslateApiException(listAuthorizedViewsAsync(tableId)); } /** - * Asynchronously lists all authorized view IDs in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listAuthorizedViewsPagedCallable()}. + * + *

    Asynchronously lists all authorized view IDs in the specified table. * *

    Sample code: * @@ -1747,6 +1960,7 @@ public List listAuthorizedViews(String tableId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> listAuthorizedViewsAsync(String tableId) { ListAuthorizedViewsRequest request = ListAuthorizedViewsRequest.newBuilder() @@ -1812,9 +2026,12 @@ public List apply(List prot } /** - * Deletes an authorized view with the specified authorized view ID in the specified table. Note - * that the deletion is prohibited if the authorized view has deletion_protection field set to - * true. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteAuthorizedView(com.google.bigtable.admin.v2.DeleteAuthorizedViewRequest)}. + * + *

    Deletes an authorized view with the specified authorized view ID in the specified table. + * Note that the deletion is prohibited if the authorized view has deletion_protection field set + * to true. * *

    Sample code: * @@ -1822,13 +2039,17 @@ public List apply(List prot * client.deleteAuthorizedView("my-table", "my-authorized-view"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void deleteAuthorizedView(String tableId, String authorizedViewId) { ApiExceptions.callAndTranslateApiException( deleteAuthorizedViewAsync(tableId, authorizedViewId)); } /** - * Asynchronously deletes an authorized view with the specified authorized view ID in the + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteAuthorizedViewCallable()}. + * + *

    Asynchronously deletes an authorized view with the specified authorized view ID in the * specified table. Note that the deletion is prohibited if the authorized view has * deletion_protection field set to true. * @@ -1852,6 +2073,7 @@ public void deleteAuthorizedView(String tableId, String authorizedViewId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture deleteAuthorizedViewAsync(String tableId, String authorizedViewId) { DeleteAuthorizedViewRequest request = DeleteAuthorizedViewRequest.newBuilder() @@ -1863,7 +2085,10 @@ public ApiFuture deleteAuthorizedViewAsync(String tableId, String authoriz } /** - * Creates a new schema bundle with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createSchemaBundle(com.google.bigtable.admin.v2.CreateSchemaBundleRequest)}. + * + *

    Creates a new schema bundle with the specified configuration. * *

    Sample code: * @@ -1881,12 +2106,16 @@ public ApiFuture deleteAuthorizedViewAsync(String tableId, String authoriz * * @see CreateSchemaBundleRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public SchemaBundle createSchemaBundle(CreateSchemaBundleRequest request) { return ApiExceptions.callAndTranslateApiException(createSchemaBundleAsync(request)); } /** - * Asynchronously creates a new schema bundle with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#createSchemaBundleOperationCallable()}. + * + *

    Asynchronously creates a new schema bundle with the specified configuration. * *

    Sample code: * @@ -1917,6 +2146,7 @@ public SchemaBundle createSchemaBundle(CreateSchemaBundleRequest request) { * * @see CreateSchemaBundleRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture createSchemaBundleAsync(CreateSchemaBundleRequest request) { return ApiFutures.transform( stub.createSchemaBundleOperationCallable() @@ -1931,7 +2161,10 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle } /** - * Updates an existing schema bundle with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateSchemaBundle(com.google.bigtable.admin.v2.UpdateSchemaBundleRequest)}. + * + *

    Updates an existing schema bundle with the specified configuration. * *

    Sample code: * @@ -1945,12 +2178,16 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * * @see UpdateSchemaBundleRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public SchemaBundle updateSchemaBundle(UpdateSchemaBundleRequest request) { return ApiExceptions.callAndTranslateApiException(updateSchemaBundleAsync(request)); } /** - * Asynchronously updates an existing schema bundle with the specified configuration. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#updateSchemaBundleOperationCallable()}. + * + *

    Asynchronously updates an existing schema bundle with the specified configuration. * *

    Sample code: * @@ -1977,6 +2214,7 @@ public SchemaBundle updateSchemaBundle(UpdateSchemaBundleRequest request) { * * @see UpdateSchemaBundleRequest for available options. */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture updateSchemaBundleAsync(UpdateSchemaBundleRequest request) { return ApiFutures.transform( stub.updateSchemaBundleOperationCallable() @@ -1991,7 +2229,10 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle } /** - * Gets an schema bundle with the specified schema bundle ID in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getSchemaBundle(com.google.bigtable.admin.v2.GetSchemaBundleRequest)}. + * + *

    Gets an schema bundle with the specified schema bundle ID in the specified table. * *

    Sample code: * @@ -1999,13 +2240,17 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * SchemaBundle schemaBundle = client.getSchemaBundle("my-table", "my-schema-bundle"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public SchemaBundle getSchemaBundle(String tableId, String schemaBundleId) { return ApiExceptions.callAndTranslateApiException( getSchemaBundleAsync(tableId, schemaBundleId)); } /** - * Asynchronously gets an schema bundle with the specified schema bundle ID in the specified + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getSchemaBundleCallable()}. + * + *

    Asynchronously gets an schema bundle with the specified schema bundle ID in the specified * table. * *

    Sample code: @@ -2027,6 +2272,7 @@ public SchemaBundle getSchemaBundle(String tableId, String schemaBundleId) { * MoreExecutors.directExecutor()); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getSchemaBundleAsync(String tableId, String schemaBundleId) { GetSchemaBundleRequest request = GetSchemaBundleRequest.newBuilder() @@ -2045,7 +2291,10 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle } /** - * Lists all schema bundle IDs in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listSchemaBundles(com.google.bigtable.admin.v2.ListSchemaBundlesRequest)}. + * + *

    Lists all schema bundle IDs in the specified table. * *

    Sample code: * @@ -2053,12 +2302,16 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * List schemaBundles = client.listSchemaBundles("my-table"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List listSchemaBundles(String tableId) { return ApiExceptions.callAndTranslateApiException(listSchemaBundlesAsync(tableId)); } /** - * Asynchronously lists all schema bundle IDs in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#listSchemaBundlesPagedCallable()}. + * + *

    Asynchronously lists all schema bundle IDs in the specified table. * *

    Sample code: * @@ -2082,6 +2335,7 @@ public List listSchemaBundles(String tableId) { * MoreExecutors.directExecutor()); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> listSchemaBundlesAsync(String tableId) { ListSchemaBundlesRequest request = ListSchemaBundlesRequest.newBuilder() @@ -2148,7 +2402,10 @@ public List apply(List protos } /** - * Deletes an schema bundle with the specified schema bundle ID in the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteSchemaBundle(com.google.bigtable.admin.v2.DeleteSchemaBundleRequest)}. + * + *

    Deletes an schema bundle with the specified schema bundle ID in the specified table. * *

    Sample code: * @@ -2156,12 +2413,16 @@ public List apply(List protos * client.deleteSchemaBundle("my-table", "my-schema-bundle"); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public void deleteSchemaBundle(String tableId, String schemaBundleId) { ApiExceptions.callAndTranslateApiException(deleteSchemaBundleAsync(tableId, schemaBundleId)); } /** - * Asynchronously deletes an schema bundle with the specified schema bundle ID in the specified + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#deleteSchemaBundleCallable()}. + * + *

    Asynchronously deletes an schema bundle with the specified schema bundle ID in the specified * table. * *

    Sample code: @@ -2184,6 +2445,7 @@ public void deleteSchemaBundle(String tableId, String schemaBundleId) { * ); * } */ + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture deleteSchemaBundleAsync(String tableId, String schemaBundleId) { DeleteSchemaBundleRequest request = DeleteSchemaBundleRequest.newBuilder() @@ -2234,7 +2496,10 @@ public Void apply(Empty empty) { } /** - * Gets the IAM access control policy for the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicy(com.google.iam.v1.GetIamPolicyRequest)}. + * + *

    Gets the IAM access control policy for the specified table. * *

    Sample code: * @@ -2250,12 +2515,16 @@ public Void apply(Empty empty) { * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy getIamPolicy(String tableId) { return ApiExceptions.callAndTranslateApiException(getIamPolicyAsync(tableId)); } /** - * Asynchronously gets the IAM access control policy for the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicyCallable()}. + * + *

    Asynchronously gets the IAM access control policy for the specified table. * *

    Sample code: * @@ -2282,13 +2551,17 @@ public Policy getIamPolicy(String tableId) { * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getIamPolicyAsync(String tableId) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return getResourceIamPolicy(tableName); } /** - * Replaces the IAM policy associated with the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicy(com.google.iam.v1.SetIamPolicyRequest)}. + * + *

    Replaces the IAM policy associated with the specified table. * *

    Sample code: * @@ -2305,12 +2578,16 @@ public ApiFuture getIamPolicyAsync(String tableId) { * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy setIamPolicy(String tableId, Policy policy) { return ApiExceptions.callAndTranslateApiException(setIamPolicyAsync(tableId, policy)); } /** - * Asynchronously replaces the IAM policy associated with the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicyCallable()}. + * + *

    Asynchronously replaces the IAM policy associated with the specified table. * *

    Sample code: * @@ -2341,14 +2618,18 @@ public Policy setIamPolicy(String tableId, Policy policy) { * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture setIamPolicyAsync(String tableId, Policy policy) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return setResourceIamPolicy(policy, tableName); } /** - * Tests whether the caller has the given permissions for the specified table. Returns a subset of - * the specified permissions that the caller has. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissions(com.google.iam.v1.TestIamPermissionsRequest)}. + * + *

    Tests whether the caller has the given permissions for the specified table. Returns a subset + * of the specified permissions that the caller has. * *

    Sample code: * @@ -2365,12 +2646,16 @@ public ApiFuture setIamPolicyAsync(String tableId, Policy policy) { * permissions */ @SuppressWarnings({"WeakerAccess"}) + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List testIamPermission(String tableId, String... permissions) { return ApiExceptions.callAndTranslateApiException(testIamPermissionAsync(tableId, permissions)); } /** - * Asynchronously tests whether the caller has the given permissions for the specified table. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissionsCallable()}. + * + *

    Asynchronously tests whether the caller has the given permissions for the specified table. * Returns a subset of the specified permissions that the caller has. * *

    Sample code: @@ -2397,13 +2682,17 @@ public List testIamPermission(String tableId, String... permissions) { * permissions */ @SuppressWarnings({"WeakerAccess"}) + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> testIamPermissionAsync(String tableId, String... permissions) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return testResourceIamPermissions(tableName, permissions); } /** - * Gets the IAM access control policy for the specified backup. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicy(com.google.iam.v1.GetIamPolicyRequest)}. + * + *

    Gets the IAM access control policy for the specified backup. * *

    Sample code: * @@ -2419,12 +2708,16 @@ public ApiFuture> testIamPermissionAsync(String tableId, String... * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy getBackupIamPolicy(String clusterId, String backupId) { return ApiExceptions.callAndTranslateApiException(getBackupIamPolicyAsync(clusterId, backupId)); } /** - * Asynchronously gets the IAM access control policy for the specified backup. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicyCallable()}. + * + *

    Asynchronously gets the IAM access control policy for the specified backup. * *

    Sample code: * @@ -2451,13 +2744,17 @@ public Policy getBackupIamPolicy(String clusterId, String backupId) { * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getBackupIamPolicyAsync(String clusterId, String backupId) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); return getResourceIamPolicy(backupName); } /** - * Replaces the IAM policy associated with the specified backup. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicy(com.google.iam.v1.SetIamPolicyRequest)}. + * + *

    Replaces the IAM policy associated with the specified backup. * *

    Sample code: * @@ -2474,13 +2771,17 @@ public ApiFuture getBackupIamPolicyAsync(String clusterId, String backup * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy setBackupIamPolicy(String clusterId, String backupId, Policy policy) { return ApiExceptions.callAndTranslateApiException( setBackupIamPolicyAsync(clusterId, backupId, policy)); } /** - * Asynchronously replaces the IAM policy associated with the specified backup. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicyCallable()}. + * + *

    Asynchronously replaces the IAM policy associated with the specified backup. * *

    Sample code: * @@ -2511,6 +2812,7 @@ public Policy setBackupIamPolicy(String clusterId, String backupId, Policy polic * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture setBackupIamPolicyAsync( String clusterId, String backupId, Policy policy) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); @@ -2518,8 +2820,11 @@ public ApiFuture setBackupIamPolicyAsync( } /** - * Tests whether the caller has the given permissions for the specified backup. Returns a subset - * of the specified permissions that the caller has. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissions(com.google.iam.v1.TestIamPermissionsRequest)}. + * + *

    Tests whether the caller has the given permissions for the specified backup. Returns a + * subset of the specified permissions that the caller has. * *

    Sample code: * @@ -2538,6 +2843,7 @@ public ApiFuture setBackupIamPolicyAsync( * permissions */ @SuppressWarnings({"WeakerAccess"}) + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List testBackupIamPermission( String clusterId, String backupId, String... permissions) { return ApiExceptions.callAndTranslateApiException( @@ -2545,7 +2851,10 @@ public List testBackupIamPermission( } /** - * Asynchronously tests whether the caller has the given permissions for the specified backup. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissionsCallable()}. + * + *

    Asynchronously tests whether the caller has the given permissions for the specified backup. * Returns a subset of the specified permissions that the caller has. * *

    Sample code: @@ -2572,6 +2881,7 @@ public List testBackupIamPermission( * permissions */ @SuppressWarnings({"WeakerAccess"}) + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture> testBackupIamPermissionAsync( String clusterId, String backupId, String... permissions) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); @@ -2579,7 +2889,10 @@ public ApiFuture> testBackupIamPermissionAsync( } /** - * Gets the IAM access control policy for the specified authorized view. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicy(com.google.iam.v1.GetIamPolicyRequest)}. + * + *

    Gets the IAM access control policy for the specified authorized view. * *

    Sample code: * @@ -2595,13 +2908,17 @@ public ApiFuture> testBackupIamPermissionAsync( * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy getAuthorizedViewIamPolicy(String tableId, String authorizedViewId) { return ApiExceptions.callAndTranslateApiException( getAuthorizedViewIamPolicyAsync(tableId, authorizedViewId)); } /** - * Asynchronously gets the IAM access control policy for the specified authorized view. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#getIamPolicyCallable()}. + * + *

    Asynchronously gets the IAM access control policy for the specified authorized view. * *

    Sample code: * @@ -2628,6 +2945,7 @@ public Policy getAuthorizedViewIamPolicy(String tableId, String authorizedViewId * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture getAuthorizedViewIamPolicyAsync( String tableId, String authorizedViewId) { String authorizedViewName = @@ -2636,7 +2954,10 @@ public ApiFuture getAuthorizedViewIamPolicyAsync( } /** - * Replaces the IAM policy associated with the specified authorized view. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicy(com.google.iam.v1.SetIamPolicyRequest)}. + * + *

    Replaces the IAM policy associated with the specified authorized view. * *

    Sample code: * @@ -2653,13 +2974,17 @@ public ApiFuture getAuthorizedViewIamPolicyAsync( * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public Policy setAuthorizedViewIamPolicy(String tableId, String authorizedViewId, Policy policy) { return ApiExceptions.callAndTranslateApiException( setAuthorizedViewIamPolicyAsync(tableId, authorizedViewId, policy)); } /** - * Asynchronously replaces the IAM policy associated with the specified authorized view. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#setIamPolicyCallable()}. + * + *

    Asynchronously replaces the IAM policy associated with the specified authorized view. * *

    Sample code: * @@ -2690,6 +3015,7 @@ public Policy setAuthorizedViewIamPolicy(String tableId, String authorizedViewId * IAM management */ @SuppressWarnings("WeakerAccess") + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public ApiFuture setAuthorizedViewIamPolicyAsync( String tableId, String authorizedViewId, Policy policy) { String authorizedViewName = @@ -2698,8 +3024,11 @@ public ApiFuture setAuthorizedViewIamPolicyAsync( } /** - * Tests whether the caller has the given permissions for the specified authorized view. Returns a - * subset of the specified permissions that the caller has. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissions(com.google.iam.v1.TestIamPermissionsRequest)}. + * + *

    Tests whether the caller has the given permissions for the specified authorized view. + * Returns a subset of the specified permissions that the caller has. * *

    Sample code: * @@ -2718,6 +3047,7 @@ public ApiFuture setAuthorizedViewIamPolicyAsync( * permissions */ @SuppressWarnings({"WeakerAccess"}) + @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") public List testAuthorizedViewIamPermission( String tableId, String authorizedViewId, String... permissions) { return ApiExceptions.callAndTranslateApiException( @@ -2725,14 +3055,17 @@ public List testAuthorizedViewIamPermission( } /** - * Asynchronously tests whether the caller has the given permissions for the specified authorized - * view. Returns a subset of the specified permissions that the caller has. + * This method is obsolete. For the recommended proto-based approach, please see {@link + * com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient#testIamPermissionsCallable()}. + * + *

    Asynchronously tests whether the caller has the given permissions for the specified + * authorized view. Returns a subset of the specified permissions that the caller has. * *

    Sample code: * *

    {@code
    -   * ApiFuture> grantedPermissionsFuture = client.testAuthorizedViewIamPermissionAsync("my-table-id", "my-authorized-view-id",
    -   *   "bigtable.authorizedViews.get", "bigtable.authorizedViews.delete");
    +   * ApiFuture> grantedPermissionsFuture = client.testAuthorizedViewIamPermissionAsync("my-table-id",
    +   *   "my-authorized-view-id", "bigtable.authorizedViews.get", "bigtable.authorizedViews.delete");
        *
        * ApiFutures.addCallback(grantedPermissionsFuture,
        *   new ApiFutureCallback>() {
    @@ -2752,6 +3085,7 @@ public List testAuthorizedViewIamPermission(
        *     permissions
        */
       @SuppressWarnings({"WeakerAccess"})
    +  @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.")
       public ApiFuture> testAuthorizedViewIamPermissionAsync(
           String tableId, String authorizedViewId, String... permissions) {
         String authorizedViewName =
    
    From c2ed0f121627bf78c114b34d6fdef6d119fa876a Mon Sep 17 00:00:00 2001
    From: Igor Bernstein 
    Date: Fri, 27 Feb 2026 15:50:26 -0500
    Subject: [PATCH 29/33] chore: add pacemaker (#2818)
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    Change-Id: Ic84f92e783121df888403c0f06c47ce5286ac728
    
    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://togithub.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)
    - [ ] Rollback plan is reviewed and LGTMed
    - [ ] All new data plane features have a completed end to end testing plan
    
    Fixes # ☕️
    
    If you write sample code, please follow the [samples format](
    https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
    ---
     .../data/v2/internal/csm/MetricsImpl.java     | 39 ++++++++++++-------
     1 file changed, 26 insertions(+), 13 deletions(-)
    
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    index 51adb36ea4..c1ae38f7c4 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java
    @@ -52,6 +52,7 @@
     import java.util.List;
     import java.util.concurrent.ScheduledExecutorService;
     import java.util.concurrent.ScheduledFuture;
    +import java.util.concurrent.TimeUnit;
     import javax.annotation.Nullable;
     
     public class MetricsImpl implements Metrics, Closeable {
    @@ -59,13 +60,16 @@ public class MetricsImpl implements Metrics, Closeable {
     
       private final ApiTracerFactory userTracerFactory;
       private final @Nullable OpenTelemetrySdk internalOtel;
    +  private final @Nullable MetricRegistry.RecorderRegistry internalRecorder;
       private final @Nullable OpenTelemetry userOtel;
    +  private final @Nullable MetricRegistry.RecorderRegistry userRecorder;
       private final ScheduledExecutorService executor;
       private final Tagger ocTagger;
       private final StatsRecorder ocRecorder;
     
       @Nullable private final GrpcOpenTelemetry grpcOtel;
       @Nullable private final ChannelPoolMetricsTracer channelPoolMetricsTracer;
    +  @Nullable private final Pacemaker pacemaker;
       private final List> tasks = new ArrayList<>();
     
       public MetricsImpl(
    @@ -89,6 +93,9 @@ public MetricsImpl(
         this.executor = executor;
     
         if (internalOtel != null) {
    +      this.internalRecorder = metricRegistry.newRecorderRegistry(internalOtel.getMeterProvider());
    +      this.pacemaker = new Pacemaker(internalRecorder, clientInfo, "background");
    +      this.channelPoolMetricsTracer = new ChannelPoolMetricsTracer(internalRecorder, clientInfo);
           this.grpcOtel =
               GrpcOpenTelemetry.newBuilder()
                   .sdk(internalOtel)
    @@ -98,16 +105,18 @@ public MetricsImpl(
                   // Enable specific grpc metrics
                   .enableMetrics(metricRegistry.getGrpcMetricNames())
                   .build();
    +
         } else {
    +      this.internalRecorder = null;
           this.grpcOtel = null;
    +      this.pacemaker = null;
    +      this.channelPoolMetricsTracer = null;
         }
     
    -    if (internalOtel != null) {
    -      this.channelPoolMetricsTracer =
    -          new ChannelPoolMetricsTracer(
    -              metricRegistry.newRecorderRegistry(internalOtel.getMeterProvider()), clientInfo);
    +    if (userOtel != null) {
    +      this.userRecorder = metricRegistry.newRecorderRegistry(userOtel.getMeterProvider());
         } else {
    -      this.channelPoolMetricsTracer = null;
    +      this.userRecorder = null;
         }
       }
     
    @@ -126,6 +135,14 @@ public void start() {
         if (channelPoolMetricsTracer != null) {
           tasks.add(channelPoolMetricsTracer.start(executor));
         }
    +    if (pacemaker != null) {
    +      tasks.add(
    +          executor.scheduleAtFixedRate(
    +              pacemaker,
    +              Pacemaker.PACEMAKER_INTERVAL.toMillis(),
    +              Pacemaker.PACEMAKER_INTERVAL.toMillis(),
    +              TimeUnit.MILLISECONDS));
    +    }
       }
     
       @Override
    @@ -145,15 +162,11 @@ public ApiTracerFactory createTracerFactory(ClientInfo clientInfo) {
             .add(createOCMetricsFactory(clientInfo, ocTagger, ocRecorder))
             .add(userTracerFactory);
     
    -    if (internalOtel != null) {
    -      tracerFactories.add(
    -          createOtelMetricsFactory(
    -              metricRegistry.newRecorderRegistry(internalOtel.getMeterProvider()), clientInfo));
    +    if (internalRecorder != null) {
    +      tracerFactories.add(createOtelMetricsFactory(internalRecorder, clientInfo));
         }
    -    if (userOtel != null) {
    -      tracerFactories.add(
    -          createOtelMetricsFactory(
    -              metricRegistry.newRecorderRegistry(userOtel.getMeterProvider()), clientInfo));
    +    if (userRecorder != null) {
    +      tracerFactories.add(createOtelMetricsFactory(userRecorder, clientInfo));
         }
     
         return new CompositeTracerFactory(tracerFactories.build());
    
    From 45f9941d6dd23bf45f016e7a322953c863c027e1 Mon Sep 17 00:00:00 2001
    From: Jin Seop Kim 
    Date: Fri, 27 Feb 2026 17:39:33 -0500
    Subject: [PATCH 30/33] chore: rename newApi to baseClient (#2820)
    
    ---
     .../admin/v2/BigtableTableAdminClient.java    | 162 +++++++++---------
     1 file changed, 81 insertions(+), 81 deletions(-)
    
    diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java
    index 0b7028c2ea..cdb95062e3 100644
    --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java
    +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java
    @@ -127,7 +127,7 @@
      *         .build())
      *     .build();
      *
    - * client.getNewApi().createTable(request);
    + * client.getBaseClient().createTable(request);
      *
      * // Cleanup during application shutdown.
      * client.close();
    @@ -169,7 +169,7 @@ public final class BigtableTableAdminClient implements AutoCloseable {
       private final EnhancedBigtableTableAdminStub stub;
       private final String projectId;
       private final String instanceId;
    -  private final BaseBigtableTableAdminClient newApi;
    +  private final BaseBigtableTableAdminClient baseClient;
     
       /** Constructs an instance of BigtableTableAdminClient with the given project and instance IDs. */
       public static BigtableTableAdminClient create(
    @@ -209,7 +209,7 @@ private BigtableTableAdminClient(
         this.projectId = projectId;
         this.instanceId = instanceId;
         this.stub = stub;
    -    this.newApi = BaseBigtableTableAdminClient.create(stub);
    +    this.baseClient = BaseBigtableTableAdminClient.create(stub);
       }
     
       /** Gets the project ID of the instance whose tables this client manages. */
    @@ -226,8 +226,8 @@ public String getInstanceId() {
        * Returns the modern autogenerated client. This provides access to the newest features and
        * proto-based methods.
        */
    -  public BaseBigtableTableAdminClient getNewApi() {
    -    return newApi;
    +  public BaseBigtableTableAdminClient getBaseClient() {
    +    return baseClient;
       }
     
       @Override
    @@ -263,7 +263,7 @@ public void close() {
        * @see CreateTableRequest for available options.
        * @see GCRules for the documentation on available garbage collection rules.
        */
    -  @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.")
    +  @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.")
       public Table createTable(CreateTableRequest request) {
         return ApiExceptions.callAndTranslateApiException(createTableAsync(request));
       }
    @@ -311,7 +311,7 @@ public Table createTable(CreateTableRequest request) {
        * @see GCRules for the documentation on available garbage collection rules.
        */
       @SuppressWarnings("WeakerAccess")
    -  @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.")
    +  @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.")
       public ApiFuture
    createTableAsync(CreateTableRequest request) { return transformToTableResponse( this.stub.createTableCallable().futureCall(request.toProto(projectId, instanceId))); @@ -341,7 +341,7 @@ public ApiFuture
    createTableAsync(CreateTableRequest request) { * * @see UpdateTableRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Table updateTable(UpdateTableRequest request) { return ApiExceptions.callAndTranslateApiException(updateTableAsync(request)); } @@ -378,7 +378,7 @@ public Table updateTable(UpdateTableRequest request) { * * @see UpdateTableRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture
    updateTableAsync(UpdateTableRequest request) { return ApiFutures.transform( stub.updateTableOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -428,7 +428,7 @@ public Table apply(com.google.bigtable.admin.v2.Table tableProto) { * * @see ModifyColumnFamiliesRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Table modifyFamilies(ModifyColumnFamiliesRequest request) { return ApiExceptions.callAndTranslateApiException(modifyFamiliesAsync(request)); } @@ -484,7 +484,7 @@ public Table modifyFamilies(ModifyColumnFamiliesRequest request) { * @see ModifyColumnFamiliesRequest for available options. */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture
    modifyFamiliesAsync(ModifyColumnFamiliesRequest request) { return transformToTableResponse( this.stub @@ -504,7 +504,7 @@ public ApiFuture
    modifyFamiliesAsync(ModifyColumnFamiliesRequest request) * client.deleteTable("my-table"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void deleteTable(String tableId) { ApiExceptions.callAndTranslateApiException(deleteTableAsync(tableId)); } @@ -536,7 +536,7 @@ public void deleteTable(String tableId) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture deleteTableAsync(String tableId) { DeleteTableRequest request = DeleteTableRequest.newBuilder().setName(getTableName(tableId)).build(); @@ -558,7 +558,7 @@ public ApiFuture deleteTableAsync(String tableId) { * } * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public boolean exists(String tableId) { return ApiExceptions.callAndTranslateApiException(existsAsync(tableId)); } @@ -593,7 +593,7 @@ public boolean exists(String tableId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture existsAsync(String tableId) { ApiFuture
    protoFuture = @@ -641,7 +641,7 @@ public Boolean apply(NotFoundException ignored) { * } * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Table getTable(String tableId) { return ApiExceptions.callAndTranslateApiException(getTableAsync(tableId)); } @@ -677,7 +677,7 @@ public Table getTable(String tableId) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture
    getTableAsync(String tableId) { return getTableAsync(tableId, com.google.bigtable.admin.v2.Table.View.SCHEMA_VIEW); } @@ -699,7 +699,7 @@ private ApiFuture
    getTableAsync( *

    The returned Map will be keyed by cluster id and contain a status for all of the keys in * use. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Map> getEncryptionInfo(String tableId) { return ApiExceptions.callAndTranslateApiException(getEncryptionInfoAsync(tableId)); } @@ -713,7 +713,7 @@ public Map> getEncryptionInfo(String tableId) { *

    The returned Map will be keyed by cluster id and contain a status for all of the keys in * use. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture>> getEncryptionInfoAsync(String tableId) { GetTableRequest request = GetTableRequest.newBuilder() @@ -760,7 +760,7 @@ public Map> apply(com.google.bigtable.admin.v2.Tabl * } * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List listTables() { return ApiExceptions.callAndTranslateApiException(listTablesAsync()); } @@ -794,7 +794,7 @@ public List listTables() { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> listTablesAsync() { ListTablesRequest request = ListTablesRequest.newBuilder() @@ -871,7 +871,7 @@ public List apply(List protos) { * client.dropRowRange("my-table", "prefix"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void dropRowRange(String tableId, String rowKeyPrefix) { ApiExceptions.callAndTranslateApiException(dropRowRangeAsync(tableId, rowKeyPrefix)); } @@ -905,7 +905,7 @@ public void dropRowRange(String tableId, String rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture dropRowRangeAsync(String tableId, String rowKeyPrefix) { return dropRowRangeAsync(tableId, ByteString.copyFromUtf8(rowKeyPrefix)); } @@ -925,7 +925,7 @@ public ApiFuture dropRowRangeAsync(String tableId, String rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void dropRowRange(String tableId, ByteString rowKeyPrefix) { ApiExceptions.callAndTranslateApiException(dropRowRangeAsync(tableId, rowKeyPrefix)); } @@ -959,7 +959,7 @@ public void dropRowRange(String tableId, ByteString rowKeyPrefix) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture dropRowRangeAsync(String tableId, ByteString rowKeyPrefix) { DropRowRangeRequest request = DropRowRangeRequest.newBuilder() @@ -982,7 +982,7 @@ public ApiFuture dropRowRangeAsync(String tableId, ByteString rowKeyPrefix * client.dropAllRows("my-table"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void dropAllRows(String tableId) { ApiExceptions.callAndTranslateApiException(dropAllRowsAsync(tableId)); } @@ -1014,7 +1014,7 @@ public void dropAllRows(String tableId) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture dropAllRowsAsync(String tableId) { DropRowRangeRequest request = DropRowRangeRequest.newBuilder() @@ -1041,7 +1041,7 @@ public ApiFuture dropAllRowsAsync(String tableId) { * * @throws com.google.api.gax.retrying.PollException when polling exceeds the total timeout */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void awaitReplication(String tableId) { // TODO(igorbernstein2): remove usage of typesafe names com.google.bigtable.admin.v2.TableName tableName = @@ -1072,7 +1072,7 @@ public void awaitConsistency(ConsistencyRequest consistencyRequest) { * Backup response = client.createBackup(request); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Backup createBackup(CreateBackupRequest request) { return ApiExceptions.callAndTranslateApiException(createBackupAsync(request)); } @@ -1107,7 +1107,7 @@ public Backup createBackup(CreateBackupRequest request) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture createBackupAsync(CreateBackupRequest request) { return ApiFutures.transform( stub.createBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -1132,7 +1132,7 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { * Backup backup = client.getBackup(clusterId, backupId); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Backup getBackup(String clusterId, String backupId) { return ApiExceptions.callAndTranslateApiException(getBackupAsync(clusterId, backupId)); } @@ -1163,7 +1163,7 @@ public Backup getBackup(String clusterId, String backupId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getBackupAsync(String clusterId, String backupId) { GetBackupRequest request = GetBackupRequest.newBuilder() @@ -1192,7 +1192,7 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backup) { * List backups = client.listBackups(clusterId); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List listBackups(String clusterId) { return ApiExceptions.callAndTranslateApiException(listBackupsAsync(clusterId)); } @@ -1226,7 +1226,7 @@ public List listBackups(String clusterId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> listBackupsAsync(String clusterId) { ListBackupsRequest request = ListBackupsRequest.newBuilder() @@ -1301,7 +1301,7 @@ public List apply(List protos) { * client.deleteBackup(clusterId, backupId); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void deleteBackup(String clusterId, String backupId) { ApiExceptions.callAndTranslateApiException(deleteBackupAsync(clusterId, backupId)); } @@ -1332,7 +1332,7 @@ public void deleteBackup(String clusterId, String backupId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture deleteBackupAsync(String clusterId, String backupId) { DeleteBackupRequest request = DeleteBackupRequest.newBuilder() @@ -1354,7 +1354,7 @@ public ApiFuture deleteBackupAsync(String clusterId, String backupId) { * Backup backup = client.updateBackup(clusterId, backupId); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Backup updateBackup(UpdateBackupRequest request) { return ApiExceptions.callAndTranslateApiException(updateBackupAsync(request)); } @@ -1385,7 +1385,7 @@ public Backup updateBackup(UpdateBackupRequest request) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture updateBackupAsync(UpdateBackupRequest request) { return ApiFutures.transform( stub.updateBackupCallable().futureCall(request.toProto(projectId, instanceId)), @@ -1411,7 +1411,7 @@ public Backup apply(com.google.bigtable.admin.v2.Backup proto) { * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId)); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public RestoredTableResult restoreTable(RestoreTableRequest request) throws ExecutionException, InterruptedException { return ApiExceptions.callAndTranslateApiException(restoreTableAsync(request)); @@ -1444,7 +1444,7 @@ public RestoredTableResult restoreTable(RestoreTableRequest request) * ); * */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture restoreTableAsync(RestoreTableRequest request) { final OperationFuture future = this.stub @@ -1587,7 +1587,7 @@ public ApiFuture awaitOptimizeRestoredTableAsync( * Backup response = client.copyBackup(request); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Backup copyBackup(CopyBackupRequest request) { return ApiExceptions.callAndTranslateApiException(copyBackupAsync(request)); } @@ -1623,7 +1623,7 @@ public Backup copyBackup(CopyBackupRequest request) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture copyBackupAsync(CopyBackupRequest request) { return ApiFutures.transform( stub.copyBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)), @@ -1666,7 +1666,7 @@ public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { * } */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture awaitReplicationAsync(final String tableId) { // TODO(igorbernstein2): remove usage of typesafe names com.google.bigtable.admin.v2.TableName tableName = @@ -1721,7 +1721,7 @@ public ApiFuture waitForConsistencyAsync(String tableId, String consistenc * * @see CreateAuthorizedViewRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public AuthorizedView createAuthorizedView(CreateAuthorizedViewRequest request) { return ApiExceptions.callAndTranslateApiException(createAuthorizedViewAsync(request)); } @@ -1763,7 +1763,7 @@ public AuthorizedView createAuthorizedView(CreateAuthorizedViewRequest request) * * @see CreateAuthorizedViewRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture createAuthorizedViewAsync(CreateAuthorizedViewRequest request) { return ApiFutures.transform( stub.createAuthorizedViewOperationCallable() @@ -1797,7 +1797,7 @@ public AuthorizedView apply( * * @see UpdateAuthorizedViewRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public AuthorizedView updateAuthorizedView(UpdateAuthorizedViewRequest request) { return ApiExceptions.callAndTranslateApiException(updateAuthorizedViewAsync(request)); } @@ -1835,7 +1835,7 @@ public AuthorizedView updateAuthorizedView(UpdateAuthorizedViewRequest request) * * @see UpdateAuthorizedViewRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture updateAuthorizedViewAsync(UpdateAuthorizedViewRequest request) { return ApiFutures.transform( stub.updateAuthorizedViewOperationCallable() @@ -1862,7 +1862,7 @@ public AuthorizedView apply( * AuthorizedView authorizedView = client.getAuthorizedView("my-table", "my-authorized-view"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public AuthorizedView getAuthorizedView(String tableId, String authorizedViewId) { return ApiExceptions.callAndTranslateApiException( getAuthorizedViewAsync(tableId, authorizedViewId)); @@ -1895,7 +1895,7 @@ public AuthorizedView getAuthorizedView(String tableId, String authorizedViewId) * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getAuthorizedViewAsync(String tableId, String authorizedViewId) { GetAuthorizedViewRequest request = GetAuthorizedViewRequest.newBuilder() @@ -1926,7 +1926,7 @@ public AuthorizedView apply( * List authorizedViews = client.listAuthorizedViews("my-table"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List listAuthorizedViews(String tableId) { return ApiExceptions.callAndTranslateApiException(listAuthorizedViewsAsync(tableId)); } @@ -1960,7 +1960,7 @@ public List listAuthorizedViews(String tableId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> listAuthorizedViewsAsync(String tableId) { ListAuthorizedViewsRequest request = ListAuthorizedViewsRequest.newBuilder() @@ -2039,7 +2039,7 @@ public List apply(List prot * client.deleteAuthorizedView("my-table", "my-authorized-view"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void deleteAuthorizedView(String tableId, String authorizedViewId) { ApiExceptions.callAndTranslateApiException( deleteAuthorizedViewAsync(tableId, authorizedViewId)); @@ -2073,7 +2073,7 @@ public void deleteAuthorizedView(String tableId, String authorizedViewId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture deleteAuthorizedViewAsync(String tableId, String authorizedViewId) { DeleteAuthorizedViewRequest request = DeleteAuthorizedViewRequest.newBuilder() @@ -2106,7 +2106,7 @@ public ApiFuture deleteAuthorizedViewAsync(String tableId, String authoriz * * @see CreateSchemaBundleRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public SchemaBundle createSchemaBundle(CreateSchemaBundleRequest request) { return ApiExceptions.callAndTranslateApiException(createSchemaBundleAsync(request)); } @@ -2146,7 +2146,7 @@ public SchemaBundle createSchemaBundle(CreateSchemaBundleRequest request) { * * @see CreateSchemaBundleRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture createSchemaBundleAsync(CreateSchemaBundleRequest request) { return ApiFutures.transform( stub.createSchemaBundleOperationCallable() @@ -2178,7 +2178,7 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * * @see UpdateSchemaBundleRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public SchemaBundle updateSchemaBundle(UpdateSchemaBundleRequest request) { return ApiExceptions.callAndTranslateApiException(updateSchemaBundleAsync(request)); } @@ -2214,7 +2214,7 @@ public SchemaBundle updateSchemaBundle(UpdateSchemaBundleRequest request) { * * @see UpdateSchemaBundleRequest for available options. */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture updateSchemaBundleAsync(UpdateSchemaBundleRequest request) { return ApiFutures.transform( stub.updateSchemaBundleOperationCallable() @@ -2240,7 +2240,7 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * SchemaBundle schemaBundle = client.getSchemaBundle("my-table", "my-schema-bundle"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public SchemaBundle getSchemaBundle(String tableId, String schemaBundleId) { return ApiExceptions.callAndTranslateApiException( getSchemaBundleAsync(tableId, schemaBundleId)); @@ -2272,7 +2272,7 @@ public SchemaBundle getSchemaBundle(String tableId, String schemaBundleId) { * MoreExecutors.directExecutor()); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getSchemaBundleAsync(String tableId, String schemaBundleId) { GetSchemaBundleRequest request = GetSchemaBundleRequest.newBuilder() @@ -2302,7 +2302,7 @@ public SchemaBundle apply(com.google.bigtable.admin.v2.SchemaBundle schemaBundle * List schemaBundles = client.listSchemaBundles("my-table"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List listSchemaBundles(String tableId) { return ApiExceptions.callAndTranslateApiException(listSchemaBundlesAsync(tableId)); } @@ -2335,7 +2335,7 @@ public List listSchemaBundles(String tableId) { * MoreExecutors.directExecutor()); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> listSchemaBundlesAsync(String tableId) { ListSchemaBundlesRequest request = ListSchemaBundlesRequest.newBuilder() @@ -2413,7 +2413,7 @@ public List apply(List protos * client.deleteSchemaBundle("my-table", "my-schema-bundle"); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public void deleteSchemaBundle(String tableId, String schemaBundleId) { ApiExceptions.callAndTranslateApiException(deleteSchemaBundleAsync(tableId, schemaBundleId)); } @@ -2445,7 +2445,7 @@ public void deleteSchemaBundle(String tableId, String schemaBundleId) { * ); * } */ - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture deleteSchemaBundleAsync(String tableId, String schemaBundleId) { DeleteSchemaBundleRequest request = DeleteSchemaBundleRequest.newBuilder() @@ -2515,7 +2515,7 @@ public Void apply(Empty empty) { * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy getIamPolicy(String tableId) { return ApiExceptions.callAndTranslateApiException(getIamPolicyAsync(tableId)); } @@ -2551,7 +2551,7 @@ public Policy getIamPolicy(String tableId) { * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getIamPolicyAsync(String tableId) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return getResourceIamPolicy(tableName); @@ -2578,7 +2578,7 @@ public ApiFuture getIamPolicyAsync(String tableId) { * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy setIamPolicy(String tableId, Policy policy) { return ApiExceptions.callAndTranslateApiException(setIamPolicyAsync(tableId, policy)); } @@ -2618,7 +2618,7 @@ public Policy setIamPolicy(String tableId, Policy policy) { * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture setIamPolicyAsync(String tableId, Policy policy) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return setResourceIamPolicy(policy, tableName); @@ -2646,7 +2646,7 @@ public ApiFuture setIamPolicyAsync(String tableId, Policy policy) { * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List testIamPermission(String tableId, String... permissions) { return ApiExceptions.callAndTranslateApiException(testIamPermissionAsync(tableId, permissions)); } @@ -2682,7 +2682,7 @@ public List testIamPermission(String tableId, String... permissions) { * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> testIamPermissionAsync(String tableId, String... permissions) { String tableName = NameUtil.formatTableName(projectId, instanceId, tableId); return testResourceIamPermissions(tableName, permissions); @@ -2708,7 +2708,7 @@ public ApiFuture> testIamPermissionAsync(String tableId, String... * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy getBackupIamPolicy(String clusterId, String backupId) { return ApiExceptions.callAndTranslateApiException(getBackupIamPolicyAsync(clusterId, backupId)); } @@ -2744,7 +2744,7 @@ public Policy getBackupIamPolicy(String clusterId, String backupId) { * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getBackupIamPolicyAsync(String clusterId, String backupId) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); return getResourceIamPolicy(backupName); @@ -2771,7 +2771,7 @@ public ApiFuture getBackupIamPolicyAsync(String clusterId, String backup * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy setBackupIamPolicy(String clusterId, String backupId, Policy policy) { return ApiExceptions.callAndTranslateApiException( setBackupIamPolicyAsync(clusterId, backupId, policy)); @@ -2812,7 +2812,7 @@ public Policy setBackupIamPolicy(String clusterId, String backupId, Policy polic * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture setBackupIamPolicyAsync( String clusterId, String backupId, Policy policy) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); @@ -2843,7 +2843,7 @@ public ApiFuture setBackupIamPolicyAsync( * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List testBackupIamPermission( String clusterId, String backupId, String... permissions) { return ApiExceptions.callAndTranslateApiException( @@ -2881,7 +2881,7 @@ public List testBackupIamPermission( * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> testBackupIamPermissionAsync( String clusterId, String backupId, String... permissions) { String backupName = NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId); @@ -2908,7 +2908,7 @@ public ApiFuture> testBackupIamPermissionAsync( * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy getAuthorizedViewIamPolicy(String tableId, String authorizedViewId) { return ApiExceptions.callAndTranslateApiException( getAuthorizedViewIamPolicyAsync(tableId, authorizedViewId)); @@ -2945,7 +2945,7 @@ public Policy getAuthorizedViewIamPolicy(String tableId, String authorizedViewId * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture getAuthorizedViewIamPolicyAsync( String tableId, String authorizedViewId) { String authorizedViewName = @@ -2974,7 +2974,7 @@ public ApiFuture getAuthorizedViewIamPolicyAsync( * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public Policy setAuthorizedViewIamPolicy(String tableId, String authorizedViewId, Policy policy) { return ApiExceptions.callAndTranslateApiException( setAuthorizedViewIamPolicyAsync(tableId, authorizedViewId, policy)); @@ -3015,7 +3015,7 @@ public Policy setAuthorizedViewIamPolicy(String tableId, String authorizedViewId * IAM management */ @SuppressWarnings("WeakerAccess") - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture setAuthorizedViewIamPolicyAsync( String tableId, String authorizedViewId, Policy policy) { String authorizedViewName = @@ -3047,7 +3047,7 @@ public ApiFuture setAuthorizedViewIamPolicyAsync( * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public List testAuthorizedViewIamPermission( String tableId, String authorizedViewId, String... permissions) { return ApiExceptions.callAndTranslateApiException( @@ -3085,7 +3085,7 @@ public List testAuthorizedViewIamPermission( * permissions */ @SuppressWarnings({"WeakerAccess"}) - @ObsoleteApi("Use getNewApi() to access the auto-generated proto-based methods instead.") + @ObsoleteApi("Use getBaseClient() to access the auto-generated proto-based methods instead.") public ApiFuture> testAuthorizedViewIamPermissionAsync( String tableId, String authorizedViewId, String... permissions) { String authorizedViewName = From b9238714dccf082cbd5925830708423cbfb14d13 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Fri, 27 Feb 2026 19:06:27 -0500 Subject: [PATCH 31/33] chore: re-add prepareForShutdown to the exporter (#2821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I033f40dd88b17804a5dfd2fecd84e6c9365193eb 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://togithub.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) - [ ] Rollback plan is reviewed and LGTMed - [ ] All new data plane features have a completed end to end testing plan Fixes # ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). --- .../data/v2/internal/csm/MetricsImpl.java | 11 +- .../csm/exporter/BigtablePeriodicReader.java | 109 ++++++++++++++++++ .../v2/internal/csm/exporter/Converter.java | 1 - 3 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtablePeriodicReader.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java index c1ae38f7c4..38b383d067 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -25,6 +25,7 @@ import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.EnvInfo; import com.google.cloud.bigtable.data.v2.internal.csm.exporter.BigtableCloudMonitoringExporter; +import com.google.cloud.bigtable.data.v2.internal.csm.exporter.BigtablePeriodicReader; import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.MetricsTracerFactory; import com.google.cloud.bigtable.data.v2.internal.csm.opencensus.RpcMeasureConstants; import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BuiltinMetricsTracerFactory; @@ -43,9 +44,6 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; @@ -194,7 +192,7 @@ public static OpenTelemetrySdk createBuiltinOtel( SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); - MetricExporter publicExporter = + BigtableCloudMonitoringExporter exporter = BigtableCloudMonitoringExporter.create( metricRegistry, EnvInfo::detect, @@ -202,9 +200,8 @@ public static OpenTelemetrySdk createBuiltinOtel( credentials, metricsEndpoint, universeDomain); - PeriodicMetricReaderBuilder readerBuilder = - PeriodicMetricReader.builder(publicExporter).setExecutor(executor); - meterProvider.registerMetricReader(readerBuilder.build()); + + meterProvider.registerMetricReader(new BigtablePeriodicReader(exporter, executor)); return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtablePeriodicReader.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtablePeriodicReader.java new file mode 100644 index 0000000000..d29dbd8702 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/BigtablePeriodicReader.java @@ -0,0 +1,109 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.internal.csm.exporter; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; +import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.io.IOException; +import java.util.concurrent.ScheduledExecutorService; + +/** + * Wrapper around a {@link PeriodicMetricReader} that will notify the exporter when it's shutting + * down. This is necessary to filter out noisy error logs on shutdown. + */ +public class BigtablePeriodicReader implements MetricReader { + private final MetricReader delegate; + private final BigtableCloudMonitoringExporter exporter; + + public BigtablePeriodicReader( + BigtableCloudMonitoringExporter exporter, ScheduledExecutorService executor) { + delegate = PeriodicMetricReader.builder(exporter).setExecutor(executor).build(); + this.exporter = exporter; + } + + @Override + public void register(CollectionRegistration registration) { + delegate.register(registration); + } + + @Override + public Aggregation getDefaultAggregation(InstrumentType instrumentType) { + return delegate.getDefaultAggregation(instrumentType); + } + + @Override + public MemoryMode getMemoryMode() { + return delegate.getMemoryMode(); + } + + @Override + public CompletableResultCode forceFlush() { + return delegate.forceFlush(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public void close() throws IOException { + exporter.prepareForShutdown(); + delegate.close(); + } + + public static AggregationTemporalitySelector alwaysCumulative() { + return AggregationTemporalitySelector.alwaysCumulative(); + } + + public static AggregationTemporalitySelector deltaPreferred() { + return AggregationTemporalitySelector.deltaPreferred(); + } + + public static AggregationTemporalitySelector lowMemory() { + return AggregationTemporalitySelector.lowMemory(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return delegate.getAggregationTemporality(instrumentType); + } + + public static String asString(AggregationTemporalitySelector selector) { + return AggregationTemporalitySelector.asString(selector); + } + + public static DefaultAggregationSelector getDefault() { + return DefaultAggregationSelector.getDefault(); + } + + @Override + public DefaultAggregationSelector with(InstrumentType instrumentType, Aggregation aggregation) { + return delegate.with(instrumentType, aggregation); + } + + public static String asString(DefaultAggregationSelector selector) { + return DefaultAggregationSelector.asString(selector); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java index 68b4536a32..c5ec4b3332 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/exporter/Converter.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.google.cloud.bigtable.data.v2.internal.csm.exporter; import static com.google.api.MetricDescriptor.MetricKind.CUMULATIVE; From 0697ab908851ca777f2cdeb87927dd38165ffaf9 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Mon, 2 Mar 2026 11:41:03 -0500 Subject: [PATCH 32/33] chore: more pacemaker into tracers (#2822) * chore: more pacemaker into tracers Change-Id: I509051afa46164524fd3292564eee2d61a91c06a * oops, upate pacemaker visibility Change-Id: I326e1a485c559f3e9d108d373deac5ea73913f62 --- .../data/v2/internal/csm/MetricRegistry.java | 2 +- .../data/v2/internal/csm/MetricsImpl.java | 9 ++------- .../internal/csm/{ => tracers}/Pacemaker.java | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 11 deletions(-) rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/{ => tracers}/Pacemaker.java (71%) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java index b4caed95ee..9c0a70d30c 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java @@ -192,7 +192,7 @@ public class RecorderRegistry { public final TableDebugTagCount.Recorder debugTagCount; - final PacemakerDelay.Recorder pacemakerDelay; + public final PacemakerDelay.Recorder pacemakerDelay; private RecorderRegistry(Meter meter) { operationLatency = operationLatencyMetric.newRecorder(meter); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java index 38b383d067..f0efac7e96 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java @@ -31,6 +31,7 @@ import com.google.cloud.bigtable.data.v2.internal.csm.tracers.BuiltinMetricsTracerFactory; import com.google.cloud.bigtable.data.v2.internal.csm.tracers.ChannelPoolMetricsTracer; import com.google.cloud.bigtable.data.v2.internal.csm.tracers.CompositeTracerFactory; +import com.google.cloud.bigtable.data.v2.internal.csm.tracers.Pacemaker; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -50,7 +51,6 @@ import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; public class MetricsImpl implements Metrics, Closeable { @@ -134,12 +134,7 @@ public void start() { tasks.add(channelPoolMetricsTracer.start(executor)); } if (pacemaker != null) { - tasks.add( - executor.scheduleAtFixedRate( - pacemaker, - Pacemaker.PACEMAKER_INTERVAL.toMillis(), - Pacemaker.PACEMAKER_INTERVAL.toMillis(), - TimeUnit.MILLISECONDS)); + tasks.add(pacemaker.start(executor)); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/Pacemaker.java similarity index 71% rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/Pacemaker.java index cb2a0c9f19..8a3771406a 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/Pacemaker.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/tracers/Pacemaker.java @@ -13,14 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.cloud.bigtable.data.v2.internal.csm; +package com.google.cloud.bigtable.data.v2.internal.csm.tracers; import com.google.cloud.bigtable.data.v2.internal.csm.MetricRegistry.RecorderRegistry; import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo; import java.time.Duration; import java.time.Instant; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; -class Pacemaker implements Runnable { +public class Pacemaker implements Runnable { static final Duration PACEMAKER_INTERVAL = Duration.ofMillis(100); @@ -30,13 +33,21 @@ class Pacemaker implements Runnable { private Instant prev; - Pacemaker(RecorderRegistry registry, ClientInfo clientInfo, String name) { + public Pacemaker(RecorderRegistry registry, ClientInfo clientInfo, String name) { this.prev = Instant.now(); this.registry = registry; this.clientInfo = clientInfo; this.executorName = name; } + public ScheduledFuture start(ScheduledExecutorService executor) { + return executor.scheduleAtFixedRate( + this, + Pacemaker.PACEMAKER_INTERVAL.toMillis(), + Pacemaker.PACEMAKER_INTERVAL.toMillis(), + TimeUnit.MILLISECONDS); + } + @Override public void run() { Instant current = Instant.now(); From 8d75343b01f315d921917d19cd5e59062249ffe1 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:10:01 -0500 Subject: [PATCH 33/33] chore(main): release 2.74.0 (#2792) * chore(main): release 2.74.0 * chore: generate libraries at Mon Mar 2 16:42:17 UTC 2026 --------- Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: cloud-java-bot --- CHANGELOG.md | 21 +++++++++++++++++++ README.md | 6 +++--- google-cloud-bigtable-bom/pom.xml | 16 +++++++------- google-cloud-bigtable-deps-bom/pom.xml | 2 +- google-cloud-bigtable-emulator-core/pom.xml | 4 ++-- google-cloud-bigtable-emulator/pom.xml | 10 ++++----- google-cloud-bigtable/pom.xml | 10 ++++----- .../com/google/cloud/bigtable/Version.java | 2 +- grpc-google-cloud-bigtable-admin-v2/pom.xml | 8 +++---- grpc-google-cloud-bigtable-v2/pom.xml | 8 +++---- pom.xml | 12 +++++------ proto-google-cloud-bigtable-admin-v2/pom.xml | 8 +++---- proto-google-cloud-bigtable-v2/pom.xml | 8 +++---- samples/snapshot/pom.xml | 2 +- test-proxy/pom.xml | 4 ++-- versions.txt | 14 ++++++------- 16 files changed, 78 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0e32dff8..0f3db70c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [2.74.0](https://github.com/googleapis/java-bigtable/compare/v2.73.1...v2.74.0) (2026-03-02) + + +### Features + +* Add awaitOptimizeRestoredTable helper for Bigtable Admin ([#2781](https://github.com/googleapis/java-bigtable/issues/2781)) ([cf15d45](https://github.com/googleapis/java-bigtable/commit/cf15d45a8f4c0ee385d3e53a0bae153ee1064999)) +* Add TieredStorageConfig to table admin api ([f05a1a3](https://github.com/googleapis/java-bigtable/commit/f05a1a3b0bb730e62c349dc8a7a1a82b0cf00fa7)) +* **Bigtable:** Add support for creating instances with tags ([#2733](https://github.com/googleapis/java-bigtable/issues/2733)) ([bc46174](https://github.com/googleapis/java-bigtable/commit/bc461749a0aa702f65c26774dd4696d47ef88eae)) +* Expose generated GAPIC admin client and freeze legacy surface ([#2806](https://github.com/googleapis/java-bigtable/issues/2806)) ([c620710](https://github.com/googleapis/java-bigtable/commit/c62071092d67f8ccfebe3166ca826fb001c76e28)) + + +### Bug Fixes + +* **deps:** Update the Java code generator (gapic-generator-java) to 2.67.0 ([f05a1a3](https://github.com/googleapis/java-bigtable/commit/f05a1a3b0bb730e62c349dc8a7a1a82b0cf00fa7)) +* Ensure that per attempt metrics tracer is below the retries ([#2793](https://github.com/googleapis/java-bigtable/issues/2793)) ([1f39032](https://github.com/googleapis/java-bigtable/commit/1f390328b23855ee39e2c3dacf8a0eed8d962b08)) + + +### Dependencies + +* Update shared dependencies ([#2814](https://github.com/googleapis/java-bigtable/issues/2814)) ([dde68fe](https://github.com/googleapis/java-bigtable/commit/dde68fe0ee5c5a491a5ae5382babea57e901605c)) + ## [2.73.1](https://github.com/googleapis/java-bigtable/compare/v2.73.0...v2.73.1) (2026-02-17) diff --git a/README.md b/README.md index 0a064637ee..06e628ea43 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-bigtable' If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-bigtable:2.73.1' +implementation 'com.google.cloud:google-cloud-bigtable:2.74.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.73.1" +libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.74.0" ``` ## Authentication @@ -452,7 +452,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [javadocs]: https://cloud.google.com/java/docs/reference/google-cloud-bigtable/latest/history [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-bigtable.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.73.1 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.74.0 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml index a512f454d6..a9a033cbbf 100644 --- a/google-cloud-bigtable-bom/pom.xml +++ b/google-cloud-bigtable-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom com.google.cloud @@ -63,37 +63,37 @@ com.google.cloud google-cloud-bigtable - 2.73.2-SNAPSHOT + 2.74.0 com.google.cloud google-cloud-bigtable-emulator - 0.210.2-SNAPSHOT + 0.211.0 com.google.cloud google-cloud-bigtable-emulator-core - 0.210.2-SNAPSHOT + 0.211.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml index 56fa2b756f..d586fcdd90 100644 --- a/google-cloud-bigtable-deps-bom/pom.xml +++ b/google-cloud-bigtable-deps-bom/pom.xml @@ -13,7 +13,7 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom Google Cloud Bigtable Dependency BOM diff --git a/google-cloud-bigtable-emulator-core/pom.xml b/google-cloud-bigtable-emulator-core/pom.xml index 38841ddc1b..48c3bebdfd 100644 --- a/google-cloud-bigtable-emulator-core/pom.xml +++ b/google-cloud-bigtable-emulator-core/pom.xml @@ -7,12 +7,12 @@ google-cloud-bigtable-parent com.google.cloud - 2.73.2-SNAPSHOT + 2.74.0 Google Cloud Java - Bigtable Emulator Core google-cloud-bigtable-emulator-core - 0.210.2-SNAPSHOT + 0.211.0 A Java wrapper for the Cloud Bigtable emulator. diff --git a/google-cloud-bigtable-emulator/pom.xml b/google-cloud-bigtable-emulator/pom.xml index 449288a782..a0a1fbc2a6 100644 --- a/google-cloud-bigtable-emulator/pom.xml +++ b/google-cloud-bigtable-emulator/pom.xml @@ -5,7 +5,7 @@ 4.0.0 google-cloud-bigtable-emulator - 0.210.2-SNAPSHOT + 0.211.0 Google Cloud Java - Bigtable Emulator https://github.com/googleapis/java-bigtable @@ -14,7 +14,7 @@ com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 scm:git:git@github.com:googleapis/java-bigtable.git @@ -81,14 +81,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import @@ -99,7 +99,7 @@ com.google.cloud google-cloud-bigtable-emulator-core - 0.210.2-SNAPSHOT + 0.211.0 diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index a8e0dbeb52..cbc566f890 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-bigtable - 2.73.2-SNAPSHOT + 2.74.0 jar Google Cloud Bigtable https://github.com/googleapis/java-bigtable @@ -12,11 +12,11 @@ com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 - 2.73.2-SNAPSHOT + 2.74.0 google-cloud-bigtable @@ -54,14 +54,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java index f0d8f073b8..a07fcc36c1 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java @@ -20,6 +20,6 @@ @InternalApi("For internal use only") public final class Version { // {x-version-update-start:google-cloud-bigtable:current} - public static String VERSION = "2.73.2-SNAPSHOT"; + public static String VERSION = "2.74.0"; // {x-version-update-end} } diff --git a/grpc-google-cloud-bigtable-admin-v2/pom.xml b/grpc-google-cloud-bigtable-admin-v2/pom.xml index 44cf46c227..45cbd4bab9 100644 --- a/grpc-google-cloud-bigtable-admin-v2/pom.xml +++ b/grpc-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 grpc-google-cloud-bigtable-admin-v2 GRPC library for grpc-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import diff --git a/grpc-google-cloud-bigtable-v2/pom.xml b/grpc-google-cloud-bigtable-v2/pom.xml index fc3f6e1e3a..49e7cd5c9b 100644 --- a/grpc-google-cloud-bigtable-v2/pom.xml +++ b/grpc-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 grpc-google-cloud-bigtable-v2 GRPC library for grpc-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import diff --git a/pom.xml b/pom.xml index 91a827a4f6..e2c82a119e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ google-cloud-bigtable-parent pom - 2.73.2-SNAPSHOT + 2.74.0 Google Cloud Bigtable Parent https://github.com/googleapis/java-bigtable @@ -156,27 +156,27 @@ com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 com.google.cloud google-cloud-bigtable - 2.73.2-SNAPSHOT + 2.74.0 diff --git a/proto-google-cloud-bigtable-admin-v2/pom.xml b/proto-google-cloud-bigtable-admin-v2/pom.xml index e35a712daf..be650c0e6a 100644 --- a/proto-google-cloud-bigtable-admin-v2/pom.xml +++ b/proto-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.73.2-SNAPSHOT + 2.74.0 proto-google-cloud-bigtable-admin-v2 PROTO library for proto-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import diff --git a/proto-google-cloud-bigtable-v2/pom.xml b/proto-google-cloud-bigtable-v2/pom.xml index 7cabe4537d..664b040aba 100644 --- a/proto-google-cloud-bigtable-v2/pom.xml +++ b/proto-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.73.2-SNAPSHOT + 2.74.0 proto-google-cloud-bigtable-v2 PROTO library for proto-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.73.2-SNAPSHOT + 2.74.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.73.2-SNAPSHOT + 2.74.0 pom import diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index bff7331e66..437edfc514 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-bigtable - 2.73.2-SNAPSHOT + 2.74.0 diff --git a/test-proxy/pom.xml b/test-proxy/pom.xml index 0fe5793b86..9a2ab8b8e7 100644 --- a/test-proxy/pom.xml +++ b/test-proxy/pom.xml @@ -12,11 +12,11 @@ google-cloud-bigtable-parent com.google.cloud - 2.73.2-SNAPSHOT + 2.74.0 - 2.73.2-SNAPSHOT + 2.74.0 diff --git a/versions.txt b/versions.txt index 194138774b..53c64e0201 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -google-cloud-bigtable:2.73.1:2.73.2-SNAPSHOT -grpc-google-cloud-bigtable-admin-v2:2.73.1:2.73.2-SNAPSHOT -grpc-google-cloud-bigtable-v2:2.73.1:2.73.2-SNAPSHOT -proto-google-cloud-bigtable-admin-v2:2.73.1:2.73.2-SNAPSHOT -proto-google-cloud-bigtable-v2:2.73.1:2.73.2-SNAPSHOT -google-cloud-bigtable-emulator:0.210.1:0.210.2-SNAPSHOT -google-cloud-bigtable-emulator-core:0.210.1:0.210.2-SNAPSHOT +google-cloud-bigtable:2.74.0:2.74.0 +grpc-google-cloud-bigtable-admin-v2:2.74.0:2.74.0 +grpc-google-cloud-bigtable-v2:2.74.0:2.74.0 +proto-google-cloud-bigtable-admin-v2:2.74.0:2.74.0 +proto-google-cloud-bigtable-v2:2.74.0:2.74.0 +google-cloud-bigtable-emulator:0.211.0:0.211.0 +google-cloud-bigtable-emulator-core:0.211.0:0.211.0