1+ /*
2+ * Copyright 2026 Google LLC
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * https://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package com .google .cloud .bigtable .admin .v2 ;
17+
18+ import com .google .api .core .ApiFuture ;
19+ import com .google .api .core .ApiFutures ;
20+ import com .google .api .gax .rpc .ApiExceptions ;
21+ import com .google .cloud .bigtable .admin .v2 .models .ConsistencyRequest ;
22+ import com .google .cloud .bigtable .admin .v2 .models .OptimizeRestoredTableOperationToken ;
23+ import com .google .cloud .bigtable .admin .v2 .models .RestoredTableResult ;
24+ import com .google .cloud .bigtable .admin .v2 .stub .BigtableTableAdminStub ;
25+ import com .google .cloud .bigtable .admin .v2 .stub .EnhancedBigtableTableAdminStub ;
26+ import com .google .common .base .Strings ;
27+ import com .google .protobuf .Empty ;
28+ import java .io .IOException ;
29+ import java .util .concurrent .ExecutionException ;
30+ import javax .annotation .Nonnull ;
31+
32+ /**
33+ * Modern Cloud Bigtable Table Admin Client.
34+ *
35+ * <p>This client extends the auto-generated {@link BaseBigtableTableAdminClient} to provide manual
36+ * overrides and additional convenience methods for Critical User Journeys (CUJs) that the GAPIC
37+ * generator cannot handle natively (e.g., chained Long Running Operations, Consistency Polling).
38+ */
39+ public class BigtableTableAdminClientV2 extends BaseBigtableTableAdminClient {
40+
41+ protected BigtableTableAdminClientV2 (BaseBigtableTableAdminSettings settings ) throws IOException {
42+ super (settings );
43+ }
44+
45+ protected BigtableTableAdminClientV2 (BigtableTableAdminStub stub ) {
46+ super (stub );
47+ }
48+
49+ /** Constructs an instance of BigtableTableAdminClientV2 with the given settings. */
50+ public static final BigtableTableAdminClientV2 createClient (BaseBigtableTableAdminSettings settings )
51+ throws IOException {
52+ return new BigtableTableAdminClientV2 (settings );
53+ }
54+
55+ /** Constructs an instance of BigtableTableAdminClientV2 with the given stub. */
56+ public static final BigtableTableAdminClientV2 createClient (BigtableTableAdminStub stub ) {
57+ return new BigtableTableAdminClientV2 (stub );
58+ }
59+
60+ /**
61+ * Awaits the completion of the "Optimize Restored Table" operation.
62+ *
63+ * <p>This method blocks until the restore operation is complete, extracts the optimization token,
64+ * and returns an ApiFuture for the optimization phase.
65+ *
66+ * @param restoreFuture The future returned by restoreTableAsync().
67+ * @return An ApiFuture that tracks the optimization progress.
68+ */
69+ public ApiFuture <Empty > awaitOptimizeRestoredTable (ApiFuture <RestoredTableResult > restoreFuture ) {
70+ // 1. Block and wait for the restore operation to complete
71+ RestoredTableResult result ;
72+ try {
73+ result = restoreFuture .get ();
74+ } catch (Exception e ) {
75+ throw new RuntimeException ("Restore operation failed" , e );
76+ }
77+
78+ // 2. Extract the operation token from the result
79+ // (RestoredTableResult already wraps the OptimizeRestoredTableOperationToken)
80+ OptimizeRestoredTableOperationToken token = result .getOptimizeRestoredTableOperationToken ();
81+
82+ if (token == null || Strings .isNullOrEmpty (token .getOperationName ())) {
83+ // If there is no optimization operation, return immediate success.
84+ return ApiFutures .immediateFuture (Empty .getDefaultInstance ());
85+ }
86+
87+ // 3. Return the future for the optimization operation
88+ return ((EnhancedBigtableTableAdminStub ) getStub ()).awaitOptimizeRestoredTableCallable ().resumeFutureCall (token .getOperationName ());
89+ }
90+
91+ /**
92+ * Awaits a restored table is fully optimized.
93+ *
94+ * <p>Sample code
95+ *
96+ * <pre>{@code
97+ * RestoredTableResult result =
98+ * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId));
99+ * client.awaitOptimizeRestoredTable(result.getOptimizeRestoredTableOperationToken());
100+ * }</pre>
101+ */
102+ public void awaitOptimizeRestoredTable (OptimizeRestoredTableOperationToken token )
103+ throws ExecutionException , InterruptedException {
104+ awaitOptimizeRestoredTableAsync (token ).get ();
105+ }
106+
107+ /**
108+ * Awaits a restored table is fully optimized asynchronously.
109+ *
110+ * <p>Sample code
111+ *
112+ * <pre>{@code
113+ * RestoredTableResult result =
114+ * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId));
115+ * ApiFuture<Void> future = client.awaitOptimizeRestoredTableAsync(
116+ * result.getOptimizeRestoredTableOperationToken());
117+ *
118+ * ApiFutures.addCallback(
119+ * future,
120+ * new ApiFutureCallback<Void>() {
121+ * public void onSuccess(Void unused) {
122+ * System.out.println("The optimization of the restored table is done.");
123+ * }
124+ *
125+ * public void onFailure(Throwable t) {
126+ * t.printStackTrace();
127+ * }
128+ * },
129+ * MoreExecutors.directExecutor()
130+ * );
131+ * }</pre>
132+ */
133+ public ApiFuture <Void > awaitOptimizeRestoredTableAsync (
134+ OptimizeRestoredTableOperationToken token ) {
135+ ApiFuture <Empty > emptyFuture =
136+ ((EnhancedBigtableTableAdminStub ) getStub ()).awaitOptimizeRestoredTableCallable ().resumeFutureCall (token .getOperationName ());
137+ return ApiFutures .transform (
138+ emptyFuture ,
139+ new com .google .api .core .ApiFunction <Empty , Void >() {
140+ @ Override
141+ public Void apply (Empty input ) {
142+ return null ;
143+ }
144+ },
145+ com .google .common .util .concurrent .MoreExecutors .directExecutor ());
146+ }
147+
148+ /**
149+ * Polls an existing consistency token until table replication is consistent across all clusters.
150+ * Useful for checking consistency of a token generated in a separate process. Blocks until
151+ * completion.
152+ *
153+ * @param tableId The table to check.
154+ * @param consistencyToken The token to poll.
155+ */
156+ public void waitForConsistency (String tableId , String consistencyToken ) {
157+ ApiExceptions .callAndTranslateApiException (waitForConsistencyAsync (tableId , consistencyToken ));
158+ }
159+
160+ /**
161+ * Asynchronously polls the consistency token. Returns a future that completes when table
162+ * replication is consistent across all clusters.
163+ *
164+ * @param tableId The table to check.
165+ * @param consistencyToken The token to poll.
166+ */
167+ public ApiFuture <Void > waitForConsistencyAsync (String tableId , String consistencyToken ) {
168+ return ((EnhancedBigtableTableAdminStub ) getStub ()).awaitConsistencyCallable ()
169+ .futureCall (ConsistencyRequest .forReplication (tableId , consistencyToken ));
170+ }
171+ }
0 commit comments