mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 13:09:00 +00:00
(control) Helpful tooltips for the Actor table.
This commit is contained in:
parent
e51bf8619d
commit
8210e49b4e
@ -59,9 +59,9 @@ public class ActorStateMachine {
|
||||
registerStates(stateGraph);
|
||||
isDirectlyInitializable = stateGraph.isDirectlyInitializable();
|
||||
|
||||
for (var declaredState : stateGraph.declaredStates()) {
|
||||
if (!allStates.containsKey(declaredState.name())) {
|
||||
throw new IllegalArgumentException("State " + declaredState.name() + " is not defined in the state graph");
|
||||
stateGraph.declaredStates().forEach((name, declaredState) -> {
|
||||
if (!allStates.containsKey(name)) {
|
||||
throw new IllegalArgumentException("State " + name + " is not defined in the state graph");
|
||||
}
|
||||
if (!allStates.containsKey(declaredState.next())) {
|
||||
throw new IllegalArgumentException("State " + declaredState.next() + " is not defined in the state graph");
|
||||
@ -71,7 +71,7 @@ public class ActorStateMachine {
|
||||
throw new IllegalArgumentException("State " + state + " is not defined in the state graph");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
resume();
|
||||
|
||||
|
@ -18,6 +18,9 @@ public abstract class AbstractStateGraph {
|
||||
this.stateFactory = stateFactory;
|
||||
}
|
||||
|
||||
/** User-facing description of the actor. */
|
||||
public abstract String describe();
|
||||
|
||||
public void transition(String state) {
|
||||
throw new ControlFlowException(state, null);
|
||||
}
|
||||
@ -30,7 +33,6 @@ public abstract class AbstractStateGraph {
|
||||
throw new ControlFlowException("ERROR", "");
|
||||
}
|
||||
|
||||
|
||||
public <T> void error(T payload) {
|
||||
throw new ControlFlowException("ERROR", payload);
|
||||
}
|
||||
@ -54,13 +56,13 @@ public abstract class AbstractStateGraph {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<GraphState> declaredStates() {
|
||||
Set<GraphState> ret = new HashSet<>();
|
||||
public Map<String, GraphState> declaredStates() {
|
||||
Map<String, GraphState> ret = new HashMap<>();
|
||||
|
||||
for (var method : getClass().getMethods()) {
|
||||
var gs = method.getAnnotation(GraphState.class);
|
||||
if (gs != null) {
|
||||
ret.add(gs);
|
||||
ret.put(gs.name(), gs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package nu.marginalia.control.actor;
|
||||
public enum Actor {
|
||||
CRAWL,
|
||||
RECRAWL,
|
||||
RECONVERT_LOAD,
|
||||
CONVERT_AND_LOAD,
|
||||
CONVERTER_MONITOR,
|
||||
LOADER_MONITOR,
|
||||
CRAWLER_MONITOR,
|
||||
|
@ -35,7 +35,7 @@ public class ControlActors {
|
||||
GsonFactory gsonFactory,
|
||||
BaseServiceParams baseServiceParams,
|
||||
ConvertActor convertActor,
|
||||
ReconvertAndLoadActor reconvertAndLoadActor,
|
||||
ConvertAndLoadActor convertAndLoadActor,
|
||||
CrawlActor crawlActor,
|
||||
RecrawlActor recrawlActor,
|
||||
ConverterMonitorActor converterMonitorFSM,
|
||||
@ -56,7 +56,7 @@ public class ControlActors {
|
||||
register(Actor.CRAWL, crawlActor);
|
||||
register(Actor.RECRAWL, recrawlActor);
|
||||
register(Actor.CONVERT, convertActor);
|
||||
register(Actor.RECONVERT_LOAD, reconvertAndLoadActor);
|
||||
register(Actor.CONVERT_AND_LOAD, convertAndLoadActor);
|
||||
|
||||
register(Actor.CONVERTER_MONITOR, converterMonitorFSM);
|
||||
register(Actor.LOADER_MONITOR, loaderMonitor);
|
||||
|
@ -40,6 +40,10 @@ public class AbstractProcessSpawnerActor extends AbstractStateGraph {
|
||||
private final ProcessService.ProcessId processId;
|
||||
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
public String describe() {
|
||||
return "Spawns a(n) " + processId + " process and monitors its inbox for messages";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public AbstractProcessSpawnerActor(StateFactory stateFactory,
|
||||
MqPersistence persistence,
|
||||
|
@ -34,6 +34,11 @@ public class FileStorageMonitorActor extends AbstractStateGraph {
|
||||
private static final String END = "END";
|
||||
private final FileStorageService fileStorageService;
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Monitor the file storage directories and purge any file storage area that has been marked for deletion," +
|
||||
" and remove any file storage area that is missing from disk.";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public FileStorageMonitorActor(StateFactory stateFactory,
|
||||
|
@ -20,6 +20,10 @@ public class MessageQueueMonitorActor extends AbstractStateGraph {
|
||||
private static final String END = "END";
|
||||
private final MqPersistence persistence;
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Periodically run maintenance tasks on the message queue";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public MessageQueueMonitorActor(StateFactory stateFactory,
|
||||
|
@ -34,6 +34,11 @@ public class ProcessLivenessMonitorActor extends AbstractStateGraph {
|
||||
this.heartbeatService = heartbeatService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Periodically check to ensure that the control service's view of running processes is agreement with the process heartbeats table.";
|
||||
}
|
||||
|
||||
@GraphState(name = INITIAL, next = MONITOR)
|
||||
public void init() {
|
||||
}
|
||||
|
@ -52,6 +52,11 @@ public class ConvertActor extends AbstractStateGraph {
|
||||
public long loaderMsgId = 0L;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Convert a set of crawl data into a format suitable for loading into the database.";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public ConvertActor(StateFactory stateFactory,
|
||||
ActorProcessWatcher processWatcher,
|
||||
|
@ -30,7 +30,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
@Singleton
|
||||
public class ReconvertAndLoadActor extends AbstractStateGraph {
|
||||
public class ConvertAndLoadActor extends AbstractStateGraph {
|
||||
|
||||
// STATES
|
||||
|
||||
@ -63,13 +63,18 @@ public class ReconvertAndLoadActor extends AbstractStateGraph {
|
||||
public long loaderMsgId = 0L;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Process a set of crawl data and then load it into the database.";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public ReconvertAndLoadActor(StateFactory stateFactory,
|
||||
ActorProcessWatcher processWatcher,
|
||||
ProcessOutboxes processOutboxes,
|
||||
FileStorageService storageService,
|
||||
IndexClient indexClient,
|
||||
Gson gson
|
||||
public ConvertAndLoadActor(StateFactory stateFactory,
|
||||
ActorProcessWatcher processWatcher,
|
||||
ProcessOutboxes processOutboxes,
|
||||
FileStorageService storageService,
|
||||
IndexClient indexClient,
|
||||
Gson gson
|
||||
)
|
||||
{
|
||||
super(stateFactory);
|
@ -46,6 +46,11 @@ public class CrawlActor extends AbstractStateGraph {
|
||||
public long crawlerMsgId = 0L;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Run the crawler with the given crawl spec using no previous crawl data for a reference";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public CrawlActor(StateFactory stateFactory,
|
||||
ProcessOutboxes processOutboxes,
|
||||
|
@ -52,6 +52,11 @@ public class CrawlJobExtractorActor extends AbstractStateGraph {
|
||||
public record CrawlJobExtractorArguments(String description) { }
|
||||
public record CrawlJobExtractorArgumentsWithURL(String description, String url) { }
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Run the crawler job extractor process";
|
||||
}
|
||||
|
||||
@GraphState(name = CREATE_FROM_LINK, next = END,
|
||||
resume = ResumeBehavior.ERROR,
|
||||
description = """
|
||||
|
@ -48,6 +48,11 @@ public class ExportDataActor extends AbstractStateGraph {
|
||||
public FileStorageId storageId = null;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Export data from the database to a storage area of type EXPORT.";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public ExportDataActor(StateFactory stateFactory,
|
||||
FileStorageService storageService,
|
||||
|
@ -46,6 +46,10 @@ public class RecrawlActor extends AbstractStateGraph {
|
||||
public long crawlerMsgId = 0L;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Run the crawler with the given crawl spec using previous crawl data for a reference";
|
||||
}
|
||||
public static RecrawlMessage recrawlFromCrawlData(FileStorageId crawlData) {
|
||||
return new RecrawlMessage(null, crawlData, 0L);
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ public class TriggerAdjacencyCalculationActor extends AbstractStateGraph {
|
||||
this.processService = processService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Calculate website similarities";
|
||||
}
|
||||
|
||||
@GraphState(name = INITIAL, next = END,
|
||||
resume = ResumeBehavior.ERROR,
|
||||
description = """
|
||||
|
@ -33,6 +33,11 @@ public class TruncateLinkDatabase extends AbstractStateGraph {
|
||||
public FileStorageId storageId = null;
|
||||
};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "Remove all data from the link database.";
|
||||
}
|
||||
|
||||
@Inject
|
||||
public TruncateLinkDatabase(StateFactory stateFactory,
|
||||
HikariDataSource dataSource)
|
||||
|
@ -1,6 +1,11 @@
|
||||
package nu.marginalia.control.model;
|
||||
|
||||
public record ActorRunState(String name, String state, boolean terminal, boolean canStart) {
|
||||
public record ActorRunState(String name,
|
||||
String state,
|
||||
String actorDescription,
|
||||
String stateDescription,
|
||||
boolean terminal,
|
||||
boolean canStart) {
|
||||
public String stateIcon() {
|
||||
if (terminal) {
|
||||
return "\uD83D\uDE34";
|
||||
|
@ -7,17 +7,17 @@ import nu.marginalia.mqsm.state.MachineState;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record ActorStateGraph(List<ActorState> states) {
|
||||
public record ActorStateGraph(String description, List<ActorState> states) {
|
||||
|
||||
public ActorStateGraph(AbstractStateGraph graph, MachineState currentState) {
|
||||
this(getStateList(graph, currentState));
|
||||
this(graph.describe(), getStateList(graph, currentState));
|
||||
}
|
||||
|
||||
private static List<ActorState> getStateList(
|
||||
AbstractStateGraph graph,
|
||||
MachineState currentState)
|
||||
{
|
||||
Map<String, GraphState> declaredStates = graph.declaredStates().stream().collect(Collectors.toMap(GraphState::name, gs -> gs));
|
||||
Map<String, GraphState> declaredStates = graph.declaredStates();
|
||||
Set<GraphState> seenStates = new HashSet<>(declaredStates.size());
|
||||
LinkedList<GraphState> edge = new LinkedList<>();
|
||||
|
||||
|
@ -4,17 +4,20 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import nu.marginalia.control.actor.ControlActors;
|
||||
import nu.marginalia.control.actor.task.CrawlJobExtractorActor;
|
||||
import nu.marginalia.control.actor.task.ReconvertAndLoadActor;
|
||||
import nu.marginalia.control.actor.task.ConvertAndLoadActor;
|
||||
import nu.marginalia.control.actor.task.RecrawlActor;
|
||||
import nu.marginalia.control.actor.Actor;
|
||||
import nu.marginalia.control.model.ActorRunState;
|
||||
import nu.marginalia.control.model.ActorStateGraph;
|
||||
import nu.marginalia.db.storage.model.FileStorageId;
|
||||
import nu.marginalia.mqsm.graph.GraphState;
|
||||
import nu.marginalia.mqsm.state.MachineState;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Singleton
|
||||
public class ControlActorService {
|
||||
@ -64,7 +67,7 @@ public class ControlActorService {
|
||||
}
|
||||
public Object triggerProcessing(Request request, Response response) throws Exception {
|
||||
controlActors.start(
|
||||
Actor.RECONVERT_LOAD,
|
||||
Actor.CONVERT_AND_LOAD,
|
||||
FileStorageId.parse(request.params("fid"))
|
||||
);
|
||||
return "";
|
||||
@ -75,24 +78,45 @@ public class ControlActorService {
|
||||
|
||||
// Start the FSM from the intermediate state that triggers the load
|
||||
controlActors.startFrom(
|
||||
Actor.RECONVERT_LOAD,
|
||||
ReconvertAndLoadActor.LOAD,
|
||||
new ReconvertAndLoadActor.Message(null, fid, 0L, 0L)
|
||||
Actor.CONVERT_AND_LOAD,
|
||||
ConvertAndLoadActor.LOAD,
|
||||
new ConvertAndLoadActor.Message(null, fid, 0L, 0L)
|
||||
);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<String, String> actorStateDescriptions = new ConcurrentHashMap<>();
|
||||
|
||||
public Object getActorStates() {
|
||||
return controlActors.getActorStates().entrySet().stream().map(e -> {
|
||||
|
||||
final var stateGraph = controlActors.getActorDefinition(e.getKey());
|
||||
|
||||
final MachineState state = e.getValue();
|
||||
final String actorDescription = stateGraph.describe();
|
||||
|
||||
final String machineName = e.getKey().name();
|
||||
final String stateName = state.name();
|
||||
|
||||
final String stateDescription = actorStateDescriptions.computeIfAbsent(
|
||||
(machineName + "." + stateName),
|
||||
k -> Optional.ofNullable(stateGraph.declaredStates().get(stateName))
|
||||
.map(GraphState::description)
|
||||
.orElse("Description missing for " + stateName)
|
||||
);
|
||||
|
||||
|
||||
|
||||
final boolean terminal = state.isFinal();
|
||||
final boolean canStart = controlActors.isDirectlyInitializable(e.getKey()) && terminal;
|
||||
|
||||
return new ActorRunState(machineName, stateName, terminal, canStart);
|
||||
return new ActorRunState(machineName,
|
||||
stateName,
|
||||
actorDescription,
|
||||
stateDescription,
|
||||
terminal,
|
||||
canStart);
|
||||
})
|
||||
.filter(s -> !s.terminal() || s.canStart())
|
||||
.sorted(Comparator.comparing(ActorRunState::name))
|
||||
|
@ -9,6 +9,7 @@
|
||||
{{> control/partials/nav}}
|
||||
<section>
|
||||
<h1>{{actor}}</h1>
|
||||
<p>{{state-graph.description}}</p>
|
||||
{{> control/partials/actor-state-graph}}
|
||||
{{> control/partials/message-queue-table}}
|
||||
</section>
|
||||
|
@ -7,8 +7,8 @@
|
||||
</tr>
|
||||
{{#each actors}}
|
||||
<tr>
|
||||
<td><a href="/actors/{{name}}">{{name}}</a></td>
|
||||
<td>{{stateIcon}} {{state}}</td>
|
||||
<td title="{{actorDescription}}"><a href="/actors/{{name}}">{{name}}</a></td>
|
||||
<td title="{{stateDescription}}">{{stateIcon}} {{state}}</td>
|
||||
<td>
|
||||
{{#unless terminal}}
|
||||
<form id="toggle-{{name}}"
|
||||
|
Loading…
Reference in New Issue
Block a user