This is an automated email from the ASF dual-hosted git repository. jsinovassin pushed a commit to branch UNOMI-973-review-followups in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 6bd8077e7ab3354164e3e2714788ff0d34aa2e47 Author: jsinovassin <[email protected]> AuthorDate: Tue Sep 1 18:38:43 2026 +0200 UNOMI-973: Keep a failure to record the refusal from costing the batch its routes recordEndpointOutcome writes to the store from inside configure(), which builds the routes of every configuration of the batch. With the store unreachable -- Elasticsearch not up yet at start-up, say -- the exception leaves addRoutes and every other configuration loses its route, silently. That is the failure mode this validation exists to remove, reached through the report of a refusal rather than the refusal itself. The write is now logged and swallowed. The route is built or skipped exactly as decided; only the record of it can go missing. --- .../core/route/RouterAbstractRouteBuilder.java | 24 ++++++++++++++++++++- .../core/route/RefusedConfigurationStatusTest.java | 25 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java index fa4998998..772e96014 100644 --- a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java +++ b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/RouterAbstractRouteBuilder.java @@ -26,6 +26,8 @@ import org.apache.unomi.api.services.ProfileService; import org.apache.unomi.router.api.ImportExportConfiguration; import org.apache.unomi.router.api.RouterConstants; import org.apache.unomi.router.api.services.ImportExportConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; @@ -34,6 +36,8 @@ import java.util.Map; */ public abstract class RouterAbstractRouteBuilder extends RouteBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterAbstractRouteBuilder.class); + protected JacksonDataFormat jacksonDataFormat; protected String kafkaHost; @@ -87,10 +91,28 @@ public abstract class RouterAbstractRouteBuilder extends RouteBuilder { T configuration, ImportExportConfigurationService<T> service, String refusal) { if (refusal != null) { configuration.setStatus(RouterConstants.CONFIG_STATUS_INVALID_ENDPOINT); - service.save(configuration, false); + saveQuietly(configuration, service); } else if (RouterConstants.CONFIG_STATUS_INVALID_ENDPOINT.equals(configuration.getStatus())) { configuration.setStatus(null); + saveQuietly(configuration, service); + } + } + + /** + * Saves the mark, and keeps a failure to itself. + * + * <p>This runs inside {@code configure()}, which builds the routes of every configuration of the + * batch. An exception thrown here would leave {@code addRoutes} and cost all of them their routes + * — the very failure this validation exists to prevent, over the report of a refusal rather than + * the refusal itself. The store may be unreachable at start-up; the mark is worth what it costs, + * and no more. + */ + private <T extends ImportExportConfiguration> void saveQuietly(T configuration, ImportExportConfigurationService<T> service) { + try { service.save(configuration, false); + } catch (RuntimeException e) { + LOGGER.error("Could not record the endpoint outcome on configuration {}; its route is built " + + "or skipped as decided, only the record of it is missing", configuration.getItemId(), e); } } diff --git a/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/RefusedConfigurationStatusTest.java b/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/RefusedConfigurationStatusTest.java index d0daf23e4..41a19f966 100644 --- a/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/RefusedConfigurationStatusTest.java +++ b/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/RefusedConfigurationStatusTest.java @@ -41,6 +41,7 @@ import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -117,6 +118,18 @@ public class RefusedConfigurationStatusTest { exportConfigurations.contains("out-of-bounds")); } + @Test + public void aStoreThatCannotRecordTheRefusalDoesNotCostTheBatchItsOtherRoutes() throws Exception { + importConfigurations.unwritable = true; + + addImportRoutes(recurrentImport(fileUri(arbitraryDir, "?fileName=profiles.csv")), + inBoundsImport("in-bounds")); + + assertNull("the refused configuration still gets no route", camelContext.getRouteDefinition("out-of-bounds")); + assertNotNull("failing to record the refusal must not cost the other configurations their routes", + camelContext.getRouteDefinition("in-bounds")); + } + @Test public void recordingARefusedImportConfigurationDoesNotScheduleARouteRefresh() throws Exception { addImportRoutes(recurrentImport(fileUri(arbitraryDir, "?fileName=profiles.csv"))); @@ -200,6 +213,12 @@ public class RefusedConfigurationStatusTest { return "file://" + directory.getAbsolutePath() + suffix; } + private ImportConfiguration inBoundsImport(String itemId) { + ImportConfiguration configuration = recurrentImport(fileUri(permittedImportDir, "?fileName=profiles.csv")); + configuration.setItemId(itemId); + return configuration; + } + private ImportConfiguration recurrentImport(String source) { ImportConfiguration configuration = new ImportConfiguration(); configuration.setItemId("out-of-bounds"); @@ -265,6 +284,9 @@ public class RefusedConfigurationStatusTest { private boolean lastSaveAskedForARouteRefresh; + /** Stands in for a store that cannot be written to -- Elasticsearch unreachable at start-up. */ + private boolean unwritable; + boolean contains(String configId) { return stored.containsKey(configId); } @@ -281,6 +303,9 @@ public class RefusedConfigurationStatusTest { @Override public T save(T configuration, boolean updateRunningRoute) { + if (unwritable) { + throw new IllegalStateException("the store is unreachable"); + } lastSaveAskedForARouteRefresh = updateRunningRoute; stored.put(itemIdOf(configuration), configuration); return configuration;
