All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.ydb.core.impl.YdbTransportImpl Maven / Gradle / Ivy

package tech.ydb.core.impl;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;

import com.google.common.base.Strings;
import com.google.common.net.HostAndPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import tech.ydb.core.grpc.BalancingSettings;
import tech.ydb.core.grpc.GrpcRequestSettings;
import tech.ydb.core.grpc.GrpcTransport;
import tech.ydb.core.grpc.GrpcTransportBuilder;
import tech.ydb.core.impl.auth.AuthCallOptions;
import tech.ydb.core.impl.pool.EndpointPool;
import tech.ydb.core.impl.pool.EndpointRecord;
import tech.ydb.core.impl.pool.GrpcChannel;
import tech.ydb.core.impl.pool.GrpcChannelPool;
import tech.ydb.core.impl.pool.ManagedChannelFactory;

/**
 * @author Nikolay Perfilov
 */
public class YdbTransportImpl extends BaseGrpcTransport {
    static final int DEFAULT_PORT = 2135;

    private static final Logger logger = LoggerFactory.getLogger(YdbTransportImpl.class);

    private final String database;
    private final EndpointRecord discoveryEndpoint;
    private final ScheduledExecutorService scheduler;
    private final ManagedChannelFactory channelFactory;
    private final AuthCallOptions callOptions;
    private final EndpointPool endpointPool;
    private final GrpcChannelPool channelPool;
    private final YdbDiscovery discovery;

    public YdbTransportImpl(GrpcTransportBuilder builder) {
        BalancingSettings balancingSettings = getBalancingSettings(builder);
        Duration discoveryTimeout = Duration.ofMillis(builder.getDiscoveryTimeoutMillis());

        this.database = Strings.nullToEmpty(builder.getDatabase());
        this.discoveryEndpoint = getDiscoveryEndpoint(builder);

        logger.info("Create YDB transport with endpoint {} and {}", discoveryEndpoint, balancingSettings);

        this.channelFactory = builder.getManagedChannelFactory();
        this.scheduler = builder.getSchedulerFactory().get();
        this.callOptions = new AuthCallOptions(scheduler, Arrays.asList(discoveryEndpoint), channelFactory, builder);
        this.channelPool = new GrpcChannelPool(channelFactory, scheduler);
        this.endpointPool = new EndpointPool(balancingSettings);
        this.discovery = new YdbDiscovery(new DiscoveryHandler(), scheduler, database, discoveryTimeout);
    }

    public void start(GrpcTransportBuilder.InitMode mode) {
        if (mode == GrpcTransportBuilder.InitMode.ASYNC_FALLBACK) {
            endpointPool.setNewState(null, Collections.singletonList(discoveryEndpoint));
        }

        discovery.start();

        if (mode == GrpcTransportBuilder.InitMode.SYNC) {
            discovery.waitReady(-1);
        }
    }

    @Override
    public String toString() {
        return "YdbTransport{endpoint=" + discoveryEndpoint + ", database=" + database + "}";
    }

    @Deprecated
    public void startAsync(Runnable readyWatcher) {
        endpointPool.setNewState(null, Collections.singletonList(discoveryEndpoint));
        discovery.start();
        if (readyWatcher != null) {
            scheduler.execute(() -> {
                discovery.waitReady(-1);
                readyWatcher.run();
            });
        }
    }

    @Override
    protected void shutdown() {
        discovery.stop();
        channelPool.shutdown();
        callOptions.close();

        YdbSchedulerFactory.shutdownScheduler(scheduler);
    }

    static EndpointRecord getDiscoveryEndpoint(GrpcTransportBuilder builder) {
        URI endpointURI = null;
        try {
            String endpoint = builder.getEndpoint();
            if (endpoint != null) {
                if (endpoint.startsWith("grpc://") || endpoint.startsWith("grpcs://")) {
                    endpointURI = new URI(endpoint);
                } else {
                    endpointURI = new URI(null, endpoint, null, null, null);
                }
            }
            HostAndPort host = builder.getHost();
            if (host != null) {
                endpointURI = new URI(null, null, host.getHost(),
                        host.getPortOrDefault(DEFAULT_PORT), null, null, null);
            }
        } catch (URISyntaxException ex) {
            logger.warn("endpoint parse problem", ex);
        }
        if (endpointURI == null) {
            throw new IllegalArgumentException("Can't create discovery rpc, unreadable "
                    + "endpoint " + builder.getEndpoint() + " and empty host " + builder.getHost());
        }

        return new EndpointRecord(endpointURI.getHost(), endpointURI.getPort());
    }

    private static BalancingSettings getBalancingSettings(GrpcTransportBuilder builder) {
        BalancingSettings balancingSettings = builder.getBalancingSettings();
        if (balancingSettings != null) {
            return balancingSettings;
        }

        String localDc = builder.getLocalDc();
        if (localDc != null) {
            return BalancingSettings.fromLocation(builder.getLocalDc());
        }

        return BalancingSettings.defaultInstance();
    }

    @Override
    public ScheduledExecutorService getScheduler() {
        return scheduler;
    }

    @Override
    public String getDatabase() {
        return database;
    }

    @Override
    public AuthCallOptions getAuthCallOptions() {
        return callOptions;
    }

    @Override
    protected GrpcChannel getChannel(GrpcRequestSettings settings) {
        EndpointRecord endpoint = endpointPool.getEndpoint(settings.getPreferredNodeID());
        if (endpoint == null) {
            long timeout = -1;
            if (settings.getDeadlineAfter() != 0) {
                timeout = settings.getDeadlineAfter() - System.nanoTime();
            }
            discovery.waitReady(timeout);
            endpoint = endpointPool.getEndpoint(settings.getPreferredNodeID());
        }
        return channelPool.getChannel(endpoint);
    }

    @Override
    protected void updateChannelStatus(GrpcChannel channel, io.grpc.Status status) {
        // Usally CANCELLED is received when ClientCall is canceled on client side
        if (!status.isOk() && status.getCode() != io.grpc.Status.Code.CANCELLED) {
            endpointPool.pessimizeEndpoint(channel.getEndpoint());
        }
    }

    private class DiscoveryHandler implements YdbDiscovery.Handler {
        @Override
        public Instant instant() {
            return Instant.now();
        }

        @Override
        public boolean needToForceDiscovery() {
            return endpointPool.needToRunDiscovery();
        }

        @Override
        public CompletableFuture handleEndpoints(List endpoints, String selfLocation) {
            List removed = endpointPool.setNewState(selfLocation, endpoints);
            return channelPool.removeChannels(removed);
        }

        @Override
        public GrpcTransport createDiscoveryTransport() {
            return new FixedCallOptionsTransport(scheduler, callOptions, database, discoveryEndpoint, channelFactory);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy