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

tech.ydb.core.impl.call.ReadStreamCall Maven / Gradle / Ivy

package tech.ydb.core.impl.call;

import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Nullable;

import com.google.protobuf.Message;
import com.google.protobuf.TextFormat;
import io.grpc.ClientCall;
import io.grpc.Metadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import tech.ydb.core.Status;
import tech.ydb.core.grpc.GrpcReadStream;
import tech.ydb.core.grpc.GrpcStatuses;
import tech.ydb.core.grpc.GrpcTransport;

/**
 *
 * @author Aleksandr Gorshenin
 * @param  type of call argument
 * @param  type of read stream messages
 */
public class ReadStreamCall extends ClientCall.Listener implements GrpcReadStream {
    private static final Logger logger = LoggerFactory.getLogger(GrpcTransport.class);

    private final String traceId;
    private final ClientCall call;
    private final GrpcStatusHandler statusConsumer;
    private final ReqT request;
    private final Metadata headers;

    private final CompletableFuture statusFuture = new CompletableFuture<>();
    private final AtomicReference> observerReference = new AtomicReference<>();

    public ReadStreamCall(
            String traceId,
            ClientCall call,
            ReqT request,
            Metadata headers,
            GrpcStatusHandler statusConsumer
    ) {
        this.traceId = traceId;
        this.call = call;
        this.request = request;
        this.headers = headers;
        this.statusConsumer = statusConsumer;
    }

    @Override
    public CompletableFuture start(Observer observer) {
        if (!observerReference.compareAndSet(null, observer)) {
            throw new IllegalStateException("Read stream call is already started");
        }

        synchronized (call) {
            try {
                call.start(this, headers);
                call.request(1);
                if (logger.isTraceEnabled()) {
                    logger.trace("ReadStreamCall[{}] --> {}", traceId, TextFormat.shortDebugString((Message) request));
                }
                call.sendMessage(request);
                // close stream by client side
                call.halfClose();
            } catch (Throwable t) {
                try {
                    call.cancel(null, t);
                } catch (Throwable ex) {
                    logger.error("ReadStreamCall[{}] got exception while canceling", traceId, ex);
                }

                statusFuture.completeExceptionally(t);
            }
        }

        return statusFuture;
    }

    @Override
    public void cancel() {
        synchronized (call) {
            call.cancel("Cancelled on user request", new CancellationException());
        }
    }

    @Override
    public void onMessage(RespT message) {
        try {
            if (logger.isTraceEnabled()) {
                logger.trace("ReadStreamCall[{}] <-- {}", traceId, TextFormat.shortDebugString((Message) message));
            }
            observerReference.get().onNext(message);
            // request delivery of the next inbound message.
            synchronized (call) {
                call.request(1);
            }
        } catch (Exception ex) {
            statusFuture.completeExceptionally(ex);

            try {
                synchronized (call) {
                    call.cancel("Canceled by exception from observer", ex);
                }
            } catch (Throwable th) {
                logger.error("ReadStreamCall[{}] got exception while canceling", traceId, th);
            }
        }
    }

    @Override
    public void onClose(io.grpc.Status status, @Nullable Metadata trailers) {
        if (logger.isTraceEnabled()) {
            logger.trace("ReadStreamCall[{}] closed with status {}", status);
        }

        statusConsumer.accept(status, trailers);

        if (status.isOk()) {
            statusFuture.complete(Status.SUCCESS);
        } else {
            statusFuture.complete(GrpcStatuses.toStatus(status));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy