(control) GUI for triggering control-side actors

This commit is contained in:
Viktor Lofgren 2023-11-29 15:31:14 +01:00
parent 4155fbe94c
commit 67a1e1c874
6 changed files with 153 additions and 35 deletions

View File

@ -3,9 +3,9 @@ package nu.marginalia.control;
import spark.ResponseTransformer;
public class Redirects {
public static final HtmlRedirect redirectToActors = new HtmlRedirect("/actors");
public static final HtmlRedirect redirectToApiKeys = new HtmlRedirect("/api-keys");
public static final HtmlRedirect redirectToStorage = new HtmlRedirect("/storage");
public static final HtmlRedirect redirectToOverview = new HtmlRedirect("/");
public static final HtmlRedirect redirectToBlacklist = new HtmlRedirect("/blacklist");
public static final HtmlRedirect redirectToComplaints = new HtmlRedirect("/complaints");
public static final HtmlRedirect redirectToMessageQueue = new HtmlRedirect("/message-queue");

View File

@ -1,12 +1,16 @@
package nu.marginalia.control.sys.svc;
import com.google.inject.Inject;
import lombok.SneakyThrows;
import nu.marginalia.client.Context;
import nu.marginalia.control.Redirects;
import nu.marginalia.control.actor.ControlActor;
import nu.marginalia.control.actor.ControlActorService;
import nu.marginalia.db.DomainTypes;
import nu.marginalia.executor.client.ExecutorClient;
import nu.marginalia.mq.MessageQueueFactory;
import nu.marginalia.mq.outbox.MqOutbox;
import nu.marginalia.renderer.RendererFactory;
import nu.marginalia.service.control.ServiceEventLog;
import nu.marginalia.service.id.ServiceId;
import spark.Request;
@ -19,13 +23,23 @@ public class ControlSysActionsService {
private final MqOutbox apiOutbox;
private final DomainTypes domainTypes;
private final ServiceEventLog eventLog;
private final RendererFactory rendererFactory;
private final ControlActorService controlActorService;
private final ExecutorClient executorClient;
@Inject
public ControlSysActionsService(MessageQueueFactory mqFactory, DomainTypes domainTypes, ServiceEventLog eventLog, ExecutorClient executorClient) {
public ControlSysActionsService(MessageQueueFactory mqFactory,
DomainTypes domainTypes,
ServiceEventLog eventLog,
RendererFactory rendererFactory,
ControlActorService controlActorService,
ExecutorClient executorClient)
{
this.apiOutbox = createApiOutbox(mqFactory);
this.eventLog = eventLog;
this.domainTypes = domainTypes;
this.rendererFactory = rendererFactory;
this.controlActorService = controlActorService;
this.executorClient = executorClient;
}
@ -38,12 +52,17 @@ public class ControlSysActionsService {
return mqFactory.createOutbox(inboxName, 0, outboxName, 0, UUID.randomUUID());
}
@SneakyThrows
public void register() {
Spark.post("/public/actions/flush-api-caches", this::flushApiCaches, Redirects.redirectToActors);
Spark.post("/public/actions/reload-blogs-list", this::reloadBlogsList, Redirects.redirectToActors);
Spark.post("/public/actions/calculate-adjacencies", this::calculateAdjacencies, Redirects.redirectToActors);
Spark.post("/public/actions/truncate-links-database", this::truncateLinkDatabase, Redirects.redirectToActors);
Spark.post("/public/actions/trigger-data-exports", this::triggerDataExports, Redirects.redirectToActors);
var actionsView = rendererFactory.renderer("control/sys/sys-actions");
Spark.get("/public/actions", (rq,rsp) -> new Object(), actionsView::render);
Spark.post("/public/actions/recalculate-adjacencies-graph", this::calculateAdjacencies, Redirects.redirectToOverview);
Spark.post("/public/actions/reindex-all", this::reindexAll, Redirects.redirectToOverview);
Spark.post("/public/actions/reprocess-all", this::reprocessAll, Redirects.redirectToOverview);
Spark.post("/public/actions/flush-api-caches", this::flushApiCaches, Redirects.redirectToOverview);
Spark.post("/public/actions/reload-blogs-list", this::reloadBlogsList, Redirects.redirectToOverview);
Spark.post("/public/actions/trigger-data-exports", this::triggerDataExports, Redirects.redirectToOverview);
}
public Object triggerDataExports(Request request, Response response) throws Exception {
@ -54,23 +73,6 @@ public class ControlSysActionsService {
return "";
}
public Object truncateLinkDatabase(Request request, Response response) throws Exception {
String footgunLicense = request.queryParams("footgun-license");
if (!"YES".equals(footgunLicense)) {
Spark.halt(403);
return "You must agree to the footgun license to truncate the link database";
}
eventLog.logEvent("USER-ACTION", "FLUSH-LINK-DATABASE");
// FIXME:
// actors.start(Actor.TRUNCATE_LINK_DATABASE);
return "";
}
public Object reloadBlogsList(Request request, Response response) throws Exception {
eventLog.logEvent("USER-ACTION", "RELOAD-BLOGS-LIST");
@ -89,10 +91,26 @@ public class ControlSysActionsService {
public Object calculateAdjacencies(Request request, Response response) throws Exception {
eventLog.logEvent("USER-ACTION", "CALCULATE-ADJACENCIES");
// This is technically not a partitioned operation, but we execute it at node zero
// This is technically not a partitioned operation, but we execute it at node 1
// and let the effects be global :-)
executorClient.calculateAdjacencies(Context.fromRequest(request), 0);
executorClient.calculateAdjacencies(Context.fromRequest(request), 1);
return "";
}
public Object reindexAll(Request request, Response response) throws Exception {
eventLog.logEvent("USER-ACTION", "REINDEX-ALL");
controlActorService.start(ControlActor.REINDEX_ALL);
return "";
}
public Object reprocessAll(Request request, Response response) throws Exception {
eventLog.logEvent("USER-ACTION", "REPROCESS-ALL");
controlActorService.start(ControlActor.REPROCESS_ALL);
return "";
}

View File

@ -14,6 +14,11 @@
{{/unless}}
{{#if nodes}}
<div class="my-3 p-3 border bg-light">
Index nodes are processing units. The search engine requires at least one, but more can be added
to spread the system load across multiple physical disks or even multiple servers.
</div>
<table class="table">
<tr>
<th>Node ID</th>
@ -28,14 +33,6 @@
</tr>
{{/each}}
</table>
<div class="m-5 p-5 border bg-light">
<h2 class="my-3">Index Nodes</h2>
<p>
Index nodes are processing units. The search engine requires at least one, but more can be added
to spread the system load across multiple physical disks or even multiple servers.
</p>
</div>
{{/if}}
</div>

View File

@ -21,6 +21,7 @@
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">System</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/actions" title="System actions">Actions</a></li>
<li><a class="dropdown-item" href="/nodes" title="View and configure index nodes">Nodes</a></li>
<li><a class="dropdown-item" href="/datasets" title="View and update the data sets">Datasets</a></li>
<li><a class="dropdown-item" href="/events" title="View the event log">Events</a></li>

View File

@ -0,0 +1,102 @@
<!doctype html>
<html>
{{> control/partials/head-includes }}
<head><title>Control Service: Actions</title></head>
<body>
{{> control/partials/nav}}
<div class="container">
<h1 class="my-3">System Actions</h1>
<div class="my-3 border p-3 bg-light">
These are actions that perform system-wide operations,
on each node in the system. Nodes are included if they are
configured to be part of the <em>precession</em> in the Node Configuration.
For a single-node system, most of these actions may be triggered from the
single node's actions tab.
</div>
<div class="accordion mt-3" id="accordionActions">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseCalculateAdjacencies"
aria-expanded="false"
aria-controls="collapseCalculateAdjacencies">
Calculate Adjacencies
</button>
</h2>
<div id="collapseCalculateAdjacencies" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
<div class="mb-3">
This will re-calculate the adjacencies graph. The adjacencies graph is used to calculate the domain ranking.
</div>
<form method="post" action="actions/recalculate-adjacencies-graph">
<button
class="btn btn-primary me-md-2"
onclick="return confirm('Confirm recalculation of adjacencies graph');"
type="submit">
Recalculate Graph</button>
</form>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseReindexAll"
aria-expanded="false"
aria-controls="collapseReindexAll">
Recalculate Rankings
</button>
</h2>
<div id="collapseReindexAll" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
<div class="mb-3">
This will successively re-calculate the domain rankings for all nodes configured to be part in the precession,
and then re-construct the indexes from existing journal data.
</div>
<form method="post" action="actions/reindex-all">
<button
class="btn btn-primary me-md-2"
onclick="return confirm('Confirm reindexing of all nodes');"
type="submit">
Reconstruct Indexes</button>
</form>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseReprocessAll"
aria-expanded="false"
aria-controls="collapseReprocessAll">
Reprocess Data
</button>
</h2>
<div id="collapseReprocessAll" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
<div class="mb-3">
This will reprocess the crawl data on each node configured to be part in the precession, based on
the currently ACTIVE crawl data. If no crawl data is ACTIVE, the node will be omitted.
</div>
<form method="post" action="actions/reprocess-all">
<button
class="btn btn-primary me-md-2"
onclick="return confirm('Confirm reprocessing');"
type="submit">
Reprocess Crawl Data</button>
</form>
</div>
</div>
</div>
</div>
</body>
{{> control/partials/foot-includes }}
</html>

View File

@ -69,7 +69,7 @@ public class ProcessingService {
}
public Object startAdjacencyCalculation(Request request, Response response) throws Exception {
actorControlService.start(ExecutorActor.ADJACENCY_CALCULATION);
actorControlService.startFrom(ExecutorActor.ADJACENCY_CALCULATION, new TriggerAdjacencyCalculationActor.Run());
return "";
}