io.vitess.client.grpc.GrpcClient.java Source code

Java tutorial

Introduction

Here is the source code for io.vitess.client.grpc.GrpcClient.java

Source

/*
 * Copyright 2017 Google Inc.
 * 
 * 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 io.vitess.client.grpc;

import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.ManagedChannel;
import io.grpc.StatusRuntimeException;
import io.vitess.client.Context;
import io.vitess.client.Proto;
import io.vitess.client.RpcClient;
import io.vitess.client.StreamIterator;
import io.vitess.proto.Query.QueryResult;
import io.vitess.proto.Vtgate;
import io.vitess.proto.Vtgate.BeginRequest;
import io.vitess.proto.Vtgate.BeginResponse;
import io.vitess.proto.Vtgate.CommitRequest;
import io.vitess.proto.Vtgate.CommitResponse;
import io.vitess.proto.Vtgate.ExecuteBatchKeyspaceIdsRequest;
import io.vitess.proto.Vtgate.ExecuteBatchKeyspaceIdsResponse;
import io.vitess.proto.Vtgate.ExecuteBatchShardsRequest;
import io.vitess.proto.Vtgate.ExecuteBatchShardsResponse;
import io.vitess.proto.Vtgate.ExecuteEntityIdsRequest;
import io.vitess.proto.Vtgate.ExecuteEntityIdsResponse;
import io.vitess.proto.Vtgate.ExecuteKeyRangesRequest;
import io.vitess.proto.Vtgate.ExecuteKeyRangesResponse;
import io.vitess.proto.Vtgate.ExecuteKeyspaceIdsRequest;
import io.vitess.proto.Vtgate.ExecuteKeyspaceIdsResponse;
import io.vitess.proto.Vtgate.ExecuteRequest;
import io.vitess.proto.Vtgate.ExecuteResponse;
import io.vitess.proto.Vtgate.ExecuteShardsRequest;
import io.vitess.proto.Vtgate.ExecuteShardsResponse;
import io.vitess.proto.Vtgate.GetSrvKeyspaceRequest;
import io.vitess.proto.Vtgate.GetSrvKeyspaceResponse;
import io.vitess.proto.Vtgate.RollbackRequest;
import io.vitess.proto.Vtgate.RollbackResponse;
import io.vitess.proto.Vtgate.SplitQueryRequest;
import io.vitess.proto.Vtgate.SplitQueryResponse;
import io.vitess.proto.Vtgate.StreamExecuteKeyRangesRequest;
import io.vitess.proto.Vtgate.StreamExecuteKeyRangesResponse;
import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest;
import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsResponse;
import io.vitess.proto.Vtgate.StreamExecuteRequest;
import io.vitess.proto.Vtgate.StreamExecuteResponse;
import io.vitess.proto.Vtgate.StreamExecuteShardsRequest;
import io.vitess.proto.Vtgate.StreamExecuteShardsResponse;
import io.vitess.proto.grpc.VitessGrpc;
import io.vitess.proto.grpc.VitessGrpc.VitessFutureStub;
import io.vitess.proto.grpc.VitessGrpc.VitessStub;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransientException;
import java.util.concurrent.TimeUnit;
import org.joda.time.Duration;

/**
 * GrpcClient is a gRPC-based implementation of Vitess RpcClient.
 */
public class GrpcClient implements RpcClient {
    private final ManagedChannel channel;
    private final VitessStub asyncStub;
    private final VitessFutureStub futureStub;

    public GrpcClient(ManagedChannel channel) {
        this.channel = channel;
        asyncStub = VitessGrpc.newStub(channel);
        futureStub = VitessGrpc.newFutureStub(channel);
    }

    @Override
    public void close() throws IOException {
        channel.shutdown();
    }

