1
1
mirror of https://github.com/MarginaliaSearch/MarginaliaSearch.git synced 2025-10-06 07:32:38 +02:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Viktor Lofgren
9d3f9adb05 Force redeploy of everything 2025-07-23 13:36:02 +02:00
Viktor
a43a1773f1 Merge pull request #216 from MarginaliaSearch/deprecate-executor
Architecture: Remove the separate executor service and roll it into the index service.
2025-07-23 13:32:42 +02:00
Viktor Lofgren
1e7a3a3c4f (docs) Update docs to reflect the change 2025-07-23 13:18:23 +02:00
Viktor Lofgren
62b696b1c3 (architecture) Remove the separate executor service and merge it into the index service
The primary motivation for this is that in production, the large number of partitioned services has lead to an intermittent exhaustion of available database connections, as each service has a connection pool.

The decision to have a separate executor service dates back from when the index service was very slow to start, and the executor didn't always spin off its memory-hungry tasks into separate processes, which meant the executor would sometimes OOM and crash, and it was undesirable to bring the index down with it.
2025-07-23 12:57:13 +02:00
Viktor Lofgren
f1a900f383 (search) Clean up front page mobile design a bit 2025-07-23 12:20:40 +02:00
29 changed files with 65 additions and 287 deletions

View File

@@ -7,7 +7,6 @@ public enum ServiceId {
Search("search-service"), Search("search-service"),
Index("index-service"), Index("index-service"),
Query("query-service"), Query("query-service"),
Executor("executor-service"),
Control("control-service"), Control("control-service"),

View File

@@ -189,7 +189,7 @@ public class ExecutorClient {
String uriPath = "/transfer/file/" + fileStorage.id(); String uriPath = "/transfer/file/" + fileStorage.id();
String uriQuery = "path=" + URLEncoder.encode(path, StandardCharsets.UTF_8); String uriQuery = "path=" + URLEncoder.encode(path, StandardCharsets.UTF_8);
var endpoints = registry.getEndpoints(ServiceKey.forRest(ServiceId.Executor, fileStorage.node())); var endpoints = registry.getEndpoints(ServiceKey.forRest(ServiceId.Index, fileStorage.node()));
if (endpoints.isEmpty()) { if (endpoints.isEmpty()) {
throw new RuntimeException("No endpoints for node " + fileStorage.node()); throw new RuntimeException("No endpoints for node " + fileStorage.node());
} }

View File

@@ -1,4 +1,4 @@
package nu.marginalia.executor; package nu.marginalia.svc;
import com.google.inject.Inject; import com.google.inject.Inject;
import nu.marginalia.storage.FileStorageService; import nu.marginalia.storage.FileStorageService;

View File

@@ -1,5 +1,5 @@
The execution subsystem is responsible for the execution of long running tasks on each The execution subsystem is responsible for the execution of long running tasks on each
index node. It lives in the [executor-service](../services-core/executor-service) module. index node. It lives in the [index-service](../services-core/index-service) module.
It accomplishes this using the [message queue and actor library](../libraries/message-queue/), It accomplishes this using the [message queue and actor library](../libraries/message-queue/),
which permits program state to survive crashes and reboots. which permits program state to survive crashes and reboots.

View File

@@ -1,4 +1,4 @@
package nu.marginalia.executor; package nu.marginalia.svc;
import nu.marginalia.storage.FileStorageService; import nu.marginalia.storage.FileStorageService;
import nu.marginalia.storage.model.FileStorage; import nu.marginalia.storage.model.FileStorage;

View File

@@ -87,7 +87,7 @@ class FeedFetcherServiceTest extends AbstractModule {
bind(DomainCoordinator.class).to(LocalDomainCoordinator.class); bind(DomainCoordinator.class).to(LocalDomainCoordinator.class);
bind(HikariDataSource.class).toInstance(dataSource); bind(HikariDataSource.class).toInstance(dataSource);
bind(ServiceRegistryIf.class).toInstance(Mockito.mock(ServiceRegistryIf.class)); bind(ServiceRegistryIf.class).toInstance(Mockito.mock(ServiceRegistryIf.class));
bind(ServiceConfiguration.class).toInstance(new ServiceConfiguration(ServiceId.Executor, 1, "", "", 0, UUID.randomUUID())); bind(ServiceConfiguration.class).toInstance(new ServiceConfiguration(ServiceId.Index, 1, "", "", 0, UUID.randomUUID()));
bind(Integer.class).annotatedWith(Names.named("wmsa-system-node")).toInstance(1); bind(Integer.class).annotatedWith(Names.named("wmsa-system-node")).toInstance(1);
} }

View File

@@ -12,4 +12,5 @@ class DDGTrackerDataTest {
data.getDomainInfo("hotjar.com").ifPresent(System.out::println); data.getDomainInfo("hotjar.com").ifPresent(System.out::println);
data.getAllClassifications().forEach(System.out::println); data.getAllClassifications().forEach(System.out::println);
} }
} }

