runtime-split-patch-a
This commit is contained in:
parent
d206529162
commit
74609e481d
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Runtime split – Patch A
|
||||||
|
|
||||||
|
This patch introduces the runtime split scaffolding without performing the full behavioral cutover.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- `dip.runtime.mode` with values `LEGACY` / `NEW`
|
||||||
|
- `@ConditionalOnRuntimeMode`
|
||||||
|
- `DipSearchProperties` (`dip.search.*`)
|
||||||
|
- `LegacyTedProperties` (`legacy.ted.*`) as migration scaffold
|
||||||
|
- example `application-legacy.yml` and `application-new.yml`
|
||||||
|
|
||||||
|
## First gated beans
|
||||||
|
|
||||||
|
### LEGACY
|
||||||
|
- `GenericVectorizationRoute`
|
||||||
|
- `DocumentEmbeddingProcessingService`
|
||||||
|
- `ConfiguredEmbeddingModelStartupRunner`
|
||||||
|
- `GenericVectorizationStartupRunner`
|
||||||
|
|
||||||
|
### NEW
|
||||||
|
- `EmbeddingSubsystemStartupValidator`
|
||||||
|
- `PgVectorSemanticSearchEngine`
|
||||||
|
- `GenericSearchController`
|
||||||
|
|
||||||
|
## Intentional limitation of Patch A
|
||||||
|
This patch does **not** yet switch import/runtime orchestration to the new embedding job flow.
|
||||||
|
It only establishes the explicit runtime mode infrastructure and starts separating bean graphs.
|
||||||
|
|
||||||
|
## Follow-up patch (Patch B)
|
||||||
|
- add a NEW-mode embedding job scheduler
|
||||||
|
- make generic import enqueue new embedding jobs
|
||||||
|
- disable legacy vector route in NEW mode operationally
|
||||||
|
- move active search tuning from `ted.search.*` to `dip.search.*`
|
||||||
|
|
@ -10,11 +10,14 @@ import org.springframework.boot.ApplicationArguments;
|
||||||
import org.springframework.boot.ApplicationRunner;
|
import org.springframework.boot.ApplicationRunner;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ConditionalOnProperty(prefix = "dip.embedding", name = "enabled", havingValue = "true")
|
@ConditionalOnProperty(prefix = "dip.embedding", name = "enabled", havingValue = "true")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||||
public class EmbeddingSubsystemStartupValidator implements ApplicationRunner {
|
public class EmbeddingSubsystemStartupValidator implements ApplicationRunner {
|
||||||
|
|
||||||
private final EmbeddingProperties properties;
|
private final EmbeddingProperties properties;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package at.procon.dip.runtime.condition;
|
||||||
|
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
|
||||||
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Conditional(RuntimeModeCondition.class)
|
||||||
|
public @interface ConditionalOnRuntimeMode {
|
||||||
|
RuntimeMode value();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package at.procon.dip.runtime.condition;
|
||||||
|
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.context.annotation.Condition;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
|
||||||
|
public class RuntimeModeCondition implements Condition {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
|
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnRuntimeMode.class.getName());
|
||||||
|
if (attributes == null || !attributes.containsKey("value")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeMode expected = (RuntimeMode) attributes.get("value");
|
||||||
|
String configured = context.getEnvironment().getProperty("dip.runtime.mode", RuntimeMode.LEGACY.name());
|
||||||
|
RuntimeMode actual;
|
||||||
|
try {
|
||||||
|
actual = RuntimeMode.valueOf(configured.trim().toUpperCase());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
actual = RuntimeMode.LEGACY;
|
||||||
|
}
|
||||||
|
return actual == expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package at.procon.dip.runtime.config;
|
||||||
|
|
||||||
|
public enum RuntimeMode {
|
||||||
|
LEGACY,
|
||||||
|
NEW
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package at.procon.dip.runtime.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "dip.runtime")
|
||||||
|
@Data
|
||||||
|
public class RuntimeModeProperties {
|
||||||
|
/**
|
||||||
|
* Patch A defaults to LEGACY so existing runtime behaviour is preserved until
|
||||||
|
* NEW mode is explicitly enabled.
|
||||||
|
*/
|
||||||
|
private RuntimeMode mode = RuntimeMode.LEGACY;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package at.procon.dip.search.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "dip.search")
|
||||||
|
@Data
|
||||||
|
public class DipSearchProperties {
|
||||||
|
|
||||||
|
private Lexical lexical = new Lexical();
|
||||||
|
private Semantic semantic = new Semantic();
|
||||||
|
private Fusion fusion = new Fusion();
|
||||||
|
private Chunking chunking = new Chunking();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Lexical {
|
||||||
|
private double trigramSimilarityThreshold = 0.12;
|
||||||
|
private int fulltextCandidateLimit = 120;
|
||||||
|
private int trigramCandidateLimit = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Semantic {
|
||||||
|
private double similarityThreshold = 0.7;
|
||||||
|
private int semanticCandidateLimit = 120;
|
||||||
|
private String defaultModelKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Fusion {
|
||||||
|
private double fulltextWeight = 0.35;
|
||||||
|
private double trigramWeight = 0.20;
|
||||||
|
private double semanticWeight = 0.45;
|
||||||
|
private double recencyBoostWeight = 0.05;
|
||||||
|
private int recencyHalfLifeDays = 30;
|
||||||
|
private int debugTopHitsPerEngine = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Chunking {
|
||||||
|
private boolean enabled = true;
|
||||||
|
private int targetChars = 1800;
|
||||||
|
private int overlapChars = 200;
|
||||||
|
private int maxChunksPerDocument = 12;
|
||||||
|
private int startupLexicalBackfillLimit = 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,13 @@ import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||||
public class PgVectorSemanticSearchEngine implements SearchEngine {
|
public class PgVectorSemanticSearchEngine implements SearchEngine {
|
||||||
|
|
||||||
private final EmbeddingProperties embeddingProperties;
|
private final EmbeddingProperties embeddingProperties;
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,13 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/search")
|
@RequestMapping("/search")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||||
public class GenericSearchController {
|
public class GenericSearchController {
|
||||||
|
|
||||||
private final SearchOrchestrator searchOrchestrator;
|
private final SearchOrchestrator searchOrchestrator;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import at.procon.ted.config.TedProcessorProperties;
|
import at.procon.ted.config.TedProcessorProperties;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -24,6 +26,7 @@ import java.util.UUID;
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||||
public class GenericVectorizationRoute extends RouteBuilder {
|
public class GenericVectorizationRoute extends RouteBuilder {
|
||||||
|
|
||||||
private static final String ROUTE_ID_TRIGGER = "generic-vectorization-trigger";
|
private static final String ROUTE_ID_TRIGGER = "generic-vectorization-trigger";
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import at.procon.ted.config.TedProcessorProperties;
|
||||||
import at.procon.ted.model.entity.VectorizationStatus;
|
import at.procon.ted.model.entity.VectorizationStatus;
|
||||||
import at.procon.ted.repository.ProcurementDocumentRepository;
|
import at.procon.ted.repository.ProcurementDocumentRepository;
|
||||||
import at.procon.ted.service.VectorizationService;
|
import at.procon.ted.service.VectorizationService;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -26,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||||
public class DocumentEmbeddingProcessingService {
|
public class DocumentEmbeddingProcessingService {
|
||||||
|
|
||||||
private final DocumentEmbeddingRepository embeddingRepository;
|
private final DocumentEmbeddingRepository embeddingRepository;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package at.procon.dip.vectorization.startup;
|
||||||
import at.procon.dip.domain.document.service.DocumentEmbeddingService;
|
import at.procon.dip.domain.document.service.DocumentEmbeddingService;
|
||||||
import at.procon.dip.domain.document.service.command.RegisterEmbeddingModelCommand;
|
import at.procon.dip.domain.document.service.command.RegisterEmbeddingModelCommand;
|
||||||
import at.procon.ted.config.TedProcessorProperties;
|
import at.procon.ted.config.TedProcessorProperties;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.ApplicationArguments;
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
|
@ -15,6 +17,7 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||||
public class ConfiguredEmbeddingModelStartupRunner implements ApplicationRunner {
|
public class ConfiguredEmbeddingModelStartupRunner implements ApplicationRunner {
|
||||||
|
|
||||||
private final TedProcessorProperties properties;
|
private final TedProcessorProperties properties;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package at.procon.dip.vectorization.startup;
|
||||||
import at.procon.dip.domain.document.EmbeddingStatus;
|
import at.procon.dip.domain.document.EmbeddingStatus;
|
||||||
import at.procon.dip.domain.document.repository.DocumentEmbeddingRepository;
|
import at.procon.dip.domain.document.repository.DocumentEmbeddingRepository;
|
||||||
import at.procon.ted.config.TedProcessorProperties;
|
import at.procon.ted.config.TedProcessorProperties;
|
||||||
|
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||||
|
import at.procon.dip.runtime.config.RuntimeMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -19,6 +21,7 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||||
public class GenericVectorizationStartupRunner implements ApplicationRunner {
|
public class GenericVectorizationStartupRunner implements ApplicationRunner {
|
||||||
|
|
||||||
private static final int BATCH_SIZE = 1000;
|
private static final int BATCH_SIZE = 1000;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package at.procon.ted.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch A scaffold for the legacy runtime configuration tree.
|
||||||
|
*
|
||||||
|
* The legacy runtime still uses {@link TedProcessorProperties} today. This class is
|
||||||
|
* introduced so the old configuration can be moved gradually from `ted.*` to
|
||||||
|
* `legacy.ted.*` without blocking the runtime split.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "legacy.ted")
|
||||||
|
public class LegacyTedProperties extends TedProcessorProperties {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
dip:
|
||||||
|
runtime:
|
||||||
|
mode: LEGACY
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
dip:
|
||||||
|
runtime:
|
||||||
|
mode: NEW
|
||||||
Loading…
Reference in New Issue