Skip to content

Commit

Permalink
Spanner: Refactor SpannerImpl - Move DatabaseAdminClientImpl to separ…
Browse files Browse the repository at this point in the history
…ate file (#4892)

* refactor SpannerImpl: move DatabaseAdminClientImpl to separate file

* removed unnecessary imports
  • Loading branch information
olavloite authored and kolea2 committed Apr 8, 2019
1 parent abbf45d commit 5c16ac2
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 157 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spanner;

import com.google.api.core.ApiFunction;
import com.google.api.gax.grpc.ProtoOperationTransformers;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.api.gax.longrunning.OperationFutureImpl;
import com.google.api.gax.longrunning.OperationSnapshot;
import com.google.api.gax.paging.Page;
import com.google.cloud.spanner.Options.ListOption;
import com.google.cloud.spanner.SpannerImpl.PageFetcher;
import com.google.cloud.spanner.spi.v1.SpannerRpc;
import com.google.cloud.spanner.spi.v1.SpannerRpc.Paginated;
import com.google.common.base.Preconditions;
import com.google.protobuf.Empty;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;

/** Default implementation of {@link DatabaseAdminClient}. */
class DatabaseAdminClientImpl implements DatabaseAdminClient {
private final String projectId;
private final SpannerRpc rpc;

DatabaseAdminClientImpl(String projectId, SpannerRpc rpc) {
this.projectId = projectId;
this.rpc = rpc;
}

/** Generates a random operation id for long-running database operations. */
private static String randomOperationId() {
UUID uuid = UUID.randomUUID();
return ("r" + uuid.toString()).replace("-", "_");
}

@Override
public OperationFuture<Database, CreateDatabaseMetadata> createDatabase(
String instanceId, String databaseId, Iterable<String> statements) throws SpannerException {
// CreateDatabase() is not idempotent, so we're not retrying this request.
String instanceName = getInstanceName(instanceId);
String createStatement = "CREATE DATABASE `" + databaseId + "`";
OperationFuture<com.google.spanner.admin.database.v1.Database, CreateDatabaseMetadata>
rawOperationFuture = rpc.createDatabase(instanceName, createStatement, statements);
return new OperationFutureImpl<Database, CreateDatabaseMetadata>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
new ApiFunction<OperationSnapshot, Database>() {
@Override
public Database apply(OperationSnapshot snapshot) {
return Database.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Database.class)
.apply(snapshot),
DatabaseAdminClientImpl.this);
}
},
ProtoOperationTransformers.MetadataTransformer.create(CreateDatabaseMetadata.class),
new ApiFunction<Exception, Database>() {
@Override
public Database apply(Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
});
}

@Override
public Database getDatabase(String instanceId, String databaseId) throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<Database> callable =
new Callable<Database>() {
@Override
public Database call() throws Exception {
return Database.fromProto(rpc.getDatabase(dbName), DatabaseAdminClientImpl.this);
}
};
return SpannerImpl.runWithRetries(callable);
}

@Override
public OperationFuture<Void, UpdateDatabaseDdlMetadata> updateDatabaseDdl(
final String instanceId,
final String databaseId,
final Iterable<String> statements,
@Nullable String operationId)
throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
final String opId = operationId != null ? operationId : randomOperationId();
OperationFuture<Empty, UpdateDatabaseDdlMetadata> rawOperationFuture =
rpc.updateDatabaseDdl(dbName, statements, opId);
return new OperationFutureImpl<Void, UpdateDatabaseDdlMetadata>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
new ApiFunction<OperationSnapshot, Void>() {
@Override
public Void apply(OperationSnapshot snapshot) {
ProtoOperationTransformers.ResponseTransformer.create(Empty.class).apply(snapshot);
return null;
}
},
ProtoOperationTransformers.MetadataTransformer.create(UpdateDatabaseDdlMetadata.class),
new ApiFunction<Exception, Void>() {
@Override
public Void apply(Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
});
}

@Override
public void dropDatabase(String instanceId, String databaseId) throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<Void> callable =
new Callable<Void>() {
@Override
public Void call() throws Exception {
rpc.dropDatabase(dbName);
return null;
}
};
SpannerImpl.runWithRetries(callable);
}

@Override
public List<String> getDatabaseDdl(String instanceId, String databaseId) {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<List<String>> callable =
new Callable<List<String>>() {
@Override
public List<String> call() throws Exception {
return rpc.getDatabaseDdl(dbName);
}
};
return SpannerImpl.runWithRetries(callable);
}

