MarginaliaSearch/code/common/service/java/nu/marginalia/process/ProcessMainClass.java
Viktor Lofgren 51e46ad2b0 (refac) Move export tasks to a process and clean up process initialization for all ProcessMainClass descendents
Since some of the export tasks have been memory hungry, sometimes killing the executor-services, they've been moved to a separate process that can be given a larger Xmx.

While doing this, the ProcessMainClass was given utilities for the boilerplate surrounding receiving mq requests and responding to them, some effort was also put toward making the process boot process a bit more uniform.  It's still a bit heterogeneous between different processes, but a bit less so for now.
2024-11-21 16:00:09 +01:00

103 lines
3.2 KiB
Java

package nu.marginalia.process;
import com.google.gson.Gson;
import nu.marginalia.mq.MessageQueueFactory;
import nu.marginalia.mq.MqMessage;
import nu.marginalia.mq.inbox.MqInboxResponse;
import nu.marginalia.mq.inbox.MqSingleShotInbox;
import nu.marginalia.service.ConfigLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public abstract class ProcessMainClass {
private static final Logger logger = LoggerFactory.getLogger(ProcessMainClass.class);
private final MessageQueueFactory messageQueueFactory;
private final int node;
private final String inboxName;
static {
// Load global config ASAP
ConfigLoader.loadConfig(
ConfigLoader.getConfigPath("system")
);
}
private final Gson gson;
public ProcessMainClass(MessageQueueFactory messageQueueFactory,
ProcessConfiguration config,
Gson gson,
String inboxName
) {
this.gson = gson;
new org.mariadb.jdbc.Driver();
this.messageQueueFactory = messageQueueFactory;
this.node = config.node();
this.inboxName = inboxName;
}
protected <T> Instructions<T> fetchInstructions(Class<T> requestType) throws Exception {
var inbox = messageQueueFactory.createSingleShotInbox(inboxName, node, UUID.randomUUID());
logger.info("Waiting for instructions");
var msgOpt = getMessage(inbox, requestType.getSimpleName());
var msg = msgOpt.orElseThrow(() -> new RuntimeException("No message received"));
// for live crawl, request is empty for now
T request = gson.fromJson(msg.payload(), requestType);
return new Instructions<>(msg, inbox, request);
}
private Optional<MqMessage> getMessage(MqSingleShotInbox inbox, String expectedFunction) throws InterruptedException, SQLException {
var opt = inbox.waitForMessage(30, TimeUnit.SECONDS);
if (opt.isPresent()) {
if (!opt.get().function().equals(expectedFunction)) {
throw new RuntimeException("Unexpected function: " + opt.get().function());
}
return opt;
}
else {
var stolenMessage = inbox.stealMessage(msg -> msg.function().equals(expectedFunction));
stolenMessage.ifPresent(mqMessage -> logger.info("Stole message {}", mqMessage));
return stolenMessage;
}
}
protected static class Instructions<T> {
private final MqMessage message;
private final MqSingleShotInbox inbox;
private final T value;
Instructions(MqMessage message, MqSingleShotInbox inbox, T value)
{
this.message = message;
this.inbox = inbox;
this.value = value;
}
public T value() {
return value;
}
public void ok() {
inbox.sendResponse(message, MqInboxResponse.ok());
}
public void err() {
inbox.sendResponse(message, MqInboxResponse.err());
}
}
}