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

com.turbospaces.grpc.consumer.GrpcRequestConsumer Maven / Gradle / Ivy

There is a newer version: 2.0.33
Show newest version
package com.turbospaces.grpc.consumer;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.Any;
import com.google.protobuf.Message;
import com.turbospaces.api.Topic;
import com.turbospaces.api.TopicRegistry;
import com.turbospaces.api.facade.RequestWrapperFacade;
import com.turbospaces.api.jpa.CompositeStackTracer;
import com.turbospaces.boot.Bootstrap;
import com.turbospaces.cfg.ApplicationConfig;
import com.turbospaces.common.PlatformUtil;
import com.turbospaces.dispatch.AbstractServerRequestConsumer;
import com.turbospaces.dispatch.EmbeddedTransactionalRequest;
import com.turbospaces.dispatch.RequestQueuePostSpec;
import com.turbospaces.dispatch.SafeRequestHandler;
import com.turbospaces.dispatch.TransactionalRequestHandler;
import com.turbospaces.dispatch.TransactionalRequestOutcome;
import com.turbospaces.dispatch.WorkerCompletableTask;
import com.turbospaces.executor.WorkUnit;
import com.turbospaces.grpc.GrpcContextWorkUnit;
import com.turbospaces.grpc.GrpcContextWorkerFactory;
import com.turbospaces.grpc.GrpcHeaders;
import com.turbospaces.mdc.ContextPropagator;
import com.turbospaces.mdc.MdcTags;
import com.turbospaces.rpc.QueuePostTemplate;

import api.v1.ApiFactory;
import io.grpc.stub.StreamObserver;
import io.netty.util.AsciiString;
import io.opentracing.Span;
import jakarta.inject.Inject;

@Service
public class GrpcRequestConsumer extends AbstractServerRequestConsumer {
    private final TopicRegistry topicRegistry;
    private final Map> handlers;
    private GrpcContextWorkerFactory workerFactory;

    @Inject
    public GrpcRequestConsumer(
            TopicRegistry topicRegistry,
            QueuePostTemplate postTemplate,
            List> handlers,
            GrpcContextWorkerFactory workerFactory,
            ApiFactory apiFactory,
            CompositeStackTracer stackTracer) {
        super(postTemplate, apiFactory, stackTracer);
        this.topicRegistry = Objects.requireNonNull(topicRegistry);
        this.workerFactory = Objects.requireNonNull(workerFactory);
        this.handlers = ImmutableMap.copyOf(handlers.stream().collect(
                Collectors.toMap(h -> {
                    Message defaultInstance = com.google.protobuf.Internal.getDefaultInstance(h.requestType());
                    Any any = Any.pack(defaultInstance);
                    return any.getTypeUrl();
                }, Function.identity())));
    }
    public  void accept(
            REQ req,
            Class type,
            StreamObserver responseObserver) throws Throwable {
        UUID messageId = UUID.fromString(GrpcHeaders.CONTEXT_MESSAGE_ID.get());
        GrpcContextWorkUnit unit = GrpcContextWorkUnit.fromCurrentContext();

        RequestQueuePostSpec spec = (RequestQueuePostSpec) RequestQueuePostSpec
                .newBuilder(req)
                .setMessageId(messageId)
                .setTopic(new Topic() {
                    @Override
                    public AsciiString name() {
                        return AsciiString.cached(unit.topic());
                    }
                    @Override
                    public void configure(ApplicationConfig cfg) {

                    }
                }).build();
        RequestWrapperFacade reqw = apiFactory.requestMapper().pack(
                spec,
                new ContextPropagator() {
                    @Override
                    public void injectContext(api.v1.Headers.Builder into) {
                        into.setMessageId(messageId.toString());

                        Optional.ofNullable(GrpcHeaders.CONTEXT_TRACE_ID.get()).ifPresent(v -> into.setTraceId(v));
                        Optional.ofNullable(GrpcHeaders.CONTEXT_BRAND_NAME.get()).ifPresent(v -> into.setBrandName(v));
                    }
                }, timeout);

        Optional> completable = Optional.empty();
        CountDownLatch latch = new CountDownLatch(1);

        try {
            completable = logAndAccept(reqw, unit, latch);
            if (completable.isEmpty()) {

            }
        } catch (Exception err) {
            logger.error(err.getMessage(), err);
            latch.countDown();
            completable = Optional.of(convertUnhandledException(unit, err));
        } finally {

        }

        if (completable.isPresent()) {
            FluentFuture fluent = FluentFuture.from(completable.get());
            fluent.addCallback(new PublishOperationOutcomeCallback<>(type, responseObserver), MoreExecutors.directExecutor());
        }
    }
    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public WorkerCompletableTask schedule(WorkUnit unit, RequestWrapperFacade reqw, CountDownLatch latch) throws Throwable {
        var body = reqw.body();
        var span = span(bootstrap, unit, reqw);
        var handler = Objects.requireNonNull(handlers.get(body.getTypeUrl()));

        var callbackExecutor = MoreExecutors.directExecutor();
        var tx = new EmbeddedTransactionalRequest<>(handler.requestType(), handler.newReply(), unit, reqw, latch);
        var worker = workerFactory.worker(unit);
        var sh = new SafeRequestHandler(reqw, apiFactory, stackTracer, span, unit, tx, handler);
        var run = handler.decorate(topicRegistry, unit, sh);
        var callback = new FutureCallback() {
            @Override
            public void onSuccess(Object result) {

            }
            @Override
            public void onFailure(Throwable t) {
                logger.error(t.getMessage(), t);
            }
        };

        sh.setBootstrap(bootstrap);

        if (handler.actorIsRequired()) {
            FluentFuture.from(worker.actor(unit).submit(run)).addCallback(callback, callbackExecutor);
        } else {
            FluentFuture.from(worker.ifPresent(unit).submit(run)).addCallback(callback, callbackExecutor);
        }
        return sh.get();
    }
    private static Span span(Bootstrap bootstrap, WorkUnit record, RequestWrapperFacade reqw) {
        var body = reqw.body();
        var operation = PlatformUtil.toLowerUnderscore(body.getTypeUrl());

        //
        // ~ create child span
        //
        var buildSpan = bootstrap.tracer().buildSpan(operation);

        var span = buildSpan.start();
        span.setTag(MdcTags.MDC_MESSAGE_ID, reqw.headers().getMessageId());

        if (Objects.nonNull(record.key())) {
            AsciiString partitionKey = new AsciiString(record.key());
            span.setTag(MdcTags.MDC_ROUTING_KEY, partitionKey.toString());
        }

        return span;
    }

    private class PublishOperationOutcomeCallback implements FutureCallback {
        private final Class type;
        private final StreamObserver responseObserver;

        public PublishOperationOutcomeCallback(Class type, StreamObserver responseObserver) {
            this.type = Objects.requireNonNull(type);
            this.responseObserver = Objects.requireNonNull(responseObserver);
        }
        @Override
        public void onSuccess(TransactionalRequestOutcome result) {
            try {
                postTemplate.publishNotifications(apiFactory.notifyTopic(), result);
                postTemplate.publishEvents(apiFactory.eventsTopic(), result);

                RESP resp = result.getReply().unpack(type);
                responseObserver.onNext(resp);
                responseObserver.onCompleted();
            } catch (Exception err) {
                responseObserver.onError(err);
            }
        }
        @Override
        public void onFailure(Throwable t) {
            try {
                responseObserver.onError(t);
            } finally {

            }
        }
    }
}