    @Override
    public ListenableFuture<ExecuteResponse> execute(Context ctx, ExecuteRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).execute(request), Exception.class,
                new ExceptionConverter<ExecuteResponse>());
    }

    @Override
    public ListenableFuture<ExecuteShardsResponse> executeShards(Context ctx, ExecuteShardsRequest request)
            throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeShards(request), Exception.class,
                new ExceptionConverter<ExecuteShardsResponse>());
    }

    @Override
    public ListenableFuture<ExecuteKeyspaceIdsResponse> executeKeyspaceIds(Context ctx,
            ExecuteKeyspaceIdsRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeKeyspaceIds(request), Exception.class,
                new ExceptionConverter<ExecuteKeyspaceIdsResponse>());
    }

    @Override
    public ListenableFuture<ExecuteKeyRangesResponse> executeKeyRanges(Context ctx, ExecuteKeyRangesRequest request)
            throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeKeyRanges(request), Exception.class,
                new ExceptionConverter<ExecuteKeyRangesResponse>());
    }

    @Override
    public ListenableFuture<ExecuteEntityIdsResponse> executeEntityIds(Context ctx, ExecuteEntityIdsRequest request)
            throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeEntityIds(request), Exception.class,
                new ExceptionConverter<ExecuteEntityIdsResponse>());
    }

    @Override
    public ListenableFuture<Vtgate.ExecuteBatchResponse> executeBatch(Context ctx,
            Vtgate.ExecuteBatchRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeBatch(request), Exception.class,
                new ExceptionConverter<Vtgate.ExecuteBatchResponse>());
    }

    @Override
    public ListenableFuture<ExecuteBatchShardsResponse> executeBatchShards(Context ctx,
            ExecuteBatchShardsRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeBatchShards(request), Exception.class,
                new ExceptionConverter<ExecuteBatchShardsResponse>());
    }

    @Override
    public ListenableFuture<ExecuteBatchKeyspaceIdsResponse> executeBatchKeyspaceIds(Context ctx,
            ExecuteBatchKeyspaceIdsRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).executeBatchKeyspaceIds(request), Exception.class,
                new ExceptionConverter<ExecuteBatchKeyspaceIdsResponse>());
    }

    @Override
    public StreamIterator<QueryResult> streamExecute(Context ctx, StreamExecuteRequest request)
            throws SQLException {
        GrpcStreamAdapter<StreamExecuteResponse, QueryResult> adapter = new GrpcStreamAdapter<StreamExecuteResponse, QueryResult>() {
            @Override
            QueryResult getResult(StreamExecuteResponse response) throws SQLException {
                return response.getResult();
            }
        };
        getAsyncStub(ctx).streamExecute(request, adapter);
        return adapter;
    }

    @Override
    public StreamIterator<QueryResult> streamExecuteShards(Context ctx, StreamExecuteShardsRequest request)
            throws SQLException {
        GrpcStreamAdapter<StreamExecuteShardsResponse, QueryResult> adapter = new GrpcStreamAdapter<StreamExecuteShardsResponse, QueryResult>() {
            @Override
            QueryResult getResult(StreamExecuteShardsResponse response) throws SQLException {
                return response.getResult();
            }
        };
        getAsyncStub(ctx).streamExecuteShards(request, adapter);
        return adapter;
    }

    @Override
    public StreamIterator<QueryResult> streamExecuteKeyspaceIds(Context ctx,
            StreamExecuteKeyspaceIdsRequest request) throws SQLException {
        GrpcStreamAdapter<StreamExecuteKeyspaceIdsResponse, QueryResult> adapter = new GrpcStreamAdapter<StreamExecuteKeyspaceIdsResponse, QueryResult>() {
            @Override
            QueryResult getResult(StreamExecuteKeyspaceIdsResponse response) throws SQLException {
                return response.getResult();
            }
        };
        getAsyncStub(ctx).streamExecuteKeyspaceIds(request, adapter);
        return adapter;
    }

    @Override
    public StreamIterator<QueryResult> streamExecuteKeyRanges(Context ctx, StreamExecuteKeyRangesRequest request)
            throws SQLException {
        GrpcStreamAdapter<StreamExecuteKeyRangesResponse, QueryResult> adapter = new GrpcStreamAdapter<StreamExecuteKeyRangesResponse, QueryResult>() {
            @Override
            QueryResult getResult(StreamExecuteKeyRangesResponse response) throws SQLException {
                return response.getResult();
            }
        };
        getAsyncStub(ctx).streamExecuteKeyRanges(request, adapter);
        return adapter;
    }

    @Override
    public ListenableFuture<BeginResponse> begin(Context ctx, BeginRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).begin(request), Exception.class,
                new ExceptionConverter<BeginResponse>());
    }

    @Override
    public ListenableFuture<CommitResponse> commit(Context ctx, CommitRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).commit(request), Exception.class,
                new ExceptionConverter<CommitResponse>());
    }

    @Override
    public ListenableFuture<RollbackResponse> rollback(Context ctx, RollbackRequest request) throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).rollback(request), Exception.class,
                new ExceptionConverter<RollbackResponse>());
    }

    @Override
    public ListenableFuture<SplitQueryResponse> splitQuery(Context ctx, SplitQueryRequest request)
            throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).splitQuery(request), Exception.class,
                new ExceptionConverter<SplitQueryResponse>());
    }

    @Override
    public ListenableFuture<GetSrvKeyspaceResponse> getSrvKeyspace(Context ctx, GetSrvKeyspaceRequest request)
            throws SQLException {
        return Futures.catchingAsync(getFutureStub(ctx).getSrvKeyspace(request), Exception.class,
                new ExceptionConverter<GetSrvKeyspaceResponse>());
    }

    /**
     * Converts an exception from the gRPC framework into the appropriate {@link SQLException}.
     */
    static SQLException convertGrpcError(Throwable e) {
        if (e instanceof StatusRuntimeException) {
            StatusRuntimeException sre = (StatusRuntimeException) e;

            int errno = Proto.getErrno(sre.getMessage());
            String sqlState = Proto.getSQLState(sre.getMessage());

            switch (sre.getStatus().getCode()) {
            case INVALID_ARGUMENT:
                return new SQLSyntaxErrorException(sre.toString(), sqlState, errno, sre);
            case DEADLINE_EXCEEDED:
                return new SQLTimeoutException(sre.toString(), sqlState, errno, sre);
            case ALREADY_EXISTS:
                return new SQLIntegrityConstraintViolationException(sre.toString(), sqlState, errno, sre);
            case UNAUTHENTICATED:
                return new SQLInvalidAuthorizationSpecException(sre.toString(), sqlState, errno, sre);
            case UNAVAILABLE:
                return new SQLTransientException(sre.toString(), sqlState, errno, sre);
            case ABORTED:
                return new SQLRecoverableException(sre.toString(), sqlState, errno, sre);
            default: // Covers e.g. UNKNOWN.
                String advice = "";
                if (e.getCause() instanceof java.nio.channels.ClosedChannelException) {
                    advice = "Failed to connect to vtgate. Make sure that vtgate is running and you are using the correct address. Details: ";
                }
                return new SQLNonTransientException("gRPC StatusRuntimeException: " + advice + e.toString(),
                        sqlState, errno, e);
            }
        }
        return new SQLNonTransientException("gRPC error: " + e.toString(), e);
    }

    static class ExceptionConverter<V> implements AsyncFunction<Exception, V> {
        @Override
        public ListenableFuture<V> apply(Exception e) throws Exception {
            throw convertGrpcError(e);
        }
    }

    private VitessStub getAsyncStub(Context ctx) {
        Duration timeout = ctx.getTimeout();
        if (timeout == null) {
            return asyncStub;
        }
        return asyncStub.withDeadlineAfter(timeout.getMillis(), TimeUnit.MILLISECONDS);
    }

    private VitessFutureStub getFutureStub(Context ctx) {
        Duration timeout = ctx.getTimeout();
        if (timeout == null) {
            return futureStub;
        }
        return futureStub.withDeadlineAfter(timeout.getMillis(), TimeUnit.MILLISECONDS);
    }
}