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.RequestReplyPubsubConsumer Maven / Gradle / Ivy
package com.turbospaces.gcp.pubsub.consumer;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import org.slf4j.MDC;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.messaging.Message;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.Any;
import com.turbospaces.api.Topic;
import com.turbospaces.api.facade.ResponseWrapperFacade;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.common.NonBlockingCallOnly;
import com.turbospaces.common.PlatformUtil;
import com.turbospaces.executor.ContextWorker;
import com.turbospaces.gcp.pubsub.PubsubRecord;
import com.turbospaces.gcp.pubsub.PubsubWorkUnit;
import com.turbospaces.http.HttpProto;
import com.turbospaces.mdc.MdcTags;
import com.turbospaces.mdc.MdcUtil;
import com.turbospaces.metrics.MetricTags;
import com.turbospaces.rpc.DefaultRequestReplyMapper;
import api.v1.ApiFactory;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.vavr.CheckedRunnable;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RequestReplyPubsubConsumer implements MessageProcessor {
private final ApplicationProperties props;
private final MeterRegistry meterRegistry;
private final Topic topic;
private final DefaultRequestReplyMapper reqReplyMapper;
private final PubsubContextWorkerFactory workerFactory;
private final ApiFactory apiFactory;
@Inject
public RequestReplyPubsubConsumer(
ApplicationProperties props,
MeterRegistry meterRegistry,
Topic topic,
DefaultRequestReplyMapper reqReplyMapper,
PubsubContextWorkerFactory workerFactory,
ApiFactory apiFactory) {
this.meterRegistry = Objects.requireNonNull(meterRegistry);
this.props = Objects.requireNonNull(props);
this.topic = Objects.requireNonNull(topic);
this.reqReplyMapper = Objects.requireNonNull(reqReplyMapper);
this.workerFactory = Objects.requireNonNull(workerFactory);
this.apiFactory = Objects.requireNonNull(apiFactory);
}
@Override
public Object processMessage(Message> message) {
PubsubWorkUnit record = new PubsubRecord(topic, message);
var messageReceiveTime = System.currentTimeMillis();
ContextWorker worker = workerFactory.worker(record).ifPresent(record); // ~ schedule in parallel
FluentFuture.from(worker.submit(new CheckedRunnable() {
@Override
public void run() throws Throwable {
var mapper = apiFactory.responseMapper();
ResponseWrapperFacade respw = mapper.unpack(record);
Any body = respw.body();
api.v1.Headers headers = respw.headers();
String operation = PlatformUtil.toLowerUnderscore(body.getTypeUrl());
String status = respw.status().errorCode().toString().toLowerCase().intern();
MdcUtil.setMdc(record, operation, headers);
Thread currentThread = Thread.currentThread();
String oldName = currentThread.getName();
String newName = oldName + "|" + operation;
currentThread.setName(newName);
String createdAt = record.attributes().get(HttpProto.HEADER_X_TIMESTAMP);
if (createdAt != null) {
var respCreationTimestamp = Long.parseLong(createdAt);
var respReceiveTimestamp = System.currentTimeMillis();
var networkTrip = respReceiveTimestamp - respCreationTimestamp;
if (networkTrip > props.PUBSUB_NETWORK_TRIP_ALERT_THRESHOLD.get().toMillis()) {
log.warn("High network trip: IN ::: (t-{}):(t-{}(m-{},s-{})) attributes:{}",
record.topic(),
body.getTypeUrl(),
headers.getMessageId(),
status,
record.attributes());
}
}
try {
NonBlockingCallOnly.run(new CheckedRunnable() {
@Override
public void run() throws Throwable {
UUID messageId = UUID.fromString(headers.getMessageId());
if (reqReplyMapper.contains(messageId)) {
log.debug("IN ::: (t-{}):(t-{}(m-{},s-{})) attributes:{}",
record.topic(),
body.getTypeUrl(),
headers.getMessageId(),
status,
record.attributes());
reqReplyMapper.complete(messageId, respw);
}
}
});
} finally {
record.ack().whenComplete(new BiConsumer() {
@Override
public void accept(Void result, Throwable ex) {
if (Objects.nonNull(ex)) {
try {
MdcUtil.setMdc(record, operation, headers);
log.error("pubsub ack failure ::: (t-{}):(t-{}(m-{},s-{}))",
record.topic(),
body.getTypeUrl(),
headers.getMessageId(),
status,
ex);
} finally {
MdcUtil.clearMdc(record);
}
} else {
try {
MdcUtil.setMdc(record, operation, headers);
meterRegistry.timer(
"pubsub.reply.ack.delay",
List.of(Tag.of("topic", MDC.get(MdcTags.MDC_TOPIC)),
Tag.of(MetricTags.OPERATION, MDC.get(MdcTags.MDC_OPERATION)) //
)).record(System.currentTimeMillis() - messageReceiveTime, TimeUnit.MILLISECONDS);
log.trace("pubsub ack success ::: (t-{}):(t-{}(m-{},s-{}))",
record.topic(),
body.getTypeUrl(),
headers.getMessageId(),
status);
} finally {
MdcUtil.clearMdc(record);
}
}
}
});
currentThread.setName(oldName);
MdcUtil.clearMdc(record);
}
}
})).addCallback(new FutureCallback() {
@Override
public void onSuccess(Object result) {
}
@Override
public void onFailure(Throwable t) {
log.error(t.getMessage(), t);
}
}, MoreExecutors.directExecutor());
return new Object();
}
}