View File

@@ -13,13 +13,13 @@ A map of the most important components and how they relate can be found below.
![image](../doc/diagram/conceptual-overview.svg) ![image](../doc/diagram/conceptual-overview.svg)
The core part of the search engine is the index service, which is responsible for storing and retrieving The core part of the search engine is the index service, which is responsible for storing and retrieving
the document data. The index serive is partitioned, along with the executor service, which is responsible for executing the document data. The index service is partitioned and is responsible for both index lookups and spawning
processes. At least one instance of each service must be run, but more can be run per-partition processing tasks. At least one instance of each service must be run, but more can be run
alongside. Multiple partitions is desirable in production to distribute load across multiple physical drives, alongside. Multiple partitions is desirable in production to distribute load across multiple physical drives,
as well as reducing the impact of downtime. as well as reducing the impact of downtime.
Search queries are delegated via the query service, which is a proxy that fans out the query to all Search queries are delegated via the query service, which is a proxy that fans out the query to all
eligible index services. The control service is responsible for distributing commands to the executor eligible index services. The control service is responsible for distributing commands to the partitions
service, and for monitoring the health of the system. It also offers a web interface for operating the system. service, and for monitoring the health of the system. It also offers a web interface for operating the system.
### Services ### Services
@@ -32,7 +32,6 @@ service, and for monitoring the health of the system. It also offers a web inte
* [index](services-core/index-service) * [index](services-core/index-service)
* Exposes the [index](index) subsystem * Exposes the [index](index) subsystem
* Exposes the [functions/link-graph](functions/link-graph) subsystem * Exposes the [functions/link-graph](functions/link-graph) subsystem
* [executor](services-core/executor-service)
* Exposes the [execution](execution) subsystem * Exposes the [execution](execution) subsystem
* [assistant](services-core/assistant-service) * [assistant](services-core/assistant-service)
* Exposes the [functions/math](functions/math) subsystem * Exposes the [functions/math](functions/math) subsystem
@@ -57,7 +56,7 @@ Services that expose HTTP endpoints tend to have more code. They are marked wit
### Processes ### Processes
Processes are batch jobs that deal with data retrieval, processing and loading. These are spawned and orchestrated by Processes are batch jobs that deal with data retrieval, processing and loading. These are spawned and orchestrated by
the executor service, which is controlled by the control service. the index service, which is controlled by the control service.
* [processes](processes/) * [processes](processes/)
* [crawling-process](processes/crawling-process) * [crawling-process](processes/crawling-process)

View File

