runtime-split-patch-a

master
trifonovt 4 weeks ago
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…
Cancel
Save