@Override
public Page<Database> listDatabases(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
Preconditions.checkArgument(
!listOptions.hasFilter(), "Filter option is not support by" + "listDatabases");
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
PageFetcher<Database, com.google.spanner.admin.database.v1.Database> pageFetcher =
new PageFetcher<Database, com.google.spanner.admin.database.v1.Database>() {
@Override
public Paginated<com.google.spanner.admin.database.v1.Database> getNextPage(
String nextPageToken) {
return rpc.listDatabases(instanceName, pageSize, nextPageToken);
}

@Override
public Database fromProto(com.google.spanner.admin.database.v1.Database proto) {
return Database.fromProto(proto, DatabaseAdminClientImpl.this);
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}

private String getInstanceName(String instanceId) {
return new InstanceId(projectId, instanceId).getName();
}

private String getDatabaseName(String instanceId, String databaseId) {
return new DatabaseId(new InstanceId(projectId, instanceId), databaseId).getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,9 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.Empty;
import com.google.protobuf.FieldMask;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
import com.google.spanner.admin.instance.v1.CreateInstanceMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata;
import com.google.spanner.v1.BeginTransactionRequest;
Expand All @@ -84,7 +81,6 @@
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -391,7 +387,7 @@ private static <T extends Message> T unpack(Any response, Class<T> clazz)
}
}

private abstract static class PageFetcher<S, T> implements NextPageFetcher<S> {
abstract static class PageFetcher<S, T> implements NextPageFetcher<S> {
private String nextPageToken;

@Override
Expand All @@ -412,159 +408,13 @@ public Paginated<T> call() {
return new PageImpl<S>(this, nextPageToken, results);
}

abstract Paginated<T> getNextPage(@Nullable String nextPageToken);

abstract S fromProto(T proto);
}

private static String randomOperationId() {
UUID uuid = UUID.randomUUID();
return ("r" + uuid.toString()).replace("-", "_");
}

static class DatabaseAdminClientImpl implements DatabaseAdminClient {

private final String projectId;
private final SpannerRpc rpc;

DatabaseAdminClientImpl(String projectId, SpannerRpc rpc) {
this.projectId = projectId;
this.rpc = rpc;
void setNextPageToken(String nextPageToken) {
this.nextPageToken = nextPageToken;
}

@Override
public OperationFuture<Database, CreateDatabaseMetadata> createDatabase(
String instanceId, String databaseId, Iterable<String> statements) throws SpannerException {
// CreateDatabase() is not idempotent, so we're not retrying this request.
String instanceName = getInstanceName(instanceId);
String createStatement = "CREATE DATABASE `" + databaseId + "`";
OperationFuture<com.google.spanner.admin.database.v1.Database, CreateDatabaseMetadata>
rawOperationFuture = rpc.createDatabase(instanceName, createStatement, statements);
return new OperationFutureImpl<Database, CreateDatabaseMetadata>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
new ApiFunction<OperationSnapshot, Database>() {
@Override
public Database apply(OperationSnapshot snapshot) {
return Database.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Database.class)
.apply(snapshot),
DatabaseAdminClientImpl.this);
}
},
ProtoOperationTransformers.MetadataTransformer.create(CreateDatabaseMetadata.class),
new ApiFunction<Exception, Database>() {
@Override
public Database apply(Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
});
}

@Override
public Database getDatabase(String instanceId, String databaseId) throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<Database> callable =
new Callable<Database>() {
@Override
public Database call() throws Exception {
return Database.fromProto(rpc.getDatabase(dbName), DatabaseAdminClientImpl.this);
}
};
return runWithRetries(callable);
}

@Override
public OperationFuture<Void, UpdateDatabaseDdlMetadata> updateDatabaseDdl(
final String instanceId,
final String databaseId,
final Iterable<String> statements,
@Nullable String operationId)
throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
final String opId = operationId != null ? operationId : randomOperationId();
OperationFuture<Empty, UpdateDatabaseDdlMetadata> rawOperationFuture =
rpc.updateDatabaseDdl(dbName, statements, opId);
return new OperationFutureImpl<Void, UpdateDatabaseDdlMetadata>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
new ApiFunction<OperationSnapshot, Void>() {
@Override
public Void apply(OperationSnapshot snapshot) {
ProtoOperationTransformers.ResponseTransformer.create(Empty.class).apply(snapshot);
return null;
}
},
ProtoOperationTransformers.MetadataTransformer.create(UpdateDatabaseDdlMetadata.class),
new ApiFunction<Exception, Void>() {
@Override
public Void apply(Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
});
}

@Override
public void dropDatabase(String instanceId, String databaseId) throws SpannerException {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<Void> callable =
new Callable<Void>() {
@Override
public Void call() throws Exception {
rpc.dropDatabase(dbName);
return null;
}
};
runWithRetries(callable);
}

@Override
public List<String> getDatabaseDdl(String instanceId, String databaseId) {
final String dbName = getDatabaseName(instanceId, databaseId);
Callable<List<String>> callable =
new Callable<List<String>>() {
@Override
public List<String> call() throws Exception {
return rpc.getDatabaseDdl(dbName);
}
};
return runWithRetries(callable);
}

@Override
public Page<Database> listDatabases(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
Preconditions.checkArgument(
!listOptions.hasFilter(), "Filter option is not support by" + "listDatabases");
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
PageFetcher<Database, com.google.spanner.admin.database.v1.Database> pageFetcher =
new PageFetcher<Database, com.google.spanner.admin.database.v1.Database>() {
@Override
public Paginated<com.google.spanner.admin.database.v1.Database> getNextPage(
String nextPageToken) {
return rpc.listDatabases(instanceName, pageSize, nextPageToken);
}

@Override
public Database fromProto(com.google.spanner.admin.database.v1.Database proto) {
return Database.fromProto(proto, DatabaseAdminClientImpl.this);
}
};
if (listOptions.hasPageToken()) {
pageFetcher.nextPageToken = listOptions.pageToken();
}
return pageFetcher.getNextPage();
}

private String getInstanceName(String instanceId) {
return new InstanceId(projectId, instanceId).getName();
}
abstract Paginated<T> getNextPage(@Nullable String nextPageToken);

private String getDatabaseName(String instanceId, String databaseId) {
return new DatabaseId(new InstanceId(projectId, instanceId), databaseId).getName();
}
abstract S fromProto(T proto);
}

static class InstanceAdminClientImpl implements InstanceAdminClient {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public class DatabaseAdminClientImplTest {
"projects/my-project/instances/my-instance/databases/my-db2";

@Mock SpannerRpc rpc;
SpannerImpl.DatabaseAdminClientImpl client;
DatabaseAdminClientImpl client;

@Before
public void setUp() {
initMocks(this);
client = new SpannerImpl.DatabaseAdminClientImpl(PROJECT_ID, rpc);
client = new DatabaseAdminClientImpl(PROJECT_ID, rpc);
}

private Database getDatabaseProto() {
Expand Down

0 comments on commit 5c16ac2

Please sign in to comment.