From cb4d60e8ce2079a270739ad91efb05cbb1ff74f8 Mon Sep 17 00:00:00 2001 From: Derek Yau <134542470+djyau@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:35:53 -0500 Subject: [PATCH 1/7] feat: add support for Row Affinity app profiles (#2341) * Create of methods, add unit tests Change-Id: I7cdd6c9ce85f9132c0fa3db3aa8c70abac26f12b * Add unit tests for BigtableInstanceAdminClientTests Change-Id: Id2cedefd90c52c249b2b15734eb1fbbcc5db2bf4 * Add tests for Create and Update App Profile Change-Id: I01ef72617d391bdb956ae9f469f1fd5af208a2e1 * Add integration test Change-Id: I9f18991cb61d99f30b811cea0988963d9e2577cd * Rename ofWithRowAffinity to withRowAffinity Change-Id: I9f88209e04cde4f628c878fbdb659c7344aa1c3c * Run mvn com.coveo:fmt-maven-plugin:format Change-Id: Ieda39f0d9825fae649755350b4f228fe0e8985e5 * Remove extra test for cluster IDs - one is enough Change-Id: I6d8e7bd644ce2cd8f098c8ee37b4eec1a0d642e2 * Add unit test for row affinity with set of strings Change-Id: I7b072214c8b38646058d0ec668bbb469bf16f23e * Run formatter Change-Id: Ieb04b2141c8843ff59cc4d756d736b4c00609ee2 --- .../bigtable/admin/v2/models/AppProfile.java | 33 +++- .../v2/BigtableInstanceAdminClientTests.java | 149 ++++++++++++++++++ .../v2/it/BigtableInstanceAdminClientIT.java | 37 +++++ .../admin/v2/models/AppProfileTest.java | 50 ++++++ .../models/CreateAppProfileRequestTest.java | 13 ++ .../models/UpdateAppProfileRequestTest.java | 30 ++++ 6 files changed, 311 insertions(+), 1 deletion(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java index bd7a534640..2507ef4dd3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java @@ -18,7 +18,6 @@ import com.google.api.core.InternalApi; import com.google.bigtable.admin.v2.AppProfile.DataBoostIsolationReadOnly; import com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny; -import com.google.bigtable.admin.v2.AppProfile.Priority; import com.google.bigtable.admin.v2.AppProfile.StandardIsolation; import com.google.bigtable.admin.v2.AppProfileName; import com.google.common.base.Objects; @@ -69,6 +68,10 @@ private AppProfile(@Nonnull com.google.bigtable.admin.v2.AppProfile proto) { @SuppressWarnings("WeakerAccess") public RoutingPolicy getPolicy() { if (proto.hasMultiClusterRoutingUseAny()) { + if (proto.getMultiClusterRoutingUseAny().hasRowAffinity()) { + return MultiClusterRoutingPolicy.withRowAffinity( + ImmutableSet.copyOf(proto.getMultiClusterRoutingUseAny().getClusterIdsList())); + } return MultiClusterRoutingPolicy.of( ImmutableSet.copyOf(proto.getMultiClusterRoutingUseAny().getClusterIdsList())); } else if (proto.hasSingleClusterRouting()) { @@ -267,6 +270,34 @@ public static MultiClusterRoutingPolicy of(Set clusterIds) { MultiClusterRoutingUseAny.newBuilder().addAllClusterIds(clusterIds).build()); } + /** Creates a new instance of {@link MultiClusterRoutingPolicy}. */ + public static MultiClusterRoutingPolicy withRowAffinity() { + return new MultiClusterRoutingPolicy( + MultiClusterRoutingUseAny.newBuilder() + .setRowAffinity(MultiClusterRoutingUseAny.RowAffinity.getDefaultInstance()) + .build()); + } + + /** + * Creates a new instance of {@link MultiClusterRoutingPolicy} with row affinity enabled and + * specified cluster ids to route to. + */ + public static MultiClusterRoutingPolicy withRowAffinity(String... clusterIds) { + return withRowAffinity(ImmutableSet.copyOf(clusterIds)); + } + + /** + * Creates a new instance of {@link MultiClusterRoutingPolicy} with specified cluster ids to + * route to. + */ + public static MultiClusterRoutingPolicy withRowAffinity(Set clusterIds) { + return new MultiClusterRoutingPolicy( + MultiClusterRoutingUseAny.newBuilder() + .addAllClusterIds(clusterIds) + .setRowAffinity(MultiClusterRoutingUseAny.RowAffinity.getDefaultInstance()) + .build()); + } + /* * Returns the set of clusters to route to. The order is ignored; clusters will be * tried in order of distance. If empty, all clusters are eligible. diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java index d8522db71a..388631d93a 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java @@ -65,7 +65,9 @@ import com.google.protobuf.FieldMask; import io.grpc.Status; import io.grpc.Status.Code; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; @@ -1034,6 +1036,153 @@ public void testCreateAppProfileAddPriority() { assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); } + @Test + public void testCreateAppProfileAddRowAffinity() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance()))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance())) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.withRowAffinity())); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testCreateAppProfileAddRowAffinityAddMultipleClusterIds() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2") + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance()))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2") + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance())) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy( + MultiClusterRoutingPolicy.withRowAffinity("cluster-id-1", "cluster-id-2"))); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testCreateAppProfileAddRowAffinityAddSetOfClusterIds() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2") + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance()))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2") + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance())) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + Set clusterIds = new HashSet(); + clusterIds.add("cluster-id-1"); + clusterIds.add("cluster-id-2"); + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.withRowAffinity(clusterIds))); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + @Test public void testGetAppProfile() { // Setup diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java index c95afa9eef..93e8f5b790 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java @@ -285,6 +285,43 @@ public void appProfileTestDataBoost() { } } + @Test + public void appProfileTestRowAffinity() { + String newInstanceId = prefixGenerator.newPrefix(); + String newClusterId = newInstanceId + "-c1"; + String newClusterId2 = newInstanceId + "-c2"; + + client.createInstance( + CreateInstanceRequest.of(newInstanceId) + .addCluster(newClusterId, testEnvRule.env().getPrimaryZone(), 1, StorageType.SSD) + .addCluster(newClusterId2, testEnvRule.env().getSecondaryZone(), 1, StorageType.SSD) + .setDisplayName("Row-Affinity-Instance-Test") + .addLabel("state", "readytodelete") + .setType(Type.PRODUCTION)); + + try { + assertThat(client.exists(newInstanceId)).isTrue(); + + String testAppProfile = prefixGenerator.newPrefix(); + + CreateAppProfileRequest request = + CreateAppProfileRequest.of(newInstanceId, testAppProfile) + .setRoutingPolicy( + AppProfile.MultiClusterRoutingPolicy.withRowAffinity(newClusterId, newClusterId2)) + .setDescription("row affinity app profile"); + + AppProfile newlyCreateAppProfile = client.createAppProfile(request); + AppProfile.RoutingPolicy routingPolicy = newlyCreateAppProfile.getPolicy(); + assertThat(routingPolicy) + .isEqualTo( + AppProfile.MultiClusterRoutingPolicy.withRowAffinity(newClusterId, newClusterId2)); + } finally { + if (client.exists(newInstanceId)) { + client.deleteInstance(newInstanceId); + } + } + } + @Test public void iamUpdateTest() { Policy policy = client.getIamPolicy(instanceId); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java index 8215e5f8fc..d6e6e410e8 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java @@ -291,4 +291,54 @@ public void testFromProtoWithDataBoostIsolation() { AppProfile.DataBoostIsolationReadOnlyPolicy.of( AppProfile.ComputeBillingOwner.UNSPECIFIED)); } + + @Test + public void testFromProtoWithRowAffinityNoClusterGroup() { + AppProfile profile = + AppProfile.fromProto( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance()) + .build()) + .setEtag("my-etag") + .build()); + + assertThat(profile.getInstanceId()).isEqualTo("my-instance"); + assertThat(profile.getId()).isEqualTo("my-profile"); + assertThat(profile.getDescription()).isEqualTo("my description"); + System.out.println(profile.getPolicy()); + System.out.println(AppProfile.MultiClusterRoutingPolicy.withRowAffinity()); + assertThat(profile.getPolicy()) + .isEqualTo(AppProfile.MultiClusterRoutingPolicy.withRowAffinity()); + } + + @Test + public void testFromProtoWithRowAffinityClusterGroup() { + AppProfile profile = + AppProfile.fromProto( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2")) + .setRowAffinity( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .RowAffinity.getDefaultInstance()) + .build()) + .setEtag("my-etag") + .build()); + + assertThat(profile.getInstanceId()).isEqualTo("my-instance"); + assertThat(profile.getId()).isEqualTo("my-profile"); + assertThat(profile.getDescription()).isEqualTo("my description"); + assertThat(profile.getPolicy()) + .isEqualTo( + AppProfile.MultiClusterRoutingPolicy.withRowAffinity("cluster-id-1", "cluster-id-2")); + } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java index 088dc2bcfe..7e9cc81541 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java @@ -101,4 +101,17 @@ public void testDataBoostIsolationReadOnly() { .setComputeBillingOwner(DataBoostIsolationReadOnly.ComputeBillingOwner.HOST_PAYS) .build()); } + + @Test + public void testRowAffinity() { + CreateAppProfileRequest wrapper = + CreateAppProfileRequest.of("my-instance", "my-profile") + .setRoutingPolicy(MultiClusterRoutingPolicy.withRowAffinity()); + + assertThat(wrapper.toProto("my-project").getAppProfile().getMultiClusterRoutingUseAny()) + .isEqualTo( + MultiClusterRoutingUseAny.newBuilder() + .setRowAffinity(MultiClusterRoutingUseAny.RowAffinity.getDefaultInstance()) + .build()); + } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java index 04cf3f0813..603943c533 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java @@ -146,4 +146,34 @@ public void testUpdateExistingDataBoostIsolationReadOnly() { .setUpdateMask(FieldMask.newBuilder().addPaths("data_boost_isolation_read_only")) .build()); } + + @Test + public void testUpdateRowAffinity() { + com.google.bigtable.admin.v2.AppProfile existingProto = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName("projects/my-project/instances/my-instance/appProfiles/my-profile") + .setEtag("my-etag") + .setDescription("description") + .setMultiClusterRoutingUseAny(MultiClusterRoutingUseAny.getDefaultInstance()) + .build(); + + AppProfile existingWrapper = AppProfile.fromProto(existingProto); + + UpdateAppProfileRequest updateWrapper = + UpdateAppProfileRequest.of(existingWrapper) + .setRoutingPolicy(AppProfile.MultiClusterRoutingPolicy.withRowAffinity()); + + assertThat(updateWrapper.toProto("my-project")) + .isEqualTo( + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder() + .setAppProfile( + existingProto + .toBuilder() + .setMultiClusterRoutingUseAny( + MultiClusterRoutingUseAny.newBuilder() + .setRowAffinity( + MultiClusterRoutingUseAny.RowAffinity.getDefaultInstance()))) + .setUpdateMask(FieldMask.newBuilder().addPaths("multi_cluster_routing_use_any")) + .build()); + } } From caa622e32ab40de333e968176667cdfffd0a830a Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 4 Dec 2024 15:56:22 -0500 Subject: [PATCH 2/7] test: fix clean up of protected & cdc tables (#2445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id15852b2b3bbb62746861d7ec02842353612ae77 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). --- README.md | 2 +- .../bigtable/test_helpers/env/TestEnvRule.java | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 837665a4aa..111bc7245f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-bigtable - 2.40.0 + 2.48.0 ``` diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/TestEnvRule.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/TestEnvRule.java index b6e4651c6b..c64d7995a7 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/TestEnvRule.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/TestEnvRule.java @@ -24,7 +24,9 @@ import com.google.cloud.bigtable.admin.v2.models.AppProfile; import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.Table; import com.google.cloud.bigtable.admin.v2.models.UpdateAuthorizedViewRequest; +import com.google.cloud.bigtable.admin.v2.models.UpdateTableRequest; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.util.ArrayList; @@ -178,8 +180,19 @@ private void cleanupStaleTables(String stalePrefix) { } private void prepTableForDelete(String tableId) { - // Unprotected views if (!(env() instanceof EmulatorEnv)) { + // unprotect table + Table table = env().getTableAdminClient().getTable(tableId); + if (table.isDeletionProtected() || table.getChangeStreamRetention() != null) { + env() + .getTableAdminClient() + .updateTable( + UpdateTableRequest.of(tableId) + .setDeletionProtection(false) + .disableChangeStreamRetention()); + } + + // Unprotected views for (String viewId : env().getTableAdminClient().listAuthorizedViews(tableId)) { try { env() From 9a6602c2224ad7c5be35f6faff38d0bb17aa53db Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Thu, 5 Dec 2024 19:33:56 -0500 Subject: [PATCH 3/7] chore: improve metrics setup for easier discovery (#2446) --- .../v2/stub/metrics/BuiltinMetricsView.java | 5 ++- .../CustomOpenTelemetryMetricsProvider.java | 33 +++++++++++++++++-- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 3 +- .../v2/it/StreamingMetricsMetadataIT.java | 3 +- .../data/v2/it/UnaryMetricsMetadataIT.java | 3 +- 5 files changed, 38 insertions(+), 9 deletions(-) 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 07679af8d2..68836a7e71 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 @@ -29,8 +29,11 @@ /** * 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. Please refer to {@link CustomOpenTelemetryMetricsProvider} for example usage. + * and custom sinks. + * + * @deprecated Use methods in {@link CustomOpenTelemetryMetricsProvider} instead. */ +@Deprecated public class BuiltinMetricsView { private BuiltinMetricsView() {} 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 d728d657ae..efcec28ffa 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 @@ -15,8 +15,11 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import com.google.auth.Credentials; import com.google.common.base.MoreObjects; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import java.io.IOException; /** * Set a custom OpenTelemetry instance. @@ -26,8 +29,8 @@ *
{@code
  * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
  *
- * // register Builtin metrics on your meter provider with default credentials
- * BuiltinMetricsView.registerBuiltinMetrics(sdkMeterProvider);
+ * // Set up SdkMeterProvider for client side metrics
+ * CustomOpenTelemetryMetricsProvider.setupSdkMeterProvider(sdkMeterProvider);
  *
  * // register other metrics reader and views
  * sdkMeterProvider.registerMetricReader(..);
@@ -63,6 +66,32 @@ public OpenTelemetry getOpenTelemetry() {
     return otel;
   }
 
+  /**
+   * Convenient method to set up SdkMeterProviderBuilder with the default credential and endpoint.
+   */
+  public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder) throws IOException {
+    setupSdkMeterProvider(builder, null, null);
+  }
+
+  /** Convenient method to set up SdkMeterProviderBuilder with a custom credential. */
+  public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, Credentials credentials)
+      throws IOException {
+    setupSdkMeterProvider(builder, credentials, null);
+  }
+
+  /** Convenient method to set up SdkMeterProviderBuilder with a custom endpoint. */
+  public static void setupSdkMeterProvider(SdkMeterProviderBuilder builder, String endpoint)
+      throws IOException {
+    setupSdkMeterProvider(builder, null, endpoint);
+  }
+
+  /** Convenient method to set up SdkMeterProviderBuilder with a custom credentials and endpoint. */
+  public static void setupSdkMeterProvider(
+      SdkMeterProviderBuilder builder, Credentials credentials, String endpoint)
+      throws IOException {
+    BuiltinMetricsView.registerBuiltinMetrics(credentials, builder, endpoint);
+  }
+
   @Override
   public String toString() {
     return MoreObjects.toStringHelper(this).add("openTelemetry", otel).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 d929627e12..5ec24830c7 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
@@ -33,7 +33,6 @@
 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.BuiltinMetricsView;
 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;
@@ -135,7 +134,7 @@ public void setup() throws IOException {
 
     SdkMeterProviderBuilder meterProvider =
         SdkMeterProvider.builder().registerMetricReader(metricReader);
-    BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider);
+    CustomOpenTelemetryMetricsProvider.setupSdkMeterProvider(meterProvider);
     OpenTelemetry openTelemetry =
         OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
 
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 11da6a6c15..2685819f08 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
@@ -29,7 +29,6 @@
 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.BuiltinMetricsView;
 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;
@@ -72,7 +71,7 @@ public void setup() throws IOException {
 
     SdkMeterProviderBuilder meterProvider =
         SdkMeterProvider.builder().registerMetricReader(metricReader);
-    BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider);
+    CustomOpenTelemetryMetricsProvider.setupSdkMeterProvider(meterProvider);
     OpenTelemetry openTelemetry =
         OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
 
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 a6e4f9e88b..0196614299 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
@@ -28,7 +28,6 @@
 import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
 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.BuiltinMetricsView;
 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;
@@ -71,7 +70,7 @@ public void setup() throws IOException {
 
     SdkMeterProviderBuilder meterProvider =
         SdkMeterProvider.builder().registerMetricReader(metricReader);
-    BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider);
+    CustomOpenTelemetryMetricsProvider.setupSdkMeterProvider(meterProvider);
     OpenTelemetry openTelemetry =
         OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
 

From f98dca123363e7c5b1faee9420f67655ca4237be Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
 <55107282+release-please[bot]@users.noreply.github.com>
Date: Tue, 10 Dec 2024 15:10:35 -0500
Subject: [PATCH 4/7] chore(main): release 2.50.0 (#2444)

* chore(main): release 2.50.0

* chore: generate libraries at Fri Dec  6 00:34:58 UTC 2024

---------

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: cloud-java-bot 
---
 CHANGELOG.md                                     |  7 +++++++
 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 +++++-----
 .../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 +++++++-------
 16 files changed, 64 insertions(+), 57 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 974ce8dd2d..d0e5f1d678 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## [2.50.0](https://github.com/googleapis/java-bigtable/compare/v2.49.0...v2.50.0) (2024-12-06)
+
+
+### Features
+
+* Add support for Row Affinity app profiles ([#2341](https://github.com/googleapis/java-bigtable/issues/2341)) ([cb4d60e](https://github.com/googleapis/java-bigtable/commit/cb4d60e8ce2079a270739ad91efb05cbb1ff74f8))
+
 ## [2.49.0](https://github.com/googleapis/java-bigtable/compare/v2.48.0...v2.49.0) (2024-12-03)
 
 
diff --git a/README.md b/README.md
index 111bc7245f..c1b80b33e8 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.49.0'
+implementation 'com.google.cloud:google-cloud-bigtable:2.50.0'
 ```
 
 If you are using SBT, add this to your dependencies:
 
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.49.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.50.0"
 ```
 
 ## Authentication
@@ -543,7 +543,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
 [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-bigtable/java11.html
 [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.49.0
+[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.50.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 c1834f4c26..dc263e647b 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.49.1-SNAPSHOT
+    2.50.0
     pom
     
         com.google.cloud
@@ -63,37 +63,37 @@
         
           com.google.cloud
           google-cloud-bigtable
-          2.49.1-SNAPSHOT
+          2.50.0
         
         
           com.google.cloud
           google-cloud-bigtable-emulator
-          0.186.1-SNAPSHOT
+          0.187.0
         
         
           com.google.cloud
           google-cloud-bigtable-emulator-core
-          0.186.1-SNAPSHOT
+          0.187.0
         
         
           com.google.api.grpc
           grpc-google-cloud-bigtable-admin-v2
-          2.49.1-SNAPSHOT
+          2.50.0
         
         
           com.google.api.grpc
           grpc-google-cloud-bigtable-v2
-          2.49.1-SNAPSHOT
+          2.50.0
         
         
           com.google.api.grpc
           proto-google-cloud-bigtable-admin-v2
-          2.49.1-SNAPSHOT
+          2.50.0
         
         
           com.google.api.grpc
           proto-google-cloud-bigtable-v2
-          2.49.1-SNAPSHOT
+          2.50.0
         
       
     
diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml
index f703a985ad..72d7540fb5 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.49.1-SNAPSHOT
+  2.50.0
 
   pom
   
diff --git a/google-cloud-bigtable-emulator-core/pom.xml b/google-cloud-bigtable-emulator-core/pom.xml
index da7d0f2c3c..d8208a28c9 100644
--- a/google-cloud-bigtable-emulator-core/pom.xml
+++ b/google-cloud-bigtable-emulator-core/pom.xml
@@ -7,11 +7,11 @@
   
     google-cloud-bigtable-parent
     com.google.cloud
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   google-cloud-bigtable-emulator-core
-  0.186.1-SNAPSHOT
+  0.187.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 641d5cb0a7..55a61f951c 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.186.1-SNAPSHOT
+  0.187.0
   Google Cloud Java - Bigtable Emulator
   https://github.com/googleapis/java-bigtable
   
@@ -14,7 +14,7 @@
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
   
     scm:git:git@github.com:googleapis/java-bigtable.git
@@ -81,14 +81,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
@@ -99,7 +99,7 @@
     
       com.google.cloud
       google-cloud-bigtable-emulator-core
-      0.186.1-SNAPSHOT
+      0.187.0
     
 
     
diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml
index 02dc0bb912..28ba7bf052 100644
--- a/google-cloud-bigtable/pom.xml
+++ b/google-cloud-bigtable/pom.xml
@@ -2,7 +2,7 @@
 
   4.0.0
   google-cloud-bigtable
-  2.49.1-SNAPSHOT
+  2.50.0
   jar
   Google Cloud Bigtable
   https://github.com/googleapis/java-bigtable
@@ -12,11 +12,11 @@
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
   
     
-    2.49.1-SNAPSHOT
+    2.50.0
 
     google-cloud-bigtable
 
@@ -52,14 +52,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.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 e76e67c842..d839ac63fa 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.49.1-SNAPSHOT";
+  public static String VERSION = "2.50.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 4b8cc2c638..4894f1d527 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.49.1-SNAPSHOT
+  2.50.0
   grpc-google-cloud-bigtable-admin-v2
   GRPC library for grpc-google-cloud-bigtable-admin-v2
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
@@ -18,14 +18,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
diff --git a/grpc-google-cloud-bigtable-v2/pom.xml b/grpc-google-cloud-bigtable-v2/pom.xml
index 494d67ff67..2d54278234 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.49.1-SNAPSHOT
+  2.50.0
   grpc-google-cloud-bigtable-v2
   GRPC library for grpc-google-cloud-bigtable-v2
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
@@ -18,14 +18,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
diff --git a/pom.xml b/pom.xml
index d4fcfe8dbb..882b2303a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
     google-cloud-bigtable-parent
     pom
-    2.49.1-SNAPSHOT
+    2.50.0
     Google Cloud Bigtable Parent
     https://github.com/googleapis/java-bigtable
     
@@ -153,27 +153,27 @@
             
                 com.google.api.grpc
                 proto-google-cloud-bigtable-v2
-                2.49.1-SNAPSHOT
+                2.50.0
             
             
                 com.google.api.grpc
                 proto-google-cloud-bigtable-admin-v2
-                2.49.1-SNAPSHOT
+                2.50.0
             
             
                 com.google.api.grpc
                 grpc-google-cloud-bigtable-v2
-                2.49.1-SNAPSHOT
+                2.50.0
             
             
                 com.google.api.grpc
                 grpc-google-cloud-bigtable-admin-v2
-                2.49.1-SNAPSHOT
+                2.50.0
             
             
                 com.google.cloud
                 google-cloud-bigtable
-                2.49.1-SNAPSHOT
+                2.50.0
             
             
             
diff --git a/proto-google-cloud-bigtable-admin-v2/pom.xml b/proto-google-cloud-bigtable-admin-v2/pom.xml
index 2da529ade4..defb843298 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.49.1-SNAPSHOT
+  2.50.0
   proto-google-cloud-bigtable-admin-v2
   PROTO library for proto-google-cloud-bigtable-admin-v2
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
@@ -18,14 +18,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
diff --git a/proto-google-cloud-bigtable-v2/pom.xml b/proto-google-cloud-bigtable-v2/pom.xml
index 51416f8938..efe2dd1bcb 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.49.1-SNAPSHOT
+  2.50.0
   proto-google-cloud-bigtable-v2
   PROTO library for proto-google-cloud-bigtable-v2
   
     com.google.cloud
     google-cloud-bigtable-parent
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
@@ -18,14 +18,14 @@
       
         com.google.cloud
         google-cloud-bigtable-deps-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
       
         com.google.cloud
         google-cloud-bigtable-bom
-        2.49.1-SNAPSHOT
+        2.50.0
         pom
         import
       
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 2becdec592..621f77096e 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
     
       com.google.cloud
       google-cloud-bigtable
-      2.49.1-SNAPSHOT
+      2.50.0
     
   
 
diff --git a/test-proxy/pom.xml b/test-proxy/pom.xml
index 8ac8cfb307..e2f23bba5f 100644
--- a/test-proxy/pom.xml
+++ b/test-proxy/pom.xml
@@ -12,11 +12,11 @@
   
     google-cloud-bigtable-parent
     com.google.cloud
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
-    2.49.1-SNAPSHOT
+    2.50.0
   
 
   
diff --git a/versions.txt b/versions.txt
index 36c026c00a..f21d580d34 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,10 +1,10 @@
 # Format:
 # module:released-version:current-version
 
-google-cloud-bigtable:2.49.0:2.49.1-SNAPSHOT
-grpc-google-cloud-bigtable-admin-v2:2.49.0:2.49.1-SNAPSHOT
-grpc-google-cloud-bigtable-v2:2.49.0:2.49.1-SNAPSHOT
-proto-google-cloud-bigtable-admin-v2:2.49.0:2.49.1-SNAPSHOT
-proto-google-cloud-bigtable-v2:2.49.0:2.49.1-SNAPSHOT
-google-cloud-bigtable-emulator:0.186.0:0.186.1-SNAPSHOT
-google-cloud-bigtable-emulator-core:0.186.0:0.186.1-SNAPSHOT
+google-cloud-bigtable:2.50.0:2.50.0
+grpc-google-cloud-bigtable-admin-v2:2.50.0:2.50.0
+grpc-google-cloud-bigtable-v2:2.50.0:2.50.0
+proto-google-cloud-bigtable-admin-v2:2.50.0:2.50.0
+proto-google-cloud-bigtable-v2:2.50.0:2.50.0
+google-cloud-bigtable-emulator:0.187.0:0.187.0
+google-cloud-bigtable-emulator-core:0.187.0:0.187.0

From bb96c3e395793ba324cf658bb4c985d4315cf781 Mon Sep 17 00:00:00 2001
From: Diego Marquez 
Date: Fri, 13 Dec 2024 14:14:22 -0500
Subject: [PATCH 5/7] feat: introduce `java.time` (#2415)

This PR introduces `java.time` alternatives to existing `org.threeten.bp.*` methods, as well as switching internal variables (if any) to `java.time`

The main constraint is to keep the changes backwards compatible, so for each existing threeten method "`method1(org.threeten.bp.Duration)`" we will add an alternative with a _Duration_ (or _Timestamp_ when applicable) suffix: "`method1Duration(java.time.Duration)`".

For most cases, the implementation will be held in the `java.time` method and the old threeten method will just delegate the call to it. However, for the case of abstract classes, the implementation will be kept in the threeten method to avoid breaking changes (i.e. users that already overloaded the method in their user code).

Note: https://cloud.google.com/bigtable/docs/reference/sql/data-types#timestamp_type implies that nanosecond precision will be ignored.
---
 .../clirr-ignored-differences.xml             | 33 +++++++++++++++++
 .../google/cloud/bigtable/common/Type.java    |  2 +-
 .../data/v2/BigtableDataSettings.java         |  9 ++---
 .../internal/AbstractProtoStructReader.java   |  2 +-
 .../data/v2/internal/ResultSetImpl.java       |  2 +-
 .../data/v2/models/ChangeStreamMutation.java  | 32 ++++++++++++-----
 .../v2/models/ChangeStreamRecordAdapter.java  |  2 +-
 .../DefaultChangeStreamRecordAdapter.java     |  9 +++--
 .../bigtable/data/v2/models/Heartbeat.java    | 17 ++++++---
 .../data/v2/models/ReadChangeStreamQuery.java | 29 ++++++++++++---
 .../bigtable/data/v2/models/sql/SqlType.java  |  2 +-
 .../data/v2/models/sql/Statement.java         |  2 +-
 .../data/v2/models/sql/StructReader.java      |  2 +-
 .../RateLimitingServerStreamingCallable.java  |  4 +--
 .../ChangeStreamStateMachine.java             |  7 ++--
 .../BigtableCloudMonitoringExporter.java      |  5 ++-
 .../v2/stub/metrics/BuiltinMetricsTracer.java | 18 ++++++++--
 .../data/v2/stub/metrics/CompositeTracer.java | 19 ++++++++--
 .../data/v2/stub/metrics/MetricsTracer.java   | 16 +++++++--
 .../metrics/TracedBatcherUnaryCallable.java   |  4 +--
 .../gaxx/retrying/AttemptCallable.java        |  5 ++-
 .../retrying/RetryInfoRetryAlgorithm.java     | 12 +++----
 .../v2/BigtableTableAdminClientTests.java     |  3 ++
 .../v2/BigtableTableAdminSettingsTest.java    |  5 +--
 .../AbstractProtoStructReaderTest.java        |  2 +-
 .../data/v2/internal/ProtoStructTest.java     |  2 +-
 .../data/v2/internal/ResultSetImplTest.java   |  2 +-
 .../bigtable/data/v2/it/BuiltinMetricsIT.java |  4 +--
 .../bigtable/data/v2/it/ExecuteQueryIT.java   |  2 +-
 .../v2/models/ChangeStreamMutationTest.java   | 35 ++++++++++++-------
 .../v2/models/ChangeStreamRecordTest.java     |  4 +--
 .../DefaultChangeStreamRecordAdapterTest.java | 35 ++++++++++---------
 .../v2/models/ReadChangeStreamQueryTest.java  | 17 +++++----
 .../data/v2/models/sql/StatementTest.java     |  2 +-
 .../v2/stub/RateLimitingCallableTest.java     | 18 +++++-----
 ...ChangeStreamRecordMergingCallableTest.java |  5 +++
 .../v2/stub/metrics/CompositeTracerTest.java  | 26 ++++++++++----
 .../MutateRowsAttemptCallableTest.java        | 13 +++----
 .../v2/stub/sql/ExecuteQueryCallableTest.java |  6 ++--
 .../test_helpers/env/PrefixGenerator.java     |  2 +-
 .../test_helpers/env/TestEnvRule.java         |  4 +--
 test-proxy/README.md                          |  2 +-
 .../bigtable/testproxy/CbtTestProxy.java      |  8 ++---
 .../testproxy/ResultSetSerializer.java        |  2 +-
 .../testproxy/StatementDeserializer.java      |  2 +-
 45 files changed, 289 insertions(+), 145 deletions(-)

diff --git a/google-cloud-bigtable/clirr-ignored-differences.xml b/google-cloud-bigtable/clirr-ignored-differences.xml
index a3dc564c44..4052e1b4a7 100644
--- a/google-cloud-bigtable/clirr-ignored-differences.xml
+++ b/google-cloud-bigtable/clirr-ignored-differences.xml
@@ -275,4 +275,37 @@
         com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider
         *
     
+    
+        
+        7006
+        com/google/cloud/bigtable/data/v2/internal/*
+        *getTimestamp(*)
+        java.time.Instant
+    
+    
+        
+        7006
+        com/google/cloud/bigtable/data/v2/models/sql/StructReader
+        *getTimestamp(*)
+        java.time.Instant
+    
+    
+        
+        7005
+        com/google/cloud/bigtable/data/v2/models/sql/Statement$Builder
+        *setTimestampParam(java.lang.String, org.threeten.bp.Instant)
+        *setTimestampParam(java.lang.String, java.time.Instant)
+    
+    
+        
+        7013
+        com/google/cloud/bigtable/data/v2/models/ChangeStreamMutation
+        *get*Time()
+    
+    
+        
+        7013
+        com/google/cloud/bigtable/data/v2/models/Heartbeat
+        *getEstimatedLowWatermarkTime()
+    
 
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Type.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Type.java
index df5c6dcd95..cd6a0a5407 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Type.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Type.java
@@ -23,8 +23,8 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.List;
-import org.threeten.bp.Instant;
 
 /**
  * Shared type implementations. Right now this is only used by SqlType but this will become a shared
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 25ff2ff30d..ad1e70ba6a 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
@@ -39,7 +39,6 @@
 import java.util.logging.Logger;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.threeten.bp.Duration;
 
 /**
  * Settings class to configure an instance of {@link BigtableDataClient}.
@@ -134,9 +133,11 @@ public static Builder newBuilderForEmulator(String hostname, int port) {
                 .setMaxInboundMessageSize(256 * 1024 * 1024)
                 .setChannelPoolSettings(ChannelPoolSettings.staticallySized(1))
                 .setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
-                .setKeepAliveTime(Duration.ofSeconds(61)) // sends ping in this interval
-                .setKeepAliveTimeout(
-                    Duration.ofSeconds(10)) // wait this long before considering the connection dead
+                .setKeepAliveTimeDuration(
+                    java.time.Duration.ofSeconds(61)) // sends ping in this interval
+                .setKeepAliveTimeoutDuration(
+                    java.time.Duration.ofSeconds(
+                        10)) // wait this long before considering the connection dead
                 .build());
 
     LOGGER.info("Connecting to the Bigtable emulator at " + hostname + ":" + port);
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReader.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReader.java
index dff4d4e0b2..2a74fccd22 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReader.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReader.java
@@ -25,12 +25,12 @@
 import com.google.common.base.Preconditions;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.Timestamp;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import org.threeten.bp.Instant;
 
 @InternalApi
 public abstract class AbstractProtoStructReader implements StructReader {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImpl.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImpl.java
index 1d2bd37f2d..53044c3b37 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImpl.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImpl.java
@@ -28,10 +28,10 @@
 import com.google.cloud.bigtable.data.v2.stub.sql.SqlServerStream;
 import com.google.common.base.Preconditions;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import org.threeten.bp.Instant;
 
 /**
  * The primary implementation of a ResultSet.
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutation.java
index 23fb47bd82..838a7ec62f 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutation.java
@@ -15,7 +15,10 @@
  */
 package com.google.cloud.bigtable.data.v2.models;
 
+import static com.google.api.gax.util.TimeConversionUtils.toThreetenInstant;
+
 import com.google.api.core.InternalApi;
+import com.google.api.core.ObsoleteApi;
 import com.google.auto.value.AutoValue;
 import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
 import com.google.cloud.bigtable.data.v2.stub.changestream.ChangeStreamRecordMerger;
@@ -23,7 +26,6 @@
 import com.google.protobuf.ByteString;
 import java.io.Serializable;
 import javax.annotation.Nonnull;
-import org.threeten.bp.Instant;
 
 /**
  * A ChangeStreamMutation represents a list of mods(represented by List<{@link Entry}>) targeted at
@@ -73,13 +75,13 @@ public enum MutationType {
   static Builder createUserMutation(
       @Nonnull ByteString rowKey,
       @Nonnull String sourceClusterId,
-      Instant commitTimestamp,
+      java.time.Instant commitTimestamp,
       int tieBreaker) {
     return builder()
         .setRowKey(rowKey)
         .setType(MutationType.USER)
         .setSourceClusterId(sourceClusterId)
-        .setCommitTimestamp(commitTimestamp)
+        .setCommitTime(commitTimestamp)
         .setTieBreaker(tieBreaker);
   }
 
@@ -89,12 +91,12 @@ static Builder createUserMutation(
    * mutation.
    */
   static Builder createGcMutation(
-      @Nonnull ByteString rowKey, Instant commitTimestamp, int tieBreaker) {
+      @Nonnull ByteString rowKey, java.time.Instant commitTimestamp, int tieBreaker) {
     return builder()
         .setRowKey(rowKey)
         .setType(MutationType.GARBAGE_COLLECTION)
         .setSourceClusterId("")
-        .setCommitTimestamp(commitTimestamp)
+        .setCommitTime(commitTimestamp)
         .setTieBreaker(tieBreaker);
   }
 
@@ -110,8 +112,14 @@ static Builder createGcMutation(
   @Nonnull
   public abstract String getSourceClusterId();
 
+  /** This method is obsolete. Use {@link #getCommitTime()} instead. */
+  @ObsoleteApi("Use getCommitTime() instead")
+  public org.threeten.bp.Instant getCommitTimestamp() {
+    return toThreetenInstant(getCommitTime());
+  }
+
   /** Get the commit timestamp of the current mutation. */
-  public abstract Instant getCommitTimestamp();
+  public abstract java.time.Instant getCommitTime();
 
   /**
    * Get the tie breaker of the current mutation. This is used to resolve conflicts when multiple
@@ -123,8 +131,14 @@ static Builder createGcMutation(
   @Nonnull
   public abstract String getToken();
 
+  /** This method is obsolete. Use {@link #getEstimatedLowWatermarkTime()} instead. */
+  @ObsoleteApi("Use getEstimatedLowWatermarkTime() instead")
+  public org.threeten.bp.Instant getEstimatedLowWatermark() {
+    return toThreetenInstant(getEstimatedLowWatermarkTime());
+  }
+
   /** Get the low watermark of the current mutation. */
-  public abstract Instant getEstimatedLowWatermark();
+  public abstract java.time.Instant getEstimatedLowWatermarkTime();
 
   /** Get the list of mods of the current mutation. */
   @Nonnull
@@ -145,7 +159,7 @@ abstract static class Builder {
 
     abstract Builder setSourceClusterId(@Nonnull String sourceClusterId);
 
-    abstract Builder setCommitTimestamp(Instant commitTimestamp);
+    abstract Builder setCommitTime(java.time.Instant commitTimestamp);
 
     abstract Builder setTieBreaker(int tieBreaker);
 
@@ -153,7 +167,7 @@ abstract static class Builder {
 
     abstract Builder setToken(@Nonnull String token);
 
-    abstract Builder setEstimatedLowWatermark(Instant estimatedLowWatermark);
+    abstract Builder setEstimatedLowWatermarkTime(java.time.Instant estimatedLowWatermark);
 
     Builder setCell(
         @Nonnull String familyName,
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordAdapter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordAdapter.java
index 40a71b1d3c..9b892b14ea 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordAdapter.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordAdapter.java
@@ -19,8 +19,8 @@
 import com.google.bigtable.v2.ReadChangeStreamResponse;
 import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import javax.annotation.Nonnull;
-import org.threeten.bp.Instant;
 
 /**
  * An extension point that allows end users to plug in a custom implementation of logical change
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapter.java
index d40ad7621c..54bf05cd7d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapter.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapter.java
@@ -20,9 +20,9 @@
 import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
 import com.google.common.base.Preconditions;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.threeten.bp.Instant;
 
 /**
  * Default implementation of a {@link ChangeStreamRecordAdapter} that uses {@link
@@ -112,8 +112,7 @@ public void startUserMutation(
 
     /** {@inheritDoc} */
     @Override
-    public void startGcMutation(
-        @Nonnull ByteString rowKey, Instant commitTimestamp, int tieBreaker) {
+    public void startGcMutation(ByteString rowKey, Instant commitTimestamp, int tieBreaker) {
       this.changeStreamMutationBuilder =
           ChangeStreamMutation.createGcMutation(rowKey, commitTimestamp, tieBreaker);
     }
@@ -176,9 +175,9 @@ public void finishCell() {
     /** {@inheritDoc} */
     @Override
     public ChangeStreamRecord finishChangeStreamMutation(
-        @Nonnull String token, Instant estimatedLowWatermark) {
+        String token, Instant estimatedLowWatermark) {
       this.changeStreamMutationBuilder.setToken(token);
-      this.changeStreamMutationBuilder.setEstimatedLowWatermark(estimatedLowWatermark);
+      this.changeStreamMutationBuilder.setEstimatedLowWatermarkTime(estimatedLowWatermark);
       return this.changeStreamMutationBuilder.build();
     }
 
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Heartbeat.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Heartbeat.java
index 8e3d865790..ae5507ae75 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Heartbeat.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Heartbeat.java
@@ -15,12 +15,14 @@
  */
 package com.google.cloud.bigtable.data.v2.models;
 
+import static com.google.api.gax.util.TimeConversionUtils.toThreetenInstant;
+
 import com.google.api.core.InternalApi;
+import com.google.api.core.ObsoleteApi;
 import com.google.auto.value.AutoValue;
 import com.google.bigtable.v2.ReadChangeStreamResponse;
 import java.io.Serializable;
 import javax.annotation.Nonnull;
-import org.threeten.bp.Instant;
 
 /** A simple wrapper for {@link ReadChangeStreamResponse.Heartbeat}. */
 @InternalApi("Intended for use by the BigtableIO in apache/beam only.")
@@ -29,7 +31,8 @@ public abstract class Heartbeat implements ChangeStreamRecord, Serializable {
   private static final long serialVersionUID = 7316215828353608504L;
 
   private static Heartbeat create(
-      ChangeStreamContinuationToken changeStreamContinuationToken, Instant estimatedLowWatermark) {
+      ChangeStreamContinuationToken changeStreamContinuationToken,
+      java.time.Instant estimatedLowWatermark) {
     return new AutoValue_Heartbeat(changeStreamContinuationToken, estimatedLowWatermark);
   }
 
@@ -37,7 +40,7 @@ private static Heartbeat create(
   static Heartbeat fromProto(@Nonnull ReadChangeStreamResponse.Heartbeat heartbeat) {
     return create(
         ChangeStreamContinuationToken.fromProto(heartbeat.getContinuationToken()),
-        Instant.ofEpochSecond(
+        java.time.Instant.ofEpochSecond(
             heartbeat.getEstimatedLowWatermark().getSeconds(),
             heartbeat.getEstimatedLowWatermark().getNanos()));
   }
@@ -45,6 +48,12 @@ static Heartbeat fromProto(@Nonnull ReadChangeStreamResponse.Heartbeat heartbeat
   @InternalApi("Intended for use by the BigtableIO in apache/beam only.")
   public abstract ChangeStreamContinuationToken getChangeStreamContinuationToken();
 
+  /** This method is obsolete. Use {@link #getEstimatedLowWatermarkTime()} instead. */
+  @ObsoleteApi("Use getEstimatedLowWatermarkTime() instead")
+  public org.threeten.bp.Instant getEstimatedLowWatermark() {
+    return toThreetenInstant(getEstimatedLowWatermarkTime());
+  }
+
   @InternalApi("Intended for use by the BigtableIO in apache/beam only.")
-  public abstract Instant getEstimatedLowWatermark();
+  public abstract java.time.Instant getEstimatedLowWatermarkTime();
 }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQuery.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQuery.java
index a6dfb7666d..2c9cf54354 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQuery.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQuery.java
@@ -15,7 +15,11 @@
  */
 package com.google.cloud.bigtable.data.v2.models;
 
+import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
+import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeInstant;
+
 import com.google.api.core.InternalApi;
+import com.google.api.core.ObsoleteApi;
 import com.google.bigtable.v2.ReadChangeStreamRequest;
 import com.google.bigtable.v2.RowRange;
 import com.google.bigtable.v2.StreamContinuationTokens;
@@ -36,7 +40,6 @@
 import java.util.List;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.threeten.bp.Instant;
 
 /** A simple wrapper to construct a query for the ReadChangeStream RPC. */
 @InternalApi("Intended for use by the BigtableIO in apache/beam only.")
@@ -143,8 +146,14 @@ public ReadChangeStreamQuery streamPartition(ByteStringRange range) {
     return streamPartition(rangeBuilder.build());
   }
 
+  /** This method is obsolete. Use {@link #startTime(java.time.Instant)} instead. */
+  @ObsoleteApi("Use startTime(java.time.Instant) instead")
+  public ReadChangeStreamQuery startTime(org.threeten.bp.Instant value) {
+    return startTime(toJavaTimeInstant(value));
+  }
+
   /** Sets the startTime to read the change stream. */
-  public ReadChangeStreamQuery startTime(Instant value) {
+  public ReadChangeStreamQuery startTime(java.time.Instant value) {
     Preconditions.checkState(
         !builder.hasContinuationTokens(),
         "startTime and continuationTokens can't be specified together");
@@ -156,8 +165,14 @@ public ReadChangeStreamQuery startTime(Instant value) {
     return this;
   }
 
+  /** This method is obsolete. Use {@link #endTime(java.time.Instant)} instead. */
+  @ObsoleteApi("Use endTime(java.time.Instant) instead")
+  public ReadChangeStreamQuery endTime(org.threeten.bp.Instant value) {
+    return endTime(toJavaTimeInstant(value));
+  }
+
   /** Sets the endTime to read the change stream. */
-  public ReadChangeStreamQuery endTime(Instant value) {
+  public ReadChangeStreamQuery endTime(java.time.Instant value) {
     builder.setEndTime(
         Timestamp.newBuilder()
             .setSeconds(value.getEpochSecond())
@@ -181,8 +196,14 @@ public ReadChangeStreamQuery continuationTokens(
     return this;
   }
 
-  /** Sets the heartbeat duration for the change stream. */
+  /** This method is obsolete. Use {@link #heartbeatDuration(java.time.Duration)} instead. */
+  @ObsoleteApi("Use heartbeatDuration(java.time.Duration) instead")
   public ReadChangeStreamQuery heartbeatDuration(org.threeten.bp.Duration duration) {
+    return heartbeatDuration(toJavaTimeDuration(duration));
+  }
+
+  /** Sets the heartbeat duration for the change stream. */
+  public ReadChangeStreamQuery heartbeatDuration(java.time.Duration duration) {
     builder.setHeartbeatDuration(
         Duration.newBuilder()
             .setSeconds(duration.getSeconds())
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/SqlType.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/SqlType.java
index 50146f292a..d4d3261dcf 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/SqlType.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/SqlType.java
@@ -23,8 +23,8 @@
 import com.google.cloud.bigtable.common.Type.StructWithSchema;
 import com.google.protobuf.ByteString;
 import java.io.Serializable;
+import java.time.Instant;
 import java.util.List;
-import org.threeten.bp.Instant;
 
 /**
  * Represents a data type in a SQL query.
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/Statement.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/Statement.java
index 58c16e2c5c..c1831219a6 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/Statement.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/Statement.java
@@ -27,11 +27,11 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.Timestamp;
+import java.time.Instant;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
-import org.threeten.bp.Instant;
 
 /**
  * A SQL statement that can be executed by calling {@link
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/StructReader.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/StructReader.java
index 8f450bbd92..f127b6b54c 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/StructReader.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/StructReader.java
@@ -18,9 +18,9 @@
 import com.google.api.core.BetaApi;
 import com.google.cloud.Date;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.List;
 import java.util.Map;
-import org.threeten.bp.Instant;
 
 /**
  * An interface for reading the columns of a {@code Struct} or {@code
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 62f8b5abf6..c3b0f94ec7 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
@@ -30,13 +30,13 @@
 import com.google.common.base.Preconditions;
 import com.google.common.base.Stopwatch;
 import com.google.common.util.concurrent.RateLimiter;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Logger;
 import javax.annotation.Nonnull;
-import org.threeten.bp.Duration;
-import org.threeten.bp.Instant;
 
 class RateLimitingServerStreamingCallable
     extends ServerStreamingCallable {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamStateMachine.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamStateMachine.java
index b41acc4ac3..27cb6f1478 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamStateMachine.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamStateMachine.java
@@ -22,7 +22,6 @@
 import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
 import com.google.cloud.bigtable.data.v2.models.Value;
 import com.google.common.base.Preconditions;
-import org.threeten.bp.Instant;
 
 /**
  * A state machine to produce change stream records from a stream of {@link
@@ -334,7 +333,7 @@ State handleDataChange(ReadChangeStreamResponse.DataChange dataChange) {
                 "AWAITING_NEW_STREAM_RECORD: GC mutation shouldn't have source cluster id.");
             builder.startGcMutation(
                 dataChange.getRowKey(),
-                Instant.ofEpochSecond(
+                java.time.Instant.ofEpochSecond(
                     dataChange.getCommitTimestamp().getSeconds(),
                     dataChange.getCommitTimestamp().getNanos()),
                 dataChange.getTiebreaker());
@@ -345,7 +344,7 @@ State handleDataChange(ReadChangeStreamResponse.DataChange dataChange) {
             builder.startUserMutation(
                 dataChange.getRowKey(),
                 dataChange.getSourceClusterId(),
-                Instant.ofEpochSecond(
+                java.time.Instant.ofEpochSecond(
                     dataChange.getCommitTimestamp().getSeconds(),
                     dataChange.getCommitTimestamp().getNanos()),
                 dataChange.getTiebreaker());
@@ -578,7 +577,7 @@ private State checkAndFinishMutationIfNeeded(ReadChangeStreamResponse.DataChange
       completeChangeStreamRecord =
           builder.finishChangeStreamMutation(
               dataChange.getToken(),
-              Instant.ofEpochSecond(
+              java.time.Instant.ofEpochSecond(
                   dataChange.getEstimatedLowWatermark().getSeconds(),
                   dataChange.getEstimatedLowWatermark().getNanos()));
       return AWAITING_STREAM_RECORD_CONSUME;
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 ff5bcd81c1..28dc981730 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
@@ -65,7 +65,6 @@
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
-import org.threeten.bp.Duration;
 
 /**
  * Bigtable Cloud Monitoring OpenTelemetry Exporter.
@@ -144,10 +143,10 @@ public static BigtableCloudMonitoringExporter create(
       settingsBuilder.setEndpoint(endpoint);
     }
 
-    org.threeten.bp.Duration timeout = Duration.ofMinutes(1);
+    java.time.Duration timeout = java.time.Duration.ofMinutes(1);
     // TODO: createServiceTimeSeries needs special handling if the request failed. Leaving
     // it as not retried for now.
-    settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetries(timeout);
+    settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout);
 
     // Detect the resource that the client application is running on. For example,
     // this could be a GCE instance or a GKE pod. Currently, we only support GCE instance and
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 4683ff9c8e..92aa26c50c 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,6 +16,7 @@
 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.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;
@@ -24,6 +25,7 @@
 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.core.ObsoleteApi;
 import com.google.api.gax.retrying.ServerStreamingAttemptException;
 import com.google.api.gax.tracing.SpanName;
 import com.google.cloud.bigtable.Version;
@@ -33,6 +35,7 @@
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.metrics.DoubleHistogram;
 import io.opentelemetry.api.metrics.LongCounter;
+import java.time.Duration;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -40,7 +43,6 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Logger;
 import javax.annotation.Nullable;
-import org.threeten.bp.Duration;
 
 /**
  * A {@link BigtableTracer} that records built-in metrics and publish under the
@@ -200,8 +202,18 @@ public void attemptCancelled() {
     recordAttemptCompletion(new CancellationException());
   }
 
+  /**
+   * This method is obsolete. Use {@link #attemptFailedDuration(Throwable, java.time.Duration)}
+   * instead.
+   */
+  @ObsoleteApi("Use attemptFailedDuration(Throwable, java.time.Duration) instead")
   @Override
-  public void attemptFailed(Throwable error, Duration delay) {
+  public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
+    attemptFailedDuration(error, toJavaTimeDuration(delay));
+  }
+
+  @Override
+  public void attemptFailedDuration(Throwable error, Duration delay) {
     recordAttemptCompletion(error);
   }
 
@@ -291,7 +303,7 @@ public void setLocations(String zone, String cluster) {
 
   @Override
   public void batchRequestThrottled(long throttledTimeNanos) {
-    totalClientBlockingTime.addAndGet(Duration.ofNanos(throttledTimeNanos).toMillis());
+    totalClientBlockingTime.addAndGet(java.time.Duration.ofNanos(throttledTimeNanos).toMillis());
   }
 
   @Override
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 7882c82d93..cb846f19b7 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
@@ -15,12 +15,14 @@
  */
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
+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.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.Nullable;
-import org.threeten.bp.Duration;
 
 /**
  * Combines multiple {@link ApiTracer}s and {@link BigtableTracer}s into a single {@link ApiTracer}.
@@ -124,9 +126,20 @@ public void attemptCancelled() {
     }
   }
 
-  public void attemptFailed(Throwable error, Duration delay) {
+  /**
+   * This method is obsolete. Use {@link #attemptFailedDuration(Throwable, java.time.Duration)}
+   * instead.
+   */
+  @ObsoleteApi("Use attemptFailedDuration(Throwable, java.time.Duration) instead")
+  @Override
+  public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
+    attemptFailedDuration(error, toJavaTimeDuration(delay));
+  }
+
+  @Override
+  public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
     for (ApiTracer child : children) {
-      child.attemptFailed(error, delay);
+      child.attemptFailedDuration(error, delay);
     }
   }
 
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 a2c5bdac1f..f1c6893447 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
@@ -15,6 +15,9 @@
  */
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
+import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration;
+
+import com.google.api.core.ObsoleteApi;
 import com.google.api.gax.retrying.ServerStreamingAttemptException;
 import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
 import com.google.api.gax.tracing.SpanName;
@@ -32,7 +35,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import javax.annotation.Nullable;
-import org.threeten.bp.Duration;
 
 class MetricsTracer extends BigtableTracer {
 
@@ -152,8 +154,18 @@ public void attemptCancelled() {
     recordAttemptCompletion(new CancellationException());
   }
 
+  /**
+   * This method is obsolete. Use {@link #attemptFailedDuration(Throwable, java.time.Duration)}
+   * instead.
+   */
+  @ObsoleteApi("Use attemptFailedDuration(Throwable, java.time.Duration) instead")
+  @Override
+  public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
+    attemptFailedDuration(error, toJavaTimeDuration(delay));
+  }
+
   @Override
-  public void attemptFailed(Throwable throwable, Duration duration) {
+  public void attemptFailedDuration(Throwable throwable, java.time.Duration duration) {
     recordAttemptCompletion(throwable);
   }
 
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/stub/metrics/TracedBatcherUnaryCallable.java
index ce73d75dc1..44ba688d55 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/stub/metrics/TracedBatcherUnaryCallable.java
@@ -21,7 +21,6 @@
 import com.google.api.gax.rpc.ApiCallContext;
 import com.google.api.gax.rpc.UnaryCallable;
 import com.google.api.gax.tracing.ApiTracer;
-import org.threeten.bp.Duration;
 
 /**
  * This callable will extract total throttled time from {@link ApiCallContext} and add it to {@link
@@ -44,7 +43,8 @@ public ApiFuture futureCall(RequestT request, ApiCallContext context)
       if (tracer instanceof BigtableTracer) {
         ((BigtableTracer) tracer)
             .batchRequestThrottled(
-                Duration.ofMillis(context.getOption(Batcher.THROTTLED_TIME_KEY)).toNanos());
+                java.time.Duration.ofMillis(context.getOption(Batcher.THROTTLED_TIME_KEY))
+                    .toNanos());
       }
     }
     return innerCallable.futureCall(request, context);
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/AttemptCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/AttemptCallable.java
index 3599e1e4df..6d5c75ea99 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/AttemptCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/AttemptCallable.java
@@ -24,7 +24,6 @@
 import com.google.api.gax.rpc.UnaryCallable;
 import com.google.common.base.Preconditions;
 import java.util.concurrent.Callable;
-import org.threeten.bp.Duration;
 
 // TODO: remove this once ApiResultRetryAlgorithm is added to gax.
 /**
@@ -59,9 +58,9 @@ public ResponseT call() {
 
     try {
       // Set the RPC timeout if the caller did not provide their own.
-      Duration rpcTimeout = externalFuture.getAttemptSettings().getRpcTimeout();
+      java.time.Duration rpcTimeout = externalFuture.getAttemptSettings().getRpcTimeoutDuration();
       if (!rpcTimeout.isZero() && callContext.getTimeout() == null) {
-        callContext = callContext.withTimeout(rpcTimeout);
+        callContext = callContext.withTimeoutDuration(rpcTimeout);
       }
 
       externalFuture.setAttemptFuture(new NonCancellableFuture());
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/RetryInfoRetryAlgorithm.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/RetryInfoRetryAlgorithm.java
index 085b48bbb5..98e549cee1 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/RetryInfoRetryAlgorithm.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/RetryInfoRetryAlgorithm.java
@@ -22,8 +22,7 @@
 import com.google.api.gax.rpc.ApiException;
 import com.google.protobuf.util.Durations;
 import com.google.rpc.RetryInfo;
-import org.checkerframework.checker.nullness.qual.Nullable;
-import org.threeten.bp.Duration;
+import javax.annotation.Nullable;
 
 // TODO move this algorithm to gax
 /**
@@ -36,11 +35,11 @@ public class RetryInfoRetryAlgorithm extends BasicResultRetryAlgorith
   @Override
   public TimedAttemptSettings createNextAttempt(
       Throwable prevThrowable, ResponseT prevResponse, TimedAttemptSettings prevSettings) {
-    Duration retryDelay = extractRetryDelay(prevThrowable);
+    java.time.Duration retryDelay = extractRetryDelay(prevThrowable);
     if (retryDelay != null) {
       return prevSettings
           .toBuilder()
-          .setRandomizedRetryDelay(retryDelay)
+          .setRandomizedRetryDelayDuration(retryDelay)
           .setAttemptCount(prevSettings.getAttemptCount() + 1)
           .setOverallAttemptCount(prevSettings.getAttemptCount() + 1)
           .build();
@@ -81,8 +80,7 @@ public boolean shouldRetry(
         && ((ApiException) previousThrowable).isRetryable();
   }
 
-  @Nullable
-  static Duration extractRetryDelay(@Nullable Throwable throwable) {
+  static java.time.Duration extractRetryDelay(@Nullable Throwable throwable) {
     if (throwable == null) {
       return null;
     }
@@ -97,6 +95,6 @@ static Duration extractRetryDelay(@Nullable Throwable throwable) {
       return null;
     }
     RetryInfo retryInfo = exception.getErrorDetails().getRetryInfo();
-    return Duration.ofMillis(Durations.toMillis(retryInfo.getRetryDelay()));
+    return java.time.Duration.ofMillis(Durations.toMillis(retryInfo.getRetryDelay()));
   }
 }
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 0ba472f783..15713b17f1 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
@@ -368,6 +368,8 @@ public void testUpdateTable() {
     assertThat(actualResult.getId()).isEqualTo(TABLE_ID);
     assertThat(actualResult.getChangeStreamRetention())
         .isEqualTo(org.threeten.bp.Duration.ofHours(24));
+    assertThat(actualResult.getChangeStreamRetention().toMillis())
+        .isEqualTo(actualResult.getChangeStreamRetention().toMillis());
   }
 
   @Test
@@ -1015,6 +1017,7 @@ public void testCopyBackup() {
     String srcTableId = "src-table";
     String srcClusterId = "src-cluster";
     String srcBackupId = "src-backup";
+
     Instant expireTime = Instant.now().plus(org.threeten.bp.Duration.ofDays(15));
     long sizeBytes = 123456789;
 
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettingsTest.java
index 506f73700b..735083bfee 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettingsTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettingsTest.java
@@ -31,7 +31,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mockito;
-import org.threeten.bp.Duration;
 
 @RunWith(JUnit4.class)
 public class BigtableTableAdminSettingsTest {
@@ -165,7 +164,9 @@ public void testToString() throws IOException {
     stubSettings
         .getBackupSettings()
         .setRetrySettings(
-            RetrySettings.newBuilder().setTotalTimeout(Duration.ofMinutes(812)).build());
+            RetrySettings.newBuilder()
+                .setTotalTimeout(org.threeten.bp.Duration.ofMinutes(812))
+                .build());
 
     BigtableTableAdminSettings settings = builder.build();
     checkToString(settings);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReaderTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReaderTest.java
index 95955bab94..8770880983 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReaderTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReaderTest.java
@@ -57,6 +57,7 @@
 import com.google.cloud.bigtable.data.v2.models.sql.Struct;
 import com.google.cloud.bigtable.data.v2.stub.sql.SqlProtoFactory;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -71,7 +72,6 @@
 import org.junit.runners.JUnit4;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
-import org.threeten.bp.Instant;
 
 @RunWith(Enclosed.class)
 public class AbstractProtoStructReaderTest {
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ProtoStructTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ProtoStructTest.java
index 6fa71e7fff..66808bb98a 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ProtoStructTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ProtoStructTest.java
@@ -47,13 +47,13 @@
 import com.google.cloud.Date;
 import com.google.cloud.bigtable.data.v2.models.sql.SqlType;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ProtoStructTest {
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImplTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImplTest.java
index a5b823b205..a8c5776a87 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImplTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/ResultSetImplTest.java
@@ -54,6 +54,7 @@
 import com.google.cloud.bigtable.data.v2.stub.sql.SqlServerStreamImpl;
 import com.google.cloud.bigtable.gaxx.testing.FakeStreamingApi.ServerStreamingStashCallable;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -62,7 +63,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ResultSetImplTest {
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 5ec24830c7..d01ecd0575 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
@@ -59,6 +59,8 @@
 import io.opentelemetry.sdk.metrics.data.MetricData;
 import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
 import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -75,8 +77,6 @@
 import org.junit.rules.Timeout;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Duration;
-import org.threeten.bp.Instant;
 
 @Ignore("Temporarily disable flaky test")
 @RunWith(JUnit4.class)
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ExecuteQueryIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ExecuteQueryIT.java
index 620d290338..34d0952401 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ExecuteQueryIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ExecuteQueryIT.java
@@ -31,6 +31,7 @@
 import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
 import com.google.protobuf.ByteString;
 import java.io.IOException;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -41,7 +42,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ExecuteQueryIT {
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutationTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutationTest.java
index 61c028cdb6..761bec3765 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutationTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamMutationTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.cloud.bigtable.data.v2.models;
 
+import static com.google.api.gax.util.TimeConversionUtils.toThreetenInstant;
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.bigtable.v2.MutateRowRequest;
@@ -29,11 +30,11 @@
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.time.Instant;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ChangeStreamMutationTest {
@@ -45,6 +46,10 @@ public class ChangeStreamMutationTest {
       RequestContext.create(PROJECT_ID, INSTANCE_ID, APP_PROFILE_ID);
   private static final Instant FAKE_COMMIT_TIMESTAMP = Instant.ofEpochSecond(0, 1000L);
   private static final Instant FAKE_LOW_WATERMARK = Instant.ofEpochSecond(0, 2000L);
+  private static final org.threeten.bp.Instant FAKE_COMMIT_TIMESTAMP_THREETEN =
+      toThreetenInstant(FAKE_COMMIT_TIMESTAMP);
+  private static final org.threeten.bp.Instant FAKE_LOW_WATERMARK_THREETEN =
+      toThreetenInstant(FAKE_LOW_WATERMARK);
 
   @Test
   public void userInitiatedMutationTest() throws IOException, ClassNotFoundException {
@@ -73,17 +78,20 @@ public void userInitiatedMutationTest() throws IOException, ClassNotFoundExcepti
                 Value.rawTimestamp(1000),
                 Value.rawValue(ByteString.copyFrom(Longs.toByteArray(1234L))))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Test the getters.
     assertThat(changeStreamMutation.getRowKey()).isEqualTo(ByteString.copyFromUtf8("key"));
     assertThat(changeStreamMutation.getType()).isEqualTo(ChangeStreamMutation.MutationType.USER);
     assertThat(changeStreamMutation.getSourceClusterId()).isEqualTo("fake-source-cluster-id");
-    assertThat(changeStreamMutation.getCommitTimestamp()).isEqualTo(FAKE_COMMIT_TIMESTAMP);
+    assertThat(changeStreamMutation.getCommitTime()).isEqualTo(FAKE_COMMIT_TIMESTAMP);
+    assertThat(changeStreamMutation.getCommitTimestamp()).isEqualTo(FAKE_COMMIT_TIMESTAMP_THREETEN);
     assertThat(changeStreamMutation.getTieBreaker()).isEqualTo(0);
     assertThat(changeStreamMutation.getToken()).isEqualTo("fake-token");
-    assertThat(changeStreamMutation.getEstimatedLowWatermark()).isEqualTo(FAKE_LOW_WATERMARK);
+    assertThat(changeStreamMutation.getEstimatedLowWatermarkTime()).isEqualTo(FAKE_LOW_WATERMARK);
+    assertThat(changeStreamMutation.getEstimatedLowWatermark())
+        .isEqualTo(FAKE_LOW_WATERMARK_THREETEN);
 
     // Test serialization.
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -112,7 +120,7 @@ public void gcMutationTest() throws IOException, ClassNotFoundException {
                 ByteString.copyFromUtf8("fake-qualifier"),
                 Range.TimestampRange.create(1000L, 2000L))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Test the getters.
@@ -120,10 +128,13 @@ public void gcMutationTest() throws IOException, ClassNotFoundException {
     assertThat(changeStreamMutation.getType())
         .isEqualTo(ChangeStreamMutation.MutationType.GARBAGE_COLLECTION);
     Assert.assertTrue(changeStreamMutation.getSourceClusterId().isEmpty());
-    assertThat(changeStreamMutation.getCommitTimestamp()).isEqualTo(FAKE_COMMIT_TIMESTAMP);
+    assertThat(changeStreamMutation.getCommitTime()).isEqualTo(FAKE_COMMIT_TIMESTAMP);
+    assertThat(changeStreamMutation.getCommitTimestamp()).isEqualTo(FAKE_COMMIT_TIMESTAMP_THREETEN);
     assertThat(changeStreamMutation.getTieBreaker()).isEqualTo(0);
     assertThat(changeStreamMutation.getToken()).isEqualTo("fake-token");
-    assertThat(changeStreamMutation.getEstimatedLowWatermark()).isEqualTo(FAKE_LOW_WATERMARK);
+    assertThat(changeStreamMutation.getEstimatedLowWatermarkTime()).isEqualTo(FAKE_LOW_WATERMARK);
+    assertThat(changeStreamMutation.getEstimatedLowWatermark())
+        .isEqualTo(FAKE_LOW_WATERMARK_THREETEN);
 
     // Test serialization.
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -161,7 +172,7 @@ public void toRowMutationTest() {
                 Value.rawTimestamp(1000),
                 Value.rawValue(ByteString.copyFrom(Longs.toByteArray(1234L))))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Convert it to a rowMutation and construct a MutateRowRequest.
@@ -204,7 +215,7 @@ public void toRowMutationWithoutTokenShouldFailTest() {
         ChangeStreamMutation.createUserMutation(
                 ByteString.copyFromUtf8("key"), "fake-source-cluster-id", FAKE_COMMIT_TIMESTAMP, 0)
             .deleteFamily("fake-family")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK);
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK);
     Assert.assertThrows(IllegalStateException.class, builder::build);
   }
 
@@ -244,7 +255,7 @@ public void toRowMutationEntryTest() {
                 Value.rawTimestamp(1000),
                 Value.rawValue(ByteString.copyFrom(Longs.toByteArray(1234L))))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Convert it to a rowMutationEntry and construct a MutateRowRequest.
@@ -284,7 +295,7 @@ public void toRowMutationEntryWithoutTokenShouldFailTest() {
         ChangeStreamMutation.createUserMutation(
                 ByteString.copyFromUtf8("key"), "fake-source-cluster-id", FAKE_COMMIT_TIMESTAMP, 0)
             .deleteFamily("fake-family")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK);
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK);
     Assert.assertThrows(IllegalStateException.class, builder::build);
   }
 
@@ -309,7 +320,7 @@ public void testWithLongValue() {
                 1000L,
                 ByteString.copyFrom(Longs.toByteArray(1L)))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     RowMutation rowMutation = changeStreamMutation.toRowMutation(TABLE_ID);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordTest.java
index 3f09d9b443..9dd66acc73 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ChangeStreamRecordTest.java
@@ -30,6 +30,7 @@
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.time.Instant;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -37,7 +38,6 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ChangeStreamRecordTest {
@@ -129,7 +129,7 @@ public void heartbeatTest() {
             .build();
     Heartbeat actualHeartbeat = Heartbeat.fromProto(heartbeatProto);
 
-    assertThat(actualHeartbeat.getEstimatedLowWatermark())
+    assertThat(actualHeartbeat.getEstimatedLowWatermarkTime())
         .isEqualTo(Instant.ofEpochSecond(lowWatermark.getSeconds(), lowWatermark.getNanos()));
     assertThat(actualHeartbeat.getChangeStreamContinuationToken().getPartition())
         .isEqualTo(ByteStringRange.create(rowRange.getStartKeyClosed(), rowRange.getEndKeyOpen()));
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapterTest.java
index 22270bc269..b6997ae9dd 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapterTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/DefaultChangeStreamRecordAdapterTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.cloud.bigtable.data.v2.models;
 
+import static com.google.api.gax.util.TimeConversionUtils.toThreetenInstant;
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.bigtable.v2.Mutation;
@@ -25,6 +26,7 @@
 import com.google.protobuf.ByteString;
 import com.google.protobuf.Timestamp;
 import com.google.rpc.Status;
+import java.time.Instant;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -32,7 +34,6 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class DefaultChangeStreamRecordAdapterTest {
@@ -41,6 +42,8 @@ public class DefaultChangeStreamRecordAdapterTest {
   private ChangeStreamRecordBuilder changeStreamRecordBuilder;
   private static final Instant FAKE_COMMIT_TIMESTAMP = Instant.ofEpochSecond(0L, 1000L);
   private static final Instant FAKE_LOW_WATERMARK = Instant.ofEpochSecond(0L, 2000L);
+  private static final org.threeten.bp.Instant FAKE_LOW_WATERMARK_THREETEN =
+      toThreetenInstant(FAKE_LOW_WATERMARK);
 
   @Rule public ExpectedException expect = ExpectedException.none();
 
@@ -59,7 +62,7 @@ public void isHeartbeatTest() {
         ChangeStreamMutation.createGcMutation(
                 ByteString.copyFromUtf8("key"), FAKE_COMMIT_TIMESTAMP, 0)
             .setToken("token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
     Assert.assertTrue(adapter.isHeartbeat(heartbeatRecord));
     Assert.assertFalse(adapter.isHeartbeat(closeStreamRecord));
@@ -73,8 +76,8 @@ public void getTokenFromHeartbeatTest() {
             ReadChangeStreamResponse.Heartbeat.newBuilder()
                 .setEstimatedLowWatermark(
                     Timestamp.newBuilder()
-                        .setSeconds(FAKE_LOW_WATERMARK.getEpochSecond())
-                        .setNanos(FAKE_LOW_WATERMARK.getNano()))
+                        .setSeconds(FAKE_LOW_WATERMARK_THREETEN.getEpochSecond())
+                        .setNanos(FAKE_LOW_WATERMARK_THREETEN.getNano()))
                 .setContinuationToken(
                     StreamContinuationToken.newBuilder().setToken("heartbeat-token").build())
                 .build());
@@ -99,7 +102,7 @@ public void isChangeStreamMutationTest() {
         ChangeStreamMutation.createGcMutation(
                 ByteString.copyFromUtf8("key"), FAKE_COMMIT_TIMESTAMP, 0)
             .setToken("token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
     Assert.assertFalse(adapter.isChangeStreamMutation(heartbeatRecord));
     Assert.assertFalse(adapter.isChangeStreamMutation(closeStreamRecord));
@@ -112,7 +115,7 @@ public void getTokenFromChangeStreamMutationTest() {
         ChangeStreamMutation.createGcMutation(
                 ByteString.copyFromUtf8("key"), FAKE_COMMIT_TIMESTAMP, 0)
             .setToken("change-stream-mutation-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
     Assert.assertEquals(
         adapter.getTokenFromChangeStreamMutation(changeStreamMutationRecord),
@@ -133,8 +136,8 @@ public void heartbeatTest() {
         ReadChangeStreamResponse.Heartbeat.newBuilder()
             .setEstimatedLowWatermark(
                 Timestamp.newBuilder()
-                    .setSeconds(FAKE_LOW_WATERMARK.getEpochSecond())
-                    .setNanos(FAKE_LOW_WATERMARK.getNano())
+                    .setSeconds(FAKE_LOW_WATERMARK_THREETEN.getEpochSecond())
+                    .setNanos(FAKE_LOW_WATERMARK_THREETEN.getNano())
                     .build())
             .setContinuationToken(
                 StreamContinuationToken.newBuilder().setToken("random-token").build())
@@ -186,7 +189,7 @@ public void singleDeleteFamilyTest() {
                 ByteString.copyFromUtf8("key"), "fake-source-cluster-id", FAKE_COMMIT_TIMESTAMP, 0)
             .deleteFamily("fake-family")
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
@@ -225,7 +228,7 @@ public void singleDeleteCellTest() {
                 ByteString.copyFromUtf8("fake-qualifier"),
                 Range.TimestampRange.create(1000L, 2000L))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
@@ -258,7 +261,7 @@ public void singleNonChunkedCellTest() {
                 100L,
                 ByteString.copyFromUtf8("fake-value"))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
@@ -290,7 +293,7 @@ public void singleChunkedCellTest() {
                 100L,
                 ByteString.copyFromUtf8("fake-value1-value2"))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
@@ -327,7 +330,7 @@ public void multipleChunkedCellsTest() {
     }
     expectedChangeStreamMutationBuilder
         .setToken("fake-token")
-        .setEstimatedLowWatermark(FAKE_LOW_WATERMARK);
+        .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK);
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
     changeStreamRecordBuilder.startUserMutation(
@@ -369,7 +372,7 @@ public void multipleDifferentModsTest() {
                 100L,
                 ByteString.copyFromUtf8("chunked-value"))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK);
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK);
 
     // Create the ChangeStreamMutation through the ChangeStreamRecordBuilder.
     changeStreamRecordBuilder.startUserMutation(
@@ -418,7 +421,7 @@ public void resetTest() {
                 ByteString.copyFromUtf8("key"), "fake-source-cluster-id", FAKE_COMMIT_TIMESTAMP, 0)
             .deleteFamily("fake-family")
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
     changeStreamRecordBuilder.startUserMutation(
         ByteString.copyFromUtf8("key"), "fake-source-cluster-id", FAKE_COMMIT_TIMESTAMP, 0);
@@ -438,7 +441,7 @@ public void resetTest() {
                 100L,
                 ByteString.copyFromUtf8("fake-value1-value2"))
             .setToken("fake-token")
-            .setEstimatedLowWatermark(FAKE_LOW_WATERMARK)
+            .setEstimatedLowWatermarkTime(FAKE_LOW_WATERMARK)
             .build();
 
     changeStreamRecordBuilder.startUserMutation(
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQueryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQueryTest.java
index 699f60a8d1..13e1bcb915 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQueryTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadChangeStreamQueryTest.java
@@ -34,6 +34,7 @@
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.time.Instant;
 import java.util.Collections;
 import org.junit.Before;
 import org.junit.Rule;
@@ -41,7 +42,6 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class ReadChangeStreamQueryTest {
@@ -152,8 +152,7 @@ public void endTimeTest() {
   @Test
   public void heartbeatDurationTest() {
     ReadChangeStreamQuery query =
-        ReadChangeStreamQuery.create(TABLE_ID)
-            .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5));
+        ReadChangeStreamQuery.create(TABLE_ID).heartbeatDuration(java.time.Duration.ofSeconds(5));
 
     Builder expectedProto =
         expectedProtoBuilder().setHeartbeatDuration(Duration.newBuilder().setSeconds(5).build());
@@ -232,7 +231,7 @@ public void serializationTest() throws IOException, ClassNotFoundException {
             .streamPartition("simple-begin", "simple-end")
             .continuationTokens(Collections.singletonList(token))
             .endTime(FAKE_END_TIME)
-            .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5));
+            .heartbeatDuration(java.time.Duration.ofSeconds(5));
 
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(bos);
@@ -302,7 +301,7 @@ public void testEquality() {
             .streamPartition("simple-begin", "simple-end")
             .startTime(FAKE_START_TIME)
             .endTime(FAKE_END_TIME)
-            .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5));
+            .heartbeatDuration(java.time.Duration.ofSeconds(5));
 
     // ReadChangeStreamQuery#toProto should not change the ReadChangeStreamQuery instance state
     request.toProto(requestContext);
@@ -312,7 +311,7 @@ public void testEquality() {
                 .streamPartition("simple-begin", "simple-end")
                 .startTime(FAKE_START_TIME)
                 .endTime(FAKE_END_TIME)
-                .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5)));
+                .heartbeatDuration(java.time.Duration.ofSeconds(5)));
 
     assertThat(ReadChangeStreamQuery.create(TABLE_ID).streamPartition("begin-1", "end-1"))
         .isNotEqualTo(ReadChangeStreamQuery.create(TABLE_ID).streamPartition("begin-2", "end-1"));
@@ -324,10 +323,10 @@ public void testEquality() {
             ReadChangeStreamQuery.create(TABLE_ID).endTime(Instant.ofEpochSecond(1L, 1001L)));
     assertThat(
             ReadChangeStreamQuery.create(TABLE_ID)
-                .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5)))
+                .heartbeatDuration(java.time.Duration.ofSeconds(5)))
         .isNotEqualTo(
             ReadChangeStreamQuery.create(TABLE_ID)
-                .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(6)));
+                .heartbeatDuration(java.time.Duration.ofSeconds(6)));
   }
 
   @Test
@@ -350,7 +349,7 @@ public void testClone() {
             .streamPartition("begin", "end")
             .continuationTokens(Collections.singletonList(token))
             .endTime(FAKE_END_TIME)
-            .heartbeatDuration(org.threeten.bp.Duration.ofSeconds(5));
+            .heartbeatDuration(java.time.Duration.ofSeconds(5));
     ReadChangeStreamRequest request =
         ReadChangeStreamRequest.newBuilder()
             .setTableName(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID))
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/StatementTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/StatementTest.java
index cb19a7fde9..6d4765230e 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/StatementTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/StatementTest.java
@@ -41,12 +41,12 @@
 import com.google.cloud.Date;
 import com.google.cloud.bigtable.data.v2.internal.RequestContext;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class StatementTest {
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingCallableTest.java
index f2fe77725d..652049b266 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingCallableTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingCallableTest.java
@@ -36,12 +36,12 @@
 import com.google.protobuf.Duration;
 import com.google.rpc.Code;
 import com.google.rpc.Status;
+import java.time.Instant;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mockito;
-import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class RateLimitingCallableTest {
@@ -72,7 +72,7 @@ public void testUpdateRate() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant earlier = Instant.now().minus(org.threeten.bp.Duration.ofHours(1));
+    Instant earlier = Instant.now().minus(java.time.Duration.ofHours(1));
 
     // Make sure rate will be updated.
     callableToTest.getNextRateUpdateTime().set(earlier);
@@ -105,7 +105,7 @@ public void testNoRateLimitInfoDoesNotUpdateRate() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant earlier = Instant.now().minus(org.threeten.bp.Duration.ofHours(1));
+    Instant earlier = Instant.now().minus(java.time.Duration.ofHours(1));
 
     // Make sure rate will be updated.
     callableToTest.getNextRateUpdateTime().set(earlier);
@@ -131,7 +131,7 @@ public void testInvalidRateLimitInfoDoesNotUpdateRate() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant earlier = Instant.now().minus(org.threeten.bp.Duration.ofHours(1));
+    Instant earlier = Instant.now().minus(java.time.Duration.ofHours(1));
 
     // make sure QPS will be updated
     callableToTest.getNextRateUpdateTime().set(earlier);
@@ -166,7 +166,7 @@ public void testMissingRateLimitInfoFactorDoesNotUpdateRate() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant earlier = Instant.now().minus(org.threeten.bp.Duration.ofHours(1));
+    Instant earlier = Instant.now().minus(java.time.Duration.ofHours(1));
 
     // Make sure rate can be updated.
     callableToTest.getNextRateUpdateTime().set(earlier);
@@ -199,7 +199,7 @@ public void testNoUpdateBeforeAllowedTime() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant later = Instant.now().plus(org.threeten.bp.Duration.ofHours(1));
+    Instant later = Instant.now().plus(java.time.Duration.ofHours(1));
     // Make sure rate will not be updated.
     callableToTest.getNextRateUpdateTime().set(later);
     double oldQps = callableToTest.getCurrentRate();
@@ -232,7 +232,7 @@ public void testDoesNotDisableBeforeAllowedTime() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setLimiterEnabled(true);
 
-    Instant later = Instant.now().plus(org.threeten.bp.Duration.ofHours(1));
+    Instant later = Instant.now().plus(java.time.Duration.ofHours(1));
     // Make sure limiter will not be disabled.
     callableToTest.getNextRateUpdateTime().set(later);
     double oldQps = callableToTest.getCurrentRate();
@@ -257,7 +257,7 @@ public void testEnableWithinPeriodDoesNotUpdateRate() throws Exception {
     callableToTest.call(request, responseObserver, context);
     callableToTest.setRate(1.5);
 
-    Instant later = Instant.now().plus(org.threeten.bp.Duration.ofHours(1));
+    Instant later = Instant.now().plus(java.time.Duration.ofHours(1));
     // Even though the rate update time is far in the future, enable is always allowed.
     callableToTest.getNextRateUpdateTime().set(later);
     double oldQps = callableToTest.getCurrentRate();
@@ -289,7 +289,7 @@ public void testEnableWithinPeriodDoesNotUpdateRate() throws Exception {
   public void testErrorInfoLowerQPS() throws Exception {
     callableToTest.call(request, responseObserver, context);
 
-    Instant earlier = Instant.now().minus(org.threeten.bp.Duration.ofHours(1));
+    Instant earlier = Instant.now().minus(java.time.Duration.ofHours(1));
 
     // make sure QPS will be updated
     callableToTest.getNextRateUpdateTime().set(earlier);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamRecordMergingCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamRecordMergingCallableTest.java
index f0939fb0cf..a5201770ee 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamRecordMergingCallableTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/changestream/ChangeStreamRecordMergingCallableTest.java
@@ -85,6 +85,11 @@ public void heartbeatTest() {
             Instant.ofEpochSecond(
                 heartbeatProto.getEstimatedLowWatermark().getSeconds(),
                 heartbeatProto.getEstimatedLowWatermark().getNanos()));
+    assertThat(heartbeat.getEstimatedLowWatermarkTime())
+        .isEqualTo(
+            java.time.Instant.ofEpochSecond(
+                heartbeatProto.getEstimatedLowWatermark().getSeconds(),
+                heartbeatProto.getEstimatedLowWatermark().getNanos()));
   }
 
   @Test
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 cb0916ad28..71a4728f9f 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
@@ -15,6 +15,7 @@
  */
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
+import static com.google.api.gax.util.TimeConversionUtils.toThreetenDuration;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -39,7 +40,6 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
-import org.threeten.bp.Duration;
 
 @RunWith(JUnit4.class)
 public class CompositeTracerTest {
@@ -148,12 +148,24 @@ public void testAttemptCancelled() {
   @Test
   public void testAttemptFailed() {
     RuntimeException error = new RuntimeException();
-    Duration delay = Duration.ofMillis(10);
-    compositeTracer.attemptFailed(error, delay);
-    verify(child1, times(1)).attemptFailed(error, delay);
-    verify(child2, times(1)).attemptFailed(error, delay);
-    verify(child3, times(1)).attemptFailed(error, delay);
-    verify(child4, times(1)).attemptFailed(error, delay);
+    java.time.Duration delay = java.time.Duration.ofMillis(10);
+    compositeTracer.attemptFailed(error, toThreetenDuration(delay));
+    // the implementation of CompositeTracer.attemptFailed delegates to attemptFailedDuration.
+    verify(child1, times(1)).attemptFailedDuration(error, delay);
+    verify(child2, times(1)).attemptFailedDuration(error, delay);
+    verify(child3, times(1)).attemptFailedDuration(error, delay);
+    verify(child4, times(1)).attemptFailedDuration(error, delay);
+  }
+
+  @Test
+  public void testAttemptFailedDuration() {
+    RuntimeException error = new RuntimeException();
+    java.time.Duration delay = java.time.Duration.ofMillis(10);
+    compositeTracer.attemptFailedDuration(error, delay);
+    verify(child1, times(1)).attemptFailedDuration(error, delay);
+    verify(child2, times(1)).attemptFailedDuration(error, delay);
+    verify(child3, times(1)).attemptFailedDuration(error, delay);
+    verify(child4, times(1)).attemptFailedDuration(error, delay);
   }
 
   @Test
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallableTest.java
index 6dd1ff9bd0..60ec5193e4 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallableTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallableTest.java
@@ -41,6 +41,7 @@
 import com.google.common.collect.Lists;
 import com.google.protobuf.ByteString;
 import com.google.rpc.Status;
+import java.time.Duration;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -49,7 +50,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mockito;
-import org.threeten.bp.Duration;
 
 @RunWith(JUnit4.class)
 public class MutateRowsAttemptCallableTest {
@@ -140,7 +140,7 @@ public void missingEntry() throws Exception {
   @Test
   public void testNoRpcTimeout() {
     parentFuture.timedAttemptSettings =
-        parentFuture.timedAttemptSettings.toBuilder().setRpcTimeout(Duration.ZERO).build();
+        parentFuture.timedAttemptSettings.toBuilder().setRpcTimeoutDuration(Duration.ZERO).build();
 
     MutateRowsRequest request =
         MutateRowsRequest.newBuilder().addEntries(Entry.getDefaultInstance()).build();
@@ -405,12 +405,13 @@ static class MockRetryingFuture extends AbstractApiFuture 0) {
-      settings.setInitialRpcTimeout(newTimeout).setMaxRpcTimeout(newTimeout);
+      settings.setInitialRpcTimeoutDuration(newTimeout).setMaxRpcTimeoutDuration(newTimeout);
     }
 
-    settings.setTotalTimeout(newTimeout);
+    settings.setTotalTimeoutDuration(newTimeout);
   }
 
   /** Helper method to get a client object by its id. */
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java
index c138c82a6b..7400986b6e 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/ResultSetSerializer.java
@@ -33,9 +33,9 @@
 import com.google.cloud.bigtable.data.v2.models.sql.SqlType;
 import com.google.cloud.bigtable.data.v2.models.sql.StructReader;
 import com.google.protobuf.ByteString;
+import java.time.Instant;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import org.threeten.bp.Instant;
 
 public class ResultSetSerializer {
   public static ExecuteQueryResult toExecuteQueryResult(ResultSet resultSet)
diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java
index ae3b50aa7f..4eb5f47e3a 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/StatementDeserializer.java
@@ -21,10 +21,10 @@
 import com.google.cloud.bigtable.data.v2.models.sql.SqlType;
 import com.google.cloud.bigtable.data.v2.models.sql.Statement;
 import com.google.protobuf.Timestamp;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import org.threeten.bp.Instant;
 
 public class StatementDeserializer {
 

From cec010aa64f2b190f8e742915be41baae2ad2083 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Mon, 16 Dec 2024 12:54:21 -0500
Subject: [PATCH 6/7] fix: move resource detection to the first export to avoid
 slow start (#2450)
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).
---
 .../BigtableCloudMonitoringExporter.java      | 31 +++++++------------
 .../stub/metrics/BigtableExporterUtils.java   | 15 ++++++++-
 .../BigtableCloudMonitoringExporterTest.java  | 14 +++++----
 3 files changed, 33 insertions(+), 27 deletions(-)

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 28dc981730..a829c3f719 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
@@ -40,6 +40,8 @@
 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.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -96,8 +98,9 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter {
 
   private final String taskId;
 
-  // The resource the client application is running on
-  private final MonitoredResource applicationResource;
+  // Application resource is initialized on the first export, which runs on a background thread
+  // to avoid slowness when starting the client.
+  private final Supplier applicationResource;
 
   private final AtomicBoolean isShutdown = new AtomicBoolean(false);
 
@@ -148,28 +151,15 @@ public static BigtableCloudMonitoringExporter create(
     // it as not retried for now.
     settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout);
 
-    // Detect the resource that the client application is running on. For example,
-    // this could be a GCE instance or a GKE pod. Currently, we only support GCE instance and
-    // GKE pod. This method will return null for everything else.
-    MonitoredResource applicationResource = null;
-    try {
-      applicationResource = BigtableExporterUtils.detectResource();
-    } catch (Exception e) {
-      logger.log(
-          Level.WARNING,
-          "Failed to detect resource, will skip exporting application level metrics ",
-          e);
-    }
-
     return new BigtableCloudMonitoringExporter(
         MetricServiceClient.create(settingsBuilder.build()),
-        applicationResource,
+        Suppliers.memoize(BigtableExporterUtils::detectResourceSafe),
         BigtableExporterUtils.getDefaultTaskValue());
   }
 
   @VisibleForTesting
   BigtableCloudMonitoringExporter(
-      MetricServiceClient client, @Nullable MonitoredResource applicationResource, String taskId) {
+      MetricServiceClient client, Supplier applicationResource, String taskId) {
     this.client = client;
     this.taskId = taskId;
     this.applicationResource = applicationResource;
@@ -257,7 +247,7 @@ public void onSuccess(List emptyList) {
   /** Export metrics associated with the resource the Application is running on. */
   private CompletableResultCode exportApplicationResourceMetrics(
       Collection collection) {
-    if (applicationResource == null) {
+    if (applicationResource.get() == null) {
       return CompletableResultCode.ofSuccess();
     }
 
@@ -276,7 +266,7 @@ private CompletableResultCode exportApplicationResourceMetrics(
     try {
       timeSeries =
           BigtableExporterUtils.convertToApplicationResourceTimeSeries(
-              metricData, taskId, applicationResource);
+              metricData, taskId, applicationResource.get());
     } catch (Throwable e) {
       logger.log(
           Level.WARNING,
@@ -291,7 +281,8 @@ private CompletableResultCode exportApplicationResourceMetrics(
     CompletableResultCode exportCode = new CompletableResultCode();
     try {
       ProjectName projectName =
-          ProjectName.of(applicationResource.getLabelsOrThrow(APPLICATION_RESOURCE_PROJECT_ID));
+          ProjectName.of(
+              applicationResource.get().getLabelsOrThrow(APPLICATION_RESOURCE_PROJECT_ID));
 
       gceOrGkeFuture = exportTimeSeries(projectName, timeSeries);
 
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 821c2295e0..95df887f0d 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
@@ -156,7 +156,20 @@ static List convertToApplicationResourceTimeSeries(
   }
 
   @Nullable
-  static MonitoredResource detectResource() {
+  static MonitoredResource detectResourceSafe() {
+    try {
+      return detectResource();
+    } 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() {
     GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE;
     DetectedPlatform detectedPlatform = detector.detectPlatform();
     MonitoredResource monitoredResource = null;
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 657db7d8ae..e471b19a20 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
@@ -37,6 +37,7 @@
 import com.google.api.gax.rpc.UnaryCallable;
 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;
@@ -95,7 +96,7 @@ public void setUp() {
 
     exporter =
         new BigtableCloudMonitoringExporter(
-            fakeMetricServiceClient, /* applicationResource= */ null, taskId);
+            fakeMetricServiceClient, /* applicationResource= */ Suppliers.ofInstance(null), taskId);
 
     attributes =
         Attributes.builder()
@@ -308,11 +309,12 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() {
     BigtableCloudMonitoringExporter exporter =
         new BigtableCloudMonitoringExporter(
             fakeMetricServiceClient,
-            MonitoredResource.newBuilder()
-                .setType("gce-instance")
-                .putLabels("some-gce-key", "some-gce-value")
-                .putLabels("project_id", gceProjectId)
-                .build(),
+            Suppliers.ofInstance(
+                MonitoredResource.newBuilder()
+                    .setType("gce-instance")
+                    .putLabels("some-gce-key", "some-gce-value")
+                    .putLabels("project_id", gceProjectId)
+                    .build()),
             taskId);
     ArgumentCaptor argumentCaptor =
         ArgumentCaptor.forClass(CreateTimeSeriesRequest.class);

From 825e717e9d8ae3853d7509d0849b58f2c47c9803 Mon Sep 17 00:00:00 2001
From: Mend Renovate 
Date: Tue, 17 Dec 2024 00:43:31 +0100
Subject: [PATCH 7/7] deps: update sdk-platform-java dependencies (#2448)

---
 .github/workflows/hermetic_library_generation.yaml | 2 +-
 .github/workflows/unmanaged_dependency_check.yaml  | 2 +-
 .kokoro/presubmit/graalvm-native-17.cfg            | 2 +-
 .kokoro/presubmit/graalvm-native.cfg               | 2 +-
 google-cloud-bigtable-bom/pom.xml                  | 2 +-
 google-cloud-bigtable-deps-bom/pom.xml             | 4 ++--
 pom.xml                                            | 2 +-
 7 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml
index 35aa3b151d..604b674bad 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.50.0
+    - uses: googleapis/sdk-platform-java/.github/scripts@v2.51.0
       if: env.SHOULD_RUN == 'true'
       with:
         base_ref: ${{ github.base_ref }}
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index dde9dfaa0b..74fa1adf9f 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.40.0
+      uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.41.0
       with:
         bom-path: google-cloud-bigtable-bom/pom.xml
diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg
index f8f242fab9..f0caedc501 100644
--- a/.kokoro/presubmit/graalvm-native-17.cfg
+++ b/.kokoro/presubmit/graalvm-native-17.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.40.0"
+  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.41.0"
 }
 
 env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg
index 29c96a6d30..203d5d7be0 100644
--- a/.kokoro/presubmit/graalvm-native.cfg
+++ b/.kokoro/presubmit/graalvm-native.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.40.0"
+  value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.41.0"
 }
 
 env_vars: {
diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml
index dc263e647b..9cbdf0626d 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.40.0
+        3.41.0
         
     
 
diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml
index 72d7540fb5..360bcebd51 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.40.0
+    3.41.0
     
   
 
@@ -67,7 +67,7 @@
       
         com.google.cloud
         gapic-libraries-bom
-        1.48.0
+        1.49.0
         pom
         import
       
diff --git a/pom.xml b/pom.xml
index 882b2303a4..bab3bd00f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
     
         com.google.cloud
         sdk-platform-java-config
-        3.40.0
+        3.41.0