Skip to content

Commit db81392

Browse files
Polish instance crudl
1 parent 21bb3fa commit db81392

File tree

10 files changed

+809
-86
lines changed

10 files changed

+809
-86
lines changed

google-cloud-clients/google-cloud-bigtable-admin/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
<type>test-jar</type>
6262
<scope>test</scope>
6363
</dependency>
64+
<dependency>
65+
<groupId>com.google.api</groupId>
66+
<artifactId>gax</artifactId>
67+
<classifier>testlib</classifier>
68+
<scope>test</scope>
69+
</dependency>
6470
<dependency>
6571
<groupId>junit</groupId>
6672
<artifactId>junit</artifactId>

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

+85-60
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
import com.google.bigtable.admin.v2.ProjectName;
2828
import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest;
2929
import com.google.cloud.bigtable.admin.v2.models.Instance;
30+
import com.google.cloud.bigtable.admin.v2.models.PartialListInstancesException;
3031
import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest;
3132
import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
3233
import com.google.common.base.Verify;
3334
import com.google.common.collect.ImmutableList;
35+
import com.google.common.util.concurrent.Futures;
3436
import com.google.common.util.concurrent.MoreExecutors;
37+
import com.google.common.util.concurrent.UncheckedExecutionException;
3538
import com.google.protobuf.Empty;
3639
import java.io.IOException;
3740
import java.util.List;
@@ -96,7 +99,7 @@ public static BigtableInstanceAdminClient create(@Nonnull BigtableInstanceAdminS
9699
return create(settings.getProjectName(), settings.getStubSettings().createStub());
97100
}
98101

