Skip to content

Commit 55d7dec

Browse files
committed
feat: Add awaitOptimizeRestoredTable helper for Bigtable Admin
Adds `awaitOptimizeRestoredTable` to simplify waiting for the secondary "Optimize" operation after a table restore. This method automatically extracts the operation token from the restore metadata and resumes the optimization LRO. This addresses the Long Running Sub-operations CUJ. Tracking Bug: b/475820271
1 parent d36e897 commit 55d7dec

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@
8888
import java.util.Map;
8989
import java.util.concurrent.ExecutionException;
9090
import javax.annotation.Nonnull;
91+
import com.google.bigtable.admin.v2.OptimizeRestoredTableMetadata;
92+
import com.google.bigtable.admin.v2.RestoreTableMetadata;
93+
import com.google.common.base.Strings;
9194

9295
/**
9396
* Client for creating, configuring, and deleting Cloud Bigtable tables
@@ -1296,6 +1299,38 @@ public ApiFuture<RestoredTableResult> apply(com.google.bigtable.admin.v2.Table t
12961299
MoreExecutors.directExecutor());
12971300
}
12981301

1302+
/**
1303+
* Awaits the completion of the "Optimize Restored Table" operation.
1304+
*
1305+
* <p>This method blocks until the restore operation is complete, extracts the optimization token,
1306+
* and returns an ApiFuture for the optimization phase.
1307+
*
1308+
* @param restoreFuture The future returned by restoreTableAsync().
1309+
* @return An ApiFuture that tracks the optimization progress.
1310+
*/
1311+
public ApiFuture<Empty> awaitOptimizeRestoredTable(
1312+
ApiFuture<RestoredTableResult> restoreFuture) {
1313+
// 1. Block and wait for the restore operation to complete
1314+
RestoredTableResult result;
1315+
try {
1316+
result = restoreFuture.get();
1317+
} catch (Exception e) {
1318+
throw new RuntimeException("Restore operation failed", e);
1319+
}
1320+
1321+
// 2. Extract the operation token from the result
1322+
// (RestoredTableResult already wraps the OptimizeRestoredTableOperationToken)
1323+
OptimizeRestoredTableOperationToken token = result.getOptimizeRestoredTableOperationToken();
1324+
1325+
if (token == null || Strings.isNullOrEmpty(token.getOperationName())) {
1326+
// If there is no optimization operation, return immediate success.
1327+
return ApiFutures.immediateFuture(Empty.getDefaultInstance());
1328+
}
1329+
1330+
// 3. Return the future for the optimization operation
1331+
return stub.awaitOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName());
1332+
}
1333+
12991334
/**
13001335
* Awaits a restored table is fully optimized.
13011336
*

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import static com.google.common.truth.Truth.assertThat;
1919

20+
import com.google.bigtable.admin.v2.OptimizeRestoredTableMetadata;
21+
import com.google.cloud.bigtable.admin.v2.models.OptimizeRestoredTableOperationToken;
22+
import com.google.cloud.bigtable.admin.v2.models.RestoredTableResult;
2023
import com.google.api.core.ApiFuture;
2124
import com.google.api.core.ApiFutures;
2225
import com.google.api.gax.grpc.GrpcStatusCode;
@@ -285,6 +288,10 @@ public class BigtableTableAdminClientTests {
285288
com.google.iam.v1.TestIamPermissionsRequest, com.google.iam.v1.TestIamPermissionsResponse>
286289
mockTestIamPermissionsCallable;
287290

291+
@Mock
292+
private OperationCallable<Void, Empty, OptimizeRestoredTableMetadata>
293+
mockOptimizeRestoredTableCallable;
294+
288295
@Before
289296
public void setUp() {
290297
adminClient = BigtableTableAdminClient.create(PROJECT_ID, INSTANCE_ID, mockStub);
@@ -1682,6 +1689,59 @@ public void testWaitForConsistencyWithToken() {
16821689
assertThat(wasCalled.get()).isTrue();
16831690
}
16841691

1692+
@Test
1693+
public void testAwaitOptimizeRestoredTable() throws Exception {
1694+
// Setup
1695+
Mockito.when(mockStub.awaitOptimizeRestoredTableCallable())
1696+
.thenReturn(mockOptimizeRestoredTableCallable);
1697+
1698+
String optimizeToken = "my-optimization-token";
1699+
1700+
// 1. Mock the Token
1701+
OptimizeRestoredTableOperationToken mockToken =
1702+
Mockito.mock(OptimizeRestoredTableOperationToken.class);
1703+
Mockito.when(mockToken.getOperationName()).thenReturn(optimizeToken);
1704+
1705+
// 2. Mock the Result (wrapping the token)
1706+
RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class);
1707+
Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(mockToken);
1708+
1709+
// 3. Mock the Input Future (returning the result)
1710+
ApiFuture<RestoredTableResult> mockRestoreFuture = Mockito.mock(ApiFuture.class);
1711+
Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult);
1712+
1713+
// 4. Mock the Stub's behavior (resuming the Optimize Op)
1714+
OperationFuture<Empty, OptimizeRestoredTableMetadata> mockOptimizeOp =
1715+
Mockito.mock(OperationFuture.class);
1716+
Mockito.when(mockOptimizeRestoredTableCallable.resumeFutureCall(optimizeToken))
1717+
.thenReturn(mockOptimizeOp);
1718+
1719+
// Execute
1720+
ApiFuture<Empty> result = adminClient.awaitOptimizeRestoredTable(mockRestoreFuture);
1721+
1722+
// Verify
1723+
assertThat(result).isEqualTo(mockOptimizeOp);
1724+
Mockito.verify(mockOptimizeRestoredTableCallable).resumeFutureCall(optimizeToken);
1725+
}
1726+
1727+
@Test
1728+
public void testAwaitOptimizeRestoredTable_NoOp() throws Exception {
1729+
// Setup: Result with NO optimization token (null or empty)
1730+
RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class);
1731+
Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(null);
1732+
1733+
// Mock the Input Future
1734+
ApiFuture<RestoredTableResult> mockRestoreFuture = Mockito.mock(ApiFuture.class);
1735+
Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult);
1736+
1737+
// Execute
1738+
ApiFuture<Empty> result = adminClient.awaitOptimizeRestoredTable(mockRestoreFuture);
1739+
1740+
// Verify: Returns immediate success (Empty) without calling the stub
1741+
assertThat(result.get()).isEqualTo(Empty.getDefaultInstance());
1742+
Mockito.verifyNoInteractions(mockStub);
1743+
}
1744+
16851745
private <ReqT, RespT, MetaT> void mockOperationResult(
16861746
OperationCallable<ReqT, RespT, MetaT> callable,
16871747
ReqT request,

0 commit comments

Comments
 (0)