mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 04:58:59 +00:00
(conf) Introduce a new concept of node profiles
Node profiles decide which actors are started, and which views are available in the control GUI. This helps keep the system organized, and hides real-time clutter from the batch-oriented nodes.
This commit is contained in:
parent
f94911541a
commit
47dfbacb00
@ -3,6 +3,7 @@ package nu.marginalia.nodecfg;
|
||||
import com.google.inject.Inject;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import nu.marginalia.nodecfg.model.NodeConfiguration;
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -20,10 +21,10 @@ public class NodeConfigurationService {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public NodeConfiguration create(int id, String description, boolean acceptQueries, boolean keepWarcs) throws SQLException {
|
||||
public NodeConfiguration create(int id, String description, boolean acceptQueries, boolean keepWarcs, NodeProfile nodeProfile) throws SQLException {
|
||||
try (var conn = dataSource.getConnection();
|
||||
var is = conn.prepareStatement("""
|
||||
INSERT IGNORE INTO NODE_CONFIGURATION(ID, DESCRIPTION, ACCEPT_QUERIES, KEEP_WARCS) VALUES(?, ?, ?, ?)
|
||||
INSERT IGNORE INTO NODE_CONFIGURATION(ID, DESCRIPTION, ACCEPT_QUERIES, KEEP_WARCS, NODE_PROFILE) VALUES(?, ?, ?, ?, ?)
|
||||
""")
|
||||
)
|
||||
{
|
||||
@ -31,6 +32,7 @@ public class NodeConfigurationService {
|
||||
is.setString(2, description);
|
||||
is.setBoolean(3, acceptQueries);
|
||||
is.setBoolean(4, keepWarcs);
|
||||
is.setString(5, nodeProfile.name());
|
||||
|
||||
if (is.executeUpdate() <= 0) {
|
||||
throw new IllegalStateException("Failed to insert configuration");
|
||||
@ -43,7 +45,7 @@ public class NodeConfigurationService {
|
||||
public List<NodeConfiguration> getAll() {
|
||||
try (var conn = dataSource.getConnection();
|
||||
var qs = conn.prepareStatement("""
|
||||
SELECT ID, DESCRIPTION, ACCEPT_QUERIES, AUTO_CLEAN, PRECESSION, KEEP_WARCS, DISABLED
|
||||
SELECT ID, DESCRIPTION, ACCEPT_QUERIES, AUTO_CLEAN, PRECESSION, KEEP_WARCS, NODE_PROFILE, DISABLED
|
||||
FROM NODE_CONFIGURATION
|
||||
""")) {
|
||||
var rs = qs.executeQuery();
|
||||
@ -58,6 +60,7 @@ public class NodeConfigurationService {
|
||||
rs.getBoolean("AUTO_CLEAN"),
|
||||
rs.getBoolean("PRECESSION"),
|
||||
rs.getBoolean("KEEP_WARCS"),
|
||||
NodeProfile.valueOf(rs.getString("NODE_PROFILE")),
|
||||
rs.getBoolean("DISABLED")
|
||||
));
|
||||
}
|
||||
@ -72,7 +75,7 @@ public class NodeConfigurationService {
|
||||
public NodeConfiguration get(int nodeId) throws SQLException {
|
||||
try (var conn = dataSource.getConnection();
|
||||
var qs = conn.prepareStatement("""
|
||||
SELECT ID, DESCRIPTION, ACCEPT_QUERIES, AUTO_CLEAN, PRECESSION, KEEP_WARCS, DISABLED
|
||||
SELECT ID, DESCRIPTION, ACCEPT_QUERIES, AUTO_CLEAN, PRECESSION, KEEP_WARCS, NODE_PROFILE, DISABLED
|
||||
FROM NODE_CONFIGURATION
|
||||
WHERE ID=?
|
||||
""")) {
|
||||
@ -86,6 +89,7 @@ public class NodeConfigurationService {
|
||||
rs.getBoolean("AUTO_CLEAN"),
|
||||
rs.getBoolean("PRECESSION"),
|
||||
rs.getBoolean("KEEP_WARCS"),
|
||||
NodeProfile.valueOf(rs.getString("NODE_PROFILE")),
|
||||
rs.getBoolean("DISABLED")
|
||||
);
|
||||
}
|
||||
@ -98,7 +102,7 @@ public class NodeConfigurationService {
|
||||
try (var conn = dataSource.getConnection();
|
||||
var us = conn.prepareStatement("""
|
||||
UPDATE NODE_CONFIGURATION
|
||||
SET DESCRIPTION=?, ACCEPT_QUERIES=?, AUTO_CLEAN=?, PRECESSION=?, KEEP_WARCS=?, DISABLED=?
|
||||
SET DESCRIPTION=?, ACCEPT_QUERIES=?, AUTO_CLEAN=?, PRECESSION=?, KEEP_WARCS=?, DISABLED=?, NODE_PROFILE=?
|
||||
WHERE ID=?
|
||||
"""))
|
||||
{
|
||||
@ -108,7 +112,8 @@ public class NodeConfigurationService {
|
||||
us.setBoolean(4, config.includeInPrecession());
|
||||
us.setBoolean(5, config.keepWarcs());
|
||||
us.setBoolean(6, config.disabled());
|
||||
us.setInt(7, config.node());
|
||||
us.setString(7, config.profile().name());
|
||||
us.setInt(8, config.node());
|
||||
|
||||
if (us.executeUpdate() <= 0)
|
||||
throw new IllegalStateException("Failed to update configuration");
|
||||
|
@ -6,6 +6,7 @@ public record NodeConfiguration(int node,
|
||||
boolean autoClean,
|
||||
boolean includeInPrecession,
|
||||
boolean keepWarcs,
|
||||
NodeProfile profile,
|
||||
boolean disabled
|
||||
)
|
||||
{
|
||||
|
@ -0,0 +1,28 @@
|
||||
package nu.marginalia.nodecfg.model;
|
||||
|
||||
public enum NodeProfile {
|
||||
BATCH_CRAWL,
|
||||
REALTIME,
|
||||
MIXED,
|
||||
SIDELOAD;
|
||||
|
||||
public boolean isBatchCrawl() {
|
||||
return this == BATCH_CRAWL;
|
||||
}
|
||||
public boolean isRealtime() {
|
||||
return this == REALTIME;
|
||||
}
|
||||
public boolean isMixed() {
|
||||
return this == MIXED;
|
||||
}
|
||||
public boolean isSideload() {
|
||||
return this == SIDELOAD;
|
||||
}
|
||||
|
||||
public boolean permitBatchCrawl() {
|
||||
return isBatchCrawl() ||isMixed();
|
||||
}
|
||||
public boolean permitSideload() {
|
||||
return isMixed() || isSideload();
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
ALTER TABLE WMSA_prod.NODE_CONFIGURATION ADD COLUMN NODE_PROFILE VARCHAR(255) DEFAULT 'MIXED';
|
@ -4,6 +4,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import nu.marginalia.mq.persistence.MqPersistence;
|
||||
import nu.marginalia.nodecfg.NodeConfigurationService;
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
import nu.marginalia.storage.FileStorageService;
|
||||
import nu.marginalia.storage.model.FileStorageBaseType;
|
||||
import org.slf4j.Logger;
|
||||
@ -56,7 +57,9 @@ public class NodeStatusWatcher {
|
||||
|
||||
private void setupNode() {
|
||||
try {
|
||||
configurationService.create(nodeId, "Node " + nodeId, true, false);
|
||||
NodeProfile profile = NodeProfile.MIXED;
|
||||
|
||||
configurationService.create(nodeId, "Node " + nodeId, true, false, profile);
|
||||
|
||||
fileStorageService.createStorageBase("Index Data", Path.of("/idx"), nodeId, FileStorageBaseType.CURRENT);
|
||||
fileStorageService.createStorageBase("Index Backups", Path.of("/backup"), nodeId, FileStorageBaseType.BACKUP);
|
||||
|
@ -182,4 +182,10 @@ public class ExecutorClient {
|
||||
}
|
||||
}
|
||||
|
||||
public void restartExecutorService(int node) {
|
||||
channelPool.call(ExecutorApiBlockingStub::restartExecutorService)
|
||||
.forNode(node)
|
||||
.run(Empty.getDefaultInstance());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ service ExecutorApi {
|
||||
rpc downloadSampleData(RpcDownloadSampleData) returns (Empty) {}
|
||||
rpc calculateAdjacencies(Empty) returns (Empty) {}
|
||||
rpc restoreBackup(RpcFileStorageId) returns (Empty) {}
|
||||
|
||||
rpc restartExecutorService(Empty) returns (Empty) {}
|
||||
}
|
||||
|
||||
service ExecutorCrawlApi {
|
||||
|
@ -1,31 +1,37 @@
|
||||
package nu.marginalia.actor;
|
||||
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public enum ExecutorActor {
|
||||
CRAWL,
|
||||
LIVE_CRAWL,
|
||||
RECRAWL,
|
||||
RECRAWL_SINGLE_DOMAIN,
|
||||
CONVERT_AND_LOAD,
|
||||
PROC_CONVERTER_SPAWNER,
|
||||
PROC_LOADER_SPAWNER,
|
||||
PROC_CRAWLER_SPAWNER,
|
||||
PROC_LIVE_CRAWL_SPAWNER,
|
||||
MONITOR_PROCESS_LIVENESS,
|
||||
MONITOR_FILE_STORAGE,
|
||||
ADJACENCY_CALCULATION,
|
||||
CRAWL_JOB_EXTRACTOR,
|
||||
EXPORT_DATA,
|
||||
EXPORT_SEGMENTATION_MODEL,
|
||||
EXPORT_ATAGS,
|
||||
EXPORT_TERM_FREQUENCIES,
|
||||
EXPORT_FEEDS,
|
||||
PROC_INDEX_CONSTRUCTOR_SPAWNER,
|
||||
CONVERT,
|
||||
RESTORE_BACKUP,
|
||||
EXPORT_SAMPLE_DATA,
|
||||
DOWNLOAD_SAMPLE,
|
||||
SCRAPE_FEEDS,
|
||||
UPDATE_RSS;
|
||||
CRAWL(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
RECRAWL(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
RECRAWL_SINGLE_DOMAIN(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
PROC_CONVERTER_SPAWNER(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
PROC_CRAWLER_SPAWNER(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
ADJACENCY_CALCULATION(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_DATA(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_SEGMENTATION_MODEL(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_ATAGS(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_TERM_FREQUENCIES(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_FEEDS(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
EXPORT_SAMPLE_DATA(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
DOWNLOAD_SAMPLE(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED),
|
||||
|
||||
PROC_LOADER_SPAWNER(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
RESTORE_BACKUP(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
CONVERT(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
|
||||
CONVERT_AND_LOAD(NodeProfile.BATCH_CRAWL, NodeProfile.MIXED, NodeProfile.REALTIME, NodeProfile.SIDELOAD),
|
||||
MONITOR_PROCESS_LIVENESS(NodeProfile.BATCH_CRAWL, NodeProfile.REALTIME, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
MONITOR_FILE_STORAGE(NodeProfile.BATCH_CRAWL, NodeProfile.REALTIME, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
PROC_INDEX_CONSTRUCTOR_SPAWNER(NodeProfile.BATCH_CRAWL, NodeProfile.REALTIME, NodeProfile.MIXED, NodeProfile.SIDELOAD),
|
||||
|
||||
LIVE_CRAWL(NodeProfile.REALTIME),
|
||||
PROC_LIVE_CRAWL_SPAWNER(NodeProfile.REALTIME),
|
||||
SCRAPE_FEEDS(NodeProfile.REALTIME),
|
||||
UPDATE_RSS(NodeProfile.REALTIME);
|
||||
|
||||
public String id() {
|
||||
return "fsm:" + name().toLowerCase();
|
||||
@ -35,4 +41,9 @@ public enum ExecutorActor {
|
||||
return "fsm:" + name().toLowerCase() + ":" + node;
|
||||
}
|
||||
|
||||
ExecutorActor(NodeProfile... profileSet) {
|
||||
this.profileSet = Set.of(profileSet);
|
||||
}
|
||||
|
||||
public Set<NodeProfile> profileSet;
|
||||
}
|
||||
|
@ -10,11 +10,14 @@ import nu.marginalia.actor.state.ActorStateInstance;
|
||||
import nu.marginalia.actor.state.ActorStep;
|
||||
import nu.marginalia.actor.task.*;
|
||||
import nu.marginalia.mq.MessageQueueFactory;
|
||||
import nu.marginalia.nodecfg.NodeConfigurationService;
|
||||
import nu.marginalia.nodecfg.model.NodeConfiguration;
|
||||
import nu.marginalia.service.control.ServiceEventLog;
|
||||
import nu.marginalia.service.server.BaseServiceParams;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@ -28,10 +31,13 @@ public class ExecutorActorControlService {
|
||||
public Map<ExecutorActor, ActorPrototype> actorDefinitions = new HashMap<>();
|
||||
private final int node;
|
||||
|
||||
private final NodeConfiguration nodeConfiguration;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Inject
|
||||
public ExecutorActorControlService(MessageQueueFactory messageQueueFactory,
|
||||
NodeConfigurationService configurationService,
|
||||
BaseServiceParams baseServiceParams,
|
||||
ConvertActor convertActor,
|
||||
ConvertAndLoadActor convertAndLoadActor,
|
||||
@ -56,12 +62,14 @@ public class ExecutorActorControlService {
|
||||
DownloadSampleActor downloadSampleActor,
|
||||
ScrapeFeedsActor scrapeFeedsActor,
|
||||
ExecutorActorStateMachines stateMachines,
|
||||
UpdateRssActor updateRssActor) {
|
||||
UpdateRssActor updateRssActor) throws SQLException {
|
||||
this.messageQueueFactory = messageQueueFactory;
|
||||
this.eventLog = baseServiceParams.eventLog;
|
||||
this.stateMachines = stateMachines;
|
||||
this.node = baseServiceParams.configuration.node();
|
||||
|
||||
this.nodeConfiguration = configurationService.get(node);
|
||||
|
||||
register(ExecutorActor.CRAWL, crawlActor);
|
||||
register(ExecutorActor.LIVE_CRAWL, liveCrawlActor);
|
||||
register(ExecutorActor.RECRAWL_SINGLE_DOMAIN, recrawlSingleDomainActor);
|
||||
@ -95,6 +103,11 @@ public class ExecutorActorControlService {
|
||||
}
|
||||
|
||||
private void register(ExecutorActor process, RecordActorPrototype graph) {
|
||||
|
||||
if (!process.profileSet.contains(nodeConfiguration.profile())) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sm = new ActorStateMachine(messageQueueFactory, process.id(), node, UUID.randomUUID(), graph);
|
||||
sm.listen((function, param) -> logStateChange(process, function));
|
||||
|
||||
|
@ -10,6 +10,8 @@ import nu.marginalia.actor.state.ActorResumeBehavior;
|
||||
import nu.marginalia.actor.state.ActorStep;
|
||||
import nu.marginalia.actor.state.Resume;
|
||||
import nu.marginalia.model.EdgeDomain;
|
||||
import nu.marginalia.nodecfg.NodeConfigurationService;
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
import nu.marginalia.service.control.ServiceEventLog;
|
||||
import nu.marginalia.service.module.ServiceConfiguration;
|
||||
import org.jsoup.Jsoup;
|
||||
@ -39,6 +41,7 @@ public class ScrapeFeedsActor extends RecordActorPrototype {
|
||||
private final Duration pollInterval = Duration.ofHours(6);
|
||||
|
||||
private final ServiceEventLog eventLog;
|
||||
private final NodeConfigurationService nodeConfigurationService;
|
||||
private final HikariDataSource dataSource;
|
||||
private final int nodeId;
|
||||
|
||||
@ -54,8 +57,8 @@ public class ScrapeFeedsActor extends RecordActorPrototype {
|
||||
public ActorStep transition(ActorStep self) throws Exception {
|
||||
return switch(self) {
|
||||
case Initial() -> {
|
||||
if (nodeId > 1) {
|
||||
yield new End();
|
||||
if (nodeConfigurationService.get(nodeId).profile() != NodeProfile.REALTIME) {
|
||||
yield new Error("Invalid node profile for RSS update");
|
||||
}
|
||||
else {
|
||||
yield new Wait(LocalDateTime.now().toString());
|
||||
@ -177,10 +180,12 @@ public class ScrapeFeedsActor extends RecordActorPrototype {
|
||||
public ScrapeFeedsActor(Gson gson,
|
||||
ServiceEventLog eventLog,
|
||||
ServiceConfiguration configuration,
|
||||
NodeConfigurationService nodeConfigurationService,
|
||||
HikariDataSource dataSource)
|
||||
{
|
||||
super(gson);
|
||||
this.eventLog = eventLog;
|
||||
this.nodeConfigurationService = nodeConfigurationService;
|
||||
this.dataSource = dataSource;
|
||||
this.nodeId = configuration.node();
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import nu.marginalia.api.feeds.RpcFeedUpdateMode;
|
||||
import nu.marginalia.mq.MqMessage;
|
||||
import nu.marginalia.mq.MqMessageState;
|
||||
import nu.marginalia.mq.persistence.MqPersistence;
|
||||
import nu.marginalia.nodecfg.NodeConfigurationService;
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
import nu.marginalia.service.module.ServiceConfiguration;
|
||||
|
||||
import java.time.Duration;
|
||||
@ -25,13 +27,19 @@ public class UpdateRssActor extends RecordActorPrototype {
|
||||
private final Duration updateInterval = Duration.ofHours(24);
|
||||
private final int cleanInterval = 60;
|
||||
|
||||
private final NodeConfigurationService nodeConfigurationService;
|
||||
private final MqPersistence persistence;
|
||||
|
||||
@Inject
|
||||
public UpdateRssActor(Gson gson, FeedsClient feedsClient, ServiceConfiguration serviceConfiguration, MqPersistence persistence) {
|
||||
public UpdateRssActor(Gson gson,
|
||||
FeedsClient feedsClient,
|
||||
ServiceConfiguration serviceConfiguration,
|
||||
NodeConfigurationService nodeConfigurationService,
|
||||
MqPersistence persistence) {
|
||||
super(gson);
|
||||
this.feedsClient = feedsClient;
|
||||
this.nodeId = serviceConfiguration.node();
|
||||
this.nodeConfigurationService = nodeConfigurationService;
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
@ -55,9 +63,8 @@ public class UpdateRssActor extends RecordActorPrototype {
|
||||
public ActorStep transition(ActorStep self) throws Exception {
|
||||
return switch (self) {
|
||||
case Initial() -> {
|
||||
if (nodeId > 1) {
|
||||
// Only run on the first node
|
||||
yield new End();
|
||||
if (nodeConfigurationService.get(nodeId).profile() != NodeProfile.REALTIME) {
|
||||
yield new Error("Invalid node profile for RSS update");
|
||||
}
|
||||
else {
|
||||
// Wait for 5 minutes before starting the first update, to give the system time to start up properly
|
||||
|
@ -15,9 +15,12 @@ import nu.marginalia.service.module.ServiceConfiguration;
|
||||
import nu.marginalia.service.server.DiscoverableService;
|
||||
import nu.marginalia.storage.FileStorageService;
|
||||
import nu.marginalia.storage.model.FileStorageId;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@ -32,6 +35,8 @@ public class ExecutorGrpcService
|
||||
private final ServiceConfiguration serviceConfiguration;
|
||||
private final ExecutorActorControlService actorControlService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExecutorGrpcService.class);
|
||||
|
||||
@Inject
|
||||
public ExecutorGrpcService(ActorApi actorApi,
|
||||
FileStorageService fileStorageService,
|
||||
@ -240,5 +245,22 @@ public class ExecutorGrpcService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartExecutorService(Empty request, StreamObserver<Empty> responseObserver) {
|
||||
responseObserver.onNext(Empty.getDefaultInstance());
|
||||
responseObserver.onCompleted();
|
||||
|
||||
logger.info("Restarting executor service on node {}", serviceConfiguration.node());
|
||||
|
||||
try {
|
||||
// Wait for the response to be sent before restarting
|
||||
Thread.sleep(Duration.ofSeconds(5));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
logger.warn("Interrupted while waiting for restart", e);
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import nu.marginalia.control.sys.svc.HeartbeatService;
|
||||
import nu.marginalia.executor.client.ExecutorClient;
|
||||
import nu.marginalia.nodecfg.NodeConfigurationService;
|
||||
import nu.marginalia.nodecfg.model.NodeConfiguration;
|
||||
import nu.marginalia.nodecfg.model.NodeProfile;
|
||||
import nu.marginalia.service.ServiceId;
|
||||
import nu.marginalia.service.ServiceMonitors;
|
||||
import nu.marginalia.storage.FileStorageService;
|
||||
@ -52,7 +53,8 @@ public class ControlNodeService {
|
||||
HikariDataSource dataSource,
|
||||
ServiceMonitors monitors,
|
||||
RedirectControl redirectControl,
|
||||
NodeConfigurationService nodeConfigurationService, ControlCrawlDataService crawlDataService)
|
||||
NodeConfigurationService nodeConfigurationService,
|
||||
ControlCrawlDataService crawlDataService)
|
||||
{
|
||||
this.fileStorageService = fileStorageService;
|
||||
this.rendererFactory = rendererFactory;
|
||||
@ -269,6 +271,8 @@ public class ControlNodeService {
|
||||
String act = request.queryParams("act");
|
||||
|
||||
if ("config".equals(act)) {
|
||||
var oldConfig = nodeConfigurationService.get(nodeId);
|
||||
|
||||
var newConfig = new NodeConfiguration(
|
||||
nodeId,
|
||||
request.queryParams("description"),
|
||||
@ -276,10 +280,19 @@ public class ControlNodeService {
|
||||
"on".equalsIgnoreCase(request.queryParams("autoClean")),
|
||||
"on".equalsIgnoreCase(request.queryParams("includeInPrecession")),
|
||||
"on".equalsIgnoreCase(request.queryParams("keepWarcs")),
|
||||
NodeProfile.valueOf(request.queryParams("profile")),
|
||||
"on".equalsIgnoreCase(request.queryParams("disabled"))
|
||||
);
|
||||
|
||||
nodeConfigurationService.save(newConfig);
|
||||
|
||||
if (!(Objects.equals(oldConfig.profile(), newConfig.profile()))) {
|
||||
// Restart the executor service if the profile has changed
|
||||
executorClient.restartExecutorService(nodeId);
|
||||
}
|
||||
else if (newConfig.disabled()) {
|
||||
executorClient.restartExecutorService(nodeId);
|
||||
}
|
||||
}
|
||||
else if ("storage".equals(act)) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -21,6 +21,35 @@
|
||||
<input class="form-control" type="text" name="description" value="{{config.description}}"/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-5">
|
||||
<label for="profile" class="form-label">Profile</label>
|
||||
<select class="form-select" name="profile" id="profile">
|
||||
<option value="BATCH_CRAWL" {{#if node.profile.batchCrawl}}selected{{/if}}>Batch Crawl</option>
|
||||
<option value="SIDELOAD" {{#if node.profile.sideload}}selected{{/if}}>Sideload</option>
|
||||
<option value="REALTIME" {{#if node.profile.realtime}}selected{{/if}}>Real Time</option>
|
||||
<option value="MIXED" {{#if node.profile.mixed}}selected{{/if}}>Mixed Use</option>
|
||||
</select>
|
||||
|
||||
<div class="form-text">The node profile configures which actors are available.
|
||||
<ul class="my-1">
|
||||
<li>
|
||||
<strong>Batch Crawl</strong> - This node is configured for batch crawling. It will not have the sideload actors available.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Sideload</strong> - This node is configured for sideloading. It will not have the batch crawl actors available.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Real Time</strong> - This node is configured for real time processing.
|
||||
It will not have the batch crawl or sideload actors available, but have actors for real time (daily) crawling.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Mixed Use</strong> - This node is configured for both batch crawling and sideloading.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" name="acceptQueries" {{#if config.acceptQueries}}checked{{/if}}>
|
||||
<label class="form-check-label" for="acceptQueries">Accept queries</label>
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
<h1 class="my-3">Index Node {{node.id}}</h1>
|
||||
<h1 class="my-3">Index Node {{node.id}}: {{node.profile}}</h1>
|
||||
{{#if node.disabled}}
|
||||
<small class="text-danger">This index node is disabled!</small>
|
||||
{{/if}}
|
||||
@ -10,8 +10,10 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{#if tab.overview}}active{{/if}}" href="/nodes/{{node.id}}/">Overview</a>
|
||||
</li>
|
||||
{{#unless node.profile.realtime}}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle {{#if tab.actions}}active{{/if}}" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">Actions</a>
|
||||
{{#if node.profile.permitBatchCrawl}}
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=new-crawl">New Crawl</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
@ -19,11 +21,6 @@
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=load">Load Processed Data</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=repartition">Repartition Index</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-encyclopedia">Sideload Encyclopedia</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-stackexchange">Sideload Stackexchange</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-warc">Sideload WARC Files</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-dirtree">Sideload Dirtree</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-reddit">Sideload Reddit</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=download-sample-data">Download Sample Crawl Data</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=export-db-data">Export Database Data</a></li>
|
||||
@ -33,14 +30,32 @@
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=restore-backup">Restore Index Backup</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{#if node.profile.permitSideload}}
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-encyclopedia">Sideload Encyclopedia</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-stackexchange">Sideload Stackexchange</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-warc">Sideload WARC Files</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-dirtree">Sideload Dirtree</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=sideload-reddit">Sideload Reddit</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=load">Load Processed Data</a></li>
|
||||
<li><a class="dropdown-item" href="/nodes/{{node.id}}/actions?view=restore-backup">Restore Index Backup</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/unless}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{#if tab.actors}}active{{/if}}" href="/nodes/{{node.id}}/actors">Actors</a>
|
||||
</li>
|
||||
{{#unless node.profile.realtime}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{#if tab.storage}}active{{/if}}" href="/nodes/{{node.id}}/storage/">Storage</a>
|
||||
</li>
|
||||
{{/unless}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{#if tab.config}}active{{/if}}" href="/nodes/{{node.id}}/configuration">Configuration</a>
|
||||
</li>
|
||||
|
||||
|
||||
</nav>
|
||||
|
@ -2,13 +2,16 @@
|
||||
<h2>Nodes</h2>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Node</th><th>Queries</th><th>Enabled</th><th>Index</th><th>Executor</th>
|
||||
<th>Node</th><th>Profile</th><th>Queries</th><th>Enabled</th><th>Index</th><th>Executor</th>
|
||||
</tr>
|
||||
{{#each .}}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/nodes/{{id}}">node-{{id}}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{configuration.profile}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if configuration.acceptQueries}}
|
||||
✓
|
||||
|
Loading…
Reference in New Issue
Block a user