@@ -38,8 +38,8 @@
<a href="https://old-search.marginalia.nu/" class="underline text-liteblue dark:text-blue-200">here</a>. <a href="https://old-search.marginalia.nu/" class="underline text-liteblue dark:text-blue-200">here</a>.
</div> </div>
</div> </div>
<div class="mx-auto flex flex-col sm:flex-row my-4 sm:space-x-2 space-y-2 sm:space-y-0 w-full md:w-auto px-2 items-center sm:items-stretch"> <div class="mx-auto px-8 flex flex-col sm:flex-row my-4 sm:space-x-2 space-y-2 sm:space-y-0 w-full md:w-auto items-center sm:items-stretch">
<div class="flex flex-col border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-96 sm:w-64"> <div class="flex flex-col items-center border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-[300px] sm:w-64">
<div><i class="fas fa-sailboat mx-2 text-margeblue dark:text-slate-200"></i>Explore the Web</div> <div><i class="fas fa-sailboat mx-2 text-margeblue dark:text-slate-200"></i>Explore the Web</div>
<ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5"> <ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5">
<li>Prioritizes non-commercial content</li> <li>Prioritizes non-commercial content</li>
@@ -48,14 +48,14 @@
</ul> </ul>
</div> </div>
<div class="flex flex-col border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-96 sm:w-64"> <div class="flex flex-col items-center border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-[300px] sm:w-64">
<div><i class="fas fa-hand-holding-hand mx-2 text-margeblue dark:text-slate-200"></i>Open Source</div> <div><i class="fas fa-hand-holding-hand mx-2 text-margeblue dark:text-slate-200"></i>Open Source</div>
<ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5"> <ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5">
<li>Custom index and crawler software</li> <li>Custom index and crawler software</li>
<li>Simple technology, no AI</li> <li>Simple technology, no AI</li>
<li>AGPL license</li> <li>AGPL license</li>
</ul> </ul>
<div class="flex pt-4 gap-2"> <div class="flex pt-4 gap-2 flex-col md:flex-row">
<div class="text-xs text-liteblue dark:text-blue-200"> <div class="text-xs text-liteblue dark:text-blue-200">
<i class="fa-brands fa-github"></i> <i class="fa-brands fa-github"></i>
<a href="https://git.marginalia.nu/" class="underline">Git Repository</a> <a href="https://git.marginalia.nu/" class="underline">Git Repository</a>
@@ -67,7 +67,7 @@
</div> </div>
</div> </div>
<div class="flex flex-col border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-96 sm:w-64"> <div class="flex flex-col items-center border border-gray-300 dark:border-gray-600 rounded overflow-hidden dark:bg-gray-800 bg-white p-8 sm:p-4 space-y-3 w-[300px] sm:w-64">
<div><i class="fas fa-lock mx-2 text-margeblue dark:text-slate-200"></i> Privacy by default</div> <div><i class="fas fa-lock mx-2 text-margeblue dark:text-slate-200"></i> Privacy by default</div>
<ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5"> <ul class="list-disc ml-8 sm:ml-6 text-slate-700 dark:text-white text-xs leading-5">
<li>Filter out tracking </li> <li>Filter out tracking </li>

View File

