All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.turbospaces.gcp.pubsub.consumer.PubsubRequestConsumer Maven / Gradle / Ivy
package com.turbospaces.gcp.pubsub.consumer;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import com.google.cloud.spring.pubsub.support.BasicAcknowledgeablePubsubMessage;
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.facade.RequestWrapperFacade;
import com.turbospaces.api.jpa.CompositeStackTracer;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.common.PlatformUtil;
import com.turbospaces.dispatch.AbstractServerRequestConsumer;
import com.turbospaces.dispatch.SafeRequestHandler;
import com.turbospaces.dispatch.TransactionalRequestHandler;
import com.turbospaces.dispatch.TransactionalRequestOutcome;
import com.turbospaces.executor.WorkUnit;
import com.turbospaces.gcp.pubsub.PubsubRecord;
import com.turbospaces.gcp.pubsub.PubsubTransactionalRequest;
import com.turbospaces.gcp.pubsub.PubsubWorkUnit;
import com.turbospaces.mdc.MdcTags;
import com.turbospaces.rpc.QueuePostTemplate;
import api.v1.ApiFactory;
import io.micrometer.core.instrument.MeterRegistry;
import io.netty.util.AsciiString;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import jakarta.inject.Inject;
public class PubsubRequestConsumer extends AbstractServerRequestConsumer implements Consumer {
private final QueuePostTemplate> postTemplate;
private final Tracer tracer;
private final MeterRegistry meterRegistry;
private final Topic topic;
private final PubsubContextWorkerFactory workerFactory;
private final Map> handlers;
@Inject
public PubsubRequestConsumer(
ApplicationProperties props,
Tracer tracer,
MeterRegistry meterRegistry,
Topic topic,
ApiFactory apiFactory,
CompositeStackTracer stackTracer,
QueuePostTemplate> postTemplate,
List> handlers,
PubsubContextWorkerFactory workerFactory) {
super(props, apiFactory, stackTracer);
this.postTemplate = Objects.requireNonNull(postTemplate);
this.meterRegistry = Objects.requireNonNull(meterRegistry);
this.tracer = Objects.requireNonNull(tracer);
this.topic = Objects.requireNonNull(topic);
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())));
}
@Override
public void accept(BasicAcknowledgeablePubsubMessage message) {
PubsubWorkUnit unit = new PubsubRecord(topic, message);
Optional> completable = Optional.empty();
CountDownLatch latch = new CountDownLatch(1);
try {
var decoder = apiFactory.requestMapper();
var reqw = decoder.unpack(unit);
completable = logAndAccept(reqw, unit, latch);
if (completable.isEmpty()) {
unit.ack();
}
} catch (Throwable err) {
logger.error(err.getMessage(), err);
latch.countDown();
completable = Optional.of(convertUnhandledException(unit, err));
}
if (completable.isPresent()) {
FluentFuture.from(completable.get()).addCallback(new PublishOperationOutcomeCallback<>(), MoreExecutors.directExecutor());
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public ListenableFuture schedule(WorkUnit unit, RequestWrapperFacade reqw, CountDownLatch latch) throws Throwable {
var record = (PubsubWorkUnit) unit;
var body = reqw.body();
var span = span(record, reqw);
var typeUrl = body.getTypeUrl();
var handler = Objects.requireNonNull(handlers.get(typeUrl), "unable to locate handler for type: " + typeUrl);
var callbackExecutor = MoreExecutors.directExecutor();
var worker = workerFactory.worker(record);
var tx = new PubsubTransactionalRequest<>(handler.requestType(), handler.newReply(), unit, reqw, latch);
var sh = new SafeRequestHandler(meterRegistry, tracer, reqw, apiFactory, stackTracer, span, record, tx, handler);
var callback = new FutureCallback<>() {
@Override
public void onSuccess(Object result) {
//
// ~ access executor not to clean it up, in case there are pending long running tasks in queue
//
worker.actor(unit, handler.actorIsRequired());
}
@Override
public void onFailure(Throwable t) {
logger.error(t.getMessage(), t);
}
};
if (handler.isImmediateAcknowledge()) {
logger.debug("got request from topic: {}, about to early acknowledge: {}", topic.name(), unit);
sh.getTransaction().ack();
} else if (BooleanUtils.isFalse(handler.actorIsRequired())) {
logger.debug("got non-sequential request from topic: {}, about to early acknowledge: {}", topic.name(), unit);
sh.getTransaction().ack();
}
//
// ~ should not happen
//
if (handler.actorIsRequired() && Objects.isNull(unit.key())) {
throw new IllegalArgumentException("no routing key provided for " + unit.toString());
}
var actor = worker.actor(unit, handler.actorIsRequired());
FluentFuture.from(actor.submit(sh)).addCallback(callback, callbackExecutor);
return sh.get();
}
private Span span(PubsubWorkUnit record, RequestWrapperFacade reqw) {
var body = reqw.body();
var operation = PlatformUtil.toLowerUnderscore(body.getTypeUrl());
//
// ~ extract tracing information
//
var parentScope = tracer.extract(Format.Builtin.TEXT_MAP, new PubsubSpanContextExtractor(record));
//
// ~ create child span
//
var buildSpan = tracer.buildSpan(operation);
if (parentScope != null) {
buildSpan = buildSpan.asChildOf(parentScope);
}
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 {
@Override
public void onSuccess(TransactionalRequestOutcome result) {
try {
postTemplate.publishReply(result);
postTemplate.publishNotifications(apiFactory.notifyTopic(), result);
postTemplate.publishEvents(apiFactory.eventsTopic(), result);
} catch (Exception err) {
logger.atError().setCause(err).log();
}
}
@Override
public void onFailure(Throwable t) {
try {
} finally {
}
}
}
}