nl.topicus.jdbc.shaded.io.grpc.internal.OobChannel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spanner-jdbc Show documentation
Show all versions of spanner-jdbc Show documentation
JDBC Driver for Google Cloud Spanner
/*
* Copyright 2016, gRPC Authors All rights reserved.
*
* 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 nl.topicus.jdbc.shaded.io.grpc.internal;
import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkNotNull;
import nl.topicus.jdbc.shaded.com.google.common.annotations.VisibleForTesting;
import nl.topicus.jdbc.shaded.com.google.common.util.concurrent.ListenableFuture;
import nl.topicus.jdbc.shaded.com.google.common.util.concurrent.SettableFuture;
import nl.topicus.jdbc.shaded.io.grpc.Attributes;
import nl.topicus.jdbc.shaded.io.grpc.CallOptions;
import nl.topicus.jdbc.shaded.io.grpc.ClientCall;
import nl.topicus.jdbc.shaded.io.grpc.ConnectivityStateInfo;
import nl.topicus.jdbc.shaded.io.grpc.Context;
import nl.topicus.jdbc.shaded.io.grpc.EquivalentAddressGroup;
import nl.topicus.jdbc.shaded.io.grpc.InternalChannelStats;
import nl.topicus.jdbc.shaded.io.grpc.InternalInstrumented;
import nl.topicus.jdbc.shaded.io.grpc.InternalLogId;
import nl.topicus.jdbc.shaded.io.grpc.LoadBalancer;
import nl.topicus.jdbc.shaded.io.grpc.LoadBalancer.PickResult;
import nl.topicus.jdbc.shaded.io.grpc.LoadBalancer.PickSubchannelArgs;
import nl.topicus.jdbc.shaded.io.grpc.LoadBalancer.Subchannel;
import nl.topicus.jdbc.shaded.io.grpc.LoadBalancer.SubchannelPicker;
import nl.topicus.jdbc.shaded.io.grpc.ManagedChannel;
import nl.topicus.jdbc.shaded.io.grpc.Metadata;
import nl.topicus.jdbc.shaded.io.grpc.MethodDescriptor;
import nl.topicus.jdbc.shaded.io.grpc.Status;
import nl.topicus.jdbc.shaded.io.grpc.internal.ClientCallImpl.ClientTransportProvider;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import nl.topicus.jdbc.shaded.javax.annotation.concurrent.ThreadSafe;
/**
* A ManagedChannel backed by a single {@link InternalSubchannel} and used for {@link LoadBalancer}
* to its own RPC needs.
*/
@ThreadSafe
final class OobChannel
extends ManagedChannel implements InternalInstrumented {
private static final Logger log = Logger.getLogger(OobChannel.class.getName());
private InternalSubchannel subchannel;
private AbstractSubchannel subchannelImpl;
private SubchannelPicker subchannelPicker;
private final InternalLogId logId = InternalLogId.allocate(getClass().getName());
private final String authority;
private final DelayedClientTransport delayedTransport;
private final ObjectPool extends Executor> executorPool;
private final Executor executor;
private final ScheduledExecutorService deadlineCancellationExecutor;
private final CountDownLatch terminatedLatch = new CountDownLatch(1);
private volatile boolean shutdown;
private final ChannelTracer channelTracer;
private final ClientTransportProvider transportProvider = new ClientTransportProvider() {
@Override
public ClientTransport get(PickSubchannelArgs args) {
// delayed transport's newStream() always acquires a lock, but concurrent performance doesn't
// matter here because OOB communication should be sparse, and it's not on application RPC's
// critical path.
return delayedTransport;
}
@Override
public RetriableStream newRetriableStream(MethodDescriptor method,
CallOptions callOptions, Metadata headers, Context context) {
throw new UnsupportedOperationException("OobChannel should not create retriable streams");
}
};
OobChannel(
String authority, ObjectPool extends Executor> executorPool,
ScheduledExecutorService deadlineCancellationExecutor, ChannelExecutor channelExecutor,
ChannelTracer channelTracer) {
this.authority = checkNotNull(authority, "authority");
this.executorPool = checkNotNull(executorPool, "executorPool");
this.executor = checkNotNull(executorPool.getObject(), "executor");
this.deadlineCancellationExecutor = checkNotNull(
deadlineCancellationExecutor, "deadlineCancellationExecutor");
this.delayedTransport = new DelayedClientTransport(executor, channelExecutor);
this.delayedTransport.start(new ManagedClientTransport.Listener() {
@Override
public void transportShutdown(Status s) {
// Don't care
}
@Override
public void transportTerminated() {
subchannelImpl.shutdown();
}
@Override
public void transportReady() {
// Don't care
}
@Override
public void transportInUse(boolean inUse) {
// Don't care
}
});
this.channelTracer = channelTracer;
}
// Must be called only once, right after the OobChannel is created.
void setSubchannel(final InternalSubchannel subchannel) {
log.log(Level.FINE, "[{0}] Created with [{1}]", new Object[] {this, subchannel});
this.subchannel = subchannel;
subchannelImpl = new AbstractSubchannel() {
@Override
public void shutdown() {
subchannel.shutdown(Status.UNAVAILABLE.withDescription("OobChannel is shutdown"));
}
@Override
ClientTransport obtainActiveTransport() {
return subchannel.obtainActiveTransport();
}
@Override
public void requestConnection() {
subchannel.obtainActiveTransport();
}
@Override
public EquivalentAddressGroup getAddresses() {
return subchannel.getAddressGroup();
}
@Override
public Attributes getAttributes() {
return Attributes.EMPTY;
}
};
subchannelPicker = new SubchannelPicker() {
final PickResult result = PickResult.withSubchannel(subchannelImpl);
@Override
public PickResult pickSubchannel(PickSubchannelArgs args) {
return result;
}
};
delayedTransport.reprocess(subchannelPicker);
}
void updateAddresses(EquivalentAddressGroup eag) {
subchannel.updateAddresses(eag);
}
@Override
public ClientCall newCall(
MethodDescriptor methodDescriptor, CallOptions callOptions) {
return new ClientCallImpl(methodDescriptor,
callOptions.getExecutor() == null ? executor : callOptions.getExecutor(),
callOptions, transportProvider, deadlineCancellationExecutor, channelTracer);
}
@Override
public String authority() {
return authority;
}
@Override
public boolean isTerminated() {
return terminatedLatch.getCount() == 0;
}
@Override
public boolean awaitTermination(long time, TimeUnit unit) throws InterruptedException {
return terminatedLatch.await(time, unit);
}
@Override
public ManagedChannel shutdown() {
shutdown = true;
delayedTransport.shutdown(Status.UNAVAILABLE.withDescription("OobChannel.shutdown() called"));
return this;
}
@Override
public boolean isShutdown() {
return shutdown;
}
@Override
public ManagedChannel shutdownNow() {
shutdown = true;
delayedTransport.shutdownNow(
Status.UNAVAILABLE.withDescription("OobChannel.shutdownNow() called"));
return this;
}
void handleSubchannelStateChange(final ConnectivityStateInfo newState) {
switch (newState.getState()) {
case READY:
case IDLE:
delayedTransport.reprocess(subchannelPicker);
break;
case TRANSIENT_FAILURE:
delayedTransport.reprocess(new SubchannelPicker() {
final PickResult errorResult = PickResult.withError(newState.getStatus());
@Override
public PickResult pickSubchannel(PickSubchannelArgs args) {
return errorResult;
}
});
break;
default:
// Do nothing
}
}
void handleSubchannelTerminated() {
// When delayedTransport is terminated, it shuts down subchannel. Therefore, at this point
// both delayedTransport and subchannel have terminated.
executorPool.returnObject(executor);
terminatedLatch.countDown();
}
@VisibleForTesting
Subchannel getSubchannel() {
return subchannelImpl;
}
@Override
public ListenableFuture getStats() {
SettableFuture ret = SettableFuture.create();
ret.set(channelTracer.getStats());
return ret;
}
@Override
public InternalLogId getLogId() {
return logId;
}
}