@@ -2,7 +2,7 @@ package nu.marginalia.control.node.model;
import nu.marginalia.nodecfg.model.NodeConfiguration; import nu.marginalia.nodecfg.model.NodeConfiguration;
public record IndexNodeStatus(NodeConfiguration configuration, boolean indexServiceOnline, boolean executorServiceOnline) { public record IndexNodeStatus(NodeConfiguration configuration, boolean indexServiceOnline) {
public int id() { public int id() {
return configuration.node(); return configuration.node();
} }

View File

@@ -338,7 +338,7 @@ public class ControlNodeService {
} }
private List<EventLogEntry> getEvents(int nodeId) { private List<EventLogEntry> getEvents(int nodeId) {
List<String> services = List.of(ServiceId.Index.serviceName +":"+nodeId, ServiceId.Executor.serviceName +":"+nodeId); List<String> services = List.of(ServiceId.Index.serviceName +":"+nodeId);
List<EventLogEntry> events = new ArrayList<>(20); List<EventLogEntry> events = new ArrayList<>(20);
for (var service :services) { for (var service :services) {
events.addAll(eventLogService.getLastEntriesForService(service, Long.MAX_VALUE, 10)); events.addAll(eventLogService.getLastEntriesForService(service, Long.MAX_VALUE, 10));
@@ -358,8 +358,7 @@ public class ControlNodeService {
public IndexNodeStatus getStatus(NodeConfiguration config) { public IndexNodeStatus getStatus(NodeConfiguration config) {
return new IndexNodeStatus(config, return new IndexNodeStatus(config,
monitors.isServiceUp(ServiceId.Index, config.node()), monitors.isServiceUp(ServiceId.Index, config.node())
monitors.isServiceUp(ServiceId.Executor, config.node())
); );
} }

View File

@@ -2,7 +2,7 @@
<h2>Nodes</h2> <h2>Nodes</h2>
<table class="table"> <table class="table">
<tr> <tr>
<th>Node</th><th>Profile</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>
</tr> </tr>
{{#each .}} {{#each .}}
<tr> <tr>
@@ -24,9 +24,6 @@
</td> </td>
{{#if indexServiceOnline}}<td>Online</td>{{/if}} {{#if indexServiceOnline}}<td>Online</td>{{/if}}
{{#unless indexServiceOnline}}<td class="table-danger">Offline</td>{{/unless}} {{#unless indexServiceOnline}}<td class="table-danger">Offline</td>{{/unless}}
{{#if executorServiceOnline}}<td>Online</td>{{/if}}
{{#unless executorServiceOnline}}<td class="table-warning">Offline</td>{{/unless}}
</tr> </tr>
{{/each}} {{/each}}
</table> </table>

View File

@@ -1,100 +0,0 @@
plugins {
id 'java'
id 'application'
id 'jvm-test-suite'
id 'com.google.cloud.tools.jib' version '3.4.5'
}
application {
mainClass = 'nu.marginalia.executor.ExecutorMain'
applicationName = 'executor-service'
}
tasks.distZip.enabled = false
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(rootProject.ext.jvmVersion))
}
}
apply from: "$rootProject.projectDir/srcsets.gradle"
apply from: "$rootProject.projectDir/docker.gradle"
dependencies {
// These look weird but they're needed to be able to spawn the processes
// from the executor service
implementation project(':code:processes:crawling-process')
implementation project(':code:processes:loading-process')
implementation project(':code:processes:converting-process')
implementation project(':code:processes:index-constructor-process')
implementation project(':code:common:config')
implementation project(':code:common:model')
implementation project(':code:common:db')
implementation project(':code:common:linkdb')
implementation project(':code:common:service')
implementation project(':third-party:commons-codec')
implementation project(':code:libraries:message-queue')
implementation project(':code:functions:link-graph:api')
implementation project(':code:functions:favicon')
implementation project(':code:functions:favicon:api')
implementation project(':code:functions:nsfw-domain-filter')
implementation project(':code:processes:crawling-process:model')
implementation project(':code:processes:crawling-process:model')
implementation project(':code:processes:crawling-process:ft-link-parser')
implementation project(':code:index:index-journal')
implementation project(':code:index:api')
implementation project(':code:processes:process-mq-api')
implementation project(':code:execution')
implementation project(':code:execution:api')
implementation project(':third-party:encyclopedia-marginalia-nu')
implementation libs.bundles.slf4j
implementation dependencies.create(libs.spark.get()) {
exclude group: 'org.eclipse.jetty'
}
implementation libs.bundles.jetty
implementation libs.guava
libs.bundles.grpc.get().each {
implementation dependencies.create(it) {
exclude group: 'com.google.guava'
}
}
implementation libs.gson
implementation libs.prometheus
implementation libs.notnull
implementation libs.guava
implementation dependencies.create(libs.guice.get()) {
exclude group: 'com.google.guava'
}
implementation libs.trove
implementation libs.zstd
implementation libs.jsoup
implementation libs.commons.io
implementation libs.commons.compress
implementation libs.commons.lang3
implementation libs.bundles.mariadb
testImplementation libs.bundles.slf4j.test
testImplementation libs.bundles.junit
testImplementation libs.mockito
testImplementation platform('org.testcontainers:testcontainers-bom:1.17.4')
testImplementation libs.commons.codec
testImplementation 'org.testcontainers:mariadb:1.17.4'
testImplementation 'org.testcontainers:junit-jupiter:1.17.4'
testImplementation project(':code:libraries:test-helpers')
}

View File

@@ -1,45 +0,0 @@
package nu.marginalia.executor;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import nu.marginalia.nsfw.NsfwFilterModule;
import nu.marginalia.service.MainClass;
import nu.marginalia.service.ServiceId;
import nu.marginalia.service.discovery.ServiceRegistryIf;
import nu.marginalia.service.module.DatabaseModule;
import nu.marginalia.service.module.ServiceConfiguration;
import nu.marginalia.service.module.ServiceConfigurationModule;
import nu.marginalia.service.module.ServiceDiscoveryModule;
import nu.marginalia.service.server.Initialization;
import nu.marginalia.service.server.NodeStatusWatcher;
public class ExecutorMain extends MainClass {
private final ExecutorSvc service;
@Inject
public ExecutorMain(ExecutorSvc service) {
this.service = service;
}
public static void main(String... args) {
init(ServiceId.Executor, args);
Injector injector = Guice.createInjector(
new ExecutorModule(),
new DatabaseModule(false),
new NsfwFilterModule(),
new ServiceDiscoveryModule(),
new ServiceConfigurationModule(ServiceId.Executor)
);
// Orchestrate the boot order for the services
var registry = injector.getInstance(ServiceRegistryIf.class);
var configuration = injector.getInstance(ServiceConfiguration.class);
orchestrateBoot(registry, configuration);
injector.getInstance(NodeStatusWatcher.class);
injector.getInstance(ExecutorMain.class);
injector.getInstance(Initialization.class).setReady();
}
}

View File

@@ -1,8 +0,0 @@
package nu.marginalia.executor;
import com.google.inject.AbstractModule;
public class ExecutorModule extends AbstractModule {
public void configure() {
}
}

View File

@@ -1,55 +0,0 @@
package nu.marginalia.executor;
import com.google.inject.Inject;
import nu.marginalia.execution.*;
import nu.marginalia.functions.favicon.FaviconGrpcService;
import nu.marginalia.service.discovery.property.ServicePartition;
import nu.marginalia.service.server.BaseServiceParams;
import nu.marginalia.service.server.SparkService;
import nu.marginalia.service.server.mq.MqRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Spark;
import java.util.List;
// Weird name for this one to not have clashes with java.util.concurrent.ExecutorService
public class ExecutorSvc extends SparkService {
private static final Logger logger = LoggerFactory.getLogger(ExecutorSvc.class);
private final ExecutionInit executionInit;
@Inject
public ExecutorSvc(BaseServiceParams params,
ExecutorGrpcService executorGrpcService,
ExecutorCrawlGrpcService executorCrawlGrpcService,
ExecutorSideloadGrpcService executorSideloadGrpcService,
ExecutorExportGrpcService executorExportGrpcService,
FaviconGrpcService faviconGrpcService,
ExecutionInit executionInit,
ExecutorFileTransferService fileTransferService) throws Exception {
super(params,
ServicePartition.partition(params.configuration.node()),
List.of(executorGrpcService,
executorCrawlGrpcService,
executorSideloadGrpcService,
executorExportGrpcService,
faviconGrpcService)
);
this.executionInit = executionInit;
Spark.get("/transfer/file/:fid", fileTransferService::transferFile);
Spark.head("/transfer/file/:fid", fileTransferService::transferFile);
}
@MqRequest(endpoint="FIRST-BOOT")
public void setUpDefaultActors(String message) throws Exception {
logger.info("Initializing default actors");
executionInit.initDefaultActors();
}
}

View File

@@ -1,10 +0,0 @@
The executor service is a partitioned service responsible for executing and keeping
track of long-running maintenance and operational tasks, such as crawling or data
processing.
The executor service is closely linked to the [control-service](../control-service),
which provides a user interface for much of the executor's functionality.
The service it itself relatively bare of code, but imports and exposes the [execution subsystem](../../execution),
which is responsible for the actual execution of tasks.

View File

@@ -30,10 +30,16 @@ dependencies {
implementation project(':code:common:db') implementation project(':code:common:db')
implementation project(':code:common:linkdb') implementation project(':code:common:linkdb')
implementation project(':code:execution')
implementation project(':code:execution:api')
implementation project(':code:functions:favicon')
implementation project(':code:functions:favicon:api')
implementation project(':code:index') implementation project(':code:index')
implementation project(':code:functions:link-graph:partition') implementation project(':code:functions:link-graph:partition')
implementation project(':code:functions:link-graph:api') implementation project(':code:functions:link-graph:api')
implementation project(':code:functions:search-query:api') implementation project(':code:functions:search-query:api')
implementation project(':code:functions:nsfw-domain-filter')
implementation project(':code:index:api') implementation project(':code:index:api')
testImplementation project(path: ':code:services-core:control-service') testImplementation project(path: ':code:services-core:control-service')

View File

@@ -3,13 +3,14 @@ package nu.marginalia.index;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import nu.marginalia.nsfw.NsfwFilterModule;
import nu.marginalia.service.MainClass; import nu.marginalia.service.MainClass;
import nu.marginalia.service.discovery.ServiceRegistryIf;
import nu.marginalia.service.module.ServiceConfiguration;
import nu.marginalia.service.module.ServiceDiscoveryModule;
import nu.marginalia.service.ServiceId; import nu.marginalia.service.ServiceId;
import nu.marginalia.service.module.ServiceConfigurationModule; import nu.marginalia.service.discovery.ServiceRegistryIf;
import nu.marginalia.service.module.DatabaseModule; import nu.marginalia.service.module.DatabaseModule;
import nu.marginalia.service.module.ServiceConfiguration;
import nu.marginalia.service.module.ServiceConfigurationModule;
import nu.marginalia.service.module.ServiceDiscoveryModule;
import nu.marginalia.service.server.Initialization; import nu.marginalia.service.server.Initialization;
import nu.marginalia.service.server.NodeStatusWatcher; import nu.marginalia.service.server.NodeStatusWatcher;
@@ -28,6 +29,7 @@ public class IndexMain extends MainClass {
new IndexModule(), new IndexModule(),
new DatabaseModule(false), new DatabaseModule(false),
new ServiceDiscoveryModule(), new ServiceDiscoveryModule(),
new NsfwFilterModule(),
new ServiceConfigurationModule(ServiceId.Index) new ServiceConfigurationModule(ServiceId.Index)
); );

View File

@@ -2,6 +2,8 @@ package nu.marginalia.index;
import com.google.inject.Inject; import com.google.inject.Inject;
import nu.marginalia.IndexLocations; import nu.marginalia.IndexLocations;
import nu.marginalia.execution.*;
import nu.marginalia.functions.favicon.FaviconGrpcService;
import nu.marginalia.index.api.IndexMqEndpoints; import nu.marginalia.index.api.IndexMqEndpoints;
import nu.marginalia.index.index.StatefulIndex; import nu.marginalia.index.index.StatefulIndex;
import nu.marginalia.linkdb.docs.DocumentDbReader; import nu.marginalia.linkdb.docs.DocumentDbReader;
@@ -14,9 +16,11 @@ import nu.marginalia.service.server.Initialization;
import nu.marginalia.service.server.SparkService; import nu.marginalia.service.server.SparkService;
import nu.marginalia.service.server.mq.MqRequest; import nu.marginalia.service.server.mq.MqRequest;
import nu.marginalia.storage.FileStorageService; import nu.marginalia.storage.FileStorageService;
import nu.marginalia.svc.ExecutorFileTransferService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import spark.Spark;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -38,6 +42,7 @@ public class IndexService extends SparkService {
private final DomainLinks domainLinks; private final DomainLinks domainLinks;
private final ServiceEventLog eventLog; private final ServiceEventLog eventLog;
private final ExecutionInit executionInit;
@Inject @Inject
public IndexService(BaseServiceParams params, public IndexService(BaseServiceParams params,
@@ -48,13 +53,25 @@ public class IndexService extends SparkService {
DocumentDbReader documentDbReader, DocumentDbReader documentDbReader,
DomainLinks domainLinks, DomainLinks domainLinks,
PartitionLinkGraphService partitionLinkGraphService, PartitionLinkGraphService partitionLinkGraphService,
ExecutorGrpcService executorGrpcService,
ExecutorCrawlGrpcService executorCrawlGrpcService,
ExecutorSideloadGrpcService executorSideloadGrpcService,
ExecutorExportGrpcService executorExportGrpcService,
FaviconGrpcService faviconGrpcService,
ExecutionInit executionInit,
ExecutorFileTransferService fileTransferService,
ServiceEventLog eventLog) ServiceEventLog eventLog)
throws Exception throws Exception
{ {
super(params, super(params,
ServicePartition.partition(params.configuration.node()), ServicePartition.partition(params.configuration.node()),
List.of(indexQueryService, List.of(indexQueryService,
partitionLinkGraphService) partitionLinkGraphService,
executorGrpcService,
executorCrawlGrpcService,
executorSideloadGrpcService,
executorExportGrpcService,
faviconGrpcService)
); );
this.opsService = opsService; this.opsService = opsService;
@@ -62,15 +79,26 @@ public class IndexService extends SparkService {
this.fileStorageService = fileStorageService; this.fileStorageService = fileStorageService;
this.documentDbReader = documentDbReader; this.documentDbReader = documentDbReader;
this.domainLinks = domainLinks; this.domainLinks = domainLinks;
this.executionInit = executionInit;
this.eventLog = eventLog; this.eventLog = eventLog;
this.init = params.initialization; this.init = params.initialization;
Spark.get("/transfer/file/:fid", fileTransferService::transferFile);
Spark.head("/transfer/file/:fid", fileTransferService::transferFile);
Thread.ofPlatform().name("initialize-index").start(this::initialize); Thread.ofPlatform().name("initialize-index").start(this::initialize);
} }
volatile boolean initialized = false; volatile boolean initialized = false;
@MqRequest(endpoint="FIRST-BOOT")
public void setUpDefaultActors(String message) throws Exception {
logger.info("Initializing default actors");
executionInit.initDefaultActors();
}
@MqRequest(endpoint = IndexMqEndpoints.INDEX_RERANK) @MqRequest(endpoint = IndexMqEndpoints.INDEX_RERANK)
public String rerank(String message) { public String rerank(String message) {
if (!opsService.rerank()) { if (!opsService.rerank()) {

View File

@@ -24,7 +24,6 @@ dependencies {
implementation project(':code:services-core:query-service') implementation project(':code:services-core:query-service')
implementation project(':code:services-core:index-service') implementation project(':code:services-core:index-service')
implementation project(':code:services-core:control-service') implementation project(':code:services-core:control-service')
implementation project(':code:services-core:executor-service')
testImplementation libs.bundles.slf4j.test testImplementation libs.bundles.slf4j.test
testImplementation libs.bundles.junit testImplementation libs.bundles.junit

View File

@@ -13,3 +13,4 @@
2025-06-06: Deploy assistant and browserless. 2025-06-06: Deploy assistant and browserless.
2025-07-21: Deploy executor partition 1. 2025-07-21: Deploy executor partition 1.
2025-07-21: Deploy search. 2025-07-21: Deploy search.
2025-07-23: Redeploy all.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -40,11 +40,6 @@ services:
<<: *partition-1 <<: *partition-1
image: "marginalia/index-service" image: "marginalia/index-service"
container_name: "index-service-1" container_name: "index-service-1"
executor-service-1:
<<: *partition-1
restart: always
image: "marginalia/executor-service"
container_name: "executor-service-1"
query-service: query-service:
<<: *service <<: *service
image: "marginalia/query-service" image: "marginalia/query-service"

View File

@@ -62,20 +62,10 @@ services:
<<: *partition-1 <<: *partition-1
image: "marginalia/index-service" image: "marginalia/index-service"
container_name: "index-service-1" container_name: "index-service-1"
executor-service-1:
<<: *partition-1
restart: always
image: "marginalia/executor-service"
container_name: "executor-service-1"
index-service-2: index-service-2:
<<: *partition-2 <<: *partition-2
image: "marginalia/index-service" image: "marginalia/index-service"
container_name: "index-service-2" container_name: "index-service-2"
executor-service-2:
<<: *partition-2
restart: always
image: "marginalia/executor-service"
container_name: "executor-service-2"
query-service: query-service:
<<: *service <<: *service
image: "marginalia/query-service" image: "marginalia/query-service"

View File

@@ -62,20 +62,10 @@ services:
<<: *partition-1 <<: *partition-1
image: "marginalia/index-service" image: "marginalia/index-service"
container_name: "index-service-1" container_name: "index-service-1"
executor-service-1:
<<: *partition-1
restart: always
image: "marginalia/executor-service"
container_name: "executor-service-1"
index-service-2: index-service-2:
<<: *partition-2 <<: *partition-2
image: "marginalia/index-service" image: "marginalia/index-service"
container_name: "index-service-2" container_name: "index-service-2"
executor-service-2:
<<: *partition-2
restart: always
image: "marginalia/executor-service"
container_name: "executor-service-2"
query-service: query-service:
<<: *service <<: *service
image: "marginalia/query-service" image: "marginalia/query-service"

View File

@@ -31,13 +31,11 @@ A working setup needs at all the services
* control [ http port is the control GUI ] * control [ http port is the control GUI ]
* query [ http port is the query GUI ] * query [ http port is the query GUI ]
* index [ http port is internal ] * index [ http port is internal ]
* executor [ http port is internal ]
Since you will need to manage ports yourself, you must assign distinct ports-pairs to each service. Since you will need to manage ports yourself, you must assign distinct ports-pairs to each service.
* An index and executor services should exist on the same partition e.g. index:1 and executor:1. The partition * An index service should exist on the same partition e.g. index:1. The partition
number is the last digit of the service name, and should be positive. You can have multiple pairs of index number is the last digit of the service name, and should be positive.
and executor partitions, but the pair should run on the same physical machine with the same install directory.
* The query service can use any partition number. * The query service can use any partition number.

View File

@@ -4,7 +4,6 @@ include 'code:services-core:index-service'
include 'code:services-core:assistant-service' include 'code:services-core:assistant-service'
include 'code:services-core:control-service' include 'code:services-core:control-service'
include 'code:services-core:query-service' include 'code:services-core:query-service'
include 'code:services-core:executor-service'
include 'code:services-core:single-service-runner' include 'code:services-core:single-service-runner'
include 'code:services-application:search-service' include 'code:services-application:search-service'

View File

@@ -76,13 +76,6 @@ SERVICE_CONFIG = {
deploy_tier=3, deploy_tier=3,
groups={"all", "index"} groups={"all", "index"}
), ),
'executor': ServiceConfig(
gradle_target=':code:services-core:executor-service:docker',
docker_name='executor-service',
instances=10,
deploy_tier=3,
groups={"all", "executor"}
),
'control': ServiceConfig( 'control': ServiceConfig(
gradle_target=':code:services-core:control-service:docker', gradle_target=':code:services-core:control-service:docker',
docker_name='control-service', docker_name='control-service',