99-
/** Constructs an instance of BigtableInstanceAdminClient with the given Projectname and stub. */
102+
/** Constructs an instance of BigtableInstanceAdminClient with the given ProjectName and stub. */
100103
public static BigtableInstanceAdminClient create(@Nonnull ProjectName projectName,
101104
@Nonnull BigtableInstanceAdminStub stub) {
102105
return new BigtableInstanceAdminClient(projectName, stub);
@@ -110,6 +113,7 @@ private BigtableInstanceAdminClient(
110113
}
111114

112115
/** Gets the ProjectName this client is associated with. */
116+
@SuppressWarnings("WeakerAccess")
113117
public ProjectName getProjectName() {
114118
return projectName;
115119
}
@@ -125,6 +129,7 @@ public void close() {
125129
*
126130
* @see CreateInstanceRequest for details.
127131
*/
132+
@SuppressWarnings("WeakerAccess")
128133
public Instance createInstance(CreateInstanceRequest request) {
129134
return awaitFuture(createInstanceAsync(request));
130135
}
@@ -134,10 +139,16 @@ public Instance createInstance(CreateInstanceRequest request) {
134139
*
135140
* @see CreateInstanceRequest for details.
136141
*/
142+
@SuppressWarnings("WeakerAccess")
137143
public ApiFuture<Instance> createInstanceAsync(CreateInstanceRequest request) {
138144
return ApiFutures.transform(
139145
stub.createInstanceOperationCallable().futureCall(request.toProto(projectName)),
140-
Instance.PROTO_TRANSFORMER,
146+
new ApiFunction<com.google.bigtable.admin.v2.Instance, Instance>() {
147+
@Override
148+
public Instance apply(com.google.bigtable.admin.v2.Instance proto) {
149+
return Instance.fromProto(proto);
150+
}
151+
},
141152
MoreExecutors.directExecutor());
142153
}
143154

@@ -146,6 +157,7 @@ public ApiFuture<Instance> createInstanceAsync(CreateInstanceRequest request) {
146157
*
147158
* @see UpdateInstanceRequest for details.
148159
*/
160+
@SuppressWarnings("WeakerAccess")
149161
public Instance updateInstance(UpdateInstanceRequest request) {
150162
return awaitFuture(updateInstanceAsync(request));
151163
}
@@ -155,21 +167,28 @@ public Instance updateInstance(UpdateInstanceRequest request) {
155167
*
156168
* @see UpdateInstanceRequest for details.
157169
*/
170+
@SuppressWarnings("WeakerAccess")
158171
public ApiFuture<Instance> updateInstanceAsync(UpdateInstanceRequest request) {
159172
return ApiFutures.transform(
160173
stub.partialUpdateInstanceOperationCallable().futureCall(request.toProto(projectName)),
161-
Instance.PROTO_TRANSFORMER,
174+
new ApiFunction<com.google.bigtable.admin.v2.Instance, Instance>() {
175+
@Override
176+
public Instance apply(com.google.bigtable.admin.v2.Instance proto) {
177+
return Instance.fromProto(proto);
178+
}
179+
},
162180
MoreExecutors.directExecutor());
163181
}
164182

165183
/** Get the instance representation. */
184+
@SuppressWarnings("WeakerAccess")
166185
public Instance getInstance(String id) {
167186
return awaitFuture(getInstanceAsync(id));
168187
}
169188

170189
/** Asynchronously gets the instance representation wrapped in a future. */
190+
@SuppressWarnings("WeakerAccess")
171191
public ApiFuture<Instance> getInstanceAsync(String instanceId) {
172-
173192
InstanceName name = InstanceName.of(projectName.getProject(), instanceId);
174193

175194
GetInstanceRequest request = GetInstanceRequest.newBuilder()
@@ -178,16 +197,23 @@ public ApiFuture<Instance> getInstanceAsync(String instanceId) {
178197

179198
return ApiFutures.transform(
180199
stub.getInstanceCallable().futureCall(request),
181-
Instance.PROTO_TRANSFORMER,
200+
new ApiFunction<com.google.bigtable.admin.v2.Instance, Instance>() {
201+
@Override
202+
public Instance apply(com.google.bigtable.admin.v2.Instance proto) {
203+
return Instance.fromProto(proto);
204+
}
205+
},
182206
MoreExecutors.directExecutor());
183207
}
184208

185209
/** Lists all of the instances in the current project. */
210+
@SuppressWarnings("WeakerAccess")
186211
public List<Instance> listInstances() {
187212
return awaitFuture(listInstancesAsync());
188213
}
189214

190215
/** Asynchronously lists all of the instances in the current project. */
216+
@SuppressWarnings("WeakerAccess")
191217
public ApiFuture<List<Instance>> listInstancesAsync() {
192218
ListInstancesRequest request = ListInstancesRequest.newBuilder()
193219
.setParent(projectName.toString())
@@ -196,41 +222,47 @@ public ApiFuture<List<Instance>> listInstancesAsync() {
196222
ApiFuture<ListInstancesResponse> responseFuture = stub.listInstancesCallable()
197223
.futureCall(request);
198224

199-
return ApiFutures.transform(responseFuture, new ApiFunction<ListInstancesResponse, List<Instance>>() {
200-
@Override
201-
public List<Instance> apply(ListInstancesResponse proto) {
202-
// NOTE: pagination is intentionally ignored. The server does not implement it.
203-
Verify.verify(proto.getNextPageToken().isEmpty(),
204-
"Server returned an unexpected paginated response");
205-
206-
ImmutableList.Builder<Instance> instances = ImmutableList.builder();
207-
208-
for (com.google.bigtable.admin.v2.Instance protoInstance : proto.getInstancesList()) {
209-
instances.add(Instance.PROTO_TRANSFORMER.apply(protoInstance));
210-
}
211-
212-
ImmutableList.Builder<String> failedZones = ImmutableList.builder();
213-
for (String locationStr : proto.getFailedLocationsList()) {
214-
failedZones.add(LocationName.parse(locationStr).getLocation());
215-
}
216-
217-
218-
if (!failedZones.build().isEmpty()) {
219-
throw new PartialListInstancesException(failedZones.build(), instances.build());
220-
}
221-
222-
return instances.build();
223-
}
224-
}, MoreExecutors.directExecutor());
225+
return ApiFutures
226+
.transform(responseFuture, new ApiFunction<ListInstancesResponse, List<Instance>>() {
227+
@Override
228+
public List<Instance> apply(ListInstancesResponse proto) {
229+
// NOTE: pagination is intentionally ignored. The server does not implement it.
230+
Verify.verify(proto.getNextPageToken().isEmpty(),
231+
"Server returned an unexpected paginated response");
232+
233+
ImmutableList.Builder<Instance> instances = ImmutableList.builder();
234+
235+
for (com.google.bigtable.admin.v2.Instance protoInstance : proto.getInstancesList()) {
236+
instances.add(Instance.fromProto(protoInstance));
237+
}
238+
239+
ImmutableList.Builder<String> failedZones = ImmutableList.builder();
240+
for (String locationStr : proto.getFailedLocationsList()) {
241+
LocationName fullLocation = LocationName.parse(locationStr);
242+
if (fullLocation == null) {
243+
continue;
244+
}
245+
failedZones.add(fullLocation.getLocation());
246+
}
247+
248+
if (!failedZones.build().isEmpty()) {
249+
throw new PartialListInstancesException(failedZones.build(), instances.build());
250+
}
251+
252+
return instances.build();
253+
}
254+
}, MoreExecutors.directExecutor());
225255
}
226256

227257
/** Deletes the specified instance. */
258+
@SuppressWarnings("WeakerAccess")
228259
public void deleteInstance(String instanceId) {
229260
awaitFuture(deleteInstanceAsync(instanceId));
230261
}
231262

232263
/** Asynchronously deletes the specified instance. */
233-
private ApiFuture<Void> deleteInstanceAsync(String instanceId) {
264+
@SuppressWarnings("WeakerAccess")
265+
public ApiFuture<Void> deleteInstanceAsync(String instanceId) {
234266
InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId);
235267

236268
DeleteInstanceRequest request = DeleteInstanceRequest.newBuilder()
@@ -248,39 +280,32 @@ public Void apply(Empty input) {
248280
);
249281
}
250282

251-
252-
private <T> T awaitFuture(ApiFuture<T> future) {
253-
try {
254-
return future.get();
255-
} catch(Throwable t) {
256-
// TODO(igorbernstein2): figure out a better wrapper exception.
257-
throw new RuntimeException(t);
258-
}
259-
}
260-
261283
/**
262-
* Exception thrown when some zones are unavailable and listInstances is unable to return a full
263-
* instance list. This exception can be inspected to get a partial list.
284+
* Awaits the result of a future, taking care to propagate errors while maintaining the call site
285+
* in a suppressed exception. This allows semantic errors to be caught across threads, while
286+
* preserving the call site in the error. The caller's stacktrace will be made available as a
287+
* suppressed exception.
264288
*/
265-
public static class PartialListInstancesException extends RuntimeException {
266-
private final List<String> failedZones;
267-
private final List<Instance> instances;
268-
269-
PartialListInstancesException(List<String> failedZones, List<Instance> instances) {
270-
super("Failed to list all instances, some zones where unavailable");
289+
// TODO(igorbernstein2): try to move this into gax
290+
private <T> T awaitFuture(ApiFuture<T> future) {
291+
RuntimeException error;
271292

272-
this.failedZones = failedZones;
273-
this.instances = instances;
293+
try {
294+
return Futures.getUnchecked(future);
295+
} catch (UncheckedExecutionException e) {
296+
if (e.getCause() instanceof RuntimeException) {
297+
error = (RuntimeException) e.getCause();
298+
} else {
299+
error = e;
300+
}
301+
} catch (RuntimeException e) {
302+
error = e;
274303
}
275304

276-
/** A list of zones, whose unavailability caused this error. */
277-
public List<String> getFailedZones() {
278-
return failedZones;
279-
}
305+
// Add the caller's stack as a suppressed exception
306+
error.addSuppressed(new RuntimeException("Encountered error while awaiting future"));
280307

281-
/** A partial list of instances that were found in the available zones. */
282-
public List<Instance> getInstances() {
283-
return instances;
284-
}
308+
throw error;
285309
}
310+
286311
}

google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
public final class CreateClusterRequest {
5050
private final com.google.bigtable.admin.v2.CreateClusterRequest.Builder proto = com.google.bigtable.admin.v2.CreateClusterRequest
5151
.newBuilder();
52+
// Partial ids will be set when the project is passed to toProto
5253
private final String instanceId;
5354
private String zone;
5455

@@ -70,12 +71,14 @@ private CreateClusterRequest(String instanceId, String clusterId) {
7071
* Sets the zone where the new cluster will be located. Must be different from the existing
7172
* cluster.
7273
*/
74+
@SuppressWarnings("WeakerAccess")
7375
public CreateClusterRequest setZone(String zone) {
7476
this.zone = zone;
7577
return this;
7678
}
7779

7880
/** Sets the type of storage used by this cluster to serve its parent instance's tables. */
81+
@SuppressWarnings("WeakerAccess")
7982
public CreateClusterRequest setServeNodes(int numNodes) {
8083
proto.getClusterBuilder().setServeNodes(numNodes);
8184
return this;
@@ -86,6 +89,7 @@ public CreateClusterRequest setServeNodes(int numNodes) {
8689
* Defaults to {@code SSD}.
8790
*/
8891
// TODO(igorbernstein2): try to avoid leaking protobuf generated enums
92+
@SuppressWarnings("WeakerAccess")
8993
public CreateClusterRequest setStorageType(StorageType storageType) {
9094
proto.getClusterBuilder().setDefaultStorageType(storageType);
9195
return this;
@@ -113,8 +117,8 @@ String getClusterId() {
113117
}
114118

115119
/**
116-
* Creates the request protobuf. This method is considered an internal implementation detail and
117-
* not meant to be used by applications.
120+
* Creates the request protobuf to be used in {@link CreateInstanceRequest}. This method is
121+
* considered an internal implementation detail and not meant to be used by applications.
118122
*/
119123
@InternalApi
120124
com.google.bigtable.admin.v2.Cluster toEmbeddedProto(ProjectName projectName) {

0 commit comments

Comments
 (0)