/*
* Copyright 2017 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.spi.v1;
import com.google.api.core.ApiFuture;
import com.google.api.core.InternalApi;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ApiCallContext;
import com.google.api.gax.rpc.ServerStream;
import com.google.api.gax.rpc.StatusCode.Code;
import com.google.cloud.ServiceRpc;
import com.google.cloud.spanner.BackupId;
import com.google.cloud.spanner.Restore;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStub;
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings;
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStub;
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings;
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
import com.google.common.collect.ImmutableList;
import com.google.iam.v1.GetPolicyOptions;
import com.google.iam.v1.Policy;
import com.google.iam.v1.TestIamPermissionsResponse;
import com.google.longrunning.Operation;
import com.google.protobuf.Empty;
import com.google.protobuf.FieldMask;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.CopyBackupMetadata;
import com.google.spanner.admin.database.v1.CreateBackupMetadata;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.DatabaseRole;
import com.google.spanner.admin.database.v1.GetDatabaseDdlResponse;
import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata;
import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.CreateInstanceMetadata;
import com.google.spanner.admin.instance.v1.Instance;
import com.google.spanner.admin.instance.v1.InstanceConfig;
import com.google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata;
import com.google.spanner.v1.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.threeten.bp.Duration;
/**
* Abstracts remote calls to the Cloud Spanner service. Typically end-consumer code will never use
* this interface; it's main purpose is to abstract the implementation of the public Cloud Spanner
* API from the underlying transport mechanism.
*
* Each {@code SpannerRPC} instance is bound to a particular project and set of authorization
* credentials.
*
*
The interface is currently defined in terms of the generated HTTP client model classes. This
* is purely for expedience; a future version of this interface is likely to be independent of
* transport to allow switching between gRPC and HTTP.
*/
@InternalApi
public interface SpannerRpc extends ServiceRpc {
/** Options passed in {@link SpannerRpc} methods to control how an RPC is issued. */
enum Option {
CHANNEL_HINT("Channel Hint");
private final String value;
Option(String value) {
this.value = value;
}
@SuppressWarnings("unchecked")
T get(@Nullable Map options) {
if (options == null) {
return null;
}
return (T) options.get(this);
}
Long getLong(@Nullable Map options) {
return get(options);
}
@Override
public String toString() {
return value;
}
}
/**
* Represents results from paginated RPCs, i.e., those where up to a maximum number of items is
* returned from each call and a followup call must be made to fetch more.
*
* @param the type of result
*/
final class Paginated {
private final Iterable results;
private final String nextPageToken;
/**
* Creates a new page of results.
*
* @param results the result, or null for no results.
* @param nextPageToken the token for the next page of results, or null if no more pages exist
*/
public Paginated(@Nullable Iterable results, @Nullable String nextPageToken) {
// The generated HTTP client has null members when no results are present, rather than an
// empty list. Implicitly convert to an empty list to minimize the risk of NPEs.
this.results = (results == null) ? ImmutableList.of() : results;
this.nextPageToken =
(nextPageToken == null || nextPageToken.isEmpty()) ? null : nextPageToken;
}
/**
* Returns the current page of results. Always returns non-null; if a null "results" was passed
* to the constructor, a default empty {@code Iterable} will be returned.
*/
public Iterable getResults() {
return results;
}
/**
* Returns the token to use in the request for the next page, or null if this is the last page.
*/
@Nullable
public String getNextPageToken() {
return nextPageToken;
}
}
/** Consumer for the results produced by a streaming read or query call. */
interface ResultStreamConsumer {
void onPartialResultSet(PartialResultSet results);
void onCompleted();
void onError(SpannerException e);
}
/** Handle for cancellation of a streaming read or query call. */
interface StreamingCall {
/** Returns the {@link ApiCallContext} that is used for this streaming call. */
ApiCallContext getCallContext();
/**
* Requests more messages from the stream. We disable the auto flow control mechanism in grpc,
* so we need to request messages ourself. This gives us more control over how much buffer we
* maintain in the client. Grpc will request 1 initial message automatically so we don't need to
* call this at the beginning. After that it should be called whenever there is a flow control
* window available based on the flow control setting configured by the client. Currently we do
* not have any flow control so this is called automatically when a message is received.
*/
void request(int numMessages);
/**
* Cancels the call.
*
* @param message a message to use in the final status of any underlying RPC
*/
void cancel(@Nullable String message);
}
// Instance admin APIs.
Paginated listInstanceConfigs(int pageSize, @Nullable String pageToken)
throws SpannerException;
default OperationFuture createInstanceConfig(
String parent,
String instanceConfigId,
InstanceConfig instanceConfig,
@Nullable Boolean validateOnly)
throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}
default OperationFuture updateInstanceConfig(
InstanceConfig instanceConfig, @Nullable Boolean validateOnly, FieldMask fieldMask)
throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}
InstanceConfig getInstanceConfig(String instanceConfigName) throws SpannerException;
default void deleteInstanceConfig(
String instanceConfigName, @Nullable String etag, @Nullable Boolean validateOnly)
throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}
/** List all long-running instance config operations on the given project. */
default Paginated listInstanceConfigOperations(
int pageSize, @Nullable String filter, @Nullable String pageToken) {
throw new UnsupportedOperationException("Not implemented");
}
Paginated listInstances(
int pageSize, @Nullable String pageToken, @Nullable String filter) throws SpannerException;
OperationFuture createInstance(
String parent, String instanceId, Instance instance) throws SpannerException;
OperationFuture updateInstance(
Instance instance, FieldMask fieldMask) throws SpannerException;
Instance getInstance(String instanceName) throws SpannerException;
void deleteInstance(String instanceName) throws SpannerException;
// Database admin APIs.
Paginated listDatabases(String instanceName, int pageSize, @Nullable String pageToken)
throws SpannerException;
OperationFuture createDatabase(
String instanceName,
String createDatabaseStatement,
Iterable additionalStatements,
com.google.cloud.spanner.Database database)
throws SpannerException;
OperationFuture updateDatabaseDdl(
com.google.cloud.spanner.Database database,
Iterable updateDatabaseStatements,
@Nullable String updateId)
throws SpannerException;
void dropDatabase(String databaseName) throws SpannerException;
Database getDatabase(String databaseName) throws SpannerException;
/**
* Updates the specified fields of a Cloud Spanner database.
*
* @param database The database proto whose field values will be used as the new values in the
* stored database.
* @param fieldMask The fields to update. Currently, only the "enable_drop_protection" field of
* the database supports updates.
* @return an `OperationFuture` that can be used to track the status of the update.
* @throws SpannerException
*/
OperationFuture updateDatabase(
Database database, FieldMask fieldMask) throws SpannerException;
GetDatabaseDdlResponse getDatabaseDdl(String databaseName) throws SpannerException;
/** Lists the backups in the specified instance. */
Paginated listBackups(
String instanceName, int pageSize, @Nullable String filter, @Nullable String pageToken)
throws SpannerException;
/**
* Creates a new backup from the source database specified in the {@link
* com.google.cloud.spanner.Backup} instance.
*
* @param backupInfo the backup to create. The instance, database and expireTime fields of the
* backup must be filled.
* @return the operation that monitors the backup creation.
*/
OperationFuture createBackup(
com.google.cloud.spanner.Backup backupInfo) throws SpannerException;
/**
* Creates a copy backup from the source backup specified.
*
* @param destinationBackup the backup to create. The instance, database, and expireTime fields of
* the backup must be filled. It may also optionally have an encryption config set. If no
* encryption config has been set, the new backup will use the same encryption config as the
* source backup.
* @return the operation that monitors the backup creation.
*/
default OperationFuture copyBackup(
BackupId sourceBackupId, com.google.cloud.spanner.Backup destinationBackup) {
throw new UnsupportedOperationException("Unimplemented");
}
/**
* Restore a backup into the given database.
*
* @param restore a {@link Restore} instance with the backup source and destination database
*/
OperationFuture restoreDatabase(Restore restore);
/** Gets the backup with the specified name. */
Backup getBackup(String backupName) throws SpannerException;
/** Updates the specified backup. The only supported field for updates is expireTime. */
Backup updateBackup(Backup backup, FieldMask updateMask);
/** List all long-running backup operations on the given instance. */
Paginated listBackupOperations(
String instanceName, int pageSize, @Nullable String filter, @Nullable String pageToken);
/**
* Deletes a pending or completed backup.
*
* @param backupName Required. The fully qualified name of the backup to delete.
*/
void deleteBackup(String backupName);
Paginated listDatabaseOperations(
String instanceName, int pageSize, @Nullable String filter, @Nullable String pageToken);
Paginated listDatabaseRoles(
String databaseName, int pageSize, @Nullable String pageToken);
/** Retrieves a long running operation. */
Operation getOperation(String name) throws SpannerException;
/** Cancels the specified long-running operation. */
void cancelOperation(String name) throws SpannerException;
List batchCreateSessions(
String databaseName,
int sessionCount,
@Nullable String databaseRole,
@Nullable Map labels,
@Nullable Map options)
throws SpannerException;
Session createSession(
String databaseName,
@Nullable String databaseRole,
@Nullable Map labels,
@Nullable Map options)
throws SpannerException;
default Session createSession(
String databaseName,
@Nullable String databaseRole,
@Nullable Map labels,
@Nullable Map options,
boolean isMultiplexed)
throws SpannerException {
throw new UnsupportedOperationException("Unimplemented");
}
void deleteSession(String sessionName, @Nullable Map options) throws SpannerException;
ApiFuture asyncDeleteSession(String sessionName, @Nullable Map options)
throws SpannerException;
/** Returns the retry settings for streaming read operations. */
default RetrySettings getReadRetrySettings() {
return SpannerStubSettings.newBuilder().streamingReadSettings().getRetrySettings();
}
/** Returns the retryable codes for streaming read operations. */
default Set getReadRetryableCodes() {
return SpannerStubSettings.newBuilder().streamingReadSettings().getRetryableCodes();
}
/**
* Performs a streaming read.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
StreamingCall read(
ReadRequest request,
ResultStreamConsumer consumer,
@Nullable Map options,
boolean routeToLeader);
/** Returns the retry settings for streaming query operations. */
default RetrySettings getExecuteQueryRetrySettings() {
return SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetrySettings();
}
/** Returns the retryable codes for streaming query operations. */
default Set getExecuteQueryRetryableCodes() {
return SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetryableCodes();
}
/**
* Executes a query.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
ResultSet executeQuery(
ExecuteSqlRequest request, @Nullable Map options, boolean routeToLeader);
/**
* Executes a query asynchronously.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
ApiFuture executeQueryAsync(
ExecuteSqlRequest request, @Nullable Map options, boolean routeToLeader);
ResultSet executePartitionedDml(ExecuteSqlRequest request, @Nullable Map options);
RetrySettings getPartitionedDmlRetrySettings();
ServerStream executeStreamingPartitionedDml(
ExecuteSqlRequest request, @Nullable Map options, Duration timeout);
ServerStream batchWriteAtLeastOnce(
BatchWriteRequest request, @Nullable Map options);
/**
* Executes a query with streaming result.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
StreamingCall executeQuery(
ExecuteSqlRequest request,
ResultStreamConsumer consumer,
@Nullable Map options,
boolean routeToLeader);
ExecuteBatchDmlResponse executeBatchDml(ExecuteBatchDmlRequest build, Map options);
ApiFuture executeBatchDmlAsync(
ExecuteBatchDmlRequest build, Map options);
/**
* Begins a transaction.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
Transaction beginTransaction(
BeginTransactionRequest request, @Nullable Map options, boolean routeToLeader)
throws SpannerException;
/**
* Begins a transaction asynchronously.
*
* @param routeToLeader Set to true to route the request to the leader region, and false to route
* the request to any region. When leader aware routing is enabled, RW/PDML requests are
* preferred to be routed to the leader region, and RO requests (except for
* PartitionRead/PartitionQuery) are preferred to be routed to any region for optimal latency.
*/
ApiFuture beginTransactionAsync(
BeginTransactionRequest request, @Nullable Map options, boolean routeToLeader);
CommitResponse commit(CommitRequest commitRequest, @Nullable Map options)
throws SpannerException;
ApiFuture commitAsync(
CommitRequest commitRequest, @Nullable Map options);
default RetrySettings getCommitRetrySettings() {
return SpannerStubSettings.newBuilder().commitSettings().getRetrySettings();
}
void rollback(RollbackRequest request, @Nullable Map options) throws SpannerException;
ApiFuture rollbackAsync(RollbackRequest request, @Nullable Map options);
PartitionResponse partitionQuery(PartitionQueryRequest request, @Nullable Map options)
throws SpannerException;
PartitionResponse partitionRead(PartitionReadRequest request, @Nullable Map options)
throws SpannerException;
/** Gets the IAM policy for the given resource using the {@link DatabaseAdminStub}. */
Policy getDatabaseAdminIAMPolicy(String resource, @Nullable GetPolicyOptions options);
/**
* Updates the IAM policy for the given resource using the {@link DatabaseAdminStub}. It is highly
* recommended to first get the current policy and base the updated policy on the returned policy.
* See {@link Policy.Builder#setEtag(com.google.protobuf.ByteString)} for information on the
* recommended read-modify-write cycle.
*/
Policy setDatabaseAdminIAMPolicy(String resource, Policy policy);
/** Tests the IAM permissions for the given resource using the {@link DatabaseAdminStub}. */
TestIamPermissionsResponse testDatabaseAdminIAMPermissions(
String resource, Iterable permissions);
/** Gets the IAM policy for the given resource using the {@link InstanceAdminStub}. */
Policy getInstanceAdminIAMPolicy(String resource);
/**
* Updates the IAM policy for the given resource using the {@link InstanceAdminStub}. It is highly
* recommended to first get the current policy and base the updated policy on the returned policy.
* See {@link Policy.Builder#setEtag(com.google.protobuf.ByteString)} for information on the
* recommended read-modify-write cycle.
*/
Policy setInstanceAdminIAMPolicy(String resource, Policy policy);
/** Tests the IAM permissions for the given resource using the {@link InstanceAdminStub}. */
TestIamPermissionsResponse testInstanceAdminIAMPermissions(
String resource, Iterable permissions);
void shutdown();
boolean isClosed();
/**
* Getter method to obtain the auto-generated instance admin client stub settings.
*
* @return InstanceAdminStubSettings
*/
@InternalApi
default InstanceAdminStubSettings getInstanceAdminStubSettings() {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Getter method to obtain the auto-generated database admin client stub settings.
*
* @return DatabaseAdminStubSettings
*/
@InternalApi
default DatabaseAdminStubSettings getDatabaseAdminStubSettings() {
throw new UnsupportedOperationException("Not implemented");
}
}