From b5e3f2a6771466137f0319f3a25b886af5b6fd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Wed, 1 May 2024 10:05:00 +0200 Subject: [PATCH 01/24] feat(#550): not working way of Java agent --- docker/Dockerfile | 3 + .../evita_external_api_observability/pom.xml | 5 + .../observability/agent/OOMAgent.java | 94 +++++++++++++++++++ .../observability/metric/MetricHandler.java | 12 +++ .../src/main/java/module-info.java | 2 + .../system/SystemProviderRegistrar.java | 14 +++ evita_server/pom.xml | 5 + evita_server/run-server.sh | 1 + 8 files changed, 136 insertions(+) create mode 100644 evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java diff --git a/docker/Dockerfile b/docker/Dockerfile index 113c8f77c..ec5ffdae3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -12,6 +12,9 @@ LABEL vendor="FG Forrest, a.s." \ io.evitadb.version="${VERSION}" \ io.evitadb.release-date="${RELEASE_DATE}" +HEALTHCHECK --interval=10s --timeout=2s --retries=2 \ + CMD curl -f http://localhost:5557/health || exit 1 + USER root # Install bash diff --git a/evita_external_api/evita_external_api_observability/pom.xml b/evita_external_api/evita_external_api_observability/pom.xml index f5ae9c54f..c5b3b4c9a 100644 --- a/evita_external_api/evita_external_api_observability/pom.xml +++ b/evita_external_api/evita_external_api_observability/pom.xml @@ -61,6 +61,11 @@ evita_query ${project.parent.version} + + net.bytebuddy + byte-buddy + 1.14.14 + ${project.parent.groupId} diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java new file mode 100644 index 000000000..8d74388be --- /dev/null +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java @@ -0,0 +1,94 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.externalApi.observability.agent; + +import io.evitadb.exception.EvitaInternalError; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.implementation.MethodCall; +import net.bytebuddy.implementation.SuperMethodCall; + +import java.io.File; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; + +import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.REDEFINITION; +import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION; +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.none; + +/** + * TODO JNO - document me + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public class OOMAgent { + private static final Method HANDLE_OOM; + + static { + try { + HANDLE_OOM = OOMAgent.class.getDeclaredMethod("handleOOM"); + } catch (NoSuchMethodException e) { + throw new EvitaInternalError("!!! OOMAgent initialization failed !!!", e); + } + } + + public static void premain(String agentArgs, Instrumentation inst) { + if (HANDLE_OOM != null) { + new AgentBuilder.Default() + .disableClassFormatChanges() + .with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(inst, new File("/www/oss/evitaDB-temporary/evita_external_api/evita_external_api_observability/target/evita_external_api_observability-2024.5-SNAPSHOT.jar"))) + .with(REDEFINITION) + // Make sure we see helpful logs + .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError()) + .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly()) + .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError()) + .ignore(none()) + // Ignore Byte Buddy and JDK classes we are not interested in + .ignore( + nameStartsWith("net.bytebuddy.") + .or(nameStartsWith("jdk.internal.reflect.")) + .or(nameStartsWith("java.lang.invoke.")) + .or(nameStartsWith("com.sun.proxy.")) + ) + .disableClassFormatChanges() + .with(RETRANSFORMATION) + .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) + .with(AgentBuilder.TypeStrategy.Default.REDEFINE) + .type(named("java.lang.OutOfMemoryError")) + .transform( + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> builder + .constructor(any()) + .intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(HANDLE_OOM))) + ) + .installOn(inst); + } + } + + public static void handleOOM() { + System.out.println("!!! OOM !!!"); + } + +} diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java index a83774872..f3ec82405 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java @@ -69,6 +69,11 @@ public class MetricHandler { private static final Map DEFAULT_JVM_METRICS; private static final String DEFAULT_JVM_METRICS_NAME = "AllMetrics"; + // Define a Prometheus counter for OutOfMemoryError events + private static final Counter OUT_OF_MEMORY_ERRORS_TOTAL = Counter.builder() + .name("jvm_out_of_memory_errors_total") + .help("Total number of Out of Memory errors") + .register(); static { DEFAULT_JVM_METRICS = Map.of( @@ -89,6 +94,13 @@ public MetricHandler(@Nonnull ObservabilityConfig observabilityConfig) { this.observabilityConfig = observabilityConfig; } + /** + * TODO JNO - document me + */ + public static void outOfMemoryErrorEvent() { + OUT_OF_MEMORY_ERRORS_TOTAL.inc(); + } + /** * Based on configuration, this method enables collecting and publishing metrics into Prometheus scraping endpoint. * If no configuration of such event has been provided, all JVM and custom events are logged. For limiting the logged diff --git a/evita_external_api/evita_external_api_observability/src/main/java/module-info.java b/evita_external_api/evita_external_api_observability/src/main/java/module-info.java index 46cf6e9d5..41a715dea 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/module-info.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/module-info.java @@ -76,6 +76,8 @@ requires io.opentelemetry.exporter.logging; requires io.opentelemetry.exporter.otlp; requires io.opentelemetry.sdk.autoconfigure; + requires java.instrument; + requires net.bytebuddy; exports io.evitadb.externalApi.observability.configuration; exports io.evitadb.externalApi.observability.trace; diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index 9c2a35d98..b46919c2c 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -119,6 +119,20 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull } ); + router.addExactPath( + "/oom", + exchange -> { + exchange.setStatusCode(StatusCodes.OK); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain"); + try { + final OutOfMemoryError oom = new OutOfMemoryError("XXX"); + exchange.getResponseSender().send(String.valueOf(oom.getMessage())); + } finally { + //exchange.getResponseSender().send("!!! OOM !!!"); + } + } + ); + final ResourceHandler fileSystemHandler; try (ResourceManager resourceManager = new FileResourceManager(file, 100)) { fileSystemHandler = new ResourceHandler( diff --git a/evita_server/pom.xml b/evita_server/pom.xml index 47b4bf648..7a18a899a 100644 --- a/evita_server/pom.xml +++ b/evita_server/pom.xml @@ -143,6 +143,11 @@ io.evitadb.server.EvitaServer true + + io.evitadb.externalApi.observability.agent.OOMAgent + true + true + diff --git a/evita_server/run-server.sh b/evita_server/run-server.sh index 9f4a6498c..4b8b48865 100755 --- a/evita_server/run-server.sh +++ b/evita_server/run-server.sh @@ -24,6 +24,7 @@ java \ -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8005 \ + -javaagent:target/evita-server.jar \ -jar "target/evita-server.jar" \ "-DconfigFile=../docker/evita-configuration.yaml" \ "-Dstorage.storageDirectory=../data " \ From 9fc545662c0184b7cb448d601e118d59bd0a19c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Thu, 2 May 2024 10:12:04 +0200 Subject: [PATCH 02/24] fix: when cause was null, the stacktrace might have been dropped --- .../src/main/java/io/evitadb/core/SessionRegistry.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java index 69dd9021b..627180e39 100644 --- a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java +++ b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java @@ -182,6 +182,7 @@ private static class EvitaSessionProxy implements InvocationHandler { private final EvitaSession evitaSession; private final TracingContext tracingContext; + @Nullable @Override public Object invoke(Object proxy, Method method, Object[] args) { try { @@ -211,11 +212,14 @@ public Object invoke(Object proxy, Method method, Object[] args) { // unwrap and rethrow throw evitaInternalError; } else { - log.error("Unexpected internal Evita error occurred: " + ex.getCause().getMessage(), targetException); + log.error( + "Unexpected internal Evita error occurred: " + ex.getCause().getMessage(), + targetException == null ? ex : targetException + ); throw new EvitaInternalError( "Unexpected internal Evita error occurred: " + ex.getCause().getMessage(), "Unexpected internal Evita error occurred.", - targetException + targetException == null ? ex : targetException ); } } catch (Throwable ex) { From 38251e0bd8e7a45d0ed05ba644981c2a7d1dbdf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Thu, 2 May 2024 11:34:34 +0200 Subject: [PATCH 03/24] fix(#550): partial implementation of health probe --- docker/Dockerfile | 3 + .../requestResponse/system/SystemStatus.java | 24 +- .../src/main/java/io/evitadb/core/Evita.java | 4 +- .../java/io/evitadb/driver/EvitaClient.java | 7 +- .../generated/EvitaSessionServiceGrpc.java | 2 +- .../grpc/generated/GrpcCatalogState.java | 2 +- .../grpc/generated/GrpcCloseRequest.java | 8 +- .../generated/GrpcCloseRequestOrBuilder.java | 2 +- .../grpc/generated/GrpcCloseResponse.java | 6 +- .../generated/GrpcCloseResponseOrBuilder.java | 2 +- .../grpc/generated/GrpcCommitBehavior.java | 2 +- .../externalApi/grpc/generated/GrpcEnums.java | 9 +- .../grpc/generated/GrpcEvitaAPI.java | 184 +++++----- .../GrpcEvitaServerStatusResponse.java | 338 ++++++++++++++++++ ...rpcEvitaServerStatusResponseOrBuilder.java | 49 +++ .../grpc/generated/GrpcEvitaSessionAPI.java | 96 ++--- .../generated/GrpcEvitaSessionRequest.java | 24 +- .../GrpcEvitaSessionRequestOrBuilder.java | 2 +- .../generated/GrpcEvitaSessionResponse.java | 32 +- .../GrpcEvitaSessionResponseOrBuilder.java | 2 +- .../generated/GrpcGoLiveAndCloseResponse.java | 10 +- .../GrpcGoLiveAndCloseResponseOrBuilder.java | 2 +- .../grpc/generated/GrpcHealthProblem.java | 160 +++++++++ .../requestResponse/EvitaEnumConverter.java | 68 ++-- .../evitadb/externalApi/grpc/GrpcEnums.proto | 12 + .../externalApi/grpc/GrpcEvitaAPI.proto | 4 +- .../observability/metric/MetricHandler.java | 4 + .../system/SystemProviderRegistrar.java | 32 +- 28 files changed, 860 insertions(+), 230 deletions(-) create mode 100644 evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java diff --git a/docker/Dockerfile b/docker/Dockerfile index 113c8f77c..3c0160c32 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -12,6 +12,9 @@ LABEL vendor="FG Forrest, a.s." \ io.evitadb.version="${VERSION}" \ io.evitadb.release-date="${RELEASE_DATE}" +HEALTHCHECK --interval=10s --timeout=2s --retries=2 \ + CMD curl -f http://localhost:5557/system/liveness || exit 1 + USER root # Install bash diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java index 09fe8647e..93f286fee 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java @@ -27,6 +27,7 @@ import java.io.Serializable; import java.time.Duration; import java.time.OffsetDateTime; +import java.util.Set; /** * Contains basic information about evitaDB server. @@ -37,6 +38,7 @@ * @param instanceId unique identifier of the server instance * @param catalogsCorrupted number of corrupted catalogs * @param catalogsOk number of catalogs that are ok + * @param healthProblems set of flags that indicate health problems of the server */ public record SystemStatus( @Nonnull String version, @@ -44,5 +46,23 @@ public record SystemStatus( @Nonnull Duration uptime, @Nonnull String instanceId, int catalogsCorrupted, - int catalogsOk -) implements Serializable { } + int catalogsOk, + @Nonnull Set healthProblems +) implements Serializable { + + public enum HealthProblem { + + /** + * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free + * old generation multiple times consuming a log of CPU power. + */ + MEMORY_SHORTAGE, + /** + * Signalized when the input queues are full and the server is not able to process incoming requests. This flag + * is cleared when the server is able to process incoming requests again. + */ + INPUT_QUEUES_OVERLOADED + + } + +} diff --git a/evita_engine/src/main/java/io/evitadb/core/Evita.java b/evita_engine/src/main/java/io/evitadb/core/Evita.java index 47377c3f4..eb3890c80 100644 --- a/evita_engine/src/main/java/io/evitadb/core/Evita.java +++ b/evita_engine/src/main/java/io/evitadb/core/Evita.java @@ -51,6 +51,7 @@ import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyCatalogSchemaNameMutation; import io.evitadb.api.requestResponse.schema.mutation.catalog.RemoveCatalogSchemaMutation; import io.evitadb.api.requestResponse.system.SystemStatus; +import io.evitadb.api.requestResponse.system.SystemStatus.HealthProblem; import io.evitadb.api.trace.TracingContext; import io.evitadb.api.trace.TracingContextProvider; import io.evitadb.core.cache.CacheSupervisor; @@ -527,7 +528,8 @@ public SystemStatus getSystemStatus() { Duration.between(this.started, OffsetDateTime.now()), this.configuration.name(), corruptedCatalogs, - this.catalogs.size() - corruptedCatalogs + this.catalogs.size() - corruptedCatalogs, + EnumSet.noneOf(HealthProblem.class) ); } diff --git a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java index 59021d0ff..146b7e303 100644 --- a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java +++ b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java @@ -91,6 +91,7 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static java.util.Optional.ofNullable; @@ -628,7 +629,11 @@ public SystemStatus getSystemStatus() { Duration.of(response.getUptime(), ChronoUnit.SECONDS), response.getInstanceId(), response.getCatalogsCorrupted(), - response.getCatalogsOk() + response.getCatalogsOk(), + response.getHealthProblemsList() + .stream() + .map(EvitaEnumConverter::toHealthProblem) + .collect(Collectors.toSet()) ); } ); diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java index d906c6db2..bb66938ba 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java index 84207e46c..e9ceafdeb 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java index 172d0602c..6fb6d991d 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -468,7 +468,7 @@ public Builder mergeFrom( * @return This builder for chaining. */ public Builder setCommitBehaviourValue(int value) { - + commitBehaviour_ = value; onChanged(); return this; @@ -500,7 +500,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm if (value == null) { throw new NullPointerException(); } - + commitBehaviour_ = value.getNumber(); onChanged(); return this; @@ -514,7 +514,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm * @return This builder for chaining. */ public Builder clearCommitBehaviour() { - + commitBehaviour_ = 0; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java index 001c02bd1..cef228c77 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java index 9bf186d69..898286538 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -457,7 +457,7 @@ public long getCatalogVersion() { * @return This builder for chaining. */ public Builder setCatalogVersion(long value) { - + catalogVersion_ = value; onChanged(); return this; @@ -471,7 +471,7 @@ public Builder setCatalogVersion(long value) { * @return This builder for chaining. */ public Builder clearCatalogVersion() { - + catalogVersion_ = 0L; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java index 16f668f3c..f0e2bd13c 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java index f753675d2..57a873495 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java index 75866c853..61a66395f 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaAssociatedDataDataType_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaAssociatedDataDataType_fieldAccessorTable; @@ -152,8 +152,9 @@ public static void registerAllExtensions( "XIST\020\001\022\016\n\nMUST_EXIST\020\002*t\n\022GrpcCommitBeha" + "vior\022 \n\034WAIT_FOR_CONFLICT_RESOLUTION\020\000\022\034" + "\n\030WAIT_FOR_LOG_PERSISTENCE\020\001\022\036\n\032WAIT_FOR" + - "_INDEX_PROPAGATION\020\002B\014P\001\252\002\007EvitaDBb\006prot" + - "o3" + "_INDEX_PROPAGATION\020\002*E\n\021GrpcHealthProble" + + "m\022\023\n\017MEMORY_SHORTAGE\020\000\022\033\n\027INPUT_QUEUES_O" + + "VERLOADED\020\001B\014P\001\252\002\007EvitaDBb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java index 8ea71740f..47559e567 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,77 +39,77 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogNamesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogNamesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEvitaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEvitaRequest_fieldAccessorTable; @@ -125,87 +125,89 @@ public static void registerAllExtensions( "lApi.grpc.generated\032\033google/protobuf/emp" + "ty.proto\032\017GrpcEnums.proto\032\030GrpcEvitaData" + "Types.proto\032\037GrpcCatalogSchemaMutation.p" + - "roto\"\321\001\n\035GrpcEvitaServerStatusResponse\022\017" + + "roto\"\243\002\n\035GrpcEvitaServerStatusResponse\022\017" + "\n\007version\030\001 \001(\t\022L\n\tstartedAt\030\002 \001(\01329.io." + "evitadb.externalApi.grpc.generated.GrpcO" + "ffsetDateTime\022\016\n\006uptime\030\003 \001(\003\022\022\n\ninstanc" + "eId\030\004 \001(\t\022\031\n\021catalogsCorrupted\030\005 \001(\005\022\022\n\n" + - "catalogsOk\030\006 \001(\005\"\221\001\n\027GrpcEvitaSessionReq" + - "uest\022\023\n\013catalogName\030\001 \001(\t\022Q\n\016commitBehav" + - "ior\030\002 \001(\01629.io.evitadb.externalApi.grpc." + - "generated.GrpcCommitBehavior\022\016\n\006dryRun\030\003" + - " \001(\010\"\235\002\n\030GrpcEvitaSessionResponse\022\021\n\tses" + - "sionId\030\001 \001(\t\022K\n\013sessionType\030\002 \001(\01626.io.e" + - "vitadb.externalApi.grpc.generated.GrpcSe" + - "ssionType\022R\n\017commitBehaviour\030\003 \001(\01629.io." + - "evitadb.externalApi.grpc.generated.GrpcC" + - "ommitBehavior\022M\n\014catalogState\030\004 \001(\01627.io" + + "catalogsOk\030\006 \001(\005\022P\n\016healthProblems\030\007 \003(\016" + + "28.io.evitadb.externalApi.grpc.generated" + + ".GrpcHealthProblem\"\221\001\n\027GrpcEvitaSessionR" + + "equest\022\023\n\013catalogName\030\001 \001(\t\022Q\n\016commitBeh" + + "avior\030\002 \001(\01629.io.evitadb.externalApi.grp" + + "c.generated.GrpcCommitBehavior\022\016\n\006dryRun" + + "\030\003 \001(\010\"\235\002\n\030GrpcEvitaSessionResponse\022\021\n\ts" + + "essionId\030\001 \001(\t\022K\n\013sessionType\030\002 \001(\01626.io" + ".evitadb.externalApi.grpc.generated.Grpc" + - "CatalogState\"L\n\"GrpcEvitaSessionTerminat" + - "ionRequest\022\023\n\013catalogName\030\001 \001(\t\022\021\n\tsessi" + - "onId\030\002 \001(\t\"9\n#GrpcEvitaSessionTerminatio" + - "nResponse\022\022\n\nterminated\030\001 \001(\010\"0\n\030GrpcCat" + - "alogNamesResponse\022\024\n\014catalogNames\030\001 \003(\t\"" + - "/\n\030GrpcDefineCatalogRequest\022\023\n\013catalogNa" + - "me\030\001 \001(\t\",\n\031GrpcDefineCatalogResponse\022\017\n" + - "\007success\030\001 \001(\010\"G\n\030GrpcRenameCatalogReque" + - "st\022\023\n\013catalogName\030\001 \001(\t\022\026\n\016newCatalogNam" + - "e\030\002 \001(\t\",\n\031GrpcRenameCatalogResponse\022\017\n\007" + - "success\030\001 \001(\010\"a\n\031GrpcReplaceCatalogReque" + - "st\022#\n\033catalogNameToBeReplacedWith\030\001 \001(\t\022" + - "\037\n\027catalogNameToBeReplaced\030\002 \001(\t\"-\n\032Grpc" + - "ReplaceCatalogResponse\022\017\n\007success\030\001 \001(\010\"" + - "7\n GrpcDeleteCatalogIfExistsRequest\022\023\n\013c" + - "atalogName\030\001 \001(\t\"4\n!GrpcDeleteCatalogIfE" + - "xistsResponse\022\017\n\007success\030\001 \001(\010\"{\n\026GrpcUp" + - "dateEvitaRequest\022a\n\017schemaMutations\030\001 \003(" + - "\0132H.io.evitadb.externalApi.grpc.generate" + - "d.GrpcTopLevelCatalogSchemaMutation2\336\r\n\014" + - "EvitaService\022l\n\014ServerStatus\022\026.google.pr" + - "otobuf.Empty\032D.io.evitadb.externalApi.gr" + - "pc.generated.GrpcEvitaServerStatusRespon" + - "se\022\230\001\n\025CreateReadOnlySession\022>.io.evitad" + - "b.externalApi.grpc.generated.GrpcEvitaSe" + - "ssionRequest\032?.io.evitadb.externalApi.gr" + - "pc.generated.GrpcEvitaSessionResponse\022\231\001" + - "\n\026CreateReadWriteSession\022>.io.evitadb.ex" + - "ternalApi.grpc.generated.GrpcEvitaSessio" + - "nRequest\032?.io.evitadb.externalApi.grpc.g" + - "enerated.GrpcEvitaSessionResponse\022\236\001\n\033Cr" + - "eateBinaryReadOnlySession\022>.io.evitadb.e" + - "xternalApi.grpc.generated.GrpcEvitaSessi" + - "onRequest\032?.io.evitadb.externalApi.grpc." + - "generated.GrpcEvitaSessionResponse\022\237\001\n\034C" + - "reateBinaryReadWriteSession\022>.io.evitadb" + + "SessionType\022R\n\017commitBehaviour\030\003 \001(\01629.i" + + "o.evitadb.externalApi.grpc.generated.Grp" + + "cCommitBehavior\022M\n\014catalogState\030\004 \001(\01627." + + "io.evitadb.externalApi.grpc.generated.Gr" + + "pcCatalogState\"L\n\"GrpcEvitaSessionTermin" + + "ationRequest\022\023\n\013catalogName\030\001 \001(\t\022\021\n\tses" + + "sionId\030\002 \001(\t\"9\n#GrpcEvitaSessionTerminat" + + "ionResponse\022\022\n\nterminated\030\001 \001(\010\"0\n\030GrpcC" + + "atalogNamesResponse\022\024\n\014catalogNames\030\001 \003(" + + "\t\"/\n\030GrpcDefineCatalogRequest\022\023\n\013catalog" + + "Name\030\001 \001(\t\",\n\031GrpcDefineCatalogResponse\022" + + "\017\n\007success\030\001 \001(\010\"G\n\030GrpcRenameCatalogReq" + + "uest\022\023\n\013catalogName\030\001 \001(\t\022\026\n\016newCatalogN" + + "ame\030\002 \001(\t\",\n\031GrpcRenameCatalogResponse\022\017" + + "\n\007success\030\001 \001(\010\"a\n\031GrpcReplaceCatalogReq" + + "uest\022#\n\033catalogNameToBeReplacedWith\030\001 \001(" + + "\t\022\037\n\027catalogNameToBeReplaced\030\002 \001(\t\"-\n\032Gr" + + "pcReplaceCatalogResponse\022\017\n\007success\030\001 \001(" + + "\010\"7\n GrpcDeleteCatalogIfExistsRequest\022\023\n" + + "\013catalogName\030\001 \001(\t\"4\n!GrpcDeleteCatalogI" + + "fExistsResponse\022\017\n\007success\030\001 \001(\010\"{\n\026Grpc" + + "UpdateEvitaRequest\022a\n\017schemaMutations\030\001 " + + "\003(\0132H.io.evitadb.externalApi.grpc.genera" + + "ted.GrpcTopLevelCatalogSchemaMutation2\336\r" + + "\n\014EvitaService\022l\n\014ServerStatus\022\026.google." + + "protobuf.Empty\032D.io.evitadb.externalApi." + + "grpc.generated.GrpcEvitaServerStatusResp" + + "onse\022\230\001\n\025CreateReadOnlySession\022>.io.evit" + + "adb.externalApi.grpc.generated.GrpcEvita" + + "SessionRequest\032?.io.evitadb.externalApi." + + "grpc.generated.GrpcEvitaSessionResponse\022" + + "\231\001\n\026CreateReadWriteSession\022>.io.evitadb." + + "externalApi.grpc.generated.GrpcEvitaSess" + + "ionRequest\032?.io.evitadb.externalApi.grpc" + + ".generated.GrpcEvitaSessionResponse\022\236\001\n\033" + + "CreateBinaryReadOnlySession\022>.io.evitadb" + ".externalApi.grpc.generated.GrpcEvitaSes" + "sionRequest\032?.io.evitadb.externalApi.grp" + - "c.generated.GrpcEvitaSessionResponse\022\251\001\n" + - "\020TerminateSession\022I.io.evitadb.externalA" + - "pi.grpc.generated.GrpcEvitaSessionTermin" + - "ationRequest\032J.io.evitadb.externalApi.gr" + - "pc.generated.GrpcEvitaSessionTermination" + - "Response\022j\n\017GetCatalogNames\022\026.google.pro" + - "tobuf.Empty\032?.io.evitadb.externalApi.grp" + - "c.generated.GrpcCatalogNamesResponse\022\222\001\n" + - "\rDefineCatalog\022?.io.evitadb.externalApi." + - "grpc.generated.GrpcDefineCatalogRequest\032" + - "@.io.evitadb.externalApi.grpc.generated." + - "GrpcDefineCatalogResponse\022\222\001\n\rRenameCata" + - "log\022?.io.evitadb.externalApi.grpc.genera" + - "ted.GrpcRenameCatalogRequest\032@.io.evitad" + - "b.externalApi.grpc.generated.GrpcRenameC" + - "atalogResponse\022\225\001\n\016ReplaceCatalog\022@.io.e" + - "vitadb.externalApi.grpc.generated.GrpcRe" + - "placeCatalogRequest\032A.io.evitadb.externa" + - "lApi.grpc.generated.GrpcReplaceCatalogRe" + - "sponse\022\252\001\n\025DeleteCatalogIfExists\022G.io.ev" + - "itadb.externalApi.grpc.generated.GrpcDel" + - "eteCatalogIfExistsRequest\032H.io.evitadb.e" + - "xternalApi.grpc.generated.GrpcDeleteCata" + - "logIfExistsResponse\022_\n\006Update\022=.io.evita" + - "db.externalApi.grpc.generated.GrpcUpdate" + - "EvitaRequest\032\026.google.protobuf.EmptyB\014P\001" + - "\252\002\007EvitaDBb\006proto3" + "c.generated.GrpcEvitaSessionResponse\022\237\001\n" + + "\034CreateBinaryReadWriteSession\022>.io.evita" + + "db.externalApi.grpc.generated.GrpcEvitaS" + + "essionRequest\032?.io.evitadb.externalApi.g" + + "rpc.generated.GrpcEvitaSessionResponse\022\251" + + "\001\n\020TerminateSession\022I.io.evitadb.externa" + + "lApi.grpc.generated.GrpcEvitaSessionTerm" + + "inationRequest\032J.io.evitadb.externalApi." + + "grpc.generated.GrpcEvitaSessionTerminati" + + "onResponse\022j\n\017GetCatalogNames\022\026.google.p" + + "rotobuf.Empty\032?.io.evitadb.externalApi.g" + + "rpc.generated.GrpcCatalogNamesResponse\022\222" + + "\001\n\rDefineCatalog\022?.io.evitadb.externalAp" + + "i.grpc.generated.GrpcDefineCatalogReques" + + "t\032@.io.evitadb.externalApi.grpc.generate" + + "d.GrpcDefineCatalogResponse\022\222\001\n\rRenameCa" + + "talog\022?.io.evitadb.externalApi.grpc.gene" + + "rated.GrpcRenameCatalogRequest\032@.io.evit" + + "adb.externalApi.grpc.generated.GrpcRenam" + + "eCatalogResponse\022\225\001\n\016ReplaceCatalog\022@.io" + + ".evitadb.externalApi.grpc.generated.Grpc" + + "ReplaceCatalogRequest\032A.io.evitadb.exter" + + "nalApi.grpc.generated.GrpcReplaceCatalog" + + "Response\022\252\001\n\025DeleteCatalogIfExists\022G.io." + + "evitadb.externalApi.grpc.generated.GrpcD" + + "eleteCatalogIfExistsRequest\032H.io.evitadb" + + ".externalApi.grpc.generated.GrpcDeleteCa" + + "talogIfExistsResponse\022_\n\006Update\022=.io.evi" + + "tadb.externalApi.grpc.generated.GrpcUpda" + + "teEvitaRequest\032\026.google.protobuf.EmptyB\014" + + "P\001\252\002\007EvitaDBb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -220,7 +222,7 @@ public static void registerAllExtensions( internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_descriptor, - new java.lang.String[] { "Version", "StartedAt", "Uptime", "InstanceId", "CatalogsCorrupted", "CatalogsOk", }); + new java.lang.String[] { "Version", "StartedAt", "Uptime", "InstanceId", "CatalogsCorrupted", "CatalogsOk", "HealthProblems", }); internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_fieldAccessorTable = new diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java index 96a32969f..3945e9756 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java @@ -45,6 +45,7 @@ private GrpcEvitaServerStatusResponse(com.google.protobuf.GeneratedMessageV3.Bui private GrpcEvitaServerStatusResponse() { version_ = ""; instanceId_ = ""; + healthProblems_ = java.util.Collections.emptyList(); } @java.lang.Override @@ -67,6 +68,7 @@ private GrpcEvitaServerStatusResponse( if (extensionRegistry == null) { throw new java.lang.NullPointerException(); } + int mutable_bitField0_ = 0; com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { @@ -117,6 +119,29 @@ private GrpcEvitaServerStatusResponse( catalogsOk_ = input.readInt32(); break; } + case 56: { + int rawValue = input.readEnum(); + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + healthProblems_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + healthProblems_.add(rawValue); + break; + } + case 58: { + int length = input.readRawVarint32(); + int oldLimit = input.pushLimit(length); + while(input.getBytesUntilLimit() > 0) { + int rawValue = input.readEnum(); + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + healthProblems_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + healthProblems_.add(rawValue); + } + input.popLimit(oldLimit); + break; + } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -132,6 +157,9 @@ private GrpcEvitaServerStatusResponse( throw new com.google.protobuf.InvalidProtocolBufferException( e).setUnfinishedMessage(this); } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + healthProblems_ = java.util.Collections.unmodifiableList(healthProblems_); + } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -324,6 +352,84 @@ public int getCatalogsOk() { return catalogsOk_; } + public static final int HEALTHPROBLEMS_FIELD_NUMBER = 7; + private java.util.List healthProblems_; + private static final com.google.protobuf.Internal.ListAdapter.Converter< + java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem> healthProblems_converter_ = + new com.google.protobuf.Internal.ListAdapter.Converter< + java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>() { + public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem convert(java.lang.Integer from) { + @SuppressWarnings("deprecation") + io.evitadb.externalApi.grpc.generated.GrpcHealthProblem result = io.evitadb.externalApi.grpc.generated.GrpcHealthProblem.valueOf(from); + return result == null ? io.evitadb.externalApi.grpc.generated.GrpcHealthProblem.UNRECOGNIZED : result; + } + }; + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the healthProblems. + */ + @java.lang.Override + public java.util.List getHealthProblemsList() { + return new com.google.protobuf.Internal.ListAdapter< + java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>(healthProblems_, healthProblems_converter_); + } + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return The count of healthProblems. + */ + @java.lang.Override + public int getHealthProblemsCount() { + return healthProblems_.size(); + } + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the element to return. + * @return The healthProblems at the given index. + */ + @java.lang.Override + public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index) { + return healthProblems_converter_.convert(healthProblems_.get(index)); + } + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the enum numeric values on the wire for healthProblems. + */ + @java.lang.Override + public java.util.List + getHealthProblemsValueList() { + return healthProblems_; + } + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the value to return. + * @return The enum numeric value on the wire of healthProblems at the given index. + */ + @java.lang.Override + public int getHealthProblemsValue(int index) { + return healthProblems_.get(index); + } + private int healthProblemsMemoizedSerializedSize; + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -338,6 +444,7 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + getSerializedSize(); if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, version_); } @@ -356,6 +463,13 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (catalogsOk_ != 0) { output.writeInt32(6, catalogsOk_); } + if (getHealthProblemsList().size() > 0) { + output.writeUInt32NoTag(58); + output.writeUInt32NoTag(healthProblemsMemoizedSerializedSize); + } + for (int i = 0; i < healthProblems_.size(); i++) { + output.writeEnumNoTag(healthProblems_.get(i)); + } unknownFields.writeTo(output); } @@ -387,6 +501,18 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeInt32Size(6, catalogsOk_); } + { + int dataSize = 0; + for (int i = 0; i < healthProblems_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeEnumSizeNoTag(healthProblems_.get(i)); + } + size += dataSize; + if (!getHealthProblemsList().isEmpty()) { size += 1; + size += com.google.protobuf.CodedOutputStream + .computeUInt32SizeNoTag(dataSize); + }healthProblemsMemoizedSerializedSize = dataSize; + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -417,6 +543,7 @@ public boolean equals(final java.lang.Object obj) { != other.getCatalogsCorrupted()) return false; if (getCatalogsOk() != other.getCatalogsOk()) return false; + if (!healthProblems_.equals(other.healthProblems_)) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -443,6 +570,10 @@ public int hashCode() { hash = (53 * hash) + getCatalogsCorrupted(); hash = (37 * hash) + CATALOGSOK_FIELD_NUMBER; hash = (53 * hash) + getCatalogsOk(); + if (getHealthProblemsCount() > 0) { + hash = (37 * hash) + HEALTHPROBLEMS_FIELD_NUMBER; + hash = (53 * hash) + healthProblems_.hashCode(); + } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -596,6 +727,8 @@ public Builder clear() { catalogsOk_ = 0; + healthProblems_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); return this; } @@ -622,6 +755,7 @@ public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse build @java.lang.Override public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse buildPartial() { io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse result = new io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse(this); + int from_bitField0_ = bitField0_; result.version_ = version_; if (startedAtBuilder_ == null) { result.startedAt_ = startedAt_; @@ -632,6 +766,11 @@ public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse build result.instanceId_ = instanceId_; result.catalogsCorrupted_ = catalogsCorrupted_; result.catalogsOk_ = catalogsOk_; + if (((bitField0_ & 0x00000001) != 0)) { + healthProblems_ = java.util.Collections.unmodifiableList(healthProblems_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.healthProblems_ = healthProblems_; onBuilt(); return result; } @@ -700,6 +839,16 @@ public Builder mergeFrom(io.evitadb.externalApi.grpc.generated.GrpcEvitaServerSt if (other.getCatalogsOk() != 0) { setCatalogsOk(other.getCatalogsOk()); } + if (!other.healthProblems_.isEmpty()) { + if (healthProblems_.isEmpty()) { + healthProblems_ = other.healthProblems_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureHealthProblemsIsMutable(); + healthProblems_.addAll(other.healthProblems_); + } + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -728,6 +877,7 @@ public Builder mergeFrom( } return this; } + private int bitField0_; private java.lang.Object version_ = ""; /** @@ -1204,6 +1354,194 @@ public Builder clearCatalogsOk() { onChanged(); return this; } + + private java.util.List healthProblems_ = + java.util.Collections.emptyList(); + private void ensureHealthProblemsIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + healthProblems_ = new java.util.ArrayList(healthProblems_); + bitField0_ |= 0x00000001; + } + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the healthProblems. + */ + public java.util.List getHealthProblemsList() { + return new com.google.protobuf.Internal.ListAdapter< + java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>(healthProblems_, healthProblems_converter_); + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return The count of healthProblems. + */ + public int getHealthProblemsCount() { + return healthProblems_.size(); + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the element to return. + * @return The healthProblems at the given index. + */ + public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index) { + return healthProblems_converter_.convert(healthProblems_.get(index)); + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index to set the value at. + * @param value The healthProblems to set. + * @return This builder for chaining. + */ + public Builder setHealthProblems( + int index, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHealthProblemsIsMutable(); + healthProblems_.set(index, value.getNumber()); + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param value The healthProblems to add. + * @return This builder for chaining. + */ + public Builder addHealthProblems(io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value) { + if (value == null) { + throw new NullPointerException(); + } + ensureHealthProblemsIsMutable(); + healthProblems_.add(value.getNumber()); + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param values The healthProblems to add. + * @return This builder for chaining. + */ + public Builder addAllHealthProblems( + java.lang.Iterable values) { + ensureHealthProblemsIsMutable(); + for (io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value : values) { + healthProblems_.add(value.getNumber()); + } + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return This builder for chaining. + */ + public Builder clearHealthProblems() { + healthProblems_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the enum numeric values on the wire for healthProblems. + */ + public java.util.List + getHealthProblemsValueList() { + return java.util.Collections.unmodifiableList(healthProblems_); + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the value to return. + * @return The enum numeric value on the wire of healthProblems at the given index. + */ + public int getHealthProblemsValue(int index) { + return healthProblems_.get(index); + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the value to return. + * @return The enum numeric value on the wire of healthProblems at the given index. + * @return This builder for chaining. + */ + public Builder setHealthProblemsValue( + int index, int value) { + ensureHealthProblemsIsMutable(); + healthProblems_.set(index, value); + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param value The enum numeric value on the wire for healthProblems to add. + * @return This builder for chaining. + */ + public Builder addHealthProblemsValue(int value) { + ensureHealthProblemsIsMutable(); + healthProblems_.add(value); + onChanged(); + return this; + } + /** + *
+     * Set of all observed health problems
+     * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param values The enum numeric values on the wire for healthProblems to add. + * @return This builder for chaining. + */ + public Builder addAllHealthProblemsValue( + java.lang.Iterable values) { + ensureHealthProblemsIsMutable(); + for (int value : values) { + healthProblems_.add(value); + } + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java index 1520b68fe..c52d8142a 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java @@ -126,4 +126,53 @@ public interface GrpcEvitaServerStatusResponseOrBuilder extends * @return The catalogsOk. */ int getCatalogsOk(); + + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the healthProblems. + */ + java.util.List getHealthProblemsList(); + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return The count of healthProblems. + */ + int getHealthProblemsCount(); + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the element to return. + * @return The healthProblems at the given index. + */ + io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index); + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @return A list containing the enum numeric values on the wire for healthProblems. + */ + java.util.List + getHealthProblemsValueList(); + /** + *
+   * Set of all observed health problems
+   * 
+ * + * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; + * @param index The index of the value to return. + * @return The enum numeric value on the wire of healthProblems at the given index. + */ + int getHealthProblemsValue(int index); } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java index 1acea46ee..77ddce1b8 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,237 +39,237 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogStateResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogStateResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcPaginatedList_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcPaginatedList_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcStripList_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcStripList_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDataChunk_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDataChunk_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcGoLiveAndCloseResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcGoLiveAndCloseResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityTypesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityTypesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryOneResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryOneResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryListResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryListResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityAndItsHierarchyResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityAndItsHierarchyResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcTransactionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcTransactionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryParam_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryParam_fieldAccessorTable; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java index dd61ef83b..0382b83e9 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -142,7 +142,7 @@ public java.lang.String getCatalogName() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); catalogName_ = s; @@ -162,7 +162,7 @@ public java.lang.String getCatalogName() { getCatalogNameBytes() { java.lang.Object ref = catalogName_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); catalogName_ = b; @@ -587,7 +587,7 @@ public java.lang.String getCatalogName() { getCatalogNameBytes() { java.lang.Object ref = catalogName_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); catalogName_ = b; @@ -610,7 +610,7 @@ public Builder setCatalogName( if (value == null) { throw new NullPointerException(); } - + catalogName_ = value; onChanged(); return this; @@ -624,7 +624,7 @@ public Builder setCatalogName( * @return This builder for chaining. */ public Builder clearCatalogName() { - + catalogName_ = getDefaultInstance().getCatalogName(); onChanged(); return this; @@ -644,7 +644,7 @@ public Builder setCatalogNameBytes( throw new NullPointerException(); } checkByteStringIsUtf8(value); - + catalogName_ = value; onChanged(); return this; @@ -672,7 +672,7 @@ public Builder setCatalogNameBytes( * @return This builder for chaining. */ public Builder setCommitBehaviorValue(int value) { - + commitBehavior_ = value; onChanged(); return this; @@ -704,7 +704,7 @@ public Builder setCommitBehavior(io.evitadb.externalApi.grpc.generated.GrpcCommi if (value == null) { throw new NullPointerException(); } - + commitBehavior_ = value.getNumber(); onChanged(); return this; @@ -718,7 +718,7 @@ public Builder setCommitBehavior(io.evitadb.externalApi.grpc.generated.GrpcCommi * @return This builder for chaining. */ public Builder clearCommitBehavior() { - + commitBehavior_ = 0; onChanged(); return this; @@ -747,7 +747,7 @@ public boolean getDryRun() { * @return This builder for chaining. */ public Builder setDryRun(boolean value) { - + dryRun_ = value; onChanged(); return this; @@ -761,7 +761,7 @@ public Builder setDryRun(boolean value) { * @return This builder for chaining. */ public Builder clearDryRun() { - + dryRun_ = false; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java index 26ac8e5eb..29ecc4e32 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java index 595f765c5..604fe3040 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -151,7 +151,7 @@ public java.lang.String getSessionId() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); sessionId_ = s; @@ -171,7 +171,7 @@ public java.lang.String getSessionId() { getSessionIdBytes() { java.lang.Object ref = sessionId_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); sessionId_ = b; @@ -649,7 +649,7 @@ public java.lang.String getSessionId() { getSessionIdBytes() { java.lang.Object ref = sessionId_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); sessionId_ = b; @@ -672,7 +672,7 @@ public Builder setSessionId( if (value == null) { throw new NullPointerException(); } - + sessionId_ = value; onChanged(); return this; @@ -686,7 +686,7 @@ public Builder setSessionId( * @return This builder for chaining. */ public Builder clearSessionId() { - + sessionId_ = getDefaultInstance().getSessionId(); onChanged(); return this; @@ -706,7 +706,7 @@ public Builder setSessionIdBytes( throw new NullPointerException(); } checkByteStringIsUtf8(value); - + sessionId_ = value; onChanged(); return this; @@ -734,7 +734,7 @@ public Builder setSessionIdBytes( * @return This builder for chaining. */ public Builder setSessionTypeValue(int value) { - + sessionType_ = value; onChanged(); return this; @@ -766,7 +766,7 @@ public Builder setSessionType(io.evitadb.externalApi.grpc.generated.GrpcSessionT if (value == null) { throw new NullPointerException(); } - + sessionType_ = value.getNumber(); onChanged(); return this; @@ -780,7 +780,7 @@ public Builder setSessionType(io.evitadb.externalApi.grpc.generated.GrpcSessionT * @return This builder for chaining. */ public Builder clearSessionType() { - + sessionType_ = 0; onChanged(); return this; @@ -808,7 +808,7 @@ public Builder clearSessionType() { * @return This builder for chaining. */ public Builder setCommitBehaviourValue(int value) { - + commitBehaviour_ = value; onChanged(); return this; @@ -840,7 +840,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm if (value == null) { throw new NullPointerException(); } - + commitBehaviour_ = value.getNumber(); onChanged(); return this; @@ -854,7 +854,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm * @return This builder for chaining. */ public Builder clearCommitBehaviour() { - + commitBehaviour_ = 0; onChanged(); return this; @@ -882,7 +882,7 @@ public Builder clearCommitBehaviour() { * @return This builder for chaining. */ public Builder setCatalogStateValue(int value) { - + catalogState_ = value; onChanged(); return this; @@ -914,7 +914,7 @@ public Builder setCatalogState(io.evitadb.externalApi.grpc.generated.GrpcCatalog if (value == null) { throw new NullPointerException(); } - + catalogState_ = value.getNumber(); onChanged(); return this; @@ -928,7 +928,7 @@ public Builder setCatalogState(io.evitadb.externalApi.grpc.generated.GrpcCatalog * @return This builder for chaining. */ public Builder clearCatalogState() { - + catalogState_ = 0; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java index e5fb4c9ac..433179ad4 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java index a9b2e3782..95532aac0 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -495,7 +495,7 @@ public boolean getSuccess() { * @return This builder for chaining. */ public Builder setSuccess(boolean value) { - + success_ = value; onChanged(); return this; @@ -509,7 +509,7 @@ public Builder setSuccess(boolean value) { * @return This builder for chaining. */ public Builder clearSuccess() { - + success_ = false; onChanged(); return this; @@ -538,7 +538,7 @@ public long getCatalogVersion() { * @return This builder for chaining. */ public Builder setCatalogVersion(long value) { - + catalogVersion_ = value; onChanged(); return this; @@ -552,7 +552,7 @@ public Builder setCatalogVersion(long value) { * @return This builder for chaining. */ public Builder clearCatalogVersion() { - + catalogVersion_ = 0L; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java index 29bb35755..2e4f99434 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java new file mode 100644 index 000000000..9b85d02fb --- /dev/null +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java @@ -0,0 +1,160 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: GrpcEnums.proto + +package io.evitadb.externalApi.grpc.generated; + +/** + *
+ * This enum represents all possible health problems that are monitored by evitaDB.
+ * 
+ * + * Protobuf enum {@code io.evitadb.externalApi.grpc.generated.GrpcHealthProblem} + */ +public enum GrpcHealthProblem + implements com.google.protobuf.ProtocolMessageEnum { + /** + *
+   * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free
+   * old generation multiple times consuming a log of CPU power.
+   * 
+ * + * MEMORY_SHORTAGE = 0; + */ + MEMORY_SHORTAGE(0), + /** + *
+   * Signalized when the input queues are full and the server is not able to process incoming requests. This flag
+   * is cleared when the server is able to process incoming requests again.
+   * 
+ * + * INPUT_QUEUES_OVERLOADED = 1; + */ + INPUT_QUEUES_OVERLOADED(1), + UNRECOGNIZED(-1), + ; + + /** + *
+   * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free
+   * old generation multiple times consuming a log of CPU power.
+   * 
+ * + * MEMORY_SHORTAGE = 0; + */ + public static final int MEMORY_SHORTAGE_VALUE = 0; + /** + *
+   * Signalized when the input queues are full and the server is not able to process incoming requests. This flag
+   * is cleared when the server is able to process incoming requests again.
+   * 
+ * + * INPUT_QUEUES_OVERLOADED = 1; + */ + public static final int INPUT_QUEUES_OVERLOADED_VALUE = 1; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static GrpcHealthProblem valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static GrpcHealthProblem forNumber(int value) { + switch (value) { + case 0: return MEMORY_SHORTAGE; + case 1: return INPUT_QUEUES_OVERLOADED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + GrpcHealthProblem> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public GrpcHealthProblem findValueByNumber(int number) { + return GrpcHealthProblem.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return io.evitadb.externalApi.grpc.generated.GrpcEnums.getDescriptor().getEnumTypes().get(23); + } + + private static final GrpcHealthProblem[] VALUES = values(); + + public static GrpcHealthProblem valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private GrpcHealthProblem(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:io.evitadb.externalApi.grpc.generated.GrpcHealthProblem) +} + diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java index d9091f27e..76d568713 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java @@ -46,6 +46,7 @@ import io.evitadb.api.requestResponse.schema.OrderBehaviour; import io.evitadb.api.requestResponse.schema.dto.AttributeUniquenessType; import io.evitadb.api.requestResponse.schema.dto.GlobalAttributeUniquenessType; +import io.evitadb.api.requestResponse.system.SystemStatus.HealthProblem; import io.evitadb.exception.EvitaInternalError; import io.evitadb.externalApi.grpc.generated.*; import lombok.AccessLevel; @@ -70,8 +71,7 @@ public static CatalogState toCatalogState(@Nonnull GrpcCatalogState grpcCatalogS return switch (grpcCatalogState.getNumber()) { case 0 -> CatalogState.WARMING_UP; case 1 -> CatalogState.ALIVE; - default -> - throw new EvitaInternalError("Unrecognized remote catalog state: " + grpcCatalogState); + default -> throw new EvitaInternalError("Unrecognized remote catalog state: " + grpcCatalogState); }; } @@ -98,8 +98,7 @@ public static QueryPriceMode toQueryPriceMode(@Nonnull GrpcQueryPriceMode grpcQu return switch (grpcQueryPriceMode.getNumber()) { case 0 -> QueryPriceMode.WITH_TAX; case 1 -> QueryPriceMode.WITHOUT_TAX; - default -> - throw new EvitaInternalError("Unrecognized remote query price mode: " + grpcQueryPriceMode); + default -> throw new EvitaInternalError("Unrecognized remote query price mode: " + grpcQueryPriceMode); }; } @@ -130,8 +129,7 @@ public static PriceContentMode toPriceContentMode(@Nonnull GrpcPriceContentMode case 0 -> PriceContentMode.NONE; case 1 -> PriceContentMode.RESPECTING_FILTER; case 2 -> PriceContentMode.ALL; - default -> - throw new EvitaInternalError("Unrecognized remote price content mode: " + grpcPriceContentMode); + default -> throw new EvitaInternalError("Unrecognized remote price content mode: " + grpcPriceContentMode); }; } @@ -162,8 +160,7 @@ public static OrderDirection toOrderDirection(@Nonnull GrpcOrderDirection grpcOr return switch (grpcOrderDirection.getNumber()) { case 0 -> OrderDirection.ASC; case 1 -> OrderDirection.DESC; - default -> - throw new EvitaInternalError("Unrecognized remote order direction: " + grpcOrderDirection); + default -> throw new EvitaInternalError("Unrecognized remote order direction: " + grpcOrderDirection); }; } @@ -193,8 +190,7 @@ public static OrderBehaviour toOrderBehaviour(@Nonnull GrpcOrderBehaviour grpcOr return switch (grpcOrderBehaviour.getNumber()) { case 0 -> OrderBehaviour.NULLS_FIRST; case 1 -> OrderBehaviour.NULLS_LAST; - default -> - throw new EvitaInternalError("Unrecognized remote order behaviour: " + grpcOrderBehaviour); + default -> throw new EvitaInternalError("Unrecognized remote order behaviour: " + grpcOrderBehaviour); }; } @@ -256,8 +252,7 @@ public static StatisticsBase toStatisticsBase(@Nonnull GrpcStatisticsBase grpcSt return switch (grpcStatisticsBase.getNumber()) { case 0 -> StatisticsBase.COMPLETE_FILTER; case 1 -> StatisticsBase.WITHOUT_USER_FILTER; - default -> - throw new EvitaInternalError("Unrecognized remote statistics base: " + grpcStatisticsBase); + default -> throw new EvitaInternalError("Unrecognized remote statistics base: " + grpcStatisticsBase); }; } @@ -287,8 +282,7 @@ public static StatisticsType toStatisticsType(@Nonnull GrpcStatisticsType grpcSt return switch (grpcStatisticsType.getNumber()) { case 0 -> StatisticsType.CHILDREN_COUNT; case 1 -> StatisticsType.QUERIED_ENTITY_COUNT; - default -> - throw new EvitaInternalError("Unrecognized remote statistics type: " + grpcStatisticsType); + default -> throw new EvitaInternalError("Unrecognized remote statistics type: " + grpcStatisticsType); }; } @@ -304,8 +298,7 @@ public static HistogramBehavior toHistogramBehavior(@Nonnull GrpcHistogramBehavi return switch (grpcHistogramBehavior.getNumber()) { case 0 -> HistogramBehavior.STANDARD; case 1 -> HistogramBehavior.OPTIMIZED; - default -> - throw new EvitaInternalError("Unrecognized remote histogram behavior: " + grpcHistogramBehavior); + default -> throw new EvitaInternalError("Unrecognized remote histogram behavior: " + grpcHistogramBehavior); }; } @@ -448,8 +441,7 @@ public static Cardinality toCardinality(@Nonnull GrpcCardinality grpcCardinality case 2 -> Cardinality.EXACTLY_ONE; case 3 -> Cardinality.ZERO_OR_MORE; case 4 -> Cardinality.ONE_OR_MORE; - default -> - throw new EvitaInternalError("Unrecognized remote cardinality: " + grpcCardinality); + default -> throw new EvitaInternalError("Unrecognized remote cardinality: " + grpcCardinality); }; } @@ -483,8 +475,7 @@ public static GrpcCardinality toGrpcCardinality(@Nullable Cardinality cardinalit public static CatalogEvolutionMode toCatalogEvolutionMode(@Nonnull GrpcCatalogEvolutionMode grpcEvolutionMode) { return switch (grpcEvolutionMode.getNumber()) { case 0 -> CatalogEvolutionMode.ADDING_ENTITY_TYPES; - default -> - throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); + default -> throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); }; } @@ -519,8 +510,7 @@ public static EvolutionMode toEvolutionMode(@Nonnull GrpcEvolutionMode grpcEvolu case 5 -> EvolutionMode.ADDING_LOCALES; case 6 -> EvolutionMode.ADDING_CURRENCIES; case 7 -> EvolutionMode.ADDING_HIERARCHY; - default -> - throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); + default -> throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); }; } @@ -575,8 +565,7 @@ public static QueryPhase toQueryPhase(@Nonnull GrpcQueryPhase grpcQueryPhase) { case 18 -> QueryPhase.FETCHING; case 19 -> QueryPhase.FETCHING_REFERENCES; case 20 -> QueryPhase.FETCHING_PARENTS; - default -> - throw new EvitaInternalError("Unrecognized remote query phase: " + grpcQueryPhase); + default -> throw new EvitaInternalError("Unrecognized remote query phase: " + grpcQueryPhase); }; } @@ -599,7 +588,8 @@ public static GrpcQueryPhase toGrpcQueryPhase(@Nonnull QueryPhase queryPhase) { case PLANNING_SORT -> GrpcQueryPhase.PLANNING_SORT; case PLANNING_SORT_ALTERNATIVE -> GrpcQueryPhase.PLANNING_SORT_ALTERNATIVE; case PLANNING_EXTRA_RESULT_FABRICATION -> GrpcQueryPhase.PLANNING_EXTRA_RESULT_FABRICATION; - case PLANNING_EXTRA_RESULT_FABRICATION_ALTERNATIVE -> GrpcQueryPhase.PLANNING_EXTRA_RESULT_FABRICATION_ALTERNATIVE; + case PLANNING_EXTRA_RESULT_FABRICATION_ALTERNATIVE -> + GrpcQueryPhase.PLANNING_EXTRA_RESULT_FABRICATION_ALTERNATIVE; case EXECUTION -> GrpcQueryPhase.EXECUTION; case EXECUTION_PREFETCH -> GrpcQueryPhase.EXECUTION_PREFETCH; case EXECUTION_FILTER -> GrpcQueryPhase.EXECUTION_FILTER; @@ -626,8 +616,7 @@ public static EntityExistence toEntityExistence(@Nonnull GrpcEntityExistence grp case 0 -> EntityExistence.MAY_EXIST; case 1 -> EntityExistence.MUST_NOT_EXIST; case 2 -> EntityExistence.MUST_EXIST; - default -> - throw new EvitaInternalError("Unrecognized remote entity existence: " + grpcEntityExistence); + default -> throw new EvitaInternalError("Unrecognized remote entity existence: " + grpcEntityExistence); }; } @@ -678,8 +667,7 @@ public static AttributeUniquenessType toAttributeUniquenessType(@Nonnull GrpcAtt case 0 -> AttributeUniquenessType.NOT_UNIQUE; case 1 -> AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION; case 2 -> AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION_LOCALE; - default -> - throw new EvitaInternalError("Unrecognized remote attribute uniqueness type: " + type); + default -> throw new EvitaInternalError("Unrecognized remote attribute uniqueness type: " + type); }; } @@ -710,8 +698,7 @@ public static GlobalAttributeUniquenessType toGlobalAttributeUniquenessType(@Non case 0 -> GlobalAttributeUniquenessType.NOT_UNIQUE; case 1 -> GlobalAttributeUniquenessType.UNIQUE_WITHIN_CATALOG; case 2 -> GlobalAttributeUniquenessType.UNIQUE_WITHIN_CATALOG_LOCALE; - default -> - throw new EvitaInternalError("Unrecognized remote global attribute uniqueness type: " + type); + default -> throw new EvitaInternalError("Unrecognized remote global attribute uniqueness type: " + type); }; } @@ -758,8 +745,23 @@ public static CommitBehavior toCommitBehavior(@Nonnull GrpcCommitBehavior commit case WAIT_FOR_CONFLICT_RESOLUTION -> CommitBehavior.WAIT_FOR_CONFLICT_RESOLUTION; case WAIT_FOR_LOG_PERSISTENCE -> CommitBehavior.WAIT_FOR_WAL_PERSISTENCE; case WAIT_FOR_INDEX_PROPAGATION -> CommitBehavior.WAIT_FOR_INDEX_PROPAGATION; - default -> - throw new EvitaInternalError("Unrecognized remote commit behavior: " + commitBehaviour); + default -> throw new EvitaInternalError("Unrecognized remote commit behavior: " + commitBehaviour); + }; + } + + /** + * Converts a GrpcHealthProblem to a HealthProblem. + * + * @param grpcHealthProblem The GrpcHealthProblem to convert. + * @return The converted HealthProblem. + * @throws EvitaInternalError if the given grpcHealthProblem is unrecognized. + */ + @Nonnull + public static HealthProblem toHealthProblem(@Nonnull GrpcHealthProblem grpcHealthProblem) { + return switch (grpcHealthProblem.getNumber()) { + case 0 -> HealthProblem.MEMORY_SHORTAGE; + case 1 -> HealthProblem.INPUT_QUEUES_OVERLOADED; + default -> throw new EvitaInternalError("Unrecognized remote health problem: " + grpcHealthProblem); }; } } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto index 96dd40893..86d43fe48 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto @@ -500,3 +500,15 @@ enum GrpcCommitBehavior { WAIT_FOR_INDEX_PROPAGATION = 2; } + +// This enum represents all possible health problems that are monitored by evitaDB. +enum GrpcHealthProblem { + + // Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free + // old generation multiple times consuming a log of CPU power. + MEMORY_SHORTAGE = 0; + // Signalized when the input queues are full and the server is not able to process incoming requests. This flag + // is cleared when the server is able to process incoming requests again. + INPUT_QUEUES_OVERLOADED = 1; + +} diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto index 88e9139ab..6f42fe19b 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto @@ -23,6 +23,8 @@ message GrpcEvitaServerStatusResponse { int32 catalogsCorrupted = 5; // Number of catalogs that are ok int32 catalogsOk = 6; + // Set of all observed health problems + repeated GrpcHealthProblem healthProblems = 7; } // Request to create a session inside of a catalog. @@ -152,4 +154,4 @@ service EvitaService { rpc DeleteCatalogIfExists(GrpcDeleteCatalogIfExistsRequest) returns (GrpcDeleteCatalogIfExistsResponse); // Procedure used to update the catalog with a set of mutations. rpc Update(GrpcUpdateEvitaRequest) returns (google.protobuf.Empty); -} \ No newline at end of file +} diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java index a83774872..082c1310f 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java @@ -146,6 +146,10 @@ public void registerHandlers(EnhancedQueueExecutor executor) { final Set classNames = eventClasses.getValue(); final Set> existingEventClasses = CustomEventProvider.getEventClasses(); + // jvm_memory_used_bytes{area="heap"} + // jvm_memory_max_bytes{area="heap"} + // jvm_gc_collection_seconds_sum{gc="G1 Old Generation"} + for (Class eventClass : existingEventClasses) { // if event is enabled if (classNames.contains(eventClass.getName())) { diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index 9c2a35d98..6157ab3c4 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -59,6 +59,7 @@ public class SystemProviderRegistrar implements ExternalApiProviderRegistrar { private static final String ENDPOINT_SERVER_NAME = "server-name"; private static final String ENDPOINT_SYSTEM_STATUS = "status"; + private static final String ENDPOINT_SYSTEM_LIVENESS = "liveness"; @Nonnull @Override @@ -119,6 +120,27 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull } ); + router.addExactPath( + "/" + ENDPOINT_SYSTEM_LIVENESS, + exchange -> { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); + final SystemStatus systemStatus = evita.getSystemStatus(); + if (systemStatus.healthProblems().isEmpty()) { + exchange.setStatusCode(StatusCodes.OK); + exchange.getResponseSender().send("{\"status\": \"healthy\"}"); + } else { + exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); + exchange.getResponseSender().send( + "{\"status\": \"unhealthy\", \"problems\": [" + + systemStatus.healthProblems().stream() + .map(it -> "\"" + it.name() + "\"") + .collect(Collectors.joining(", ")) + + "]}" + ); + } + } + ); + final ResourceHandler fileSystemHandler; try (ResourceManager resourceManager = new FileResourceManager(file, 100)) { fileSystemHandler = new ResourceHandler( @@ -182,7 +204,11 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull * @return the JSON string representing the server status */ @Nonnull - private static String renderStatus(@Nonnull String instanceId, @Nonnull SystemStatus systemStatus, ApiOptions apiOptions) { + private static String renderStatus( + @Nonnull String instanceId, + @Nonnull SystemStatus systemStatus, + @Nonnull ApiOptions apiOptions + ) { return String.format(""" { "serverName": "%s", @@ -192,6 +218,7 @@ private static String renderStatus(@Nonnull String instanceId, @Nonnull SystemSt "uptimeForHuman": "%s", "catalogsCorrupted": %d, "catalogsOk": %d, + "healthProblems": [%s], "apis": [ %s ] @@ -203,6 +230,9 @@ private static String renderStatus(@Nonnull String instanceId, @Nonnull SystemSt StringUtils.formatDuration(systemStatus.uptime()), systemStatus.catalogsCorrupted(), systemStatus.catalogsOk(), + systemStatus.healthProblems().stream() + .map(it -> "\"" + it.name() + "\"") + .collect(Collectors.joining(", ")), apiOptions.endpoints().entrySet().stream() .map( entry -> " {\n \"" + entry.getKey() + "\": [\n" + From 24fce4f8f0f04b6cca7a7230dd884867f298b2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Fri, 3 May 2024 13:57:10 +0200 Subject: [PATCH 04/24] fix(#550): Provide health check for Docker image Readiness probe implemented and tested. --- evita_common/pom.xml | 5 + .../java/io/evitadb/utils/NetworkUtils.java | 164 +++++++++++++++++ evita_common/src/main/java/module-info.java | 1 + .../externalApi/http/ExternalApiProvider.java | 9 + .../http/ExternalApiProviderRegistrar.java | 65 ++++--- .../externalApi/http/ExternalApiServer.java | 9 +- .../externalApi/graphql/GraphQLManager.java | 3 +- .../externalApi/graphql/GraphQLProvider.java | 32 ++++ .../graphql/GraphQLProviderRegistrar.java | 3 +- .../externalApi/grpc/GrpcProvider.java | 30 ++++ .../grpc/GrpcProviderRegistrar.java | 3 +- .../evitadb/externalApi/lab/LabProvider.java | 26 +++ .../externalApi/lab/LabProviderRegistrar.java | 3 +- .../observability/ObservabilityManager.java | 8 +- .../observability/ObservabilityProvider.java | 30 ++++ .../ObservabilityProviderRegistrar.java | 3 +- .../externalApi/rest/RestProvider.java | 30 ++++ .../rest/RestProviderRegistrar.java | 3 +- .../api/system/model/LivenessDescriptor.java | 4 +- .../externalApi/system/SystemProvider.java | 22 +++ .../system/SystemProviderRegistrar.java | 166 ++++++++++++------ .../io/evitadb/server/EvitaServerTest.java | 82 ++++++++- evita_server/pom.xml | 5 + pom.xml | 5 + 24 files changed, 613 insertions(+), 98 deletions(-) diff --git a/evita_common/pom.xml b/evita_common/pom.xml index 6a9cf3c1d..96cfbc1e7 100644 --- a/evita_common/pom.xml +++ b/evita_common/pom.xml @@ -59,6 +59,11 @@ net.openhft zero-allocation-hashing
+ + com.squareup.okhttp3 + okhttp + true + diff --git a/evita_common/src/main/java/io/evitadb/utils/NetworkUtils.java b/evita_common/src/main/java/io/evitadb/utils/NetworkUtils.java index 2aa524618..cdae693a8 100644 --- a/evita_common/src/main/java/io/evitadb/utils/NetworkUtils.java +++ b/evita_common/src/main/java/io/evitadb/utils/NetworkUtils.java @@ -23,9 +23,33 @@ package io.evitadb.utils; +import lombok.NoArgsConstructor; +import okhttp3.ConnectionPool; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Request.Builder; +import okhttp3.RequestBody; +import okhttp3.Response; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.concurrent.TimeUnit; /** * Utility class for network related operations. @@ -33,6 +57,7 @@ * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023 */ public class NetworkUtils { + private static OkHttpClient HTTP_CLIENT; /** * Returns human comprehensible host name of the given host. @@ -63,4 +88,143 @@ public static String getLocalHostName() { } } + /** + * Returns true if the URL is reachable and returns some content + * + * @param url URL to check + * @return true if the URL is reachable and returns some content + */ + public static boolean isReachable(@Nonnull String url) { + try { + try ( + final Response response = getHttpClient().newCall( + new Builder() + .url(url) + .get() + .build() + ).execute() + ) { + return response.code() < 500; + } + } catch (IOException e) { + return false; + } + } + + /** + * Returns content of the URL if it is reachable and returns some content. + * + * @param url URL to check + * @param method HTTP method to use + * @param contentType content type to use + * @param body body to send + * @return the content of the URL as a string or empty optional if the URL is not reachable or + * does not return any content + */ + @Nonnull + public static Optional fetchContent( + @Nonnull String url, + @Nullable String method, + @Nonnull String contentType, + @Nullable String body + ) { + try { + final RequestBody requestBody = Optional.ofNullable(body) + .map(theBody -> RequestBody.create(theBody, MediaType.parse(contentType))) + .orElse(null); + try ( + final Response response = getHttpClient().newCall( + new Request( + HttpUrl.parse(url), + Headers.of("Content-Type", contentType), + method != null ? method : "GET", + requestBody + ) + ).execute() + ) { + return Optional.of(response.body().string()); + } + } catch (IOException e) { + return Optional.empty(); + } + } + + /** + * Returns HTTP status code of the URL if it is reachable and returns some content. + * + * @param url URL to check + * @param method HTTP method to use + * @param contentType content type to use + * @return the HTTP status code of the URL or empty optional if the URL is not reachable + */ + @Nonnull + public static OptionalInt getHttpStatusCode( + @Nonnull String url, + @Nullable String method, + @Nonnull String contentType + ) { + try { + try ( + final Response response = getHttpClient().newCall( + new Builder() + .url(url) + .addHeader("Content-Type", contentType) + .method(method != null ? method : "GET", null) + .build() + ).execute() + ) { + return OptionalInt.of(response.code()); + } + } catch (IOException e) { + return OptionalInt.empty(); + } + } + + /** + * Returns the cached HTTP client instance. Instance has low timeouts and trusts all certificates, it doesn't reuse + * connections. + * + * @return the HTTP client instance + */ + @Nonnull + private static OkHttpClient getHttpClient() { + if (HTTP_CLIENT == null) { + try { + // Get a new SSL context + final SSLContext sc = SSLContext.getInstance("TLSv1.3"); + sc.init(null, new TrustManager[]{TrustAllX509TrustManager.INSTANCE}, new java.security.SecureRandom()); + + HTTP_CLIENT = new OkHttpClient.Builder() + .sslSocketFactory(sc.getSocketFactory(), TrustAllX509TrustManager.INSTANCE) + .protocols(Arrays.asList(Protocol.HTTP_1_1, Protocol.HTTP_2)) + .readTimeout(500, TimeUnit.MILLISECONDS) + .callTimeout(500, TimeUnit.MILLISECONDS) + .connectionPool(new ConnectionPool(0, 1, TimeUnit.MILLISECONDS)) + .build(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IllegalStateException("Failed to create HTTP client", e); + } + } + return HTTP_CLIENT; + } + + /** + * This trust manager is meant to be used only by this class to enable trust of all certificates. It's unsafe so + * it should be used only for fetching the data from local HTTP servers that are in the same network (trusted zone). + */ + @NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) + private static class TrustAllX509TrustManager implements X509TrustManager { + public static final TrustAllX509TrustManager INSTANCE = new TrustAllX509TrustManager(); + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + } } diff --git a/evita_common/src/main/java/module-info.java b/evita_common/src/main/java/module-info.java index f506def22..118d6419e 100644 --- a/evita_common/src/main/java/module-info.java +++ b/evita_common/src/main/java/module-info.java @@ -21,5 +21,6 @@ requires static lombok; requires com.fasterxml.jackson.databind; requires zero.allocation.hashing; + requires okhttp3; } diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProvider.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProvider.java index cbf6d0773..0151b7e27 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProvider.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProvider.java @@ -79,4 +79,13 @@ default void afterStart() { default void beforeStop() { // do nothing } + + /** + * Returns TRUE if the API is ready to accept requests. This method should physically test an API call to determine + * the API responds to the requests. + * + * @return TRUE if the API is ready to accept requests + */ + boolean isReady(); + } diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProviderRegistrar.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProviderRegistrar.java index de15b017d..1289db30c 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProviderRegistrar.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiProviderRegistrar.java @@ -31,7 +31,7 @@ /** * Configures and registers provider of particular external API to HTTP server ({@link ExternalApiServer}). - * Each provider have to have unique code and have to implement {@link #register(Evita, ApiOptions, T)} + * Each provider have to have unique code and have to implement {@link #register(Evita, ExternalApiServer, ApiOptions, AbstractApiConfiguration)} * method which registers provider to the server to be later started by the server. * * It is based on {@link java.util.ServiceLoader} which requires appropriate registration of implementation of this interface. @@ -40,35 +40,42 @@ */ public interface ExternalApiProviderRegistrar { - /** - * Returns unique identification code of the API registrar. - * - * @return same code as linked {@link ExternalApiProvider#getCode()} - */ - @Nonnull - String getExternalApiCode(); + /** + * Returns unique identification code of the API registrar. + * + * @return same code as linked {@link ExternalApiProvider#getCode()} + */ + @Nonnull + String getExternalApiCode(); - /** - * Returns configuration initialized with default values for this external API. - * - * @return configuration object instance with sane default values - */ - @Nonnull - Class getConfigurationClass(); + /** + * Returns configuration initialized with default values for this external API. + * + * @return configuration object instance with sane default values + */ + @Nonnull + Class getConfigurationClass(); - /** - * @return order of the API provider. Providers with lower order are registered first. - */ - default int getOrder() { - return 0; - } + /** + * @return order of the API provider. Providers with lower order are registered first. + */ + default int getOrder() { + return 0; + } - /** - * Configures and registers this provider - * - * @param evita ready-to-use Evita with access to internal data structures - * @param externalApiConfiguration configuration parameters for this provider (structure is defined by provider itself) - */ - @Nonnull - ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull T externalApiConfiguration); + /** + * Configures and registers this provider + * + * @param evita ready-to-use Evita with access to internal data structures + * @param externalApiServer the server the created provider will be registered to (not serving yet) + * @param apiOptions options for this provider + * @param externalApiConfiguration configuration parameters for this provider (structure is defined by provider itself) + */ + @Nonnull + ExternalApiProvider register( + @Nonnull Evita evita, + @Nonnull ExternalApiServer externalApiServer, + @Nonnull ApiOptions apiOptions, + @Nonnull T externalApiConfiguration + ); } diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java index a3d89e56c..46688cfce 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java @@ -48,7 +48,6 @@ import io.undertow.server.handlers.PathHandler; import io.undertow.server.handlers.accesslog.AccessLogHandler; import io.undertow.server.handlers.accesslog.AccessLogReceiver; -import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver; import io.undertow.server.handlers.encoding.ContentEncodingRepository; import io.undertow.server.handlers.encoding.DeflateEncodingProvider; import io.undertow.server.handlers.encoding.EncodingHandler; @@ -283,9 +282,11 @@ private static KeyManagerFactory createKeyManagerFactory( /** * Registers API providers based on configuration and returns its references. */ + @Nonnull @SuppressWarnings("unchecked") private static Map> registerApiProviders( - @Nonnull Evita evitaSystemDataProvider, + @Nonnull Evita evita, + @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @SuppressWarnings("rawtypes") @Nonnull Collection externalApiProviders ) { @@ -299,7 +300,7 @@ private static Map> registerApiProviders( } //noinspection unchecked - return registrar.register(evitaSystemDataProvider, apiOptions, apiProviderConfiguration); + return registrar.register(evita, externalApiServer, apiOptions, apiProviderConfiguration); }) .filter(Objects::nonNull) .collect( @@ -335,7 +336,7 @@ public ExternalApiServer( final ServerCertificateManager serverCertificateManager = new ServerCertificateManager(apiOptions.certificate()); final CertificatePath certificatePath = initCertificate(apiOptions, serverCertificateManager); - this.registeredApiProviders = registerApiProviders(evita, apiOptions, externalApiProviders); + this.registeredApiProviders = registerApiProviders(evita, this, apiOptions, externalApiProviders); if (this.registeredApiProviders.isEmpty()) { log.info("No external API providers were registered. No server will be created."); rootServer = null; diff --git a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLManager.java b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLManager.java index 696f52b2b..e38827c99 100644 --- a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLManager.java +++ b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLManager.java @@ -69,6 +69,7 @@ @Slf4j public class GraphQLManager { + public static final String SYSTEM_API_PREFIX = "system"; /** * Common object mapper for endpoints */ @@ -206,7 +207,7 @@ public void unregisterCatalog(@Nonnull String catalogName) { */ private void registerSystemApi() { registerGraphQLEndpoint(new RegisteredGraphQLApi( - UriPath.of("/", "system"), + UriPath.of("/", SYSTEM_API_PREFIX), new AtomicReference<>(new SystemGraphQLBuilder(evita).build(graphQLConfig)) )); } diff --git a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProvider.java b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProvider.java index 8203980d4..fcef5ed96 100644 --- a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProvider.java +++ b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProvider.java @@ -25,11 +25,16 @@ import io.evitadb.externalApi.graphql.configuration.GraphQLConfig; import io.evitadb.externalApi.http.ExternalApiProvider; +import io.evitadb.utils.NetworkUtils; import io.undertow.server.HttpHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.function.Predicate; + +import static io.evitadb.externalApi.graphql.GraphQLManager.SYSTEM_API_PREFIX; /** * Descriptor of external API provider that provides GraphQL API. @@ -50,9 +55,36 @@ public class GraphQLProvider implements ExternalApiProvider { @Getter private final HttpHandler apiHandler; + /** + * Contains url that was at least once found reachable. + */ + private String reachableUrl; + @Nonnull @Override public String getCode() { return CODE; } + + @Override + public boolean isReady() { + final Predicate isReady = url -> { + final Optional post = NetworkUtils.fetchContent(url, "POST", "application/json", "{\"query\":\"{liveness}\"}"); + return post.map(content -> content.contains("true")).orElse(false); + }; + final String[] baseUrls = this.configuration.getBaseUrls(configuration.getExposedHost()); + if (this.reachableUrl == null) { + for (String baseUrl : baseUrls) { + final String url = baseUrl + SYSTEM_API_PREFIX; + if (isReady.test(url)) { + this.reachableUrl = url; + return true; + } + } + return false; + } else { + return isReady.test(this.reachableUrl); + } + } + } diff --git a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProviderRegistrar.java b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProviderRegistrar.java index 79eee946e..ac5e8c66f 100644 --- a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProviderRegistrar.java +++ b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/GraphQLProviderRegistrar.java @@ -29,6 +29,7 @@ import io.evitadb.externalApi.graphql.configuration.GraphQLConfig; import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; import io.undertow.server.HttpHandler; import javax.annotation.Nonnull; @@ -55,7 +56,7 @@ public Class getConfigurationClass() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull GraphQLConfig graphQLConfig) { + public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull GraphQLConfig graphQLConfig) { final GraphQLManager graphQLManager = new GraphQLManager(evita, graphQLConfig); evita.registerStructuralChangeObserver(new CatalogGraphQLRefreshingObserver(graphQLManager)); final HttpHandler apiHandler = graphQLManager.getGraphQLRouter(); diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProvider.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProvider.java index d9b09a4b1..fa37c0864 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProvider.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProvider.java @@ -26,12 +26,14 @@ import io.evitadb.externalApi.grpc.configuration.GrpcConfig; import io.evitadb.externalApi.grpc.exception.GrpcServerStartFailedException; import io.evitadb.externalApi.http.ExternalApiProvider; +import io.evitadb.utils.NetworkUtils; import io.grpc.Server; import lombok.Getter; import lombok.RequiredArgsConstructor; import javax.annotation.Nonnull; import java.io.IOException; +import java.util.function.Predicate; /** * Descriptor of external API provider that provides gRPC API. @@ -51,6 +53,11 @@ public class GrpcProvider implements ExternalApiProvider { @Getter private final Server server; + /** + * Contains url that was at least once found reachable. + */ + private String reachableUrl; + @Nonnull @Override public String getCode() { @@ -79,4 +86,27 @@ public void afterStart() { public void beforeStop() { server.shutdown(); } + + @Override + public boolean isReady() { + final Predicate isReady = url -> { + final int responseCode = NetworkUtils.getHttpStatusCode(url, "GET", "application/grpc") + .orElse(-1); + // we are interested in 405 Method Not Allowed which signals gRPC server is running + return responseCode == 405; + }; + final String[] baseUrls = this.configuration.getBaseUrls(configuration.getExposedHost()); + if (this.reachableUrl == null) { + for (String baseUrl : baseUrls) { + if (isReady.test(baseUrl)) { + this.reachableUrl = baseUrl; + return true; + } + } + return false; + } else { + return isReady.test(this.reachableUrl); + } + } + } diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProviderRegistrar.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProviderRegistrar.java index 5e0f36dc8..75d5e3e05 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProviderRegistrar.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/GrpcProviderRegistrar.java @@ -29,6 +29,7 @@ import io.evitadb.externalApi.grpc.utils.GrpcServer; import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; import io.grpc.Server; import javax.annotation.Nonnull; @@ -54,7 +55,7 @@ public Class getConfigurationClass() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull GrpcConfig grpcAPIConfig) { + public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull GrpcConfig grpcAPIConfig) { final Server server = new GrpcServer(evita, apiOptions, grpcAPIConfig).getServer(); return new GrpcProvider(grpcAPIConfig, server); } diff --git a/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProvider.java b/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProvider.java index a68c29f5a..3e9c860d0 100644 --- a/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProvider.java +++ b/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProvider.java @@ -25,11 +25,13 @@ import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.lab.configuration.LabConfig; +import io.evitadb.utils.NetworkUtils; import io.undertow.server.HttpHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; import javax.annotation.Nonnull; +import java.util.function.Predicate; /** * Descriptor of provider of lab API and GUI. @@ -49,10 +51,34 @@ public class LabProvider implements ExternalApiProvider { @Getter private final HttpHandler apiHandler; + /** + * Contains url that was at least once found reachable. + */ + private String reachableUrl; + @Nonnull @Override public String getCode() { return CODE; } + @Override + public boolean isReady() { + final Predicate isReady = url -> NetworkUtils.fetchContent(url, null, "text/html", null) + .map(content -> content.contains("https://github.com/FgForrest/evitaDB/blob/main/LICENSE")) + .orElse(false); + final String[] baseUrls = this.configuration.getBaseUrls(configuration.getExposedHost()); + if (this.reachableUrl == null) { + for (String baseUrl : baseUrls) { + if (isReady.test(baseUrl)) { + this.reachableUrl = baseUrl; + return true; + } + } + return false; + } else { + return isReady.test(this.reachableUrl); + } + } + } diff --git a/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProviderRegistrar.java b/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProviderRegistrar.java index 40757e33f..31d6eb5c4 100644 --- a/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProviderRegistrar.java +++ b/evita_external_api/evita_external_api_lab/src/main/java/io/evitadb/externalApi/lab/LabProviderRegistrar.java @@ -27,6 +27,7 @@ import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; import io.evitadb.externalApi.lab.configuration.LabConfig; import io.undertow.server.HttpHandler; @@ -54,7 +55,7 @@ public Class getConfigurationClass() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull LabConfig labConfig) { + public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull LabConfig labConfig) { final LabManager labManager = new LabManager(evita, apiOptions, labConfig); final HttpHandler apiHandler = labManager.getLabRouter(); return new LabProvider(labConfig, apiHandler); diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java index 75bb8164a..849ce46cd 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java @@ -79,6 +79,8 @@ * @author Tomáš Pozler, FG Forrest a.s. (c) 2024 */ public class ObservabilityManager { + public static final String METRICS_SUFFIX = "metrics"; + public static final String METRICS_PATH = "/observability/" + METRICS_SUFFIX; /** * JFR recording instance. */ @@ -217,8 +219,8 @@ public HttpHandler getObservabilityRouter() { private void createAndRegisterPrometheusServlet() { final DeploymentInfo servletBuilder = Servlets.deployment() .setClassLoader(Undertow.class.getClassLoader()) - .setDeploymentName("metrics-deployment") - .setContextPath("/observability/metrics") + .setDeploymentName(METRICS_SUFFIX + "-deployment") + .setContextPath(METRICS_PATH) .addServlets( Servlets.servlet("MetricsServlet", PrometheusMetricsServlet.class).addMapping("/*") ); @@ -227,7 +229,7 @@ private void createAndRegisterPrometheusServlet() { servletDeploymentManager.deploy(); try { - observabilityRouter.addPrefixPath("/metrics", servletDeploymentManager.start()); + observabilityRouter.addPrefixPath("/" + METRICS_SUFFIX, servletDeploymentManager.start()); } catch (ServletException e) { throw new EvitaInternalError("Unable to add routing to Prometheus scraping servlet."); } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java index 35a1f27e6..b9a04916c 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java @@ -25,11 +25,15 @@ import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.observability.configuration.ObservabilityConfig; +import io.evitadb.utils.NetworkUtils; import io.undertow.server.HttpHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; import javax.annotation.Nonnull; +import java.util.function.Predicate; + +import static io.evitadb.externalApi.observability.ObservabilityManager.METRICS_SUFFIX; /** * Descriptor of external API provider that provides Metrics API. @@ -53,9 +57,35 @@ public class ObservabilityProvider implements ExternalApiProvider isReady = url -> NetworkUtils.fetchContent(url, "GET", "text/plain", null) + .map(content -> !content.isEmpty()) + .orElse(false); + final String[] baseUrls = this.configuration.getBaseUrls(configuration.getExposedHost()); + if (this.reachableUrl == null) { + for (String baseUrl : baseUrls) { + final String url = baseUrl + METRICS_SUFFIX; + if (isReady.test(url)) { + this.reachableUrl = url; + return true; + } + } + return false; + } else { + return isReady.test(this.reachableUrl); + } + } + } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProviderRegistrar.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProviderRegistrar.java index 1b67ce812..d9e40eff0 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProviderRegistrar.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProviderRegistrar.java @@ -27,6 +27,7 @@ import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; import io.evitadb.externalApi.observability.configuration.ObservabilityConfig; import io.evitadb.externalApi.observability.configuration.TracingConfig; import io.evitadb.externalApi.observability.trace.OpenTelemetryTracerSetup; @@ -62,7 +63,7 @@ public int getOrder() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull ObservabilityConfig observabilityConfig) { + public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull ObservabilityConfig observabilityConfig) { final ObservabilityManager observabilityManager = new ObservabilityManager(observabilityConfig, apiOptions, evita); final TracingConfig tracingConfig = observabilityConfig.getTracing(); if (tracingConfig != null && tracingConfig.getEndpoint() != null) { diff --git a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProvider.java b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProvider.java index f6d030616..b5cf1d4cc 100644 --- a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProvider.java +++ b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProvider.java @@ -24,12 +24,16 @@ package io.evitadb.externalApi.rest; import io.evitadb.externalApi.http.ExternalApiProvider; +import io.evitadb.externalApi.rest.api.openApi.OpenApiSystemEndpoint; +import io.evitadb.externalApi.rest.api.system.model.LivenessDescriptor; import io.evitadb.externalApi.rest.configuration.RestConfig; +import io.evitadb.utils.NetworkUtils; import io.undertow.server.HttpHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; import javax.annotation.Nonnull; +import java.util.function.Predicate; /** * Descriptor of external API provider that provides REST API. @@ -49,9 +53,35 @@ public class RestProvider implements ExternalApiProvider { @Getter private final HttpHandler apiHandler; + /** + * Contains url that was at least once found reachable. + */ + private String reachableUrl; + @Nonnull @Override public String getCode() { return CODE; } + + @Override + public boolean isReady() { + final Predicate isReady = url -> NetworkUtils.fetchContent(url, "GET", "application/json", null) + .map(content -> content.contains("true")) + .orElse(false); + final String[] baseUrls = this.configuration.getBaseUrls(configuration.getExposedHost()); + if (this.reachableUrl == null) { + for (String baseUrl : baseUrls) { + final String url = baseUrl + OpenApiSystemEndpoint.URL_PREFIX + "/" + LivenessDescriptor.LIVENESS_SUFFIX; + if (isReady.test(url)) { + this.reachableUrl = url; + return true; + } + } + return false; + } else { + return isReady.test(this.reachableUrl); + } + } + } diff --git a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProviderRegistrar.java b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProviderRegistrar.java index fee09c30b..127c80138 100644 --- a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProviderRegistrar.java +++ b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/RestProviderRegistrar.java @@ -27,6 +27,7 @@ import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.ExternalApiProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; import io.evitadb.externalApi.rest.api.catalog.CatalogRestRefreshingObserver; import io.evitadb.externalApi.rest.configuration.RestConfig; @@ -54,7 +55,7 @@ public Class getConfigurationClass() { @Nonnull @Override public ExternalApiProvider register(@Nonnull Evita evita, - @Nonnull ApiOptions apiOptions, + @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull RestConfig restConfiguration) { final RestManager restManager = new RestManager(evita, apiOptions.exposedOn(), restConfiguration); evita.registerStructuralChangeObserver(new CatalogRestRefreshingObserver(restManager)); diff --git a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/system/model/LivenessDescriptor.java b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/system/model/LivenessDescriptor.java index e5827342d..abb3d0e78 100644 --- a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/system/model/LivenessDescriptor.java +++ b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/system/model/LivenessDescriptor.java @@ -37,8 +37,10 @@ */ public interface LivenessDescriptor { + String LIVENESS_SUFFIX = "liveness"; + PropertyDescriptor LIVENESS = PropertyDescriptor.builder() - .name("liveness") + .name(LIVENESS_SUFFIX) .description(""" Whether REST API is alive and can handle requests. """) diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProvider.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProvider.java index d3b85a0e5..96c2d1821 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProvider.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProvider.java @@ -29,6 +29,7 @@ import io.evitadb.utils.ConsoleWriter; import io.evitadb.utils.ConsoleWriter.ConsoleColor; import io.evitadb.utils.ConsoleWriter.ConsoleDecoration; +import io.evitadb.utils.NetworkUtils; import io.evitadb.utils.StringUtils; import io.undertow.server.HttpHandler; import lombok.Getter; @@ -74,6 +75,11 @@ public class SystemProvider implements ExternalApiProviderWithConsoleOutput readiness, + @Nonnull String overallStatus + ) { + exchange.getResponseSender().send("{\n" + + "\t\"status\": \"" + overallStatus + "\",\n" + + "\t\"apis\": {\n" + + readiness.entrySet().stream() + .map(entry -> "\t\t\"" + entry.getKey() + "\": \"" + (entry.getValue() ? "ready" : "not ready") + "\"") + .collect(Collectors.joining(",\n")) + "\n" + + "\t}\n" + + "}" + ); + } + + /** + * Renders the status of the evitaDB server as a JSON string. + * + * @param instanceId the unique identifier of the server instance + * @param systemStatus the SystemStatus object containing information about the server + * @param apiOptions the common settings shared among all the API endpoints + * @return the JSON string representing the server status + */ + @Nonnull + private static String renderStatus( + @Nonnull String instanceId, + @Nonnull SystemStatus systemStatus, + @Nonnull ApiOptions apiOptions + ) { + return String.format(""" + { + "serverName": "%s", + "version": "%s", + "startedAt": "%s", + "uptime": %d, + "uptimeForHuman": "%s", + "catalogsCorrupted": %d, + "catalogsOk": %d, + "healthProblems": [%s], + "apis": [ + %s + ] + }""", + instanceId, + systemStatus.version(), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(systemStatus.startedAt()), + systemStatus.uptime().toSeconds(), + StringUtils.formatDuration(systemStatus.uptime()), + systemStatus.catalogsCorrupted(), + systemStatus.catalogsOk(), + systemStatus.healthProblems().stream() + .map(it -> "\"" + it.name() + "\"") + .collect(Collectors.joining(", ")), + apiOptions.endpoints().entrySet().stream() + .map( + entry -> " {\n \"" + entry.getKey() + "\": [\n" + + Arrays.stream(entry.getValue().getBaseUrls(apiOptions.exposedOn())) + .map(it -> " \"" + it + "\"") + .collect(Collectors.joining(",\n")) + + "\n ]\n }" + ) + .collect(Collectors.joining(",\n")) + ); + } @Nonnull @Override @@ -75,7 +157,7 @@ public Class getConfigurationClass() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @Nonnull SystemConfig systemConfig) { + public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull SystemConfig systemConfig) { final File file; final String fileName; final CertificateSettings certificateSettings = apiOptions.certificate(); @@ -129,7 +211,7 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull exchange.setStatusCode(StatusCodes.OK); exchange.getResponseSender().send("{\"status\": \"healthy\"}"); } else { - exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); + exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); exchange.getResponseSender().send( "{\"status\": \"unhealthy\", \"problems\": [" + systemStatus.healthProblems().stream() @@ -141,6 +223,36 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull } ); + router.addExactPath( + "/" + ENDPOINT_SYSTEM_READINESS, + exchange -> { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); + if (evita.isActive()) { + // check the end-points availability + final Collection availableExternalApis = ExternalApiServer.gatherExternalApiProviders(); + final Map readiness = CollectionUtils.createHashMap(availableExternalApis.size()); + for (ExternalApiProviderRegistrar externalApi : availableExternalApis) { + final AbstractApiConfiguration apiConfiguration = apiOptions.getEndpointConfiguration(externalApi.getExternalApiCode()); + if (apiConfiguration != null && apiConfiguration.isEnabled()) { + final ExternalApiProvider apiProvider = externalApiServer.getExternalApiProviderByCode(externalApi.getExternalApiCode()); + readiness.put(apiProvider.getCode(), apiProvider.isReady()); + } + } + if (readiness.values().stream().allMatch(it -> it)) { + exchange.setStatusCode(StatusCodes.OK); + printApiStatus(exchange, readiness, "ready"); + seenReady.set(true); + } else { + exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); + printApiStatus(exchange, readiness, seenReady.get() ? "starting" : "stalling"); + } + } else { + exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); + exchange.getResponseSender().send("{\"status\": \"shut down\"}"); + } + } + ); + final ResourceHandler fileSystemHandler; try (ResourceManager resourceManager = new FileResourceManager(file, 100)) { fileSystemHandler = new ResourceHandler( @@ -195,54 +307,4 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull } } - /** - * Renders the status of the evitaDB server as a JSON string. - * - * @param instanceId the unique identifier of the server instance - * @param systemStatus the SystemStatus object containing information about the server - * @param apiOptions the common settings shared among all the API endpoints - * @return the JSON string representing the server status - */ - @Nonnull - private static String renderStatus( - @Nonnull String instanceId, - @Nonnull SystemStatus systemStatus, - @Nonnull ApiOptions apiOptions - ) { - return String.format(""" - { - "serverName": "%s", - "version": "%s", - "startedAt": "%s", - "uptime": %d, - "uptimeForHuman": "%s", - "catalogsCorrupted": %d, - "catalogsOk": %d, - "healthProblems": [%s], - "apis": [ - %s - ] - }""", - instanceId, - systemStatus.version(), - DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(systemStatus.startedAt()), - systemStatus.uptime().toSeconds(), - StringUtils.formatDuration(systemStatus.uptime()), - systemStatus.catalogsCorrupted(), - systemStatus.catalogsOk(), - systemStatus.healthProblems().stream() - .map(it -> "\"" + it.name() + "\"") - .collect(Collectors.joining(", ")), - apiOptions.endpoints().entrySet().stream() - .map( - entry -> " {\n \"" + entry.getKey() + "\": [\n" + - Arrays.stream(entry.getValue().getBaseUrls(apiOptions.exposedOn())) - .map(it -> " \"" + it + "\"") - .collect(Collectors.joining(",\n")) + - "\n ]\n }" - ) - .collect(Collectors.joining(",\n")) - ); - } - } diff --git a/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java b/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java index a90bf5573..574f26966 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java @@ -24,6 +24,7 @@ package io.evitadb.server; import io.evitadb.core.Evita; +import io.evitadb.driver.interceptor.ClientSessionInterceptor; import io.evitadb.externalApi.grpc.GrpcProvider; import io.evitadb.externalApi.grpc.TestChannelCreator; import io.evitadb.externalApi.grpc.generated.EvitaServiceGrpc; @@ -32,12 +33,13 @@ import io.evitadb.externalApi.grpc.generated.GrpcEvitaSessionTerminationRequest; import io.evitadb.externalApi.grpc.generated.GrpcEvitaSessionTerminationResponse; import io.evitadb.externalApi.grpc.generated.GrpcSessionType; -import io.evitadb.driver.interceptor.ClientSessionInterceptor; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.externalApi.http.ExternalApiServer; +import io.evitadb.externalApi.system.SystemProvider; import io.evitadb.test.EvitaTestSupport; import io.evitadb.test.TestConstants; import io.evitadb.utils.CollectionUtils.Property; +import io.evitadb.utils.NetworkUtils; import io.grpc.ManagedChannel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -45,6 +47,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -73,7 +76,7 @@ void tearDown() throws IOException { } @Test - void shouldStartAndStopServerCorrectly() throws IOException { + void shouldStartAndStopServerCorrectly() { final Path configFilePath = EvitaTestSupport.bootstrapEvitaServerConfigurationFile(DIR_EVITA_SERVER_TEST); final Set apis = ExternalApiServer.gatherExternalApiProviders() @@ -143,4 +146,77 @@ void shouldStartAndStopServerCorrectly() throws IOException { } } -} \ No newline at end of file + @Test + void shouldSignalizeReadinessAndHealthinessCorrectly() { + final Path configFilePath = EvitaTestSupport.bootstrapEvitaServerConfigurationFile(DIR_EVITA_SERVER_TEST); + + final Set apis = ExternalApiServer.gatherExternalApiProviders() + .stream() + .map(ExternalApiProviderRegistrar::getExternalApiCode) + .collect(Collectors.toSet()); + + final int[] ports = getPortManager().allocatePorts(DIR_EVITA_SERVER_TEST, apis.size()); + final AtomicInteger index = new AtomicInteger(); + //noinspection unchecked + final EvitaServer evitaServer = new EvitaServer( + configFilePath, + createHashMap( + Stream.concat( + Stream.of( + property("storage.storageDirectory", getTestDirectory().resolve(DIR_EVITA_SERVER_TEST).toString()), + property("cache.enabled", "false") + ), + apis.stream() + .map(it -> property("api.endpoints." + it + ".host", "localhost:" + ports[index.getAndIncrement()])) + ) + .toArray(Property[]::new) + ) + ); + try { + evitaServer.run(); + + Optional response; + final long start = System.currentTimeMillis(); + do { + final String[] baseUrls = evitaServer.getExternalApiServer().getExternalApiProviderByCode(SystemProvider.CODE).getConfiguration().getBaseUrls(null); + response = NetworkUtils.fetchContent( + baseUrls[0] + "readiness", + "GET", + "text/plain", + null + ); + + if (response.isPresent() && response.get().contains("\"status\": \"ready\"")) { + break; + } + + } while (System.currentTimeMillis() - start < 20000); + + assertTrue(response.isPresent()); + assertEquals( + """ + { + "status": "ready", + "apis": { + "rest": "ready", + "system": "ready", + "graphQL": "ready", + "lab": "ready", + "gRPC": "ready" + } + }""", + response.get().trim() + ); + + } catch (Exception ex) { + fail(ex); + } finally { + try { + evitaServer.stop(); + } catch (Exception ex) { + fail(ex.getMessage(), ex); + } + } + } + +} diff --git a/evita_server/pom.xml b/evita_server/pom.xml index 47b4bf648..bda224219 100644 --- a/evita_server/pom.xml +++ b/evita_server/pom.xml @@ -115,6 +115,11 @@ snakeyaml + + com.squareup.okhttp3 + okhttp + + org.apache.commons commons-text diff --git a/pom.xml b/pom.xml index 3043c2039..ebb97ca1b 100644 --- a/pom.xml +++ b/pom.xml @@ -159,6 +159,11 @@ proxycian_bytebuddy 1.3.10 + + com.squareup.okhttp3 + okhttp + 5.0.0-alpha.14 + org.apache.maven.plugins maven-compiler-plugin From cf9041898ce26be01b1619fe19ad344cea110d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Fri, 3 May 2024 14:18:24 +0200 Subject: [PATCH 05/24] fix(#550): Provide health check for Docker image Healthines WIP. --- .../generated/EvitaSessionServiceGrpc.java | 2 +- .../grpc/generated/GrpcCatalogState.java | 2 +- .../grpc/generated/GrpcCloseRequest.java | 8 +- .../generated/GrpcCloseRequestOrBuilder.java | 2 +- .../grpc/generated/GrpcCloseResponse.java | 6 +- .../generated/GrpcCloseResponseOrBuilder.java | 2 +- .../grpc/generated/GrpcCommitBehavior.java | 2 +- .../externalApi/grpc/generated/GrpcEnums.java | 4 +- .../grpc/generated/GrpcEvitaAPI.java | 32 +++---- .../grpc/generated/GrpcEvitaSessionAPI.java | 96 +++++++++---------- .../generated/GrpcEvitaSessionRequest.java | 24 ++--- .../GrpcEvitaSessionRequestOrBuilder.java | 2 +- .../generated/GrpcEvitaSessionResponse.java | 32 +++---- .../GrpcEvitaSessionResponseOrBuilder.java | 2 +- .../generated/GrpcGoLiveAndCloseResponse.java | 10 +- .../GrpcGoLiveAndCloseResponseOrBuilder.java | 2 +- .../observability/agent/OOMAgent.java | 78 ++++++--------- 17 files changed, 143 insertions(+), 163 deletions(-) diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java index d906c6db2..bb66938ba 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/EvitaSessionServiceGrpc.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java index 84207e46c..e9ceafdeb 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCatalogState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java index 172d0602c..6fb6d991d 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -468,7 +468,7 @@ public Builder mergeFrom( * @return This builder for chaining. */ public Builder setCommitBehaviourValue(int value) { - + commitBehaviour_ = value; onChanged(); return this; @@ -500,7 +500,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm if (value == null) { throw new NullPointerException(); } - + commitBehaviour_ = value.getNumber(); onChanged(); return this; @@ -514,7 +514,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm * @return This builder for chaining. */ public Builder clearCommitBehaviour() { - + commitBehaviour_ = 0; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java index 001c02bd1..cef228c77 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseRequestOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java index 9bf186d69..898286538 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -457,7 +457,7 @@ public long getCatalogVersion() { * @return This builder for chaining. */ public Builder setCatalogVersion(long value) { - + catalogVersion_ = value; onChanged(); return this; @@ -471,7 +471,7 @@ public Builder setCatalogVersion(long value) { * @return This builder for chaining. */ public Builder clearCatalogVersion() { - + catalogVersion_ = 0L; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java index 16f668f3c..f0e2bd13c 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCloseResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java index f753675d2..57a873495 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcCommitBehavior.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java index 75866c853..238fbb237 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaAssociatedDataDataType_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaAssociatedDataDataType_fieldAccessorTable; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java index 8ea71740f..013853fb3 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,77 +39,77 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionTerminationResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogNamesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogNamesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCatalogResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCatalogIfExistsResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEvitaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEvitaRequest_fieldAccessorTable; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java index 1acea46ee..77ddce1b8 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionAPI.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,237 +39,237 @@ public static void registerAllExtensions( } static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogStateResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogStateResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchCatalogSchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchCatalogSchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDefineEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchEntitySchemaResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpdateAndFetchEntitySchemaResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcPaginatedList_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcPaginatedList_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcStripList_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcStripList_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDataChunk_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDataChunk_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcRenameCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcReplaceCollectionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityCollectionSizeResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcCloseResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcGoLiveAndCloseResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcGoLiveAndCloseResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityTypesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcEntityTypesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryOneResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryOneResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryListResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryListResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_NamedQueryParamsEntry_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesRequest_NamedQueryParamsEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcUpsertEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityAndItsHierarchyResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntityAndItsHierarchyResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcDeleteEntitiesResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcTransactionResponse_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcTransactionResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryParam_descriptor; - static final + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_evitadb_externalApi_grpc_generated_GrpcQueryParam_fieldAccessorTable; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java index dd61ef83b..0382b83e9 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -142,7 +142,7 @@ public java.lang.String getCatalogName() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); catalogName_ = s; @@ -162,7 +162,7 @@ public java.lang.String getCatalogName() { getCatalogNameBytes() { java.lang.Object ref = catalogName_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); catalogName_ = b; @@ -587,7 +587,7 @@ public java.lang.String getCatalogName() { getCatalogNameBytes() { java.lang.Object ref = catalogName_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); catalogName_ = b; @@ -610,7 +610,7 @@ public Builder setCatalogName( if (value == null) { throw new NullPointerException(); } - + catalogName_ = value; onChanged(); return this; @@ -624,7 +624,7 @@ public Builder setCatalogName( * @return This builder for chaining. */ public Builder clearCatalogName() { - + catalogName_ = getDefaultInstance().getCatalogName(); onChanged(); return this; @@ -644,7 +644,7 @@ public Builder setCatalogNameBytes( throw new NullPointerException(); } checkByteStringIsUtf8(value); - + catalogName_ = value; onChanged(); return this; @@ -672,7 +672,7 @@ public Builder setCatalogNameBytes( * @return This builder for chaining. */ public Builder setCommitBehaviorValue(int value) { - + commitBehavior_ = value; onChanged(); return this; @@ -704,7 +704,7 @@ public Builder setCommitBehavior(io.evitadb.externalApi.grpc.generated.GrpcCommi if (value == null) { throw new NullPointerException(); } - + commitBehavior_ = value.getNumber(); onChanged(); return this; @@ -718,7 +718,7 @@ public Builder setCommitBehavior(io.evitadb.externalApi.grpc.generated.GrpcCommi * @return This builder for chaining. */ public Builder clearCommitBehavior() { - + commitBehavior_ = 0; onChanged(); return this; @@ -747,7 +747,7 @@ public boolean getDryRun() { * @return This builder for chaining. */ public Builder setDryRun(boolean value) { - + dryRun_ = value; onChanged(); return this; @@ -761,7 +761,7 @@ public Builder setDryRun(boolean value) { * @return This builder for chaining. */ public Builder clearDryRun() { - + dryRun_ = false; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java index 26ac8e5eb..29ecc4e32 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionRequestOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java index 595f765c5..604fe3040 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -151,7 +151,7 @@ public java.lang.String getSessionId() { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); sessionId_ = s; @@ -171,7 +171,7 @@ public java.lang.String getSessionId() { getSessionIdBytes() { java.lang.Object ref = sessionId_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); sessionId_ = b; @@ -649,7 +649,7 @@ public java.lang.String getSessionId() { getSessionIdBytes() { java.lang.Object ref = sessionId_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); sessionId_ = b; @@ -672,7 +672,7 @@ public Builder setSessionId( if (value == null) { throw new NullPointerException(); } - + sessionId_ = value; onChanged(); return this; @@ -686,7 +686,7 @@ public Builder setSessionId( * @return This builder for chaining. */ public Builder clearSessionId() { - + sessionId_ = getDefaultInstance().getSessionId(); onChanged(); return this; @@ -706,7 +706,7 @@ public Builder setSessionIdBytes( throw new NullPointerException(); } checkByteStringIsUtf8(value); - + sessionId_ = value; onChanged(); return this; @@ -734,7 +734,7 @@ public Builder setSessionIdBytes( * @return This builder for chaining. */ public Builder setSessionTypeValue(int value) { - + sessionType_ = value; onChanged(); return this; @@ -766,7 +766,7 @@ public Builder setSessionType(io.evitadb.externalApi.grpc.generated.GrpcSessionT if (value == null) { throw new NullPointerException(); } - + sessionType_ = value.getNumber(); onChanged(); return this; @@ -780,7 +780,7 @@ public Builder setSessionType(io.evitadb.externalApi.grpc.generated.GrpcSessionT * @return This builder for chaining. */ public Builder clearSessionType() { - + sessionType_ = 0; onChanged(); return this; @@ -808,7 +808,7 @@ public Builder clearSessionType() { * @return This builder for chaining. */ public Builder setCommitBehaviourValue(int value) { - + commitBehaviour_ = value; onChanged(); return this; @@ -840,7 +840,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm if (value == null) { throw new NullPointerException(); } - + commitBehaviour_ = value.getNumber(); onChanged(); return this; @@ -854,7 +854,7 @@ public Builder setCommitBehaviour(io.evitadb.externalApi.grpc.generated.GrpcComm * @return This builder for chaining. */ public Builder clearCommitBehaviour() { - + commitBehaviour_ = 0; onChanged(); return this; @@ -882,7 +882,7 @@ public Builder clearCommitBehaviour() { * @return This builder for chaining. */ public Builder setCatalogStateValue(int value) { - + catalogState_ = value; onChanged(); return this; @@ -914,7 +914,7 @@ public Builder setCatalogState(io.evitadb.externalApi.grpc.generated.GrpcCatalog if (value == null) { throw new NullPointerException(); } - + catalogState_ = value.getNumber(); onChanged(); return this; @@ -928,7 +928,7 @@ public Builder setCatalogState(io.evitadb.externalApi.grpc.generated.GrpcCatalog * @return This builder for chaining. */ public Builder clearCatalogState() { - + catalogState_ = 0; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java index e5fb4c9ac..433179ad4 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaSessionResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java index a9b2e3782..95532aac0 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -495,7 +495,7 @@ public boolean getSuccess() { * @return This builder for chaining. */ public Builder setSuccess(boolean value) { - + success_ = value; onChanged(); return this; @@ -509,7 +509,7 @@ public Builder setSuccess(boolean value) { * @return This builder for chaining. */ public Builder clearSuccess() { - + success_ = false; onChanged(); return this; @@ -538,7 +538,7 @@ public long getCatalogVersion() { * @return This builder for chaining. */ public Builder setCatalogVersion(long value) { - + catalogVersion_ = value; onChanged(); return this; @@ -552,7 +552,7 @@ public Builder setCatalogVersion(long value) { * @return This builder for chaining. */ public Builder clearCatalogVersion() { - + catalogVersion_ = 0L; onChanged(); return this; diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java index 29bb35755..2e4f99434 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcGoLiveAndCloseResponseOrBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023-2024 + * Copyright (c) 2023 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java index 8d74388be..0f0be0989 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java @@ -23,20 +23,16 @@ package io.evitadb.externalApi.observability.agent; -import io.evitadb.exception.EvitaInternalError; import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.implementation.MethodCall; -import net.bytebuddy.implementation.SuperMethodCall; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.dynamic.loading.ClassInjector; -import java.io.File; import java.lang.instrument.Instrumentation; -import java.lang.reflect.Method; -import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.REDEFINITION; -import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION; -import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.none; /** @@ -45,50 +41,34 @@ * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 */ public class OOMAgent { - private static final Method HANDLE_OOM; - - static { - try { - HANDLE_OOM = OOMAgent.class.getDeclaredMethod("handleOOM"); - } catch (NoSuchMethodException e) { - throw new EvitaInternalError("!!! OOMAgent initialization failed !!!", e); - } - } public static void premain(String agentArgs, Instrumentation inst) { - if (HANDLE_OOM != null) { - new AgentBuilder.Default() - .disableClassFormatChanges() - .with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(inst, new File("/www/oss/evitaDB-temporary/evita_external_api/evita_external_api_observability/target/evita_external_api_observability-2024.5-SNAPSHOT.jar"))) - .with(REDEFINITION) - // Make sure we see helpful logs - .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError()) - .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly()) - .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError()) - .ignore(none()) - // Ignore Byte Buddy and JDK classes we are not interested in - .ignore( - nameStartsWith("net.bytebuddy.") - .or(nameStartsWith("jdk.internal.reflect.")) - .or(nameStartsWith("java.lang.invoke.")) - .or(nameStartsWith("com.sun.proxy.")) - ) - .disableClassFormatChanges() - .with(RETRANSFORMATION) - .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) - .with(AgentBuilder.TypeStrategy.Default.REDEFINE) - .type(named("java.lang.OutOfMemoryError")) - .transform( - (builder, typeDescription, classLoader, javaModule, protectionDomain) -> builder - .constructor(any()) - .intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(HANDLE_OOM))) - ) - .installOn(inst); - } + ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(inst); + AgentBuilder agentBuilder = new AgentBuilder.Default(); + agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory)); + + + agentBuilder + .disableClassFormatChanges() + .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) + .ignore(none()) + .ignore(nameStartsWith("net.bytebuddy.")) + .type(is(OutOfMemoryError.class)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder + .visit( + Advice + .to(MyAdvice.class) + .on(isConstructor()) + )) + .installOn(inst); } - public static void handleOOM() { - System.out.println("!!! OOM !!!"); + public static class MyAdvice { + @OnMethodEnter + public static boolean before() { + System.out.println("!!! OOM !!!"); + return true; + } } } From b526a9c88eb75561ceac762357ac6dc174529f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Fri, 3 May 2024 22:12:33 +0200 Subject: [PATCH 06/24] fix(#550): Provide health check for Docker image Liveness done, but not manually tested. --- .../requestResponse/system/SystemStatus.java | 20 +- .../evitadb/scheduling/RejectingExecutor.java | 15 +- .../src/main/java/io/evitadb/core/Evita.java | 7 +- .../api/system/ProbesProvider.java | 123 ++++++ .../api/system/model/HealthProblem.java | 54 +++ .../src/main/java/module-info.java | 3 +- .../java/io/evitadb/driver/EvitaClient.java | 7 +- .../externalApi/grpc/generated/GrpcEnums.java | 5 +- .../grpc/generated/GrpcEvitaAPI.java | 152 ++++---- .../GrpcEvitaServerStatusResponse.java | 338 ---------------- ...rpcEvitaServerStatusResponseOrBuilder.java | 49 --- .../grpc/generated/GrpcHealthProblem.java | 368 ++++++++++++++++++ .../requestResponse/EvitaEnumConverter.java | 16 - .../evitadb/externalApi/grpc/GrpcEnums.proto | 12 - .../externalApi/grpc/GrpcEvitaAPI.proto | 2 - .../observability/ObservabilityManager.java | 146 +++++-- .../observability/ObservabilityProvider.java | 7 +- .../ObservabilityProviderRegistrar.java | 2 +- ...OMAgent.java => ErrorMonitoringAgent.java} | 44 ++- .../observability/metric/MetricHandler.java | 26 +- .../metric/ObservabilityProbesDetector.java | 179 +++++++++ .../src/main/java/module-info.java | 4 + ...tadb.externalApi.api.system.ProbesProvider | 24 ++ .../system/SystemProviderRegistrar.java | 82 ++-- .../src/main/java/module-info.java | 5 +- evita_functional_tests/pom.xml | 7 - .../io/evitadb/server/EvitaServerTest.java | 31 +- evita_server/pom.xml | 2 +- evita_server/src/main/java/module-info.java | 4 +- .../main/resources/evita-configuration.yaml | 2 +- 30 files changed, 1093 insertions(+), 643 deletions(-) create mode 100644 evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java create mode 100644 evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java rename evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/{OOMAgent.java => ErrorMonitoringAgent.java} (63%) create mode 100644 evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java create mode 100644 evita_external_api/evita_external_api_observability/src/main/resources/META-INF/services/io.evitadb.externalApi.api.system.ProbesProvider diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java index 93f286fee..80caa617f 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/system/SystemStatus.java @@ -27,7 +27,6 @@ import java.io.Serializable; import java.time.Duration; import java.time.OffsetDateTime; -import java.util.Set; /** * Contains basic information about evitaDB server. @@ -38,7 +37,6 @@ * @param instanceId unique identifier of the server instance * @param catalogsCorrupted number of corrupted catalogs * @param catalogsOk number of catalogs that are ok - * @param healthProblems set of flags that indicate health problems of the server */ public record SystemStatus( @Nonnull String version, @@ -46,23 +44,7 @@ public record SystemStatus( @Nonnull Duration uptime, @Nonnull String instanceId, int catalogsCorrupted, - int catalogsOk, - @Nonnull Set healthProblems + int catalogsOk ) implements Serializable { - public enum HealthProblem { - - /** - * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free - * old generation multiple times consuming a log of CPU power. - */ - MEMORY_SHORTAGE, - /** - * Signalized when the input queues are full and the server is not able to process incoming requests. This flag - * is cleared when the server is able to process incoming requests again. - */ - INPUT_QUEUES_OVERLOADED - - } - } diff --git a/evita_common/src/main/java/io/evitadb/scheduling/RejectingExecutor.java b/evita_common/src/main/java/io/evitadb/scheduling/RejectingExecutor.java index 8550c1d41..32d63745c 100644 --- a/evita_common/src/main/java/io/evitadb/scheduling/RejectingExecutor.java +++ b/evita_common/src/main/java/io/evitadb/scheduling/RejectingExecutor.java @@ -23,14 +23,13 @@ package io.evitadb.scheduling; -import lombok.Setter; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nonnull; -import java.util.Optional; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; -import java.util.function.Supplier; /** * Custom rejecting executor that logs the problem when the queue gets full. @@ -38,16 +37,14 @@ * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023 */ @Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class RejectingExecutor implements Executor { - @Setter private Supplier additionalLogger; + public static final RejectingExecutor INSTANCE = new RejectingExecutor(); @Override public void execute(@Nonnull Runnable command) { - log.error( - "Evita executor queue full. Please, add more threads to the pool." + - Optional.ofNullable(additionalLogger).map(Supplier::get).orElse("") - ); - throw new RejectedExecutionException("Evita executor queue full. Please, add more threads to the pool."); + log.error("Evita executor queue full. Please add more threads to the pool."); + throw new RejectedExecutionException("Evita executor queue full. Please add more threads to the pool."); } } diff --git a/evita_engine/src/main/java/io/evitadb/core/Evita.java b/evita_engine/src/main/java/io/evitadb/core/Evita.java index eb3890c80..19322fc63 100644 --- a/evita_engine/src/main/java/io/evitadb/core/Evita.java +++ b/evita_engine/src/main/java/io/evitadb/core/Evita.java @@ -51,7 +51,6 @@ import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyCatalogSchemaNameMutation; import io.evitadb.api.requestResponse.schema.mutation.catalog.RemoveCatalogSchemaMutation; import io.evitadb.api.requestResponse.system.SystemStatus; -import io.evitadb.api.requestResponse.system.SystemStatus.HealthProblem; import io.evitadb.api.trace.TracingContext; import io.evitadb.api.trace.TracingContextProvider; import io.evitadb.core.cache.CacheSupervisor; @@ -197,12 +196,11 @@ public final class Evita implements EvitaContract { public Evita(@Nonnull EvitaConfiguration configuration) { this.configuration = configuration; - final RejectingExecutor handoffExecutor = new RejectingExecutor(); this.executor = new EnhancedQueueExecutor.Builder() .setCorePoolSize(configuration.server().coreThreadCount()) .setMaximumPoolSize(configuration.server().maxThreadCount()) .setExceptionHandler((t, e) -> log.error("Uncaught error in thread `" + t.getName() + "`: " + e.getMessage(), e)) - .setHandoffExecutor(handoffExecutor) + .setHandoffExecutor(RejectingExecutor.INSTANCE) .setThreadFactory(new EvitaThreadFactory(configuration.server().threadPriority())) .setMaximumQueueSize(configuration.server().queueSize()) .setRegisterMBean(false) @@ -528,8 +526,7 @@ public SystemStatus getSystemStatus() { Duration.between(this.started, OffsetDateTime.now()), this.configuration.name(), corruptedCatalogs, - this.catalogs.size() - corruptedCatalogs, - EnumSet.noneOf(HealthProblem.class) + this.catalogs.size() - corruptedCatalogs ); } diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java new file mode 100644 index 000000000..468994e6d --- /dev/null +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java @@ -0,0 +1,123 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.externalApi.api.system; + +import io.evitadb.api.EvitaContract; +import io.evitadb.externalApi.api.system.model.HealthProblem; +import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; + +import javax.annotation.Nonnull; +import java.util.Set; + +/** + * Integration interface allowing to provide health and readiness probes for the system API from different module. + * In our case from the observability module. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public interface ProbesProvider { + + /** + * Method returns set of health problems identified on the server side. + * Result of this method is used for determining the "liveness" or "healthiness" of the server. + * + * @param evitaContract evita instance + * @param externalApiServer external API server + * @return set of health problems + * @see HealthProblem + */ + @Nonnull + Set getHealthProblems( + @Nonnull EvitaContract evitaContract, + @Nonnull ExternalApiServer externalApiServer + ); + + /** + * Method returns data for determining whether it has already completely started and is ready to serve requests. + * + * @param evitaContract evita instance + * @param externalApiServer external API server + * @param apiCodes API codes to check (which are enabled) + * @return readiness data + */ + @Nonnull + Readiness getReadiness( + @Nonnull EvitaContract evitaContract, + @Nonnull ExternalApiServer externalApiServer, + @Nonnull String... apiCodes + ); + + /** + * Method returns data for determining whether server has already completely started and is ready to serve requests. + * + * @param state overall readiness state (over all APIs) + * @param apiStates detail of readiness state for each API + */ + record Readiness( + @Nonnull ReadinessState state, + @Nonnull ApiState[] apiStates + ) { + + } + + /** + * Detail of readiness state for particular API. + * @param apiCode API code representing {@link ExternalApiProviderRegistrar#getExternalApiCode()} + * @param isReady true if API is ready + */ + record ApiState( + @Nonnull String apiCode, + boolean isReady + ) {} + + /** + * Enum representing overall readiness state of the server. + */ + enum ReadinessState { + + /** + * At least one API is not ready. + */ + STARTING, + /** + * All APIs are ready. + */ + READY, + /** + * At least one API that was ready is not ready anymore. + */ + STALLING, + /** + * Server is shutting down. None of the APIs are ready. + */ + SHUT_DOWN, + /** + * Unknown state - cannot determine the state of the APIs (should not happen). + */ + UNKNOWN + + } + +} diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java new file mode 100644 index 000000000..f0691a160 --- /dev/null +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java @@ -0,0 +1,54 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.externalApi.api.system.model; + +/** + * This enum represents the possible health problems that can be signaled by the server. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public enum HealthProblem { + + /** + * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free + * old generation multiple times consuming a log of CPU power. + */ + MEMORY_SHORTAGE, + /** + * Signalized when the input queues are full and the server is not able to process incoming requests. This flag + * is cleared when the server is able to process incoming requests again. + */ + INPUT_QUEUES_OVERLOADED, + /** + * Signaled when there are occurrences of Java internal errors. These errors are usually caused by the server + * itself and are not related to the client's requests. + */ + JAVA_INTERNAL_ERRORS, + /** + * Signaled when there are occurrences of database internal errors. These errors are usually caused by the server + * itself and are not related to the client's requests. + */ + EVITA_DB_INTERNAL_ERRORS + +} diff --git a/evita_external_api/evita_external_api_core/src/main/java/module-info.java b/evita_external_api/evita_external_api_core/src/main/java/module-info.java index a03b29316..f95975e0b 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/module-info.java +++ b/evita_external_api/evita_external_api_core/src/main/java/module-info.java @@ -51,6 +51,7 @@ exports io.evitadb.externalApi.api.catalog.schemaApi.model; exports io.evitadb.externalApi.api.catalog.model; exports io.evitadb.externalApi.api.system.model; + exports io.evitadb.externalApi.api.system; exports io.evitadb.externalApi.certificate; exports io.evitadb.externalApi.api.catalog.schemaApi.model.mutation.sortableAttributeCompound; exports io.evitadb.externalApi.api.catalog.schemaApi.resolver.mutation.sortableAttributeCompound; @@ -75,4 +76,4 @@ requires evita.query; requires org.bouncycastle.provider; requires org.bouncycastle.pkix; -} \ No newline at end of file +} diff --git a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java index 146b7e303..59021d0ff 100644 --- a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java +++ b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java @@ -91,7 +91,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static java.util.Optional.ofNullable; @@ -629,11 +628,7 @@ public SystemStatus getSystemStatus() { Duration.of(response.getUptime(), ChronoUnit.SECONDS), response.getInstanceId(), response.getCatalogsCorrupted(), - response.getCatalogsOk(), - response.getHealthProblemsList() - .stream() - .map(EvitaEnumConverter::toHealthProblem) - .collect(Collectors.toSet()) + response.getCatalogsOk() ); } ); diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java index 61a66395f..238fbb237 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEnums.java @@ -152,9 +152,8 @@ public static void registerAllExtensions( "XIST\020\001\022\016\n\nMUST_EXIST\020\002*t\n\022GrpcCommitBeha" + "vior\022 \n\034WAIT_FOR_CONFLICT_RESOLUTION\020\000\022\034" + "\n\030WAIT_FOR_LOG_PERSISTENCE\020\001\022\036\n\032WAIT_FOR" + - "_INDEX_PROPAGATION\020\002*E\n\021GrpcHealthProble" + - "m\022\023\n\017MEMORY_SHORTAGE\020\000\022\033\n\027INPUT_QUEUES_O" + - "VERLOADED\020\001B\014P\001\252\002\007EvitaDBb\006proto3" + "_INDEX_PROPAGATION\020\002B\014P\001\252\002\007EvitaDBb\006prot" + + "o3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java index 47559e567..013853fb3 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaAPI.java @@ -125,89 +125,87 @@ public static void registerAllExtensions( "lApi.grpc.generated\032\033google/protobuf/emp" + "ty.proto\032\017GrpcEnums.proto\032\030GrpcEvitaData" + "Types.proto\032\037GrpcCatalogSchemaMutation.p" + - "roto\"\243\002\n\035GrpcEvitaServerStatusResponse\022\017" + + "roto\"\321\001\n\035GrpcEvitaServerStatusResponse\022\017" + "\n\007version\030\001 \001(\t\022L\n\tstartedAt\030\002 \001(\01329.io." + "evitadb.externalApi.grpc.generated.GrpcO" + "ffsetDateTime\022\016\n\006uptime\030\003 \001(\003\022\022\n\ninstanc" + "eId\030\004 \001(\t\022\031\n\021catalogsCorrupted\030\005 \001(\005\022\022\n\n" + - "catalogsOk\030\006 \001(\005\022P\n\016healthProblems\030\007 \003(\016" + - "28.io.evitadb.externalApi.grpc.generated" + - ".GrpcHealthProblem\"\221\001\n\027GrpcEvitaSessionR" + - "equest\022\023\n\013catalogName\030\001 \001(\t\022Q\n\016commitBeh" + - "avior\030\002 \001(\01629.io.evitadb.externalApi.grp" + - "c.generated.GrpcCommitBehavior\022\016\n\006dryRun" + - "\030\003 \001(\010\"\235\002\n\030GrpcEvitaSessionResponse\022\021\n\ts" + - "essionId\030\001 \001(\t\022K\n\013sessionType\030\002 \001(\01626.io" + + "catalogsOk\030\006 \001(\005\"\221\001\n\027GrpcEvitaSessionReq" + + "uest\022\023\n\013catalogName\030\001 \001(\t\022Q\n\016commitBehav" + + "ior\030\002 \001(\01629.io.evitadb.externalApi.grpc." + + "generated.GrpcCommitBehavior\022\016\n\006dryRun\030\003" + + " \001(\010\"\235\002\n\030GrpcEvitaSessionResponse\022\021\n\tses" + + "sionId\030\001 \001(\t\022K\n\013sessionType\030\002 \001(\01626.io.e" + + "vitadb.externalApi.grpc.generated.GrpcSe" + + "ssionType\022R\n\017commitBehaviour\030\003 \001(\01629.io." + + "evitadb.externalApi.grpc.generated.GrpcC" + + "ommitBehavior\022M\n\014catalogState\030\004 \001(\01627.io" + ".evitadb.externalApi.grpc.generated.Grpc" + - "SessionType\022R\n\017commitBehaviour\030\003 \001(\01629.i" + - "o.evitadb.externalApi.grpc.generated.Grp" + - "cCommitBehavior\022M\n\014catalogState\030\004 \001(\01627." + - "io.evitadb.externalApi.grpc.generated.Gr" + - "pcCatalogState\"L\n\"GrpcEvitaSessionTermin" + - "ationRequest\022\023\n\013catalogName\030\001 \001(\t\022\021\n\tses" + - "sionId\030\002 \001(\t\"9\n#GrpcEvitaSessionTerminat" + - "ionResponse\022\022\n\nterminated\030\001 \001(\010\"0\n\030GrpcC" + - "atalogNamesResponse\022\024\n\014catalogNames\030\001 \003(" + - "\t\"/\n\030GrpcDefineCatalogRequest\022\023\n\013catalog" + - "Name\030\001 \001(\t\",\n\031GrpcDefineCatalogResponse\022" + - "\017\n\007success\030\001 \001(\010\"G\n\030GrpcRenameCatalogReq" + - "uest\022\023\n\013catalogName\030\001 \001(\t\022\026\n\016newCatalogN" + - "ame\030\002 \001(\t\",\n\031GrpcRenameCatalogResponse\022\017" + - "\n\007success\030\001 \001(\010\"a\n\031GrpcReplaceCatalogReq" + - "uest\022#\n\033catalogNameToBeReplacedWith\030\001 \001(" + - "\t\022\037\n\027catalogNameToBeReplaced\030\002 \001(\t\"-\n\032Gr" + - "pcReplaceCatalogResponse\022\017\n\007success\030\001 \001(" + - "\010\"7\n GrpcDeleteCatalogIfExistsRequest\022\023\n" + - "\013catalogName\030\001 \001(\t\"4\n!GrpcDeleteCatalogI" + - "fExistsResponse\022\017\n\007success\030\001 \001(\010\"{\n\026Grpc" + - "UpdateEvitaRequest\022a\n\017schemaMutations\030\001 " + - "\003(\0132H.io.evitadb.externalApi.grpc.genera" + - "ted.GrpcTopLevelCatalogSchemaMutation2\336\r" + - "\n\014EvitaService\022l\n\014ServerStatus\022\026.google." + - "protobuf.Empty\032D.io.evitadb.externalApi." + - "grpc.generated.GrpcEvitaServerStatusResp" + - "onse\022\230\001\n\025CreateReadOnlySession\022>.io.evit" + - "adb.externalApi.grpc.generated.GrpcEvita" + - "SessionRequest\032?.io.evitadb.externalApi." + - "grpc.generated.GrpcEvitaSessionResponse\022" + - "\231\001\n\026CreateReadWriteSession\022>.io.evitadb." + - "externalApi.grpc.generated.GrpcEvitaSess" + - "ionRequest\032?.io.evitadb.externalApi.grpc" + - ".generated.GrpcEvitaSessionResponse\022\236\001\n\033" + - "CreateBinaryReadOnlySession\022>.io.evitadb" + + "CatalogState\"L\n\"GrpcEvitaSessionTerminat" + + "ionRequest\022\023\n\013catalogName\030\001 \001(\t\022\021\n\tsessi" + + "onId\030\002 \001(\t\"9\n#GrpcEvitaSessionTerminatio" + + "nResponse\022\022\n\nterminated\030\001 \001(\010\"0\n\030GrpcCat" + + "alogNamesResponse\022\024\n\014catalogNames\030\001 \003(\t\"" + + "/\n\030GrpcDefineCatalogRequest\022\023\n\013catalogNa" + + "me\030\001 \001(\t\",\n\031GrpcDefineCatalogResponse\022\017\n" + + "\007success\030\001 \001(\010\"G\n\030GrpcRenameCatalogReque" + + "st\022\023\n\013catalogName\030\001 \001(\t\022\026\n\016newCatalogNam" + + "e\030\002 \001(\t\",\n\031GrpcRenameCatalogResponse\022\017\n\007" + + "success\030\001 \001(\010\"a\n\031GrpcReplaceCatalogReque" + + "st\022#\n\033catalogNameToBeReplacedWith\030\001 \001(\t\022" + + "\037\n\027catalogNameToBeReplaced\030\002 \001(\t\"-\n\032Grpc" + + "ReplaceCatalogResponse\022\017\n\007success\030\001 \001(\010\"" + + "7\n GrpcDeleteCatalogIfExistsRequest\022\023\n\013c" + + "atalogName\030\001 \001(\t\"4\n!GrpcDeleteCatalogIfE" + + "xistsResponse\022\017\n\007success\030\001 \001(\010\"{\n\026GrpcUp" + + "dateEvitaRequest\022a\n\017schemaMutations\030\001 \003(" + + "\0132H.io.evitadb.externalApi.grpc.generate" + + "d.GrpcTopLevelCatalogSchemaMutation2\336\r\n\014" + + "EvitaService\022l\n\014ServerStatus\022\026.google.pr" + + "otobuf.Empty\032D.io.evitadb.externalApi.gr" + + "pc.generated.GrpcEvitaServerStatusRespon" + + "se\022\230\001\n\025CreateReadOnlySession\022>.io.evitad" + + "b.externalApi.grpc.generated.GrpcEvitaSe" + + "ssionRequest\032?.io.evitadb.externalApi.gr" + + "pc.generated.GrpcEvitaSessionResponse\022\231\001" + + "\n\026CreateReadWriteSession\022>.io.evitadb.ex" + + "ternalApi.grpc.generated.GrpcEvitaSessio" + + "nRequest\032?.io.evitadb.externalApi.grpc.g" + + "enerated.GrpcEvitaSessionResponse\022\236\001\n\033Cr" + + "eateBinaryReadOnlySession\022>.io.evitadb.e" + + "xternalApi.grpc.generated.GrpcEvitaSessi" + + "onRequest\032?.io.evitadb.externalApi.grpc." + + "generated.GrpcEvitaSessionResponse\022\237\001\n\034C" + + "reateBinaryReadWriteSession\022>.io.evitadb" + ".externalApi.grpc.generated.GrpcEvitaSes" + "sionRequest\032?.io.evitadb.externalApi.grp" + - "c.generated.GrpcEvitaSessionResponse\022\237\001\n" + - "\034CreateBinaryReadWriteSession\022>.io.evita" + - "db.externalApi.grpc.generated.GrpcEvitaS" + - "essionRequest\032?.io.evitadb.externalApi.g" + - "rpc.generated.GrpcEvitaSessionResponse\022\251" + - "\001\n\020TerminateSession\022I.io.evitadb.externa" + - "lApi.grpc.generated.GrpcEvitaSessionTerm" + - "inationRequest\032J.io.evitadb.externalApi." + - "grpc.generated.GrpcEvitaSessionTerminati" + - "onResponse\022j\n\017GetCatalogNames\022\026.google.p" + - "rotobuf.Empty\032?.io.evitadb.externalApi.g" + - "rpc.generated.GrpcCatalogNamesResponse\022\222" + - "\001\n\rDefineCatalog\022?.io.evitadb.externalAp" + - "i.grpc.generated.GrpcDefineCatalogReques" + - "t\032@.io.evitadb.externalApi.grpc.generate" + - "d.GrpcDefineCatalogResponse\022\222\001\n\rRenameCa" + - "talog\022?.io.evitadb.externalApi.grpc.gene" + - "rated.GrpcRenameCatalogRequest\032@.io.evit" + - "adb.externalApi.grpc.generated.GrpcRenam" + - "eCatalogResponse\022\225\001\n\016ReplaceCatalog\022@.io" + - ".evitadb.externalApi.grpc.generated.Grpc" + - "ReplaceCatalogRequest\032A.io.evitadb.exter" + - "nalApi.grpc.generated.GrpcReplaceCatalog" + - "Response\022\252\001\n\025DeleteCatalogIfExists\022G.io." + - "evitadb.externalApi.grpc.generated.GrpcD" + - "eleteCatalogIfExistsRequest\032H.io.evitadb" + - ".externalApi.grpc.generated.GrpcDeleteCa" + - "talogIfExistsResponse\022_\n\006Update\022=.io.evi" + - "tadb.externalApi.grpc.generated.GrpcUpda" + - "teEvitaRequest\032\026.google.protobuf.EmptyB\014" + - "P\001\252\002\007EvitaDBb\006proto3" + "c.generated.GrpcEvitaSessionResponse\022\251\001\n" + + "\020TerminateSession\022I.io.evitadb.externalA" + + "pi.grpc.generated.GrpcEvitaSessionTermin" + + "ationRequest\032J.io.evitadb.externalApi.gr" + + "pc.generated.GrpcEvitaSessionTermination" + + "Response\022j\n\017GetCatalogNames\022\026.google.pro" + + "tobuf.Empty\032?.io.evitadb.externalApi.grp" + + "c.generated.GrpcCatalogNamesResponse\022\222\001\n" + + "\rDefineCatalog\022?.io.evitadb.externalApi." + + "grpc.generated.GrpcDefineCatalogRequest\032" + + "@.io.evitadb.externalApi.grpc.generated." + + "GrpcDefineCatalogResponse\022\222\001\n\rRenameCata" + + "log\022?.io.evitadb.externalApi.grpc.genera" + + "ted.GrpcRenameCatalogRequest\032@.io.evitad" + + "b.externalApi.grpc.generated.GrpcRenameC" + + "atalogResponse\022\225\001\n\016ReplaceCatalog\022@.io.e" + + "vitadb.externalApi.grpc.generated.GrpcRe" + + "placeCatalogRequest\032A.io.evitadb.externa" + + "lApi.grpc.generated.GrpcReplaceCatalogRe" + + "sponse\022\252\001\n\025DeleteCatalogIfExists\022G.io.ev" + + "itadb.externalApi.grpc.generated.GrpcDel" + + "eteCatalogIfExistsRequest\032H.io.evitadb.e" + + "xternalApi.grpc.generated.GrpcDeleteCata" + + "logIfExistsResponse\022_\n\006Update\022=.io.evita" + + "db.externalApi.grpc.generated.GrpcUpdate" + + "EvitaRequest\032\026.google.protobuf.EmptyB\014P\001" + + "\252\002\007EvitaDBb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -222,7 +220,7 @@ public static void registerAllExtensions( internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaServerStatusResponse_descriptor, - new java.lang.String[] { "Version", "StartedAt", "Uptime", "InstanceId", "CatalogsCorrupted", "CatalogsOk", "HealthProblems", }); + new java.lang.String[] { "Version", "StartedAt", "Uptime", "InstanceId", "CatalogsCorrupted", "CatalogsOk", }); internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_io_evitadb_externalApi_grpc_generated_GrpcEvitaSessionRequest_fieldAccessorTable = new diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java index 3945e9756..96a32969f 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponse.java @@ -45,7 +45,6 @@ private GrpcEvitaServerStatusResponse(com.google.protobuf.GeneratedMessageV3.Bui private GrpcEvitaServerStatusResponse() { version_ = ""; instanceId_ = ""; - healthProblems_ = java.util.Collections.emptyList(); } @java.lang.Override @@ -68,7 +67,6 @@ private GrpcEvitaServerStatusResponse( if (extensionRegistry == null) { throw new java.lang.NullPointerException(); } - int mutable_bitField0_ = 0; com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); try { @@ -119,29 +117,6 @@ private GrpcEvitaServerStatusResponse( catalogsOk_ = input.readInt32(); break; } - case 56: { - int rawValue = input.readEnum(); - if (!((mutable_bitField0_ & 0x00000001) != 0)) { - healthProblems_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000001; - } - healthProblems_.add(rawValue); - break; - } - case 58: { - int length = input.readRawVarint32(); - int oldLimit = input.pushLimit(length); - while(input.getBytesUntilLimit() > 0) { - int rawValue = input.readEnum(); - if (!((mutable_bitField0_ & 0x00000001) != 0)) { - healthProblems_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000001; - } - healthProblems_.add(rawValue); - } - input.popLimit(oldLimit); - break; - } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -157,9 +132,6 @@ private GrpcEvitaServerStatusResponse( throw new com.google.protobuf.InvalidProtocolBufferException( e).setUnfinishedMessage(this); } finally { - if (((mutable_bitField0_ & 0x00000001) != 0)) { - healthProblems_ = java.util.Collections.unmodifiableList(healthProblems_); - } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -352,84 +324,6 @@ public int getCatalogsOk() { return catalogsOk_; } - public static final int HEALTHPROBLEMS_FIELD_NUMBER = 7; - private java.util.List healthProblems_; - private static final com.google.protobuf.Internal.ListAdapter.Converter< - java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem> healthProblems_converter_ = - new com.google.protobuf.Internal.ListAdapter.Converter< - java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>() { - public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem convert(java.lang.Integer from) { - @SuppressWarnings("deprecation") - io.evitadb.externalApi.grpc.generated.GrpcHealthProblem result = io.evitadb.externalApi.grpc.generated.GrpcHealthProblem.valueOf(from); - return result == null ? io.evitadb.externalApi.grpc.generated.GrpcHealthProblem.UNRECOGNIZED : result; - } - }; - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the healthProblems. - */ - @java.lang.Override - public java.util.List getHealthProblemsList() { - return new com.google.protobuf.Internal.ListAdapter< - java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>(healthProblems_, healthProblems_converter_); - } - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return The count of healthProblems. - */ - @java.lang.Override - public int getHealthProblemsCount() { - return healthProblems_.size(); - } - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the element to return. - * @return The healthProblems at the given index. - */ - @java.lang.Override - public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index) { - return healthProblems_converter_.convert(healthProblems_.get(index)); - } - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the enum numeric values on the wire for healthProblems. - */ - @java.lang.Override - public java.util.List - getHealthProblemsValueList() { - return healthProblems_; - } - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the value to return. - * @return The enum numeric value on the wire of healthProblems at the given index. - */ - @java.lang.Override - public int getHealthProblemsValue(int index) { - return healthProblems_.get(index); - } - private int healthProblemsMemoizedSerializedSize; - private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -444,7 +338,6 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - getSerializedSize(); if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, version_); } @@ -463,13 +356,6 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (catalogsOk_ != 0) { output.writeInt32(6, catalogsOk_); } - if (getHealthProblemsList().size() > 0) { - output.writeUInt32NoTag(58); - output.writeUInt32NoTag(healthProblemsMemoizedSerializedSize); - } - for (int i = 0; i < healthProblems_.size(); i++) { - output.writeEnumNoTag(healthProblems_.get(i)); - } unknownFields.writeTo(output); } @@ -501,18 +387,6 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeInt32Size(6, catalogsOk_); } - { - int dataSize = 0; - for (int i = 0; i < healthProblems_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeEnumSizeNoTag(healthProblems_.get(i)); - } - size += dataSize; - if (!getHealthProblemsList().isEmpty()) { size += 1; - size += com.google.protobuf.CodedOutputStream - .computeUInt32SizeNoTag(dataSize); - }healthProblemsMemoizedSerializedSize = dataSize; - } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -543,7 +417,6 @@ public boolean equals(final java.lang.Object obj) { != other.getCatalogsCorrupted()) return false; if (getCatalogsOk() != other.getCatalogsOk()) return false; - if (!healthProblems_.equals(other.healthProblems_)) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -570,10 +443,6 @@ public int hashCode() { hash = (53 * hash) + getCatalogsCorrupted(); hash = (37 * hash) + CATALOGSOK_FIELD_NUMBER; hash = (53 * hash) + getCatalogsOk(); - if (getHealthProblemsCount() > 0) { - hash = (37 * hash) + HEALTHPROBLEMS_FIELD_NUMBER; - hash = (53 * hash) + healthProblems_.hashCode(); - } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -727,8 +596,6 @@ public Builder clear() { catalogsOk_ = 0; - healthProblems_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); return this; } @@ -755,7 +622,6 @@ public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse build @java.lang.Override public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse buildPartial() { io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse result = new io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse(this); - int from_bitField0_ = bitField0_; result.version_ = version_; if (startedAtBuilder_ == null) { result.startedAt_ = startedAt_; @@ -766,11 +632,6 @@ public io.evitadb.externalApi.grpc.generated.GrpcEvitaServerStatusResponse build result.instanceId_ = instanceId_; result.catalogsCorrupted_ = catalogsCorrupted_; result.catalogsOk_ = catalogsOk_; - if (((bitField0_ & 0x00000001) != 0)) { - healthProblems_ = java.util.Collections.unmodifiableList(healthProblems_); - bitField0_ = (bitField0_ & ~0x00000001); - } - result.healthProblems_ = healthProblems_; onBuilt(); return result; } @@ -839,16 +700,6 @@ public Builder mergeFrom(io.evitadb.externalApi.grpc.generated.GrpcEvitaServerSt if (other.getCatalogsOk() != 0) { setCatalogsOk(other.getCatalogsOk()); } - if (!other.healthProblems_.isEmpty()) { - if (healthProblems_.isEmpty()) { - healthProblems_ = other.healthProblems_; - bitField0_ = (bitField0_ & ~0x00000001); - } else { - ensureHealthProblemsIsMutable(); - healthProblems_.addAll(other.healthProblems_); - } - onChanged(); - } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -877,7 +728,6 @@ public Builder mergeFrom( } return this; } - private int bitField0_; private java.lang.Object version_ = ""; /** @@ -1354,194 +1204,6 @@ public Builder clearCatalogsOk() { onChanged(); return this; } - - private java.util.List healthProblems_ = - java.util.Collections.emptyList(); - private void ensureHealthProblemsIsMutable() { - if (!((bitField0_ & 0x00000001) != 0)) { - healthProblems_ = new java.util.ArrayList(healthProblems_); - bitField0_ |= 0x00000001; - } - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the healthProblems. - */ - public java.util.List getHealthProblemsList() { - return new com.google.protobuf.Internal.ListAdapter< - java.lang.Integer, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem>(healthProblems_, healthProblems_converter_); - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return The count of healthProblems. - */ - public int getHealthProblemsCount() { - return healthProblems_.size(); - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the element to return. - * @return The healthProblems at the given index. - */ - public io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index) { - return healthProblems_converter_.convert(healthProblems_.get(index)); - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index to set the value at. - * @param value The healthProblems to set. - * @return This builder for chaining. - */ - public Builder setHealthProblems( - int index, io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHealthProblemsIsMutable(); - healthProblems_.set(index, value.getNumber()); - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param value The healthProblems to add. - * @return This builder for chaining. - */ - public Builder addHealthProblems(io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHealthProblemsIsMutable(); - healthProblems_.add(value.getNumber()); - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param values The healthProblems to add. - * @return This builder for chaining. - */ - public Builder addAllHealthProblems( - java.lang.Iterable values) { - ensureHealthProblemsIsMutable(); - for (io.evitadb.externalApi.grpc.generated.GrpcHealthProblem value : values) { - healthProblems_.add(value.getNumber()); - } - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return This builder for chaining. - */ - public Builder clearHealthProblems() { - healthProblems_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the enum numeric values on the wire for healthProblems. - */ - public java.util.List - getHealthProblemsValueList() { - return java.util.Collections.unmodifiableList(healthProblems_); - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the value to return. - * @return The enum numeric value on the wire of healthProblems at the given index. - */ - public int getHealthProblemsValue(int index) { - return healthProblems_.get(index); - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the value to return. - * @return The enum numeric value on the wire of healthProblems at the given index. - * @return This builder for chaining. - */ - public Builder setHealthProblemsValue( - int index, int value) { - ensureHealthProblemsIsMutable(); - healthProblems_.set(index, value); - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param value The enum numeric value on the wire for healthProblems to add. - * @return This builder for chaining. - */ - public Builder addHealthProblemsValue(int value) { - ensureHealthProblemsIsMutable(); - healthProblems_.add(value); - onChanged(); - return this; - } - /** - *
-     * Set of all observed health problems
-     * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param values The enum numeric values on the wire for healthProblems to add. - * @return This builder for chaining. - */ - public Builder addAllHealthProblemsValue( - java.lang.Iterable values) { - ensureHealthProblemsIsMutable(); - for (int value : values) { - healthProblems_.add(value); - } - onChanged(); - return this; - } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java index c52d8142a..1520b68fe 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcEvitaServerStatusResponseOrBuilder.java @@ -126,53 +126,4 @@ public interface GrpcEvitaServerStatusResponseOrBuilder extends * @return The catalogsOk. */ int getCatalogsOk(); - - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the healthProblems. - */ - java.util.List getHealthProblemsList(); - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return The count of healthProblems. - */ - int getHealthProblemsCount(); - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the element to return. - * @return The healthProblems at the given index. - */ - io.evitadb.externalApi.grpc.generated.GrpcHealthProblem getHealthProblems(int index); - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @return A list containing the enum numeric values on the wire for healthProblems. - */ - java.util.List - getHealthProblemsValueList(); - /** - *
-   * Set of all observed health problems
-   * 
- * - * repeated .io.evitadb.externalApi.grpc.generated.GrpcHealthProblem healthProblems = 7; - * @param index The index of the value to return. - * @return The enum numeric value on the wire of healthProblems at the given index. - */ - int getHealthProblemsValue(int index); } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java index 9b85d02fb..7a6161a34 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java @@ -21,6 +21,374 @@ * limitations under the License. */ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2023 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + // Generated by the protocol buffer compiler. DO NOT EDIT! // source: GrpcEnums.proto diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java index 76d568713..a54c0c9b2 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java @@ -46,7 +46,6 @@ import io.evitadb.api.requestResponse.schema.OrderBehaviour; import io.evitadb.api.requestResponse.schema.dto.AttributeUniquenessType; import io.evitadb.api.requestResponse.schema.dto.GlobalAttributeUniquenessType; -import io.evitadb.api.requestResponse.system.SystemStatus.HealthProblem; import io.evitadb.exception.EvitaInternalError; import io.evitadb.externalApi.grpc.generated.*; import lombok.AccessLevel; @@ -749,19 +748,4 @@ public static CommitBehavior toCommitBehavior(@Nonnull GrpcCommitBehavior commit }; } - /** - * Converts a GrpcHealthProblem to a HealthProblem. - * - * @param grpcHealthProblem The GrpcHealthProblem to convert. - * @return The converted HealthProblem. - * @throws EvitaInternalError if the given grpcHealthProblem is unrecognized. - */ - @Nonnull - public static HealthProblem toHealthProblem(@Nonnull GrpcHealthProblem grpcHealthProblem) { - return switch (grpcHealthProblem.getNumber()) { - case 0 -> HealthProblem.MEMORY_SHORTAGE; - case 1 -> HealthProblem.INPUT_QUEUES_OVERLOADED; - default -> throw new EvitaInternalError("Unrecognized remote health problem: " + grpcHealthProblem); - }; - } } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto index 86d43fe48..96dd40893 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEnums.proto @@ -500,15 +500,3 @@ enum GrpcCommitBehavior { WAIT_FOR_INDEX_PROPAGATION = 2; } - -// This enum represents all possible health problems that are monitored by evitaDB. -enum GrpcHealthProblem { - - // Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free - // old generation multiple times consuming a log of CPU power. - MEMORY_SHORTAGE = 0; - // Signalized when the input queues are full and the server is not able to process incoming requests. This flag - // is cleared when the server is able to process incoming requests again. - INPUT_QUEUES_OVERLOADED = 1; - -} diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto index 6f42fe19b..0f2e2b263 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/resources/META-INF/io/evitadb/externalApi/grpc/GrpcEvitaAPI.proto @@ -23,8 +23,6 @@ message GrpcEvitaServerStatusResponse { int32 catalogsCorrupted = 5; // Number of catalogs that are ok int32 catalogsOk = 6; - // Set of all observed health problems - repeated GrpcHealthProblem healthProblems = 7; } // Request to create a session inside of a catalog. diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java index 849ce46cd..ef653450b 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java @@ -28,6 +28,7 @@ import io.evitadb.core.metric.event.CustomMetricsExecutionEvent; import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.UnexpectedIOException; +import io.evitadb.externalApi.api.system.model.HealthProblem; import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.CorsFilter; import io.evitadb.externalApi.http.PathNormalizingHandler; @@ -67,6 +68,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; /** * This class is used as an orchestrator for all observability-related tasks. It is responsible for starting and stopping @@ -81,10 +83,6 @@ public class ObservabilityManager { public static final String METRICS_SUFFIX = "metrics"; public static final String METRICS_PATH = "/observability/" + METRICS_SUFFIX; - /** - * JFR recording instance. - */ - private final Recording recording; /** * Directory where JFR recording file is stored. */ @@ -93,6 +91,26 @@ public class ObservabilityManager { * Name of the JFR recording file. */ private static final String DUMP_FILE_NAME = "recording.jfr"; + /** + * Counter for Java errors. + */ + private static final AtomicLong JAVA_ERRORS = new AtomicLong(); + /** + * Counter for Java OutOfMemory errors. + */ + private static final AtomicLong JAVA_OOM_ERRORS = new AtomicLong(); + /** + * Counter for evitaDB errors. + */ + private static final AtomicLong EVITA_ERRORS = new AtomicLong(); + /** + * Name of the OutOfMemoryError class to detect the problems of OOM kind. + */ + private static final String OOM_NAME = OutOfMemoryError.class.getSimpleName(); + /** + * JFR recording instance. + */ + private final Recording recording; /** * Router for observability endpoints. */ @@ -114,6 +132,46 @@ public class ObservabilityManager { */ @Nonnull @Getter private final ObjectMapper objectMapper = new ObjectMapper(); + /** + * Method increments the counter of Java errors in the Prometheus metrics. + */ + public static void javaErrorEvent(@Nonnull String simpleName) { + MetricHandler.JAVA_ERRORS_TOTAL.labelValues(simpleName).inc(); + JAVA_ERRORS.incrementAndGet(); + } + + /** + * Method increments the counter of evitaDB errors in the Prometheus metrics. + */ + public static void evitaErrorEvent(@Nonnull String simpleName) { + MetricHandler.EVITA_ERRORS_TOTAL.labelValues(simpleName).inc(); + EVITA_ERRORS.incrementAndGet(); + if (simpleName.equals(OOM_NAME)) { + JAVA_OOM_ERRORS.incrementAndGet(); + } + } + + /** + * Registers specified events within {@link FlightRecorder}. + */ + private static void registerJfrEvents(@Nonnull String[] allowedEvents) { + for (String event : Arrays.stream(allowedEvents).filter(x -> !x.startsWith("jdk.")).toList()) { + if (event.endsWith(".*")) { + final Set> classes = CustomEventProvider.getEventClassesFromPackage(event); + for (Class clazz : classes) { + if (!Modifier.isAbstract(clazz.getModifiers())) { + FlightRecorder.register(clazz); + } + } + } else { + final Class clazz = CustomEventProvider.getEventClass(event); + if (!Modifier.isAbstract(clazz.getModifiers())) { + FlightRecorder.register(clazz); + } + } + } + } + public ObservabilityManager(ObservabilityConfig config, ApiOptions apiOptions, Evita evita) { this.recording = new Recording(); this.config = config; @@ -124,6 +182,49 @@ public ObservabilityManager(ObservabilityConfig config, ApiOptions apiOptions, E registerRecordingFileResourceHandler(); } + /** + * Returns the number of Java errors. + * + * @return the number of Java errors + */ + public long getJavaErrorCount() { + return JAVA_ERRORS.get(); + } + + /** + * Returns the number of Java OutOfMemory errors. + * + * @return the number of Java OutOfMemory errors + */ + public long getJavaOutOfMemoryErrorCount() { + return JAVA_OOM_ERRORS.get(); + } + + /** + * Returns the number of evitaDB errors. + * + * @return the number of evitaDB errors + */ + public long getEvitaErrorCount() { + return EVITA_ERRORS.get(); + } + + /** + * Records health problem to the Prometheus metrics. + * @param healthProblem the health problem to be recorded + */ + public void recordHealthProblem(@Nonnull HealthProblem healthProblem) { + MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem.name()).set(1); + } + + /** + * Clears health problem from the Prometheus metrics. + * @param healthProblem the health problem to be cleared + */ + public void clearHealthProblem(@Nonnull HealthProblem healthProblem) { + MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem.name()).set(0); + } + /** * Starts JFR recording that logs all specified events. */ @@ -176,6 +277,14 @@ public void registerPrometheusMetricHandler() { new MetricHandler(config).registerHandlers(evita.getExecutor()); } + /** + * Gets {@link HttpHandler} for observability endpoints. + */ + @Nonnull + public HttpHandler getObservabilityRouter() { + return new PathNormalizingHandler(observabilityRouter); + } + /** * Registers resource handler for file containing JFR recording. */ @@ -205,14 +314,6 @@ private void registerRecordingFileResourceHandler() { } } - /** - * Gets {@link HttpHandler} for observability endpoints. - */ - @Nonnull - public HttpHandler getObservabilityRouter() { - return new PathNormalizingHandler(observabilityRouter); - } - /** * Creates and registers Prometheus scraping servlet for metrics publishing. */ @@ -262,25 +363,4 @@ private void registerJfrControlEndpoints() { ) ); } - - /** - * Registers specified events within {@link FlightRecorder}. - */ - private static void registerJfrEvents(@Nonnull String[] allowedEvents) { - for (String event : Arrays.stream(allowedEvents).filter(x -> !x.startsWith("jdk.")).toList()) { - if (event.endsWith(".*")) { - final Set> classes = CustomEventProvider.getEventClassesFromPackage(event); - for (Class clazz : classes) { - if (!Modifier.isAbstract(clazz.getModifiers())) { - FlightRecorder.register(clazz); - } - } - } else { - final Class clazz = CustomEventProvider.getEventClass(event); - if (!Modifier.isAbstract(clazz.getModifiers())) { - FlightRecorder.register(clazz); - } - } - } - } } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java index b9a04916c..101eb905e 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityProvider.java @@ -51,7 +51,7 @@ public class ObservabilityProvider implements ExternalApiProvider register(@Nonnull Evita evita, @ observabilityManager.registerPrometheusMetricHandler(); return new ObservabilityProvider( observabilityConfig, - observabilityManager.getObservabilityRouter(), + observabilityManager, Arrays.stream(observabilityConfig.getBaseUrls(apiOptions.exposedOn())) .toArray(String[]::new) ); diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java similarity index 63% rename from evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java rename to evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java index 0f0be0989..da2195c47 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/OOMAgent.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java @@ -23,6 +23,8 @@ package io.evitadb.externalApi.observability.agent; +import io.evitadb.exception.EvitaInternalError; +import io.evitadb.externalApi.observability.ObservabilityManager; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice.OnMethodEnter; @@ -30,45 +32,69 @@ import java.lang.instrument.Instrumentation; -import static net.bytebuddy.matcher.ElementMatchers.is; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.none; /** - * TODO JNO - document me + * Agent that intercepts all Error constructors and sends a metric to the MetricHandler. * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 */ -public class OOMAgent { +public class ErrorMonitoringAgent { public static void premain(String agentArgs, Instrumentation inst) { ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(inst); AgentBuilder agentBuilder = new AgentBuilder.Default(); agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory)); - agentBuilder .disableClassFormatChanges() .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .ignore(none()) .ignore(nameStartsWith("net.bytebuddy.")) - .type(is(OutOfMemoryError.class)) + .type(isSubTypeOf(Error.class)) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder + .visit( + Advice + .to(JavaErrorConstructorInterceptAdvice.class) + .on(isConstructor()) + )) + .type(isSubTypeOf(EvitaInternalError.class)) .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder .visit( Advice - .to(MyAdvice.class) + .to(EvitaDbErrorConstructorInterceptAdvice.class) .on(isConstructor()) )) .installOn(inst); } - public static class MyAdvice { + /** + * Advice that sends a metric to the MetricHandler when an Error is constructed. + */ + public static class JavaErrorConstructorInterceptAdvice { + @OnMethodEnter - public static boolean before() { - System.out.println("!!! OOM !!!"); + public static boolean before(@Advice.This Object thiz) { + ObservabilityManager.javaErrorEvent(thiz.getClass().getSimpleName()); return true; } + + } + + /** + * Advice that sends a metric to the MetricHandler when an Error is constructed. + */ + public static class EvitaDbErrorConstructorInterceptAdvice { + + @OnMethodEnter + public static boolean before(@Advice.This Object thiz) { + ObservabilityManager.evitaErrorEvent(thiz.getClass().getSimpleName()); + return true; + } + } } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java index d19791b55..3a9d29f4e 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java @@ -69,10 +69,21 @@ public class MetricHandler { private static final Map DEFAULT_JVM_METRICS; private static final String DEFAULT_JVM_METRICS_NAME = "AllMetrics"; - // Define a Prometheus counter for OutOfMemoryError events - private static final Counter OUT_OF_MEMORY_ERRORS_TOTAL = Counter.builder() - .name("jvm_out_of_memory_errors_total") - .help("Total number of Out of Memory errors") + // define a Prometheus counter for errors + public static final Counter JAVA_ERRORS_TOTAL = Counter.builder() + .name("jvm_errors_total") + .labelNames("error_type") + .help("Total number of internal Java errors") + .register(); + public static final Counter EVITA_ERRORS_TOTAL = Counter.builder() + .name("evita_errors_total") + .labelNames("error_type") + .help("Total number of internal evitaDB errors") + .register(); + public static final Gauge HEALTH_PROBLEMS = Gauge.builder() + .name("evita_health_problem") + .labelNames("problem_type") + .help("Health problems detected in the system") .register(); static { @@ -94,13 +105,6 @@ public MetricHandler(@Nonnull ObservabilityConfig observabilityConfig) { this.observabilityConfig = observabilityConfig; } - /** - * TODO JNO - document me - */ - public static void outOfMemoryErrorEvent() { - OUT_OF_MEMORY_ERRORS_TOTAL.inc(); - } - /** * Based on configuration, this method enables collecting and publishing metrics into Prometheus scraping endpoint. * If no configuration of such event has been provided, all JVM and custom events are logged. For limiting the logged diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java new file mode 100644 index 000000000..12e209009 --- /dev/null +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -0,0 +1,179 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.externalApi.observability.metric; + +import io.evitadb.api.EvitaContract; +import io.evitadb.core.Evita; +import io.evitadb.externalApi.api.system.ProbesProvider; +import io.evitadb.externalApi.api.system.model.HealthProblem; +import io.evitadb.externalApi.http.ExternalApiProvider; +import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; +import io.evitadb.externalApi.http.ExternalApiServer; +import io.evitadb.externalApi.observability.ObservabilityManager; +import io.evitadb.externalApi.observability.ObservabilityProvider; +import io.evitadb.utils.CollectionUtils; +import org.jboss.threads.EnhancedQueueExecutor; + +import javax.annotation.Nonnull; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * This class is responsible for detecting health problems in the system. It monitors: + * + * 1. The ratio of rejected tasks to submitted tasks in the executor. If the ratio is greater than 2, the input queues + * are considered overloaded. + * 2. The number of Java internal errors. If the number of errors has increased since the last check, the system is + * considered unhealthy. + * 3. The number of database internal errors. If the number of errors has increased since the last check, the system is + * considered unhealthy. + * 4. The number of Java OutOfMemory errors or Old generation garbage collections. If the current memory usage is above + * 90% of the total available memory and the number of errors has increased since the last check, the system is considered + * unhealthy. + */ +public class ObservabilityProbesDetector implements ProbesProvider { + private static final Set NO_HEALTH_PROBLEMS = EnumSet.noneOf(HealthProblem.class); + private static final Set OLD_GENERATION_GC_NAMES = Set.of("G1 Old Generation", "PS MarkSweep", "ConcurrentMarkSweep"); + + private final Runtime runtime = Runtime.getRuntime(); + private final List garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans() + .stream() + .filter(gc -> OLD_GENERATION_GC_NAMES.contains(gc.getName())) + .toList(); + + private ObservabilityManager observabilityManager; + private long lastSeenRejectedTaskCount; + private long lastSeenSubmittedTaskCount; + private long lastSeenJavaErrorCount; + private long lastSeenJavaOOMErrorCount; + private long lastSeenEvitaErrorCount; + private long lastSeenJavaGarbageCollections; + private boolean seenReady; + + @Nonnull + @Override + public Set getHealthProblems(@Nonnull EvitaContract evitaContract, @Nonnull ExternalApiServer externalApiServer) { + final EnumSet healthProblems = EnumSet.noneOf(HealthProblem.class); + final Optional theObservabilityManager = getObservabilityManager(externalApiServer); + if (evitaContract instanceof Evita evita) { + final EnhancedQueueExecutor executor = evita.getExecutor(); + final long rejectedTaskCount = executor.getRejectedTaskCount(); + final long submittedTaskCount = executor.getSubmittedTaskCount(); + // if the ratio of rejected task to submitted tasks is greater than 2, we could consider queues as overloaded + if ((rejectedTaskCount - lastSeenRejectedTaskCount) / (Math.max(submittedTaskCount - lastSeenSubmittedTaskCount, 1)) > 2) { + healthProblems.add(HealthProblem.INPUT_QUEUES_OVERLOADED); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED)); + } else { + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED)); + } + this.lastSeenRejectedTaskCount = rejectedTaskCount; + this.lastSeenSubmittedTaskCount = submittedTaskCount; + } + + // if the number of errors has increased since the last check, we could consider the system as unhealthy + final long javaErrorCount = theObservabilityManager + .map(ObservabilityManager::getJavaErrorCount) + .orElse(0L); + if (javaErrorCount > this.lastSeenJavaErrorCount) { + healthProblems.add(HealthProblem.JAVA_INTERNAL_ERRORS); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS)); + } else { + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS)); + } + this.lastSeenJavaErrorCount = javaErrorCount; + + // if the number of errors has increased since the last check, we could consider the system as unhealthy + final long evitaErrorCount = theObservabilityManager + .map(ObservabilityManager::getEvitaErrorCount) + .orElse(0L); + if (evitaErrorCount > this.lastSeenEvitaErrorCount) { + healthProblems.add(HealthProblem.EVITA_DB_INTERNAL_ERRORS); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.EVITA_DB_INTERNAL_ERRORS)); + } else { + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.EVITA_DB_INTERNAL_ERRORS)); + } + this.lastSeenEvitaErrorCount = evitaErrorCount; + + // if the number of errors has increased since the last check, we could consider the system as unhealthy + final long javaOOMErrorCount = theObservabilityManager + .map(ObservabilityManager::getJavaOutOfMemoryErrorCount) + .orElse(0L); + // get used memory of the JVM + final float usedMemory = 1.0f - ((float) runtime.freeMemory() / (float) runtime.maxMemory()); + final long oldGenerationCollectionCount = garbageCollectorMXBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); + if (usedMemory > 0.9f && (javaOOMErrorCount > this.lastSeenJavaOOMErrorCount || oldGenerationCollectionCount > this.lastSeenJavaGarbageCollections)) { + healthProblems.add(HealthProblem.MEMORY_SHORTAGE); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.MEMORY_SHORTAGE)); + } else { + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.MEMORY_SHORTAGE)); + } + this.lastSeenJavaOOMErrorCount = javaOOMErrorCount; + this.lastSeenJavaGarbageCollections = oldGenerationCollectionCount; + + return healthProblems.isEmpty() ? NO_HEALTH_PROBLEMS : healthProblems; + } + + @Nonnull + @Override + public Readiness getReadiness(@Nonnull EvitaContract evitaContract, @Nonnull ExternalApiServer externalApiServer, @Nonnull String... apiCodes) { + // check the end-points availability + final Collection availableExternalApis = ExternalApiServer.gatherExternalApiProviders(); + final Map readiness = CollectionUtils.createHashMap(availableExternalApis.size()); + for (String apiCode : apiCodes) { + final ExternalApiProvider apiProvider = externalApiServer.getExternalApiProviderByCode(apiCode); + readiness.put(apiProvider.getCode(), apiProvider.isReady()); + } + final boolean ready = readiness.values().stream().allMatch(Boolean::booleanValue); + if (ready) { + this.seenReady = true; + } + return new Readiness( + ready ? ReadinessState.READY : (this.seenReady ? ReadinessState.STALLING : ReadinessState.STARTING), + readiness.entrySet().stream() + .map(entry -> new ApiState(entry.getKey(), entry.getValue())) + .toArray(ApiState[]::new) + ); + } + + @Nonnull + private Optional getObservabilityManager(@Nonnull ExternalApiServer externalApiServer) { + if (this.observabilityManager == null) { + final Optional apiProvider = Optional.ofNullable( + externalApiServer.getExternalApiProviderByCode(ObservabilityProvider.CODE) + ); + this.observabilityManager = apiProvider + .map(ObservabilityProvider::getObservabilityManager) + .orElse(null); + } + return Optional.ofNullable(this.observabilityManager); + } + + +} diff --git a/evita_external_api/evita_external_api_observability/src/main/java/module-info.java b/evita_external_api/evita_external_api_observability/src/main/java/module-info.java index 41a715dea..9d3533c47 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/module-info.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/module-info.java @@ -22,8 +22,10 @@ */ import io.evitadb.api.trace.TracingContext; +import io.evitadb.externalApi.api.system.ProbesProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.externalApi.observability.ObservabilityProviderRegistrar; +import io.evitadb.externalApi.observability.metric.ObservabilityProbesDetector; import io.evitadb.externalApi.observability.trace.DelegateExternalApiTracingContext; import io.evitadb.externalApi.observability.trace.ObservabilityTracingContext; import io.evitadb.externalApi.utils.ExternalApiTracingContext; @@ -41,6 +43,7 @@ provides TracingContext with ObservabilityTracingContext; provides ExternalApiTracingContext with DelegateExternalApiTracingContext; provides ExternalApiProviderRegistrar with ObservabilityProviderRegistrar; + provides ProbesProvider with ObservabilityProbesDetector; opens io.evitadb.externalApi.observability.configuration to com.fasterxml.jackson.databind; @@ -81,4 +84,5 @@ exports io.evitadb.externalApi.observability.configuration; exports io.evitadb.externalApi.observability.trace; + exports io.evitadb.externalApi.observability.metric; } diff --git a/evita_external_api/evita_external_api_observability/src/main/resources/META-INF/services/io.evitadb.externalApi.api.system.ProbesProvider b/evita_external_api/evita_external_api_observability/src/main/resources/META-INF/services/io.evitadb.externalApi.api.system.ProbesProvider new file mode 100644 index 000000000..bc0919ecb --- /dev/null +++ b/evita_external_api/evita_external_api_observability/src/main/resources/META-INF/services/io.evitadb.externalApi.api.system.ProbesProvider @@ -0,0 +1,24 @@ +# +# +# _ _ ____ ____ +# _____ _(_) |_ __ _| _ \| __ ) +# / _ \ \ / / | __/ _` | | | | _ \ +# | __/\ V /| | || (_| | |_| | |_) | +# \___| \_/ |_|\__\__,_|____/|____/ +# +# Copyright (c) 2024 +# +# Licensed under the Business Source License, Version 1.1 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://github.com/FgForrest/evitaDB/blob/main/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +io.evitadb.externalApi.observability.metric.ObservabilityProbesDetector diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index a66191cee..43a1ca8d0 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -26,7 +26,10 @@ import io.evitadb.api.requestResponse.system.SystemStatus; import io.evitadb.core.Evita; import io.evitadb.exception.EvitaInternalError; -import io.evitadb.externalApi.configuration.AbstractApiConfiguration; +import io.evitadb.externalApi.api.system.ProbesProvider; +import io.evitadb.externalApi.api.system.ProbesProvider.Readiness; +import io.evitadb.externalApi.api.system.ProbesProvider.ReadinessState; +import io.evitadb.externalApi.api.system.model.HealthProblem; import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.configuration.CertificatePath; import io.evitadb.externalApi.configuration.CertificateSettings; @@ -36,7 +39,6 @@ import io.evitadb.externalApi.http.ExternalApiServer; import io.evitadb.externalApi.system.configuration.SystemConfig; import io.evitadb.utils.CertificateUtils; -import io.evitadb.utils.CollectionUtils; import io.evitadb.utils.StringUtils; import io.undertow.Handlers; import io.undertow.server.HttpServerExchange; @@ -53,9 +55,10 @@ import java.io.IOException; import java.time.format.DateTimeFormatter; import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.Set; import java.util.stream.Collectors; /** @@ -68,26 +71,23 @@ public class SystemProviderRegistrar implements ExternalApiProviderRegistrar readiness, - @Nonnull String overallStatus + @Nonnull Readiness readiness ) { exchange.getResponseSender().send("{\n" + - "\t\"status\": \"" + overallStatus + "\",\n" + + "\t\"status\": \"" + readiness.state().name() + "\",\n" + "\t\"apis\": {\n" + - readiness.entrySet().stream() - .map(entry -> "\t\t\"" + entry.getKey() + "\": \"" + (entry.getValue() ? "ready" : "not ready") + "\"") - .collect(Collectors.joining(",\n")) + "\n" + + Arrays.stream(readiness.apiStates()) + .map(entry -> "\t\t\"" + entry.apiCode() + "\": \"" + (entry.isReady() ? "ready" : "not ready") + "\"") + .collect(Collectors.joining(",\n")) + "\n" + "\t}\n" + "}" ); @@ -128,9 +128,6 @@ private static String renderStatus( StringUtils.formatDuration(systemStatus.uptime()), systemStatus.catalogsCorrupted(), systemStatus.catalogsOk(), - systemStatus.healthProblems().stream() - .map(it -> "\"" + it.name() + "\"") - .collect(Collectors.joining(", ")), apiOptions.endpoints().entrySet().stream() .map( entry -> " {\n \"" + entry.getKey() + "\": [\n" + @@ -157,7 +154,12 @@ public Class getConfigurationClass() { @Nonnull @Override - public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull ExternalApiServer externalApiServer, @Nonnull ApiOptions apiOptions, @Nonnull SystemConfig systemConfig) { + public ExternalApiProvider register( + @Nonnull Evita evita, + @Nonnull ExternalApiServer externalApiServer, + @Nonnull ApiOptions apiOptions, + @Nonnull SystemConfig systemConfig + ) { final File file; final String fileName; final CertificateSettings certificateSettings = apiOptions.certificate(); @@ -206,15 +208,20 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull "/" + ENDPOINT_SYSTEM_LIVENESS, exchange -> { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); - final SystemStatus systemStatus = evita.getSystemStatus(); - if (systemStatus.healthProblems().isEmpty()) { + final Set healthProblems = ServiceLoader.load(ProbesProvider.class) + .stream() + .flatMap(it -> it.get().getHealthProblems(evita, externalApiServer).stream()) + .collect(Collectors.toSet()); + + if (healthProblems.isEmpty()) { exchange.setStatusCode(StatusCodes.OK); exchange.getResponseSender().send("{\"status\": \"healthy\"}"); } else { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); exchange.getResponseSender().send( "{\"status\": \"unhealthy\", \"problems\": [" + - systemStatus.healthProblems().stream() + healthProblems.stream() + .sorted() .map(it -> "\"" + it.name() + "\"") .collect(Collectors.joining(", ")) + "]}" @@ -223,32 +230,37 @@ public ExternalApiProvider register(@Nonnull Evita evita, @Nonnull } ); + final String[] enabledEndPoints = apiOptions.endpoints() + .entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .filter(entry -> entry.getValue().isEnabled()) + .map(Entry::getKey) + .toArray(String[]::new); + router.addExactPath( "/" + ENDPOINT_SYSTEM_READINESS, exchange -> { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); if (evita.isActive()) { - // check the end-points availability - final Collection availableExternalApis = ExternalApiServer.gatherExternalApiProviders(); - final Map readiness = CollectionUtils.createHashMap(availableExternalApis.size()); - for (ExternalApiProviderRegistrar externalApi : availableExternalApis) { - final AbstractApiConfiguration apiConfiguration = apiOptions.getEndpointConfiguration(externalApi.getExternalApiCode()); - if (apiConfiguration != null && apiConfiguration.isEnabled()) { - final ExternalApiProvider apiProvider = externalApiServer.getExternalApiProviderByCode(externalApi.getExternalApiCode()); - readiness.put(apiProvider.getCode(), apiProvider.isReady()); - } - } - if (readiness.values().stream().allMatch(it -> it)) { + final Optional readiness = ServiceLoader.load(ProbesProvider.class) + .stream() + .map(it -> it.get().getReadiness(evita, externalApiServer, enabledEndPoints)) + .findFirst(); + + if (readiness.map(it -> it.state() == ReadinessState.READY).orElse(false)) { exchange.setStatusCode(StatusCodes.OK); - printApiStatus(exchange, readiness, "ready"); - seenReady.set(true); + printApiStatus(exchange, readiness.get()); + } else if (readiness.isPresent()) { + exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); + printApiStatus(exchange, readiness.get()); } else { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); - printApiStatus(exchange, readiness, seenReady.get() ? "starting" : "stalling"); + exchange.getResponseSender().send("{\"status\": \"" + ReadinessState.UNKNOWN.name() + "\"}"); } } else { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); - exchange.getResponseSender().send("{\"status\": \"shut down\"}"); + exchange.getResponseSender().send("{\"status\": \"" + ReadinessState.SHUT_DOWN.name() + "\"}"); } } ); diff --git a/evita_external_api/evita_external_api_system/src/main/java/module-info.java b/evita_external_api/evita_external_api_system/src/main/java/module-info.java index 5157d9ba8..3982647f8 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/module-info.java +++ b/evita_external_api/evita_external_api_system/src/main/java/module-info.java @@ -21,6 +21,7 @@ * limitations under the License. */ +import io.evitadb.externalApi.api.system.ProbesProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.store.spi.CatalogPersistenceServiceFactory; /** @@ -30,6 +31,7 @@ uses CatalogPersistenceServiceFactory; uses ExternalApiProviderRegistrar; + uses ProbesProvider; provides ExternalApiProviderRegistrar with io.evitadb.externalApi.system.SystemProviderRegistrar; @@ -48,7 +50,8 @@ requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.databind; requires java.management; + requires org.bouncycastle.provider; exports io.evitadb.externalApi.system.configuration; exports io.evitadb.externalApi.system; -} \ No newline at end of file +} diff --git a/evita_functional_tests/pom.xml b/evita_functional_tests/pom.xml index 00528e1bf..78a232578 100644 --- a/evita_functional_tests/pom.xml +++ b/evita_functional_tests/pom.xml @@ -109,13 +109,6 @@ evita_server ${project.parent.version} test - - - - ${project.parent.groupId} - evita_external_api_observability - -
${project.parent.groupId} diff --git a/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java b/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java index 574f26966..b83e407a0 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/server/EvitaServerTest.java @@ -167,26 +167,33 @@ void shouldSignalizeReadinessAndHealthinessCorrectly() { property("cache.enabled", "false") ), apis.stream() - .map(it -> property("api.endpoints." + it + ".host", "localhost:" + ports[index.getAndIncrement()])) + .flatMap( + it -> Stream.of( + property("api.endpoints." + it + ".host", "localhost:" + ports[index.getAndIncrement()]), + property("api.endpoints." + it + ".enabled", "true") + ) + ) ) .toArray(Property[]::new) ) ); try { evitaServer.run(); + final String[] baseUrls = evitaServer.getExternalApiServer().getExternalApiProviderByCode(SystemProvider.CODE) + .getConfiguration() + .getBaseUrls(null); Optional response; final long start = System.currentTimeMillis(); do { - final String[] baseUrls = evitaServer.getExternalApiServer().getExternalApiProviderByCode(SystemProvider.CODE).getConfiguration().getBaseUrls(null); response = NetworkUtils.fetchContent( baseUrls[0] + "readiness", "GET", - "text/plain", + "application/json", null ); - if (response.isPresent() && response.get().contains("\"status\": \"ready\"")) { + if (response.isPresent() && response.get().contains("\"status\": \"READY\"")) { break; } @@ -196,18 +203,32 @@ void shouldSignalizeReadinessAndHealthinessCorrectly() { assertEquals( """ { - "status": "ready", + "status": "READY", "apis": { "rest": "ready", "system": "ready", "graphQL": "ready", "lab": "ready", + "observability": "ready", "gRPC": "ready" } }""", response.get().trim() ); + final Optional liveness = NetworkUtils.fetchContent( + baseUrls[0] + "liveness", + "GET", + "application/json", + null + ); + + assertTrue(liveness.isPresent()); + assertEquals( + "{\"status\": \"healthy\"}", + liveness.get().trim() + ); + } catch (Exception ex) { fail(ex); } finally { diff --git a/evita_server/pom.xml b/evita_server/pom.xml index ca43a5db1..8643e3ee8 100644 --- a/evita_server/pom.xml +++ b/evita_server/pom.xml @@ -149,7 +149,7 @@ true - io.evitadb.externalApi.observability.agent.OOMAgent + io.evitadb.externalApi.observability.agent.ErrorMonitoringAgent true true diff --git a/evita_server/src/main/java/module-info.java b/evita_server/src/main/java/module-info.java index ed8cf4f18..392423717 100644 --- a/evita_server/src/main/java/module-info.java +++ b/evita_server/src/main/java/module-info.java @@ -21,6 +21,7 @@ * limitations under the License. */ +import io.evitadb.externalApi.api.system.ProbesProvider; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.store.spi.CatalogPersistenceServiceFactory; @@ -31,6 +32,7 @@ uses CatalogPersistenceServiceFactory; uses ExternalApiProviderRegistrar; + uses ProbesProvider; opens io.evitadb.server.configuration to com.fasterxml.jackson.databind; exports io.evitadb.server; @@ -52,4 +54,4 @@ requires evita.engine; requires evita.external.api.core; requires ch.qos.logback.classic; -} \ No newline at end of file +} diff --git a/evita_test_support/src/main/resources/evita-configuration.yaml b/evita_test_support/src/main/resources/evita-configuration.yaml index 4c4b673d8..f6f6b087a 100644 --- a/evita_test_support/src/main/resources/evita-configuration.yaml +++ b/evita_test_support/src/main/resources/evita-configuration.yaml @@ -87,7 +87,7 @@ api: readOnly: ${api.endpoints.lab.gui.readOnly:false} preconfiguredConnections: !include ${api.endpoints.lab.gui.preconfiguredConnections:null} observability: - enabled: false + enabled: ${api.endpoints.observability.enabled:false} host: ${api.endpoints.observability.host:localhost:5557} exposedHost: ${api.endpoints.observability.exposedHost:null} tlsEnabled: ${api.endpoints.observability.tlsEnabled:false} From 5eee65ff2c07c0c5c17c64106a9cbaee6e4523ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Fri, 3 May 2024 22:14:57 +0200 Subject: [PATCH 07/24] fix(#550): Provide health check for Docker image Enable agent in entry point. --- docker/entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 065addb72..011057407 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -27,6 +27,7 @@ set -e if [ "$1" = "" ]; then set -x exec java \ + -javaagent:${EVITA_HOME}/bin/${EVITA_JAR_NAME} \ $EVITA_JAVA_OPTS \ -jar "${EVITA_HOME}/bin/${EVITA_JAR_NAME}" \ "-DconfigFile=$EVITA_CONFIG_FILE" \ From 885eb6d396d367a2d489105f8aee8b96fa9dcd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 12:42:20 +0200 Subject: [PATCH 08/24] fix(#550): Provide health check for Docker image Full implementation manually tested. --- .../api/configuration/EvitaConfiguration.java | 4 +- .../proxy/impl/AbstractEntityProxyState.java | 6 +- .../proxy/impl/SealedEntityProxyState.java | 10 +- .../impl/UnsatisfiedDependencyFactory.java | 17 +- .../api/requestResponse/EvitaResponse.java | 6 +- .../requestResponse/data/PricesContract.java | 8 +- .../reference/ReferenceAttributeMutation.java | 6 +- .../data/structure/EntityDecorator.java | 8 +- .../ExistingAssociatedDataBuilder.java | 6 +- .../structure/ExistingAttributesBuilder.java | 6 +- .../data/structure/ExistingEntityBuilder.java | 6 +- .../data/structure/ExistingPricesBuilder.java | 6 +- .../data/structure/Prices.java | 8 +- .../extraResult/FacetSummary.java | 10 +- .../AbstractAttributeSchemaBuilder.java | 6 +- .../builder/AssociatedDataSchemaBuilder.java | 8 +- .../builder/InternalCatalogSchemaBuilder.java | 6 +- .../builder/InternalEntitySchemaBuilder.java | 4 +- .../builder/ReferenceSchemaBuilder.java | 6 +- ...ortableAttributeCompoundSchemaBuilder.java | 6 +- .../CreateAssociatedDataSchemaMutation.java | 6 +- ...AttributeSchemaGloballyUniqueMutation.java | 8 +- .../catalog/ModifyEntitySchemaMutation.java | 6 +- .../ModifyEntitySchemaNameMutation.java | 6 +- .../catalog/RemoveEntitySchemaMutation.java | 6 +- .../CreateReferenceSchemaMutation.java | 4 +- .../api/trace/TracingContextProvider.java | 3 +- .../io/evitadb/dataType/EvitaDataTypes.java | 6 +- .../evitadb/exception/EvitaInternalError.java | 22 +- .../exception/GenericEvitaInternalError.java | 64 +++ .../main/java/io/evitadb/utils/Assert.java | 7 +- .../java/io/evitadb/utils/ClassUtils.java | 6 +- .../io/evitadb/utils/NamingConvention.java | 6 +- .../main/java/io/evitadb/core/Catalog.java | 6 +- .../io/evitadb/core/EntityCollection.java | 6 +- .../java/io/evitadb/core/SessionRegistry.java | 5 +- .../java/io/evitadb/core/cache/CacheEden.java | 4 +- .../io/evitadb/core/query/QueryContext.java | 8 +- .../algebra/attribute/AttributeFormula.java | 6 +- .../FilteredPriceRecords.java | 4 +- ...riceTerminationFormulaWithPriceFilter.java | 4 +- .../PriceIdToEntityIdTranslateFormula.java | 4 +- .../ExtraResultPlanningVisitor.java | 6 +- .../AbstractFacetFormulaGenerator.java | 4 +- .../facet/producer/FacetSummaryProducer.java | 8 +- .../producer/AttributeHistogramComputer.java | 6 +- .../core/query/filter/FilterByVisitor.java | 6 +- .../attribute/AttributeBetweenTranslator.java | 18 +- .../facet/FacetHavingTranslator.java | 6 +- ...riceListCompositionTerminationVisitor.java | 6 +- .../indexSelection/IndexSelectionVisitor.java | 6 +- .../core/query/sort/OrderByVisitor.java | 6 +- .../query/sort/ReferenceOrderByVisitor.java | 4 +- .../EntityPrimaryKeyNaturalTranslator.java | 4 +- .../TransactionTrunkFinalizer.java | 4 +- .../TrunkIncorporationTransactionStage.java | 4 +- .../index/attribute/AttributeIndex.java | 4 +- .../io/evitadb/index/attribute/SortIndex.java | 4 +- .../index/cardinality/CardinalityIndex.java | 4 +- .../evitadb/index/list/TransactionalList.java | 6 +- .../evitadb/index/map/TransactionalMap.java | 4 +- .../ContainerizedLocalMutationExecutor.java | 6 +- .../EntityIndexLocalMutationExecutor.java | 16 +- .../price/PriceListAndCurrencyPriceIndex.java | 4 +- .../io/evitadb/index/range/RangeIndex.java | 4 +- .../evitadb/index/set/TransactionalSet.java | 4 +- .../certificate/ServerCertificateManager.java | 8 +- .../externalApi/configuration/ApiOptions.java | 6 +- .../externalApi/http/ExternalApiServer.java | 12 +- .../CatalogGraphQLSchemaBuildingContext.java | 6 +- .../java/io/evitadb/driver/EvitaClient.java | 7 +- .../io/evitadb/driver/EvitaClientSession.java | 7 +- .../certificate/ClientCertificateManager.java | 6 +- .../trace/ClientTracingContextProvider.java | 4 +- .../grpc/services/EvitaSessionService.java | 22 +- .../externalApi/grpc/utils/GrpcServer.java | 6 +- .../dataType/EvitaDataTypesConverter.java | 12 +- .../grpc/generated/GrpcHealthProblem.java | 370 +----------------- .../requestResponse/EvitaEnumConverter.java | 43 +- .../requestResponse/ResponseConverter.java | 6 +- .../requestResponse/data/EntityConverter.java | 6 +- .../observability/ObservabilityManager.java | 44 ++- .../observability/agent/ErrorMonitor.java | 62 +++ .../agent/ErrorMonitoringAgent.java | 62 ++- .../observability/metric/MetricHandler.java | 9 +- .../metric/ObservabilityProbesDetector.java | 2 + .../builder/CatalogRestBuildingContext.java | 6 +- .../system/SystemProviderRegistrar.java | 44 ++- .../api/exception/EvitaInternalErrorTest.java | 9 +- .../evitadb/api/query/parser/ValueTest.java | 5 +- .../io/evitadb/documentation/JavaDocCopy.java | 17 +- .../evitadb/documentation/csharp/CShell.java | 19 +- .../ExternalApiFunctionTestsSupport.java | 12 +- ...TestMutationResolvingExceptionFactory.java | 7 +- ...ogGraphQLGetEntityQueryFunctionalTest.java | 4 +- ...QLGetUnknownEntityQueryFunctionalTest.java | 6 +- ...istUnknownEntitiesQueryFunctionalTest.java | 23 +- .../client/ClientDataFullDatabaseState.java | 6 +- .../client/state/ClientPageReadState.java | 6 +- .../client/state/ClientSingleReadState.java | 6 +- .../state/ClientTransactionalWriteState.java | 6 +- .../GraphQLArtificialBenchmarkState.java | 4 +- .../RestArtificialBenchmarkState.java | 4 +- .../generators/RandomQueryGenerator.java | 6 +- .../generators/TestDatasetGenerator.java | 6 +- .../java/io/evitadb/spike/TrieIngestion.java | 6 +- .../query/descriptor/ConstraintCreator.java | 27 +- .../descriptor/ConstraintDescriptor.java | 19 +- .../ConstraintDescriptorProvider.java | 6 +- .../query/descriptor/ConstraintProcessor.java | 50 +-- .../evitadb/api/query/parser/Classifier.java | 4 +- .../api/query/parser/ParserExecutor.java | 8 +- .../io/evitadb/api/query/parser/Value.java | 32 +- .../EvitaQLRequireConstraintVisitor.java | 24 +- ...ntityContentRequireCombiningCollector.java | 6 +- .../query/require/HierarchyOfReference.java | 10 +- .../query/require/HierarchyStatistics.java | 23 +- .../query/visitor/ConstraintCloneVisitor.java | 117 +++--- .../query/visitor/QueryPurifierVisitor.java | 98 ++--- .../serializer/DateTimeRangeSerializer.java | 6 +- .../schema/CatalogSchemaStoragePart.java | 6 +- .../serializer/EntitySchemaContext.java | 6 +- .../store/offsetIndex/OffsetIndex.java | 6 +- .../WriteOnlyOffHeapWithFileBackupHandle.java | 5 +- .../model/OffsetIndexRecordTypeRegistry.java | 8 +- .../AbstractFlattenedFormulaSerializer.java | 8 +- .../DefaultCatalogPersistenceService.java | 18 +- ...ultEntityCollectionPersistenceService.java | 4 +- ...rrencySuperIndexStoragePartSerializer.java | 6 +- .../filter/AttributeInRangeSerializer.java | 6 +- .../serializer/ReferenceSchemaSerializer.java | 6 +- .../io/evitadb/test/EvitaTestSupport.java | 10 +- .../test/builder/JsonArrayBuilder.java | 6 +- .../io/evitadb/test/client/ApiClient.java | 6 +- .../io/evitadb/test/client/GraphQLClient.java | 16 +- .../io/evitadb/test/client/RestClient.java | 10 +- .../ConstraintParameterValueResolver.java | 8 +- .../client/query/ObjectJsonSerializer.java | 5 +- .../query/graphql/EntityFetchConverter.java | 93 ++--- .../query/graphql/FacetSummaryConverter.java | 4 +- .../evitadb/test/generator/DataGenerator.java | 6 +- 141 files changed, 956 insertions(+), 1103 deletions(-) create mode 100644 evita_common/src/main/java/io/evitadb/exception/GenericEvitaInternalError.java create mode 100644 evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitor.java diff --git a/evita_api/src/main/java/io/evitadb/api/configuration/EvitaConfiguration.java b/evita_api/src/main/java/io/evitadb/api/configuration/EvitaConfiguration.java index 48aa6e7a3..32572f077 100644 --- a/evita_api/src/main/java/io/evitadb/api/configuration/EvitaConfiguration.java +++ b/evita_api/src/main/java/io/evitadb/api/configuration/EvitaConfiguration.java @@ -24,7 +24,7 @@ package io.evitadb.api.configuration; import io.evitadb.dataType.ClassifierType; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ClassifierUtils; import io.evitadb.utils.NetworkUtils; @@ -105,7 +105,7 @@ public EvitaConfiguration( this.transaction = transaction; this.cache = cache; } catch (IOException ex) { - throw new EvitaInternalError("Unable to access storage directory creation time!", ex); + throw new GenericEvitaInternalError("Unable to access storage directory creation time!", ex); } } diff --git a/evita_api/src/main/java/io/evitadb/api/proxy/impl/AbstractEntityProxyState.java b/evita_api/src/main/java/io/evitadb/api/proxy/impl/AbstractEntityProxyState.java index 1fbcb52db..1e54944e2 100644 --- a/evita_api/src/main/java/io/evitadb/api/proxy/impl/AbstractEntityProxyState.java +++ b/evita_api/src/main/java/io/evitadb/api/proxy/impl/AbstractEntityProxyState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ import io.evitadb.api.requestResponse.data.structure.EntityReference; import io.evitadb.api.requestResponse.data.structure.InitialEntityBuilder; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; import lombok.EqualsAndHashCode; @@ -604,7 +604,7 @@ public T proxy(@Nonnull Class classToImplement, @Nonnull Supplier { - throw new EvitaInternalError("Should not happen - the supplier should provide correct type!"); + throw new GenericEvitaInternalError("Should not happen - the supplier should provide correct type!"); } ); this.proxies.add(proxy); diff --git a/evita_api/src/main/java/io/evitadb/api/proxy/impl/SealedEntityProxyState.java b/evita_api/src/main/java/io/evitadb/api/proxy/impl/SealedEntityProxyState.java index 6ac12f5c2..6aded38ad 100644 --- a/evita_api/src/main/java/io/evitadb/api/proxy/impl/SealedEntityProxyState.java +++ b/evita_api/src/main/java/io/evitadb/api/proxy/impl/SealedEntityProxyState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -41,8 +41,8 @@ import io.evitadb.api.requestResponse.data.structure.InitialReferenceBuilder; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; import lombok.Setter; @@ -156,9 +156,9 @@ public EntityBuilder getEntityBuilderWithMutations(@Nonnull Collection T createEntityProxy( @Nonnull EntityContract entity, @Nonnull Map referencedEntitySchemas ) throws EntityClassInvalidException { + if (UNSATISFIED_DEPENDENCY_EXCEPTION == null) { + UNSATISFIED_DEPENDENCY_EXCEPTION = new UnsatisfiedDependencyException( + "ProxyFactory requires a Proxycian (https://github.com/FgForrest/Proxycian) and " + + "ByteBuddy (https://github.com/raphw/byte-buddy) to be present on the classpath.", + "Required dependency is not available in evitaDB engine, contact developers of the application." + ); + } throw UNSATISFIED_DEPENDENCY_EXCEPTION; } - + } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/EvitaResponse.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/EvitaResponse.java index 2390d4606..b09c2297a 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/EvitaResponse.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/EvitaResponse.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import io.evitadb.api.query.Query; import io.evitadb.api.requestResponse.extraResult.QueryTelemetry; import io.evitadb.dataType.DataChunk; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -128,7 +128,7 @@ public void addExtraResult(@Nonnull EvitaResponseExtraResult extraResult) { public S getExtraResult(Class resultType) { final Object extraResult = this.extraResults.get(resultType); if (extraResult != null && !resultType.isInstance(extraResult)) { - throw new EvitaInternalError("This should never happen!"); + throw new GenericEvitaInternalError("This should never happen!"); } //noinspection unchecked return (S) extraResult; diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/PricesContract.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/PricesContract.java index ea80a8c0b..372bc56cc 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/PricesContract.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/PricesContract.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import io.evitadb.api.requestResponse.data.structure.Entity; import io.evitadb.api.requestResponse.data.structure.Price; import io.evitadb.api.requestResponse.data.structure.Price.PriceKey; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; @@ -140,7 +140,7 @@ static Optional computePriceForSale(@Nonnull Collection throw new EvitaInternalError("Unknown price inner record handling mode: " + innerRecordHandling); + default -> throw new GenericEvitaInternalError("Unknown price inner record handling mode: " + innerRecordHandling); } } @@ -413,7 +413,7 @@ default boolean hasPriceInInterval(@Nonnull BigDecimal from, @Nonnull BigDecimal .orElse(null)); } default -> - throw new EvitaInternalError("Unknown price inner record handling mode: " + getPriceInnerRecordHandling()); + throw new GenericEvitaInternalError("Unknown price inner record handling mode: " + getPriceInnerRecordHandling()); } } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/mutation/reference/ReferenceAttributeMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/mutation/reference/ReferenceAttributeMutation.java index a0a0258cf..6a9a108d3 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/mutation/reference/ReferenceAttributeMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/mutation/reference/ReferenceAttributeMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaEditor.EntitySchemaBuilder; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaEditor.ReferenceSchemaBuilder; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -99,7 +99,7 @@ public Serializable getSkipToken(@Nonnull CatalogSchemaContract catalogSchema, @ public void verifyOrEvolveSchema(@Nonnull CatalogSchemaContract catalogSchema, @Nonnull EntitySchemaBuilder entitySchemaBuilder) throws InvalidMutationException { if (attributeMutation instanceof final AttributeSchemaEvolvingMutation schemaValidatingMutation) { final ReferenceSchemaContract referenceSchema = entitySchemaBuilder.getReference(referenceKey.referenceName()) - .orElseThrow(() -> new EvitaInternalError("Reference to type `" + referenceKey.referenceName() + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Reference to type `" + referenceKey.referenceName() + "` was not found!")); attributeMutation.verifyOrEvolveSchema( catalogSchema, diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java index 308061861..309901df0 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -52,8 +52,8 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; import io.evitadb.dataType.data.ComplexDataObjectConverter; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; import lombok.Getter; @@ -342,7 +342,7 @@ public EntityDecorator( if (referenceSchema == null) { index = i; referenceSchema = entitySchema.getReference(thisReferenceName) - .orElseThrow(() -> new EvitaInternalError("Sanity check!")); + .orElseThrow(() -> new GenericEvitaInternalError("Sanity check!")); entityFetcher = referenceFetcher.getEntityFetcher(referenceSchema); entityGroupFetcher = referenceFetcher.getEntityGroupFetcher(referenceSchema); fetchedReferenceComparator = referenceFetcher.getEntityComparator(referenceSchema); @@ -355,7 +355,7 @@ public EntityDecorator( ); index = i; referenceSchema = entitySchema.getReference(thisReferenceName) - .orElseThrow(() -> new EvitaInternalError("Sanity check!")); + .orElseThrow(() -> new GenericEvitaInternalError("Sanity check!")); entityFetcher = referenceFetcher.getEntityFetcher(referenceSchema); entityGroupFetcher = referenceFetcher.getEntityGroupFetcher(referenceSchema); fetchedReferenceComparator = referenceFetcher.getEntityComparator(referenceSchema); diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAssociatedDataBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAssociatedDataBuilder.java index 87447df4a..177e5ccce 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAssociatedDataBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAssociatedDataBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import io.evitadb.api.requestResponse.schema.AssociatedDataSchemaContract; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.dataType.data.ComplexDataObjectConverter; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; @@ -134,7 +134,7 @@ public void addMutation(@Nonnull AssociatedDataMutation localMutation) { this.associatedDataMutations.put(associatedDataKey, removeAssociatedDataMutation); } } else { - throw new EvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); + throw new GenericEvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); } } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAttributesBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAttributesBuilder.java index 125f6507d..20b58607d 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAttributesBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingAttributesBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,8 @@ import io.evitadb.api.requestResponse.data.mutation.attribute.UpsertAttributeMutation; import io.evitadb.api.requestResponse.schema.AttributeSchemaContract; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import javax.annotation.Nonnull; @@ -224,7 +224,7 @@ public T addMutation(@Nonnull AttributeMutation localMutation) { this.attributeMutations.put(attributeKey, new UpsertAttributeMutation(attributeKey, Objects.requireNonNull(updatedValue.value()))); } } else { - throw new EvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); + throw new GenericEvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); } //noinspection unchecked return (T) this; diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingEntityBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingEntityBuilder.java index f0cc81321..8d4370f24 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingEntityBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingEntityBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; import io.evitadb.dataType.DateTimeRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.Getter; import lombok.experimental.Delegate; @@ -196,7 +196,7 @@ public void addMutation(@Nonnull LocalMutation localMutation) { this.pricesBuilder.addMutation(innerRecordHandlingMutation); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + localMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + localMutation.getClass()); } } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingPricesBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingPricesBuilder.java index ac9760741..cb7c78149 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingPricesBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/ExistingPricesBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ import io.evitadb.api.requestResponse.data.structure.predicate.PriceContractSerializablePredicate; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.dataType.DateTimeRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; import io.evitadb.utils.CollectionUtils; @@ -136,7 +136,7 @@ public void addMutation(@Nonnull PriceMutation localMutation) { final RemovePriceMutation mutation = new RemovePriceMutation(priceKey); this.priceMutations.put(priceKey, mutation); } else { - throw new EvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); + throw new GenericEvitaInternalError("Unknown Evita price mutation: `" + localMutation.getClass() + "`!"); } } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/Prices.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/Prices.java index fd5caffbe..240a6ca00 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/Prices.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/Prices.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ import io.evitadb.api.requestResponse.data.Versioned; import io.evitadb.api.requestResponse.data.structure.Price.PriceKey; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -136,7 +136,7 @@ public Prices( Collectors.toMap( PriceContract::priceKey, Function.identity(), (oldValue, newValue) -> { - throw new EvitaInternalError("Duplicate price key " + oldValue.priceKey()); + throw new GenericEvitaInternalError("Duplicate price key " + oldValue.priceKey()); }, () -> new LinkedHashMap<>(prices.size()) ) @@ -171,7 +171,7 @@ public Prices( Collectors.toMap( PriceContract::priceKey, Function.identity(), (oldValue, newValue) -> { - throw new EvitaInternalError("Duplicate price key " + oldValue.priceKey()); + throw new GenericEvitaInternalError("Duplicate price key " + oldValue.priceKey()); }, () -> new LinkedHashMap<>(prices.size()) ) diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/extraResult/FacetSummary.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/extraResult/FacetSummary.java index d697e7fdc..8048d7a5f 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/extraResult/FacetSummary.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/extraResult/FacetSummary.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import io.evitadb.api.requestResponse.schema.NamedSchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; import io.evitadb.dataType.EvitaDataTypes; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.PrettyPrintable; import lombok.AccessLevel; @@ -105,7 +105,7 @@ public FacetSummary(@Nonnull Map> refer it -> it.getGroupEntity().getPrimaryKey(), Function.identity(), (o, o2) -> { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected duplicate facet group statistics." ); }, @@ -140,7 +140,7 @@ public FacetSummary(@Nonnull Collection referenceStatistic group -> group.getGroupEntity().getPrimaryKey(), Function.identity(), (o, o2) -> { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "There is already facet group for reference `" + it.getKey() + "` with id `" + o.getGroupEntity().getPrimaryKey() + "`." ); @@ -563,7 +563,7 @@ public FacetGroupStatistics( it -> it.getFacetEntity().getPrimaryKey(), Function.identity(), (facetStatistics1, facetStatistics2) -> { - throw new EvitaInternalError("Statistics are expected to be unique!"); + throw new GenericEvitaInternalError("Statistics are expected to be unique!"); }, LinkedHashMap::new ) diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AbstractAttributeSchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AbstractAttributeSchemaBuilder.java index 4c612b09a..e24dbbb83 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AbstractAttributeSchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AbstractAttributeSchemaBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ import io.evitadb.api.requestResponse.schema.mutation.attribute.SetAttributeSchemaUniqueMutation; import io.evitadb.dataType.EvitaDataTypes; import io.evitadb.dataType.Predecessor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; @@ -394,7 +394,7 @@ public S toInstance() { final AttributeSchemaMutation mutation = attributeMutations.get(i); currentSchema = mutation.mutate(null, currentSchema, getAttributeSchemaType()); if (currentSchema == null) { - throw new EvitaInternalError("Attribute unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Attribute unexpectedly removed from inside!"); } } validate(currentSchema); diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AssociatedDataSchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AssociatedDataSchemaBuilder.java index 59d083dd3..c03b9d1cc 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AssociatedDataSchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/AssociatedDataSchemaBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ import io.evitadb.api.requestResponse.schema.mutation.associatedData.ModifyAssociatedDataSchemaDescriptionMutation; import io.evitadb.api.requestResponse.schema.mutation.associatedData.SetAssociatedDataSchemaLocalizedMutation; import io.evitadb.api.requestResponse.schema.mutation.associatedData.SetAssociatedDataSchemaNullableMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.experimental.Delegate; import javax.annotation.Nonnull; @@ -232,7 +232,7 @@ public AssociatedDataSchemaContract toInstance() { final EntitySchemaMutation mutation = this.mutations.get(i); currentSchema = ((AssociatedDataSchemaMutation) mutation).mutate(currentSchema); if (currentSchema == null) { - throw new EvitaInternalError("Attribute unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Attribute unexpectedly removed from inside!"); } } this.updatedSchema = currentSchema; @@ -253,4 +253,4 @@ public Collection toMutation() { return this.mutations; } -} \ No newline at end of file +} diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalCatalogSchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalCatalogSchemaBuilder.java index e4608f98b..4a629aedd 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalCatalogSchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalCatalogSchemaBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyCatalogSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.catalog.MutationEntitySchemaAccessor; import io.evitadb.dataType.EvitaDataTypes; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.NamingConvention; import lombok.experimental.Delegate; @@ -337,7 +337,7 @@ public CatalogSchemaContract toInstance() { final LocalCatalogSchemaMutation mutation = this.mutations.get(i); final CatalogSchemaWithImpactOnEntitySchemas mutationImpact = mutation.mutate(currentSchema, updatedEntitySchemaAccessor); if (mutationImpact == null || mutationImpact.updatedCatalogSchema() == null) { - throw new EvitaInternalError("Catalog schema unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Catalog schema unexpectedly removed from inside!"); } currentSchema = mutationImpact.updatedCatalogSchema(); } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalEntitySchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalEntitySchemaBuilder.java index 474f4e2f4..838dbece9 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalEntitySchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/InternalEntitySchemaBuilder.java @@ -45,7 +45,7 @@ import io.evitadb.dataType.ClassifierType; import io.evitadb.dataType.ComplexDataObject; import io.evitadb.dataType.EvitaDataTypes; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ClassifierUtils; import io.evitadb.utils.NamingConvention; import lombok.experimental.Delegate; @@ -736,7 +736,7 @@ public EntitySchemaContract toInstance() { final EntitySchemaMutation mutation = this.mutations.get(i); currentSchema = mutation.mutate(catalogSchemaAccessor.get(), currentSchema); if (currentSchema == null) { - throw new EvitaInternalError("Catalog schema unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Catalog schema unexpectedly removed from inside!"); } } this.updatedSchema = currentSchema; diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/ReferenceSchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/ReferenceSchemaBuilder.java index 2ed87e9d4..88c49873d 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/ReferenceSchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/ReferenceSchemaBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ import io.evitadb.api.requestResponse.schema.mutation.reference.SetReferenceSchemaFacetedMutation; import io.evitadb.api.requestResponse.schema.mutation.reference.SetReferenceSchemaIndexedMutation; import io.evitadb.api.requestResponse.schema.mutation.sortableAttributeCompound.RemoveSortableAttributeCompoundSchemaMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.experimental.Delegate; @@ -457,7 +457,7 @@ public ReferenceSchemaContract toInstance() { final EntitySchemaMutation mutation = this.mutations.get(i); currentSchema = ((ReferenceSchemaMutation) mutation).mutate(entitySchema, currentSchema); if (currentSchema == null) { - throw new EvitaInternalError("Attribute unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Attribute unexpectedly removed from inside!"); } } this.updatedSchema = currentSchema; diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/SortableAttributeCompoundSchemaBuilder.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/SortableAttributeCompoundSchemaBuilder.java index c195f9723..941ffd6e2 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/SortableAttributeCompoundSchemaBuilder.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/builder/SortableAttributeCompoundSchemaBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ import io.evitadb.api.requestResponse.schema.mutation.sortableAttributeCompound.CreateSortableAttributeCompoundSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.sortableAttributeCompound.ModifySortableAttributeCompoundSchemaDeprecationNoticeMutation; import io.evitadb.api.requestResponse.schema.mutation.sortableAttributeCompound.ModifySortableAttributeCompoundSchemaDescriptionMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.experimental.Delegate; import javax.annotation.Nonnull; @@ -185,7 +185,7 @@ public SortableAttributeCompoundSchemaContract toInstance() { final EntitySchemaMutation mutation = this.mutations.get(i); currentSchema = ((SortableAttributeCompoundSchemaMutation) mutation).mutate(entitySchema, referenceSchema, currentSchema); if (currentSchema == null) { - throw new EvitaInternalError("Attribute unexpectedly removed from inside!"); + throw new GenericEvitaInternalError("Attribute unexpectedly removed from inside!"); } } this.updatedSchema = currentSchema; diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/associatedData/CreateAssociatedDataSchemaMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/associatedData/CreateAssociatedDataSchemaMutation.java index cd43b5bb9..0456ca906 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/associatedData/CreateAssociatedDataSchemaMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/associatedData/CreateAssociatedDataSchemaMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ import io.evitadb.dataType.ComplexDataObject; import io.evitadb.dataType.EvitaDataTypes; import io.evitadb.dataType.Predecessor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ClassifierUtils; import lombok.EqualsAndHashCode; @@ -106,7 +106,7 @@ public MutationCombinationResult combineWith(@Nonnull Cata if (existingMutation instanceof RemoveAssociatedDataSchemaMutation removeAssociatedDataSchema && Objects.equals(removeAssociatedDataSchema.getName(), name)) { final AssociatedDataSchemaContract createdVersion = mutate(null); final AssociatedDataSchemaContract existingVersion = currentEntitySchema.getAssociatedData(name) - .orElseThrow(() -> new EvitaInternalError("Sanity check!")); + .orElseThrow(() -> new GenericEvitaInternalError("Sanity check!")); return new MutationCombinationResult<>( null, diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/attribute/SetAttributeSchemaGloballyUniqueMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/attribute/SetAttributeSchemaGloballyUniqueMutation.java index 4029c6d36..f31229cec 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/attribute/SetAttributeSchemaGloballyUniqueMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/attribute/SetAttributeSchemaGloballyUniqueMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ import io.evitadb.api.requestResponse.schema.mutation.CombinableCatalogSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.CombinableEntitySchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -102,7 +102,7 @@ public S mutate(@Nullable CatalogSchemaContr globalAttributeSchema.getIndexedDecimalPlaces() ); } else { - throw new EvitaInternalError("Unexpected input!"); + throw new GenericEvitaInternalError("Unexpected input!"); } } @@ -129,4 +129,4 @@ public String toString() { "uniqueGlobally=" + uniqueGlobally; } -} \ No newline at end of file +} diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaMutation.java index 4d1f9bdcc..da5e60f25 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import io.evitadb.api.requestResponse.schema.mutation.CombinableCatalogSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.EntitySchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -98,7 +98,7 @@ public CatalogSchemaWithImpactOnEntitySchemas mutate(@Nullable CatalogSchemaCont .ifPresentOrElse( mutationEntitySchemaAccessor::addUpsertedEntitySchema, () -> { - throw new EvitaInternalError("Entity schema not found: " + entityType); + throw new GenericEvitaInternalError("Entity schema not found: " + entityType); } ); } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaNameMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaNameMutation.java index 5ac2bf987..1951f9127 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaNameMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/ModifyEntitySchemaNameMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ import io.evitadb.api.requestResponse.schema.mutation.CombinableEntitySchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.EntitySchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.NamingConvention; import lombok.AllArgsConstructor; @@ -73,7 +73,7 @@ public CatalogSchemaWithImpactOnEntitySchemas mutate(@Nullable CatalogSchemaCont .ifPresentOrElse( it -> mutationEntitySchemaAccessor.replaceEntitySchema(name, it), () -> { - throw new EvitaInternalError("Entity schema not found: " + name); + throw new GenericEvitaInternalError("Entity schema not found: " + name); } ); } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/RemoveEntitySchemaMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/RemoveEntitySchemaMutation.java index facce3856..327c33a1f 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/RemoveEntitySchemaMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/catalog/RemoveEntitySchemaMutation.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ import io.evitadb.api.requestResponse.schema.dto.EntitySchemaProvider; import io.evitadb.api.requestResponse.schema.mutation.CatalogSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -65,7 +65,7 @@ public CatalogSchemaWithImpactOnEntitySchemas mutate(@Nullable CatalogSchemaCont .ifPresentOrElse( it -> mutationEntitySchemaAccessor.removeEntitySchema(name), () -> { - throw new EvitaInternalError("Entity schema not found: " + name); + throw new GenericEvitaInternalError("Entity schema not found: " + name); } ); } diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/reference/CreateReferenceSchemaMutation.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/reference/CreateReferenceSchemaMutation.java index c2bb17c50..3512ab002 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/reference/CreateReferenceSchemaMutation.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/schema/mutation/reference/CreateReferenceSchemaMutation.java @@ -39,7 +39,7 @@ import io.evitadb.api.requestResponse.schema.mutation.attribute.RemoveAttributeSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.sortableAttributeCompound.RemoveSortableAttributeCompoundSchemaMutation; import io.evitadb.dataType.ClassifierType; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ClassifierUtils; import lombok.EqualsAndHashCode; @@ -115,7 +115,7 @@ public MutationCombinationResult combineWith(@Nonnull Cata if (existingMutation instanceof RemoveReferenceSchemaMutation removeReferenceMutation && Objects.equals(removeReferenceMutation.getName(), name)) { final ReferenceSchemaContract createdVersion = mutate(currentEntitySchema, null); final ReferenceSchemaContract existingVersion = currentEntitySchema.getReference(name) - .orElseThrow(() -> new EvitaInternalError("Sanity check!")); + .orElseThrow(() -> new GenericEvitaInternalError("Sanity check!")); return new MutationCombinationResult<>( null, diff --git a/evita_api/src/main/java/io/evitadb/api/trace/TracingContextProvider.java b/evita_api/src/main/java/io/evitadb/api/trace/TracingContextProvider.java index 3b64d543d..ae92f9965 100644 --- a/evita_api/src/main/java/io/evitadb/api/trace/TracingContextProvider.java +++ b/evita_api/src/main/java/io/evitadb/api/trace/TracingContextProvider.java @@ -24,6 +24,7 @@ package io.evitadb.api.trace; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import java.util.List; @@ -47,7 +48,7 @@ public class TracingContextProvider { .map(Provider::get) .toList(); if (collectedContexts.size() > 1) { - throw new EvitaInternalError("There are multiple registered implementations of TracingContext."); + throw new GenericEvitaInternalError("There are multiple registered implementations of TracingContext."); } if (collectedContexts.size() == 1) { TRACING_CONTEXT = collectedContexts.stream().findFirst().get(); diff --git a/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java b/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java index 6948e22fd..47fb3469c 100644 --- a/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java +++ b/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import io.evitadb.dataType.data.DataItem; import io.evitadb.dataType.exception.InconvertibleDataTypeException; import io.evitadb.dataType.exception.UnsupportedDataTypeException; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.MemoryMeasuringConstants; import io.evitadb.utils.NumberUtils; @@ -706,7 +706,7 @@ else if (value instanceof Number) { } else if (value instanceof Predecessor) { return value.toString(); } else if (value == null) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Null argument value should never ever happen. Null values are excluded in constructor of the class!" ); } diff --git a/evita_common/src/main/java/io/evitadb/exception/EvitaInternalError.java b/evita_common/src/main/java/io/evitadb/exception/EvitaInternalError.java index 3f646f62b..81a47dc8f 100644 --- a/evita_common/src/main/java/io/evitadb/exception/EvitaInternalError.java +++ b/evita_common/src/main/java/io/evitadb/exception/EvitaInternalError.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -36,20 +36,12 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2022 */ -public class EvitaInternalError extends IllegalStateException implements EvitaError { +public abstract class EvitaInternalError extends IllegalStateException implements EvitaError { @Serial private static final long serialVersionUID = -1040832658535384105L; @Getter private final String publicMessage; @Getter private final String errorCode; - /** - * Method is targeted to be used on the client. - */ - @Nonnull - public static EvitaInternalError createExceptionWithErrorCode(@Nonnull String publicMessage, @Nonnull String errorCode) { - return new EvitaInternalError(publicMessage, publicMessage, errorCode); - } - - public EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage) { + protected EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage) { super(privateMessage); this.publicMessage = publicMessage; final StackTraceElement stackTraceElement = getProperStackLine(); @@ -59,11 +51,11 @@ public EvitaInternalError(@Nonnull String privateMessage, @Nonnull String public stackTraceElement.getLineNumber(); } - public EvitaInternalError(@Nonnull String publicMessage) { + protected EvitaInternalError(@Nonnull String publicMessage) { this(publicMessage, publicMessage); } - public EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull Throwable cause) { + protected EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull Throwable cause) { super(privateMessage, cause); this.publicMessage = publicMessage; final StackTraceElement stackTraceElement = getProperStackLine(); @@ -72,11 +64,11 @@ public EvitaInternalError(@Nonnull String privateMessage, @Nonnull String public stackTraceElement.getLineNumber(); } - public EvitaInternalError(@Nonnull String publicMessage, @Nonnull Throwable cause) { + protected EvitaInternalError(@Nonnull String publicMessage, @Nonnull Throwable cause) { this(publicMessage, publicMessage, cause); } - private EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull String errorCode) { + protected EvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull String errorCode) { super(privateMessage); this.publicMessage = publicMessage; this.errorCode = errorCode; diff --git a/evita_common/src/main/java/io/evitadb/exception/GenericEvitaInternalError.java b/evita_common/src/main/java/io/evitadb/exception/GenericEvitaInternalError.java new file mode 100644 index 000000000..b50bc3d53 --- /dev/null +++ b/evita_common/src/main/java/io/evitadb/exception/GenericEvitaInternalError.java @@ -0,0 +1,64 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.exception; + +import javax.annotation.Nonnull; +import java.io.Serial; + +/** + * Uncategorized evitaDB internal error. All details must be passed in the messages. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public class GenericEvitaInternalError extends EvitaInternalError { + @Serial private static final long serialVersionUID = 7967637521505334780L; + + /** + * Method is targeted to be used on the client. + */ + @Nonnull + public static EvitaInternalError createExceptionWithErrorCode(@Nonnull String publicMessage, @Nonnull String errorCode) { + return new GenericEvitaInternalError(publicMessage, publicMessage, errorCode); + } + + public GenericEvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage) { + super(privateMessage, publicMessage); + } + + public GenericEvitaInternalError(@Nonnull String publicMessage) { + super(publicMessage); + } + + public GenericEvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull Throwable cause) { + super(privateMessage, publicMessage, cause); + } + + public GenericEvitaInternalError(@Nonnull String publicMessage, @Nonnull Throwable cause) { + super(publicMessage, cause); + } + + public GenericEvitaInternalError(@Nonnull String privateMessage, @Nonnull String publicMessage, @Nonnull String errorCode) { + super(privateMessage, publicMessage, errorCode); + } +} diff --git a/evita_common/src/main/java/io/evitadb/utils/Assert.java b/evita_common/src/main/java/io/evitadb/utils/Assert.java index 5e15135f1..417182d9a 100644 --- a/evita_common/src/main/java/io/evitadb/utils/Assert.java +++ b/evita_common/src/main/java/io/evitadb/utils/Assert.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import java.util.function.Supplier; @@ -85,7 +86,7 @@ public static void notNull(Object object, @Nonnull InvalidUsageExceptionFactory< */ public static void isPremiseValid(boolean theValue, @Nonnull String message) { if (!theValue) { - throw new EvitaInternalError(message); + throw new GenericEvitaInternalError(message); } } @@ -94,7 +95,7 @@ public static void isPremiseValid(boolean theValue, @Nonnull String message) { */ public static void isPremiseValid(boolean theValue, @Nonnull Supplier message) { if (!theValue) { - throw new EvitaInternalError(message.get()); + throw new GenericEvitaInternalError(message.get()); } } diff --git a/evita_common/src/main/java/io/evitadb/utils/ClassUtils.java b/evita_common/src/main/java/io/evitadb/utils/ClassUtils.java index 54100d1c7..89a6b9eba 100644 --- a/evita_common/src/main/java/io/evitadb/utils/ClassUtils.java +++ b/evita_common/src/main/java/io/evitadb/utils/ClassUtils.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.utils; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.function.ExceptionRethrowingSupplier; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -82,7 +82,7 @@ public static Optional whenPresentOnClasspath(@Nonnull String className, try { return of(factory.get()); } catch (Exception e) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Failed to evaluate lambda expression when a class `" + className + "` is present on classpath.", "Internal error.", e ); diff --git a/evita_common/src/main/java/io/evitadb/utils/NamingConvention.java b/evita_common/src/main/java/io/evitadb/utils/NamingConvention.java index d6c7d470e..9e8f131ef 100644 --- a/evita_common/src/main/java/io/evitadb/utils/NamingConvention.java +++ b/evita_common/src/main/java/io/evitadb/utils/NamingConvention.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.utils; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import java.util.Arrays; @@ -86,7 +86,7 @@ public static Map generate(@Nonnull String name) { Collectors.toMap( Function.identity(), it -> StringUtils.toSpecificCase(name, it), - (s, s2) -> { throw new EvitaInternalError("Should not ever occur!"); }, + (s, s2) -> { throw new GenericEvitaInternalError("Should not ever occur!"); }, LinkedHashMap::new ) ) diff --git a/evita_engine/src/main/java/io/evitadb/core/Catalog.java b/evita_engine/src/main/java/io/evitadb/core/Catalog.java index 41ced301d..b0adc50e9 100644 --- a/evita_engine/src/main/java/io/evitadb/core/Catalog.java +++ b/evita_engine/src/main/java/io/evitadb/core/Catalog.java @@ -88,7 +88,7 @@ import io.evitadb.core.transaction.stage.TrunkIncorporationTransactionStage; import io.evitadb.core.transaction.stage.WalAppendingTransactionStage; import io.evitadb.dataType.PaginatedList; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.CatalogIndex; import io.evitadb.index.CatalogIndexKey; import io.evitadb.index.EntityIndex; @@ -1257,7 +1257,7 @@ private TrunkIncorporationTransactionStage getTrunkIncorporationStage() { } } } - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "TrunkIncorporationTransactionStage is not present in the transactional pipeline!" ); } @@ -1583,7 +1583,7 @@ public CatalogIndex getIndexIfExists(@Nonnull CatalogIndexKey entityIndexKey) { */ @Override public void removeIndex(@Nonnull CatalogIndexKey entityIndexKey) { - throw new EvitaInternalError("Global catalog index is not expected to be removed!"); + throw new GenericEvitaInternalError("Global catalog index is not expected to be removed!"); } } diff --git a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java index 014eb9738..df9e98525 100644 --- a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java +++ b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java @@ -93,8 +93,8 @@ import io.evitadb.core.transaction.memory.TransactionalObjectVersion; import io.evitadb.core.transaction.stage.mutation.VerifiedEntityRemoveMutation; import io.evitadb.core.transaction.stage.mutation.VerifiedEntityUpsertMutation; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.EntityIndexKey; import io.evitadb.index.EntityIndexType; @@ -1246,7 +1246,7 @@ private TransactionalMap loadIndexes(@Nonnull Entit entityHeader.globalEntityIndexId(), this::getInternalSchema, () -> { - throw new EvitaInternalError("Global index is currently loading!"); + throw new GenericEvitaInternalError("Global index is currently loading!"); }, this::getPriceSuperIndex ); @@ -1640,7 +1640,7 @@ public void removeIndex(@Nonnull EntityIndexKey entityIndexKey) { entityIndexKey, EntityCollection.this.indexes::remove ); if (removedIndex == null) { - throw new EvitaInternalError("Entity index for key " + entityIndexKey + " doesn't exists!"); + throw new GenericEvitaInternalError("Entity index for key " + entityIndexKey + " doesn't exists!"); } else { ofNullable(getTransactionalLayerMaintainer()) .ifPresent(removedIndex::removeTransactionalMemoryOfReferencedProducers); diff --git a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java index 627180e39..27810f103 100644 --- a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java +++ b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java @@ -33,6 +33,7 @@ import io.evitadb.dataType.EvitaDataTypes; import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.CollectionUtils; import lombok.RequiredArgsConstructor; @@ -216,7 +217,7 @@ public Object invoke(Object proxy, Method method, Object[] args) { "Unexpected internal Evita error occurred: " + ex.getCause().getMessage(), targetException == null ? ex : targetException ); - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected internal Evita error occurred: " + ex.getCause().getMessage(), "Unexpected internal Evita error occurred.", targetException == null ? ex : targetException @@ -224,7 +225,7 @@ public Object invoke(Object proxy, Method method, Object[] args) { } } catch (Throwable ex) { log.error("Unexpected system error occurred: " + ex.getMessage(), ex); - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected system error occurred: " + ex.getMessage(), "Unexpected system error occurred.", ex diff --git a/evita_engine/src/main/java/io/evitadb/core/cache/CacheEden.java b/evita_engine/src/main/java/io/evitadb/core/cache/CacheEden.java index 9532f148a..9b70bc96c 100644 --- a/evita_engine/src/main/java/io/evitadb/core/cache/CacheEden.java +++ b/evita_engine/src/main/java/io/evitadb/core/cache/CacheEden.java @@ -37,7 +37,7 @@ import io.evitadb.core.query.response.TransactionalDataRelatedStructure; import io.evitadb.core.query.sort.CacheableSorter; import io.evitadb.dataType.array.CompositeLongArray; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import net.openhft.hashing.LongHashFunction; @@ -180,7 +180,7 @@ public S getCachedRecord( } else if (computationalObject instanceof final EntityComputationalObjectAdapter entityWrapper) { return fetchAndCacheEntity(recordHash, cachedRecord, hashFunction, entityWrapper); } else { - throw new EvitaInternalError("Unexpected object in cache `" + computationalObject.getClass() + "`!"); + throw new GenericEvitaInternalError("Unexpected object in cache `" + computationalObject.getClass() + "`!"); } } } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java b/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java index 885358395..96a583298 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java @@ -66,7 +66,7 @@ import io.evitadb.core.query.extraResult.ExtraResultCacheAccessor; import io.evitadb.core.query.extraResult.translator.facet.producer.FilteringFormulaPredicate; import io.evitadb.dataType.array.CompositeIntArray; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.function.TriFunction; import io.evitadb.index.CatalogIndexKey; import io.evitadb.index.EntityIndex; @@ -322,7 +322,7 @@ public void executeInDryRun(@Nonnull Runnable lambda) { os.writeObject(new Random()); this.frozenRandom = bos.toByteArray(); } catch (IOException e) { - throw new EvitaInternalError("Unexpected error during debug mode evaluation!", e); + throw new GenericEvitaInternalError("Unexpected error during debug mode evaluation!", e); } lambda.run(); } finally { @@ -349,7 +349,7 @@ public Random getRandom() { try (var is = new ObjectInputStream(new ByteArrayInputStream(it))) { return (Random) is.readObject(); } catch (IOException | ClassNotFoundException e) { - throw new EvitaInternalError("Unexpected error during debug mode evaluation!", e); + throw new GenericEvitaInternalError("Unexpected error during debug mode evaluation!", e); } }) .orElseGet(ThreadLocalRandom::current); @@ -805,7 +805,7 @@ public Optional getGlobalEntityIndexIfExists() { public GlobalEntityIndex getGlobalEntityIndex() { return getGlobalEntityIndexIfExists() .map(GlobalEntityIndex.class::cast) - .orElseThrow(() -> new EvitaInternalError("Global index of entity unexpectedly not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Global index of entity unexpectedly not found!")); } /** diff --git a/evita_engine/src/main/java/io/evitadb/core/query/algebra/attribute/AttributeFormula.java b/evita_engine/src/main/java/io/evitadb/core/query/algebra/attribute/AttributeFormula.java index 61f190db9..4319b588a 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/algebra/attribute/AttributeFormula.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/algebra/attribute/AttributeFormula.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ import io.evitadb.core.query.algebra.AbstractFormula; import io.evitadb.core.query.algebra.Formula; import io.evitadb.core.query.algebra.prefetch.RequirementsDefiner; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.EmptyBitmap; import io.evitadb.utils.Assert; @@ -121,7 +121,7 @@ protected Bitmap computeInternal() { } else if (innerFormulas.length == 1) { return innerFormulas[0].compute(); } else { - throw new EvitaInternalError(ERROR_SINGLE_FORMULA_EXPECTED); + throw new GenericEvitaInternalError(ERROR_SINGLE_FORMULA_EXPECTED); } } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/filteredPriceRecords/FilteredPriceRecords.java b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/filteredPriceRecords/FilteredPriceRecords.java index 6c8e77b53..076d4b955 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/filteredPriceRecords/FilteredPriceRecords.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/filteredPriceRecords/FilteredPriceRecords.java @@ -32,7 +32,7 @@ import io.evitadb.dataType.array.CompositeIntArray; import io.evitadb.dataType.array.CompositeObjectArray; import io.evitadb.dataType.iterator.BatchArrayIterator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.RoaringBitmapBackedBitmap; import io.evitadb.index.iterator.RoaringBitmapBatchArrayIterator; @@ -126,7 +126,7 @@ static FilteredPriceRecords createFromFormulas( lazyEvaluatedEntityPriceRecords.get() ); } else { - throw new EvitaInternalError("Both resolved and lazy price records are present!"); + throw new GenericEvitaInternalError("Both resolved and lazy price records are present!"); } } } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/termination/PlainPriceTerminationFormulaWithPriceFilter.java b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/termination/PlainPriceTerminationFormulaWithPriceFilter.java index 6cddfa272..119921326 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/termination/PlainPriceTerminationFormulaWithPriceFilter.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/termination/PlainPriceTerminationFormulaWithPriceFilter.java @@ -42,7 +42,7 @@ import io.evitadb.core.query.extraResult.translator.histogram.producer.PriceHistogramProducer; import io.evitadb.dataType.array.CompositeObjectArray; import io.evitadb.dataType.iterator.BatchArrayIterator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.EmptyBitmap; @@ -290,7 +290,7 @@ protected Bitmap computeInternal() { } if (noPriceFoundAtAll) { - throw new EvitaInternalError("No price found for entity with id " + entityId + "!"); + throw new GenericEvitaInternalError("No price found for entity with id " + entityId + "!"); } } } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/translate/PriceIdToEntityIdTranslateFormula.java b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/translate/PriceIdToEntityIdTranslateFormula.java index d006330b3..a42c3df0d 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/translate/PriceIdToEntityIdTranslateFormula.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/algebra/price/translate/PriceIdToEntityIdTranslateFormula.java @@ -38,7 +38,7 @@ import io.evitadb.core.query.algebra.utils.visitor.FormulaFinder; import io.evitadb.core.query.algebra.utils.visitor.FormulaFinder.LookUp; import io.evitadb.dataType.array.CompositeObjectArray; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.EmptyBitmap; @@ -207,7 +207,7 @@ protected Bitmap computeInternal() { } if (!priceIdBitmap.isEmpty()) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "These prices weren't translated to entity id: " + Arrays.toString(priceIdBitmap.getArray()) ); } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/ExtraResultPlanningVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/ExtraResultPlanningVisitor.java index 08bc69db7..a932f9f07 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/ExtraResultPlanningVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/ExtraResultPlanningVisitor.java @@ -81,7 +81,7 @@ import io.evitadb.core.query.sort.OrderByVisitor; import io.evitadb.core.query.sort.Sorter; import io.evitadb.core.query.sort.attribute.translator.EntityAttributeExtractor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.GlobalEntityIndex; import io.evitadb.utils.ArrayUtils; @@ -480,7 +480,7 @@ public void visit(@Nonnull Constraint constraint) { registerProducer(translator.apply(requireConstraint, this)); } else { // sanity check only - throw new EvitaInternalError("Should never happen"); + throw new GenericEvitaInternalError("Should never happen"); } } else { @SuppressWarnings("unchecked") final RequireConstraintTranslator translator = @@ -535,7 +535,7 @@ public final T executeInContext( @Nonnull public ProcessingScope getProcessingScope() { if (isScopeEmpty()) { - throw new EvitaInternalError("Scope should never be empty"); + throw new GenericEvitaInternalError("Scope should never be empty"); } else { return scope.peek(); } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/AbstractFacetFormulaGenerator.java b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/AbstractFacetFormulaGenerator.java index f48da2799..024a0d7b8 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/AbstractFacetFormulaGenerator.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/AbstractFacetFormulaGenerator.java @@ -46,7 +46,7 @@ import io.evitadb.core.query.filter.FilterByVisitor; import io.evitadb.core.query.filter.translator.facet.FacetHavingTranslator; import io.evitadb.dataType.array.CompositeObjectArray; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.EmptyBitmap; @@ -680,7 +680,7 @@ public void visit(@Nonnull Formula formula) { if (!targetFound) { if (formula instanceof MutableFormula mutableFormula) { if (targetFound) { - throw new EvitaInternalError("Expected single MutableFormula in the formula tree!"); + throw new GenericEvitaInternalError("Expected single MutableFormula in the formula tree!"); } else { targetFound = true; mutableFormula.setDelegate(formulaToReplaceSupplier.get()); diff --git a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/FacetSummaryProducer.java b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/FacetSummaryProducer.java index 75f012205..791b28223 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/FacetSummaryProducer.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/facet/producer/FacetSummaryProducer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ import io.evitadb.core.query.extraResult.ExtraResultProducer; import io.evitadb.core.query.sort.NestedContextSorter; import io.evitadb.core.query.sort.utils.SortUtils; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.RoaringBitmapBackedBitmap; @@ -362,7 +362,7 @@ private static Map fetchGroups(@Nonnull Entry new EvitaInternalError(ERROR_SANITY_CHECK)); + .orElseThrow(() -> new GenericEvitaInternalError(ERROR_SANITY_CHECK)); return Arrays.stream( groupAcc.getFacetSummaryRequest() .getGroupEntityFetcher(groupAcc.getReferenceSchema()) @@ -398,7 +398,7 @@ private static Map fetchFacetEntities(@Nonnull Entry< final GroupAccumulator groupAcc = entry.getValue() .stream() .findFirst() - .orElseThrow(() -> new EvitaInternalError(ERROR_SANITY_CHECK)); + .orElseThrow(() -> new GenericEvitaInternalError(ERROR_SANITY_CHECK)); return Arrays.stream( groupAcc.getFacetSummaryRequest() .getFacetEntityFetcher(groupAcc.getReferenceSchema()) diff --git a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/histogram/producer/AttributeHistogramComputer.java b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/histogram/producer/AttributeHistogramComputer.java index e64991595..973d62e32 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/histogram/producer/AttributeHistogramComputer.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/extraResult/translator/histogram/producer/AttributeHistogramComputer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import io.evitadb.core.query.extraResult.translator.histogram.cache.FlattenedHistogramComputer; import io.evitadb.core.query.extraResult.translator.histogram.producer.AttributeHistogramProducer.AttributeHistogramRequest; import io.evitadb.core.query.response.TransactionalDataRelatedStructure; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.attribute.FilterIndex; import io.evitadb.index.invertedIndex.InvertedIndexSubSet; import io.evitadb.index.invertedIndex.ValueToRecordBitmap; @@ -204,7 +204,7 @@ private static > ToIntFunction createNumberToIntegerC .scaleByPowerOfTen(histogramRequest.getDecimalPlaces()) .intValue(); } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unsupported histogram number type: " + histogramRequest.attributeSchema().getType() + ", supported are byte, short, int. Number types long and BigDecimal are allowed as long as their " + "fit into an integer range!" diff --git a/evita_engine/src/main/java/io/evitadb/core/query/filter/FilterByVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/filter/FilterByVisitor.java index 079e12a0d..148d6cc5e 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/filter/FilterByVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/filter/FilterByVisitor.java @@ -79,7 +79,7 @@ import io.evitadb.core.query.indexSelection.TargetIndexes; import io.evitadb.core.query.response.TransactionalDataRelatedStructure.CalculationContext; import io.evitadb.core.query.sort.attribute.translator.EntityNestedQueryComparator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.function.TriFunction; import io.evitadb.index.CatalogIndex; import io.evitadb.index.CatalogIndexKey; @@ -493,7 +493,7 @@ public void visit(@Nonnull Constraint constraint) { constraintFormula = translator.translate(filterConstraint, this); } else { // sanity check only - throw new EvitaInternalError("Should never happen"); + throw new GenericEvitaInternalError("Should never happen"); } // if current query is FilterBy we know we're at the top of the filtering query tree @@ -533,7 +533,7 @@ public T registerFormulaPostProcessorIfNotPrese public ProcessingScope> getProcessingScope() { final ProcessingScope> processingScope; if (scope.isEmpty()) { - throw new EvitaInternalError("Scope should never be empty"); + throw new GenericEvitaInternalError("Scope should never be empty"); } else { processingScope = scope.peek(); isPremiseValid(processingScope != null, "Scope could never be null!"); diff --git a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/attribute/AttributeBetweenTranslator.java b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/attribute/AttributeBetweenTranslator.java index 68e60f7fd..c02881deb 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/attribute/AttributeBetweenTranslator.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/attribute/AttributeBetweenTranslator.java @@ -46,7 +46,7 @@ import io.evitadb.dataType.NumberRange; import io.evitadb.dataType.Range; import io.evitadb.dataType.ShortNumberRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -109,7 +109,7 @@ public Formula translate(@Nonnull AttributeBetween filterConstraint, @Nonnull Fi comparableTo = getOffsetDateTimeComparable(toTargetType(to, OffsetDateTime.class), Long.MAX_VALUE); requestedPredicate = null; } else { - throw new EvitaInternalError("Unexpected Range type!"); + throw new GenericEvitaInternalError("Unexpected Range type!"); } filteringFormula = new AttributeFormula( attributeDefinition.isLocalized() ? @@ -198,7 +198,7 @@ private static AttributeBitmapFilter createAlternativeBitmapFilter( } else if (DateTimeRange.class.isAssignableFrom(attributeSchema.getPlainType())) { return getDateTimePredicate((OffsetDateTime) comparableFrom, (OffsetDateTime) comparableTo); } else { - throw new EvitaInternalError("Unexpected type!"); + throw new GenericEvitaInternalError("Unexpected type!"); } } else { return getComparablePredicate(comparableFrom, comparableTo); @@ -242,7 +242,7 @@ public static Predicate>> getDateTimePredicate(@ } else if (comparableValueTo != null) { return ((DateTimeRange) theValue).overlaps(DateTimeRange.until(comparableValueTo)); } else { - throw new EvitaInternalError("Between query can never be created with both bounds null!"); + throw new GenericEvitaInternalError("Between query can never be created with both bounds null!"); } }; final Serializable theValue = attr.get().value(); @@ -279,7 +279,7 @@ public static Predicate>> getNumberRangePredicat } else if (comparableValueFrom instanceof Byte || comparableValueTo instanceof Byte) { return ((ByteNumberRange) theValue).overlaps(ByteNumberRange.between((Byte) comparableValueFrom, (Byte) comparableValueTo)); } else { - throw new EvitaInternalError("Unexpected input type: " + comparableValueFrom + ", " + comparableValueTo); + throw new GenericEvitaInternalError("Unexpected input type: " + comparableValueFrom + ", " + comparableValueTo); } } else if (comparableValueFrom != null) { if (comparableValueFrom instanceof BigDecimal) { @@ -293,7 +293,7 @@ public static Predicate>> getNumberRangePredicat } else if (comparableValueFrom instanceof Byte) { return ((ByteNumberRange) theValue).overlaps(ByteNumberRange.from((Byte) comparableValueFrom)); } else { - throw new EvitaInternalError("Unexpected input type: " + comparableValueFrom); + throw new GenericEvitaInternalError("Unexpected input type: " + comparableValueFrom); } } else if (comparableValueTo != null) { if (comparableValueTo instanceof BigDecimal) { @@ -307,10 +307,10 @@ public static Predicate>> getNumberRangePredicat } else if (comparableValueTo instanceof Byte) { return ((ByteNumberRange) theValue).overlaps(ByteNumberRange.to((Byte) comparableValueTo)); } else { - throw new EvitaInternalError("Unexpected input type: " + comparableValueTo); + throw new GenericEvitaInternalError("Unexpected input type: " + comparableValueTo); } } else { - throw new EvitaInternalError("Between query can never be created with both bounds null!"); + throw new GenericEvitaInternalError("Between query can never be created with both bounds null!"); } }; final Serializable theValue = attr.get().value(); @@ -342,7 +342,7 @@ public static Predicate>> getComparablePredicate } else if (comparableTo != null) { return theValue.compareTo(comparableTo) <= 0; } else { - throw new EvitaInternalError("Between query can never be created with both bounds null!"); + throw new GenericEvitaInternalError("Between query can never be created with both bounds null!"); } }; final Serializable theValue = attr.get().value(); diff --git a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/facet/FacetHavingTranslator.java b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/facet/FacetHavingTranslator.java index c32e9ec93..cb1c6d857 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/facet/FacetHavingTranslator.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/facet/FacetHavingTranslator.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ import io.evitadb.core.query.common.translator.SelfTraversingTranslator; import io.evitadb.core.query.filter.FilterByVisitor; import io.evitadb.core.query.filter.translator.FilteringConstraintTranslator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.utils.ArrayUtils; @@ -157,7 +157,7 @@ public Formula translate(@Nonnull FacetHaving facetHaving, @Nonnull FilterByVisi if (notFormula == null) { if (andFormula == null && orFormula == null) { - throw new EvitaInternalError("This should be not possible!"); + throw new GenericEvitaInternalError("This should be not possible!"); } else if (andFormula == null) { return orFormula; } else if (orFormula == null) { diff --git a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/price/PriceListCompositionTerminationVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/price/PriceListCompositionTerminationVisitor.java index b1f2e80e0..30a6950b0 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/price/PriceListCompositionTerminationVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/filter/translator/price/PriceListCompositionTerminationVisitor.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ import io.evitadb.core.query.algebra.utils.FormulaFactory; import io.evitadb.core.query.algebra.utils.visitor.FormulaFinder; import io.evitadb.core.query.algebra.utils.visitor.FormulaFinder.LookUp; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.price.model.PriceIndexKey; import io.evitadb.utils.Assert; import lombok.Getter; @@ -185,7 +185,7 @@ public void visit(@Nonnull Formula formula) { containerFormula, priceEvaluationContext, queryPriceMode, ofNullable(priceFilter).orElse(PricePredicate.ALL_RECORD_FILTER) ); - case UNKNOWN -> throw new EvitaInternalError("Can't handle unknown price inner record handling!"); + case UNKNOWN -> throw new GenericEvitaInternalError("Can't handle unknown price inner record handling!"); }; } else { // otherwise, use the produced formula diff --git a/evita_engine/src/main/java/io/evitadb/core/query/indexSelection/IndexSelectionVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/indexSelection/IndexSelectionVisitor.java index 38a603cd1..a830eec83 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/indexSelection/IndexSelectionVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/indexSelection/IndexSelectionVisitor.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ import io.evitadb.core.query.filter.FilterByVisitor; import io.evitadb.core.query.filter.translator.hierarchy.HierarchyWithinRootTranslator; import io.evitadb.core.query.filter.translator.hierarchy.HierarchyWithinTranslator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.CatalogIndex; import io.evitadb.index.CatalogIndexKey; import io.evitadb.index.EntityIndex; @@ -152,7 +152,7 @@ hierarchyWithin, getFilterByVisitor() ); } else { //sanity check only - throw new EvitaInternalError("Should never happen"); + throw new GenericEvitaInternalError("Should never happen"); } if (requestedHierarchyNodesFormula instanceof EmptyFormula) { // if target entity has no global index present, it means that the query cannot be fulfilled diff --git a/evita_engine/src/main/java/io/evitadb/core/query/sort/OrderByVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/sort/OrderByVisitor.java index 2194fc427..c839015dd 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/sort/OrderByVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/sort/OrderByVisitor.java @@ -56,7 +56,7 @@ import io.evitadb.core.query.sort.random.translator.RandomTranslator; import io.evitadb.core.query.sort.translator.OrderByTranslator; import io.evitadb.core.query.sort.translator.OrderingConstraintTranslator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.attribute.SortIndex; import io.evitadb.utils.CollectionUtils; @@ -262,7 +262,7 @@ public final T executeInContext( @Nonnull public ProcessingScope getProcessingScope() { if (scope.isEmpty()) { - throw new EvitaInternalError("Scope should never be empty"); + throw new GenericEvitaInternalError("Scope should never be empty"); } else { return scope.peek(); } @@ -314,7 +314,7 @@ public void visit(@Nonnull Constraint constraint) { currentSorters = translator.createSorter(orderConstraint, this); } else { // sanity check only - throw new EvitaInternalError("Should never happen"); + throw new GenericEvitaInternalError("Should never happen"); } // compose sorters one after another currentSorters.forEach(this.sorters::add); diff --git a/evita_engine/src/main/java/io/evitadb/core/query/sort/ReferenceOrderByVisitor.java b/evita_engine/src/main/java/io/evitadb/core/query/sort/ReferenceOrderByVisitor.java index 110c45dc1..1dd7d9d5c 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/sort/ReferenceOrderByVisitor.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/sort/ReferenceOrderByVisitor.java @@ -42,7 +42,7 @@ import io.evitadb.core.query.sort.attribute.translator.EntityPropertyTranslator; import io.evitadb.core.query.sort.translator.OrderByTranslator; import io.evitadb.core.query.sort.translator.ReferenceOrderingConstraintTranslator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.CollectionUtils; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -167,7 +167,7 @@ public void visit(@Nonnull Constraint constraint) { translator.createComparator(orderConstraint, this); } else { // sanity check only - throw new EvitaInternalError("Should never happen"); + throw new GenericEvitaInternalError("Should never happen"); } } diff --git a/evita_engine/src/main/java/io/evitadb/core/query/sort/primaryKey/translator/EntityPrimaryKeyNaturalTranslator.java b/evita_engine/src/main/java/io/evitadb/core/query/sort/primaryKey/translator/EntityPrimaryKeyNaturalTranslator.java index c42343bbb..f9369e73b 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/sort/primaryKey/translator/EntityPrimaryKeyNaturalTranslator.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/sort/primaryKey/translator/EntityPrimaryKeyNaturalTranslator.java @@ -40,7 +40,7 @@ import io.evitadb.core.query.sort.primaryKey.ReversedSorter; import io.evitadb.core.query.sort.translator.OrderingConstraintTranslator; import io.evitadb.dataType.array.CompositeObjectArray; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.attribute.SortedRecordsSupplier; import io.evitadb.index.bitmap.Bitmap; @@ -128,7 +128,7 @@ private static TransactionalBitmap getAsTransactionalBitmap(@Nonnull Bitmap bitm } else if (bitmap instanceof EmptyBitmap) { return null; } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected bitmap type: " + bitmap.getClass().getName(), "Unexpected bitmap type!" ); diff --git a/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionTrunkFinalizer.java b/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionTrunkFinalizer.java index ad0019971..cb8c277cd 100644 --- a/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionTrunkFinalizer.java +++ b/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionTrunkFinalizer.java @@ -27,7 +27,7 @@ import io.evitadb.api.requestResponse.transaction.TransactionMutation; import io.evitadb.core.Catalog; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import javax.annotation.Nonnull; @@ -81,7 +81,7 @@ public void commit(@Nonnull TransactionalLayerMaintainer transactionalLayer) { @Override public void rollback(@Nonnull TransactionalLayerMaintainer transactionalLayer, @Nullable Throwable cause) { - throw new EvitaInternalError("Rollback is not supported here!"); + throw new GenericEvitaInternalError("Rollback is not supported here!"); } /** diff --git a/evita_engine/src/main/java/io/evitadb/core/transaction/stage/TrunkIncorporationTransactionStage.java b/evita_engine/src/main/java/io/evitadb/core/transaction/stage/TrunkIncorporationTransactionStage.java index b70ce4031..7d686cdf2 100644 --- a/evita_engine/src/main/java/io/evitadb/core/transaction/stage/TrunkIncorporationTransactionStage.java +++ b/evita_engine/src/main/java/io/evitadb/core/transaction/stage/TrunkIncorporationTransactionStage.java @@ -35,7 +35,7 @@ import io.evitadb.core.transaction.stage.TrunkIncorporationTransactionStage.UpdatedCatalogTransactionTask; import io.evitadb.core.transaction.stage.mutation.VerifiedEntityRemoveMutation; import io.evitadb.core.transaction.stage.mutation.VerifiedEntityUpsertMutation; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -165,7 +165,7 @@ public UUID processTransactions(long nextCatalogVersion, long timeoutMs, boolean Assert.isPremiseValid( transactionMutation.getCatalogVersion() == this.lastFinalizedCatalogVersion + 1 || (lastTransaction != null && transactionMutation.getCatalogVersion() == lastTransaction.getCatalogVersion() + 1), - () -> new EvitaInternalError( + () -> new GenericEvitaInternalError( "Unexpected catalog version! " + "Transaction mutation catalog version: " + transactionMutation.getCatalogVersion() + ", " + "last finalized catalog version: " + this.lastFinalizedCatalogVersion + "." diff --git a/evita_engine/src/main/java/io/evitadb/index/attribute/AttributeIndex.java b/evita_engine/src/main/java/io/evitadb/index/attribute/AttributeIndex.java index 28c71c931..5fb037a89 100644 --- a/evita_engine/src/main/java/io/evitadb/index/attribute/AttributeIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/attribute/AttributeIndex.java @@ -34,7 +34,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; import io.evitadb.dataType.Predecessor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.attribute.AttributeIndex.AttributeIndexChanges; import io.evitadb.index.attribute.SortIndex.ComparatorSource; @@ -632,7 +632,7 @@ public StoragePart createStoragePart(int entityIndexPrimaryKey, @Nonnull Attribu notNull(theChainIndex, "Chain index for attribute `" + attribute + "` was not found!"); return theChainIndex.createStoragePart(entityIndexPrimaryKey); } else { - throw new EvitaInternalError("Cannot handle attribute storage part key of type `" + indexType + "`"); + throw new GenericEvitaInternalError("Cannot handle attribute storage part key of type `" + indexType + "`"); } } diff --git a/evita_engine/src/main/java/io/evitadb/index/attribute/SortIndex.java b/evita_engine/src/main/java/io/evitadb/index/attribute/SortIndex.java index 390d031fc..8999857e3 100644 --- a/evita_engine/src/main/java/io/evitadb/index/attribute/SortIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/attribute/SortIndex.java @@ -36,7 +36,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.array.TransactionalObjArray; import io.evitadb.index.array.TransactionalUnorderedIntArray; @@ -447,7 +447,7 @@ private > void removeRecordInternal( // remove cardinality altogether - cardinality = 1 is not maintained to save memory theValueCardinalities.remove(normalizedValue); } else { - throw new EvitaInternalError("Unexpected cardinality: " + cardinality); + throw new GenericEvitaInternalError("Unexpected cardinality: " + cardinality); } } else { // remove the entire value - there is no more record ids for it diff --git a/evita_engine/src/main/java/io/evitadb/index/cardinality/CardinalityIndex.java b/evita_engine/src/main/java/io/evitadb/index/cardinality/CardinalityIndex.java index 62b630ec6..4d0ab6c57 100644 --- a/evita_engine/src/main/java/io/evitadb/index/cardinality/CardinalityIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/cardinality/CardinalityIndex.java @@ -26,7 +26,7 @@ import io.evitadb.api.requestResponse.data.AttributesContract.AttributeKey; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.VoidTransactionMemoryProducer; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.bool.TransactionalBoolean; import io.evitadb.index.map.TransactionalMap; @@ -124,7 +124,7 @@ public boolean removeRecord(@Nonnull Serializable key, int recordId) { (k, v) -> v - 1 ); if (newValue == null) { - throw new EvitaInternalError("Cardinality of key `" + key + "` for record `" + recordId + "` is null"); + throw new GenericEvitaInternalError("Cardinality of key `" + key + "` for record `" + recordId + "` is null"); } else if (newValue == 0) { cardinalities.remove(cardinalityKey); return true; diff --git a/evita_engine/src/main/java/io/evitadb/index/list/TransactionalList.java b/evita_engine/src/main/java/io/evitadb/index/list/TransactionalList.java index ba910aa91..2d7192e6f 100644 --- a/evita_engine/src/main/java/io/evitadb/index/list/TransactionalList.java +++ b/evita_engine/src/main/java/io/evitadb/index/list/TransactionalList.java @@ -28,7 +28,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.Getter; import javax.annotation.Nonnull; @@ -508,7 +508,7 @@ public void remove() { currentPosition = previousPosition; layer.remove(previousPosition); } else { - throw new EvitaInternalError("Previous position unexpectedly: " + previousPosition); + throw new GenericEvitaInternalError("Previous position unexpectedly: " + previousPosition); } } @@ -520,7 +520,7 @@ public void set(V v) { final V result = layer.remove(index); layer.add(index, v); } else { - throw new EvitaInternalError("Current position unexpectedly: " + previousPosition); + throw new GenericEvitaInternalError("Current position unexpectedly: " + previousPosition); } } diff --git a/evita_engine/src/main/java/io/evitadb/index/map/TransactionalMap.java b/evita_engine/src/main/java/io/evitadb/index/map/TransactionalMap.java index 3d5d390e3..971e088dc 100644 --- a/evita_engine/src/main/java/io/evitadb/index/map/TransactionalMap.java +++ b/evita_engine/src/main/java/io/evitadb/index/map/TransactionalMap.java @@ -28,7 +28,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -419,7 +419,7 @@ public Entry next() { @Override public void remove() { if (currentValue == null) { - throw new EvitaInternalError("Value unexpectedly not found!"); + throw new GenericEvitaInternalError("Value unexpectedly not found!"); } final K key = currentValue.getKey(); diff --git a/evita_engine/src/main/java/io/evitadb/index/mutation/ContainerizedLocalMutationExecutor.java b/evita_engine/src/main/java/io/evitadb/index/mutation/ContainerizedLocalMutationExecutor.java index 72721885b..0533832cc 100644 --- a/evita_engine/src/main/java/io/evitadb/index/mutation/ContainerizedLocalMutationExecutor.java +++ b/evita_engine/src/main/java/io/evitadb/index/mutation/ContainerizedLocalMutationExecutor.java @@ -58,8 +58,8 @@ import io.evitadb.api.requestResponse.schema.dto.ReferenceSchema; import io.evitadb.core.buffer.DataStoreChanges; import io.evitadb.core.buffer.DataStoreMemoryBuffer; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.EntityIndexKey; import io.evitadb.store.entity.model.entity.AssociatedDataStoragePart; @@ -178,7 +178,7 @@ public void applyMutation(@Nonnull LocalMutation localMutation) { updateAssociatedData(entitySchema, associatedDataMutation); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + localMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + localMutation.getClass()); } } @@ -414,7 +414,7 @@ void recomputeLanguageOnAttributeUpdate(@Nonnull AttributeMutation attributeMuta // DO NOTHING } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + attributeMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + attributeMutation.getClass()); } } diff --git a/evita_engine/src/main/java/io/evitadb/index/mutation/EntityIndexLocalMutationExecutor.java b/evita_engine/src/main/java/io/evitadb/index/mutation/EntityIndexLocalMutationExecutor.java index dbac5a6f0..7a67696de 100644 --- a/evita_engine/src/main/java/io/evitadb/index/mutation/EntityIndexLocalMutationExecutor.java +++ b/evita_engine/src/main/java/io/evitadb/index/mutation/EntityIndexLocalMutationExecutor.java @@ -55,7 +55,7 @@ import io.evitadb.api.requestResponse.schema.dto.AttributeSchema; import io.evitadb.api.requestResponse.schema.dto.EntitySchema; import io.evitadb.api.requestResponse.schema.dto.SortableAttributeCompoundSchema; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.CatalogIndex; import io.evitadb.index.CatalogIndexKey; import io.evitadb.index.EntityIndex; @@ -266,7 +266,7 @@ public void applyMutation(@Nonnull LocalMutation localMutation) { // do nothing, associated data doesn't affect entity index directly } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + localMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + localMutation.getClass()); } } @@ -406,7 +406,7 @@ void updateAttributes( ); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + attributeMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + attributeMutation.getClass()); } } @@ -511,7 +511,7 @@ private void updateReferences(@Nonnull ReferenceMutation referenceMutation, @ ); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + referenceMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + referenceMutation.getClass()); } } @@ -669,7 +669,7 @@ private void updateReferencesInReferenceIndex(@Nonnull ReferenceMutation refe } else if (targetIndexType == EntityIndexType.REFERENCED_ENTITY) { theEntityPrimaryKey = getPrimaryKeyToIndex(IndexType.REFERENCE_INDEX); } else { - throw new EvitaInternalError("Unexpected type of index: " + targetIndexType); + throw new GenericEvitaInternalError("Unexpected type of index: " + targetIndexType); } if (referenceMutation instanceof SetReferenceGroupMutation upsertReferenceGroupMutation) { ReferenceIndexMutator.setFacetGroupInIndex( @@ -708,7 +708,7 @@ private void updateReferencesInReferenceIndex(@Nonnull ReferenceMutation refe ); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + referenceMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + referenceMutation.getClass()); } } @@ -793,7 +793,7 @@ private void updatePriceIndex(@Nonnull PriceMutation priceMutation, @Nonnull Ent ); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + priceMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + priceMutation.getClass()); } } @@ -817,7 +817,7 @@ private void updateHierarchyPlacement(@Nonnull ParentMutation parentMutation, @N ); } else { // SHOULD NOT EVER HAPPEN - throw new EvitaInternalError("Unknown mutation: " + parentMutation.getClass()); + throw new GenericEvitaInternalError("Unknown mutation: " + parentMutation.getClass()); } } diff --git a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java index 0052ffc44..6348f0502 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java @@ -29,7 +29,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.dataType.array.CompositeObjectArray; import io.evitadb.dataType.iterator.BatchArrayIterator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.bitmap.Bitmap; import io.evitadb.index.bitmap.RoaringBitmapBackedBitmap; @@ -83,7 +83,7 @@ default PriceRecordContract[] getPriceRecords(@Nonnull Bitmap priceIds) { priceRecordContract -> { }, notFoundPriceId -> { - throw new EvitaInternalError("Price with id " + notFoundPriceId + " was not found in the same index!"); + throw new GenericEvitaInternalError("Price with id " + notFoundPriceId + " was not found in the same index!"); } ); } diff --git a/evita_engine/src/main/java/io/evitadb/index/range/RangeIndex.java b/evita_engine/src/main/java/io/evitadb/index/range/RangeIndex.java index 004924bc4..4db0e624e 100644 --- a/evita_engine/src/main/java/io/evitadb/index/range/RangeIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/range/RangeIndex.java @@ -33,7 +33,7 @@ import io.evitadb.core.query.algebra.utils.FormulaFactory; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.VoidTransactionMemoryProducer; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.array.TransactionalComplexObjArray; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; @@ -349,7 +349,7 @@ private static Formula createDisentangleFormulaIfNecessary(long id, @Nonnull Bit } else if (leftFormula instanceof JoinFormula joinFormula) { return joinFormula.getAsOrFormula(); } else { - throw new EvitaInternalError("Unexpected formula type: " + leftFormula.getClass().getSimpleName() + "!"); + throw new GenericEvitaInternalError("Unexpected formula type: " + leftFormula.getClass().getSimpleName() + "!"); } } else { return new DisentangleFormula(leftFormula, rightFormula); diff --git a/evita_engine/src/main/java/io/evitadb/index/set/TransactionalSet.java b/evita_engine/src/main/java/io/evitadb/index/set/TransactionalSet.java index e014adb8f..844aac425 100644 --- a/evita_engine/src/main/java/io/evitadb/index/set/TransactionalSet.java +++ b/evita_engine/src/main/java/io/evitadb/index/set/TransactionalSet.java @@ -28,7 +28,7 @@ import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.Getter; import javax.annotation.Nonnull; @@ -387,7 +387,7 @@ public K next() { @Override public void remove() { if (currentValue == null) { - throw new EvitaInternalError("Value unexpectedly not found!"); + throw new GenericEvitaInternalError("Value unexpectedly not found!"); } final K key = currentValue; diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/certificate/ServerCertificateManager.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/certificate/ServerCertificateManager.java index 1b9dfb19a..196893ebe 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/certificate/ServerCertificateManager.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/certificate/ServerCertificateManager.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.externalApi.certificate; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.configuration.AbstractApiConfiguration; import io.evitadb.externalApi.configuration.CertificatePath; import io.evitadb.externalApi.configuration.CertificateSettings; @@ -114,7 +114,7 @@ public static CertificatePath getCertificatePath(@Nonnull CertificateSettings ce } else { final CertificatePath certificatePath = certificateSettings.custom(); if (certificatePath == null || certificatePath.certificate() == null || certificatePath.privateKey() == null) { - throw new EvitaInternalError("Certificate path is not properly set in the configuration file."); + throw new GenericEvitaInternalError("Certificate path is not properly set in the configuration file."); } certPath = ofNullable(certificatePath.certificate()).map(Path::of).orElse(null); certPrivateKeyPath = ofNullable(certificatePath.privateKey()).map(Path::of).orElse(null); @@ -296,4 +296,4 @@ private void issueCertificate( privateKeyWriter.writeObject(new PemObject("PRIVATE KEY", issuedCertKeyPair.getPrivate().getEncoded())); } } -} \ No newline at end of file +} diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/configuration/ApiOptions.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/configuration/ApiOptions.java index 81b83ce40..1512bc3ea 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/configuration/ApiOptions.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/configuration/ApiOptions.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ package io.evitadb.externalApi.configuration; import io.evitadb.api.exception.ApiNotFoundOnClasspath; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.externalApi.http.ExternalApiServer; import io.evitadb.utils.Assert; @@ -145,7 +145,7 @@ public ApiOptions.Builder enable(@Nonnull String apiCode) { try { cfg = (AbstractApiConfiguration) configurationClass.getDeclaredConstructor().newInstance(); } catch (Exception ex) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Failed to instantiate default configuration of `" + apiCode + "` API: " + ex.getMessage(), "Failed to instantiate default configuration of `" + apiCode + "` API!", ex diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java index 46688cfce..749dac35e 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/http/ExternalApiServer.java @@ -24,8 +24,8 @@ package io.evitadb.externalApi.http; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.certificate.ServerCertificateManager; import io.evitadb.externalApi.configuration.AbstractApiConfiguration; import io.evitadb.externalApi.configuration.ApiOptions; @@ -132,7 +132,7 @@ public static Collection gatherExternalApiProvider public static CertificatePath initCertificate(final @Nonnull ApiOptions apiOptions, final @Nonnull ServerCertificateManager serverCertificateManager) { final CertificatePath certificatePath = ServerCertificateManager.getCertificatePath(apiOptions.certificate()); if (certificatePath.certificate() == null || certificatePath.privateKey() == null) { - throw new EvitaInternalError("Either certificate path or its private key path is not set"); + throw new GenericEvitaInternalError("Either certificate path or its private key path is not set"); } final File certificateFile = new File(certificatePath.certificate()); final File certificateKeyFile = new File(certificatePath.privateKey()); @@ -149,7 +149,7 @@ public static CertificatePath initCertificate(final @Nonnull ApiOptions apiOptio try { serverCertificateManager.generateSelfSignedCertificate(); } catch (Exception e) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Failed to generate self-signed certificate: " + e.getMessage(), "Failed to generate self-signed certificate", e @@ -162,7 +162,7 @@ public static CertificatePath initCertificate(final @Nonnull ApiOptions apiOptio } } else { if (!certificateFile.exists() || !certificateKeyFile.exists()) { - throw new EvitaInternalError("Certificate or its private key file does not exist"); + throw new GenericEvitaInternalError("Certificate or its private key file does not exist"); } } return certificatePath; @@ -194,7 +194,7 @@ private static SSLContext configureSSLContext( } catch (NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | KeyStoreException | IOException | KeyManagementException e) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Error while creating SSL context: " + e.getMessage(), "Error while creating SSL context", e @@ -308,7 +308,7 @@ private static Map> registerApiProviders( it -> it.getCode().toLowerCase(), Function.identity(), (externalApiProvider, externalApiProvider2) -> { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Multiple implementations of `" + externalApiProvider.getCode() + "` found on classpath!" ); }, diff --git a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/api/catalog/builder/CatalogGraphQLSchemaBuildingContext.java b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/api/catalog/builder/CatalogGraphQLSchemaBuildingContext.java index 0911dd972..6adb91c70 100644 --- a/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/api/catalog/builder/CatalogGraphQLSchemaBuildingContext.java +++ b/evita_external_api/evita_external_api_graphql/src/main/java/io/evitadb/externalApi/graphql/api/catalog/builder/CatalogGraphQLSchemaBuildingContext.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.SealedEntitySchema; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.graphql.api.builder.GraphQLSchemaBuildingContext; import io.evitadb.externalApi.graphql.configuration.GraphQLConfig; import lombok.Getter; @@ -69,7 +69,7 @@ public CatalogGraphQLSchemaBuildingContext(@Nonnull GraphQLConfig config, final Set schemas = createHashSet(collections.size()); collections.forEach(c -> { final SealedEntitySchema entitySchema = session.getEntitySchema(c) - .orElseThrow(() -> new EvitaInternalError("Entity `" + c + "` schema unexpectedly not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Entity `" + c + "` schema unexpectedly not found!")); supportedLocales.addAll(entitySchema.getLocales()); supportedCurrencies.addAll(entitySchema.getCurrencies()); schemas.add(entitySchema); diff --git a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java index 59021d0ff..7213e20ec 100644 --- a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java +++ b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/EvitaClient.java @@ -47,6 +47,7 @@ import io.evitadb.driver.trace.DefaultClientTracingContext; import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.exception.InvalidEvitaVersionException; import io.evitadb.externalApi.grpc.dataType.EvitaDataTypesConverter; import io.evitadb.externalApi.grpc.generated.*; @@ -717,18 +718,18 @@ private T executeWithEvitaService(@Nonnull AsyncCallFunction T executeWithEvitaSessionService( throw evitaError; } catch (Throwable e) { log.error("Unexpected internal Evita error occurred: {}", e.getMessage(), e); - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected internal Evita error occurred: " + e.getMessage(), "Unexpected internal Evita error occurred.", e @@ -1618,11 +1619,11 @@ private RuntimeException transformStatusRuntimeException(@Nonnull StatusRuntimeE } else { final Matcher expectedFormat = ERROR_MESSAGE_PATTERN.matcher(description); if (expectedFormat.matches()) { - return EvitaInternalError.createExceptionWithErrorCode( + return GenericEvitaInternalError.createExceptionWithErrorCode( expectedFormat.group(2), expectedFormat.group(1) ); } else { - return new EvitaInternalError(description); + return new GenericEvitaInternalError(description); } } } diff --git a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/certificate/ClientCertificateManager.java b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/certificate/ClientCertificateManager.java index 126466b40..1bce7497a 100644 --- a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/certificate/ClientCertificateManager.java +++ b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/certificate/ClientCertificateManager.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ package io.evitadb.driver.certificate; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.CertificateUtils; import io.netty.handler.ssl.ApplicationProtocolConfig; @@ -312,7 +312,7 @@ private TrustManager getTrustManager(@Nonnull Path usedCertificatePath) { log.info("Client's certificate fingerprint: {}", CertificateUtils.getCertificateFingerprint(clientCert)); } } catch (CertificateException | IOException | NoSuchAlgorithmException e) { - throw new EvitaInternalError(e.getMessage(), e); + throw new GenericEvitaInternalError(e.getMessage(), e); } return trustManagerTrustingProvidedRootCertificate; } diff --git a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/trace/ClientTracingContextProvider.java b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/trace/ClientTracingContextProvider.java index a61c46d01..a15ed49b8 100644 --- a/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/trace/ClientTracingContextProvider.java +++ b/evita_external_api/evita_external_api_grpc/client/src/main/java/io/evitadb/driver/trace/ClientTracingContextProvider.java @@ -23,7 +23,7 @@ package io.evitadb.driver.trace; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import java.util.List; import java.util.ServiceLoader; @@ -45,7 +45,7 @@ public static ClientTracingContext getContext() { .map(Provider::get) .toList(); if (collectedContexts.size() > 1) { - throw new EvitaInternalError("There are multiple registered implementations of ExternalApiTracingContext."); + throw new GenericEvitaInternalError("There are multiple registered implementations of ExternalApiTracingContext."); } if (collectedContexts.size() == 1) { return collectedContexts.stream().findFirst().get(); diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java index 1330f5cfe..710d5e8e1 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java @@ -26,7 +26,6 @@ import com.google.protobuf.Empty; import io.evitadb.api.CatalogState; import io.evitadb.api.EvitaSessionContract; -import io.evitadb.api.exception.UnexpectedTransactionStateException; import io.evitadb.api.query.Query; import io.evitadb.api.query.require.EntityContentRequire; import io.evitadb.api.query.require.EntityFetch; @@ -50,7 +49,7 @@ import io.evitadb.dataType.DataChunk; import io.evitadb.dataType.PaginatedList; import io.evitadb.dataType.StripList; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.grpc.builders.query.extraResults.GrpcExtraResultsBuilder; import io.evitadb.externalApi.grpc.constants.GrpcHeaders; import io.evitadb.externalApi.grpc.generated.*; @@ -682,7 +681,7 @@ public void queryOne(GrpcQueryRequest request, StreamObserver logic) { - executeWithClientContext(session -> { - try { - session.execute(logic); - } catch (UnexpectedTransactionStateException ex) { - logic.accept(session); - } - }); - } - /** * Executes entire lambda function within the scope of a tracing context. */ diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/utils/GrpcServer.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/utils/GrpcServer.java index 4d68bfde2..5aa3b8c0f 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/utils/GrpcServer.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/utils/GrpcServer.java @@ -24,7 +24,7 @@ package io.evitadb.externalApi.grpc.utils; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.certificate.ServerCertificateManager; import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.configuration.CertificatePath; @@ -86,7 +86,7 @@ private void setUpServer(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @ final HostDefinition[] hosts = config.getHost(); final CertificatePath certificatePath = ServerCertificateManager.getCertificatePath(apiOptions.certificate()); if (certificatePath.certificate() == null || certificatePath.privateKey() == null) { - throw new EvitaInternalError("Certificate path is not set."); + throw new GenericEvitaInternalError("Certificate path is not set."); } final ServerCredentials tlsServerCredentials; try { @@ -114,7 +114,7 @@ private void setUpServer(@Nonnull Evita evita, @Nonnull ApiOptions apiOptions, @ } tlsServerCredentials = tlsServerCredentialsBuilder.build(); } catch (Exception e) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Failed to create gRPC server credentials with provided certificate and private key: " + e.getMessage(), "Failed to create gRPC server credentials with provided certificate and private key.", e diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/dataType/EvitaDataTypesConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/dataType/EvitaDataTypesConverter.java index 6103c5ebb..97e32c9aa 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/dataType/EvitaDataTypesConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/dataType/EvitaDataTypesConverter.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import com.google.protobuf.Timestamp; import io.evitadb.api.requestResponse.data.AssociatedDataContract.AssociatedDataValue; import io.evitadb.dataType.*; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.grpc.generated.*; import io.evitadb.externalApi.grpc.generated.GrpcEvitaAssociatedDataValue.ValueCase; import io.evitadb.utils.NumberUtils; @@ -145,7 +145,7 @@ public static T toEvitaValue(@Nonnull GrpcEvitaValue va case UUID_ARRAY -> (T) toUuidArray(value.getUuidArrayValue()); default -> - throw new EvitaInternalError("Unsupported Evita data type in gRPC API `" + value.getValueCase() + "`."); + throw new GenericEvitaInternalError("Unsupported Evita data type in gRPC API `" + value.getValueCase() + "`."); }; } @@ -163,7 +163,7 @@ public static Serializable toEvitaValue(@Nonnull GrpcEvitaAssociatedDataValue va } else if (value.getValueCase() == ValueCase.JSONVALUE) { return ComplexDataObjectConverter.convertJsonToComplexDataObject(value.getJsonValue()); } else { - throw new EvitaInternalError("Unknown value type."); + throw new GenericEvitaInternalError("Unknown value type."); } } @@ -396,7 +396,7 @@ public static Class toEvitaDataType(@Nonnull GrpcEvitaDa case CURRENCY_ARRAY -> Currency[].class; case UUID_ARRAY -> UUID[].class; default -> - throw new EvitaInternalError("Unsupported Evita data type in gRPC API `" + dataType.getValueDescriptor() + "`."); + throw new GenericEvitaInternalError("Unsupported Evita data type in gRPC API `" + dataType.getValueDescriptor() + "`."); }; } @@ -1371,5 +1371,5 @@ public static GrpcPredecessor toGrpcPredecessor(@Nonnull Predecessor predecessor .build(); } } - + } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java index 7a6161a34..a7b48329e 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/generated/GrpcHealthProblem.java @@ -6,375 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 - * - * Licensed under the Business Source License, Version 1.1 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://github.com/FgForrest/evitaDB/blob/main/LICENSE - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * - * _ _ ____ ____ - * _____ _(_) |_ __ _| _ \| __ ) - * / _ \ \ / / | __/ _` | | | | _ \ - * | __/\ V /| | || (_| | |_| | |_) | - * \___| \_/ |_|\__\__,_|____/|____/ - * - * Copyright (c) 2023 + * Copyright (c) 2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java index a54c0c9b2..82453a154 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/EvitaEnumConverter.java @@ -47,6 +47,7 @@ import io.evitadb.api.requestResponse.schema.dto.AttributeUniquenessType; import io.evitadb.api.requestResponse.schema.dto.GlobalAttributeUniquenessType; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.grpc.generated.*; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -70,7 +71,7 @@ public static CatalogState toCatalogState(@Nonnull GrpcCatalogState grpcCatalogS return switch (grpcCatalogState.getNumber()) { case 0 -> CatalogState.WARMING_UP; case 1 -> CatalogState.ALIVE; - default -> throw new EvitaInternalError("Unrecognized remote catalog state: " + grpcCatalogState); + default -> throw new GenericEvitaInternalError("Unrecognized remote catalog state: " + grpcCatalogState); }; } @@ -97,7 +98,7 @@ public static QueryPriceMode toQueryPriceMode(@Nonnull GrpcQueryPriceMode grpcQu return switch (grpcQueryPriceMode.getNumber()) { case 0 -> QueryPriceMode.WITH_TAX; case 1 -> QueryPriceMode.WITHOUT_TAX; - default -> throw new EvitaInternalError("Unrecognized remote query price mode: " + grpcQueryPriceMode); + default -> throw new GenericEvitaInternalError("Unrecognized remote query price mode: " + grpcQueryPriceMode); }; } @@ -128,7 +129,7 @@ public static PriceContentMode toPriceContentMode(@Nonnull GrpcPriceContentMode case 0 -> PriceContentMode.NONE; case 1 -> PriceContentMode.RESPECTING_FILTER; case 2 -> PriceContentMode.ALL; - default -> throw new EvitaInternalError("Unrecognized remote price content mode: " + grpcPriceContentMode); + default -> throw new GenericEvitaInternalError("Unrecognized remote price content mode: " + grpcPriceContentMode); }; } @@ -159,7 +160,7 @@ public static OrderDirection toOrderDirection(@Nonnull GrpcOrderDirection grpcOr return switch (grpcOrderDirection.getNumber()) { case 0 -> OrderDirection.ASC; case 1 -> OrderDirection.DESC; - default -> throw new EvitaInternalError("Unrecognized remote order direction: " + grpcOrderDirection); + default -> throw new GenericEvitaInternalError("Unrecognized remote order direction: " + grpcOrderDirection); }; } @@ -189,7 +190,7 @@ public static OrderBehaviour toOrderBehaviour(@Nonnull GrpcOrderBehaviour grpcOr return switch (grpcOrderBehaviour.getNumber()) { case 0 -> OrderBehaviour.NULLS_FIRST; case 1 -> OrderBehaviour.NULLS_LAST; - default -> throw new EvitaInternalError("Unrecognized remote order behaviour: " + grpcOrderBehaviour); + default -> throw new GenericEvitaInternalError("Unrecognized remote order behaviour: " + grpcOrderBehaviour); }; } @@ -221,7 +222,7 @@ public static EmptyHierarchicalEntityBehaviour toEmptyHierarchicalEntityBehaviou case 0 -> EmptyHierarchicalEntityBehaviour.LEAVE_EMPTY; case 1 -> EmptyHierarchicalEntityBehaviour.REMOVE_EMPTY; default -> - throw new EvitaInternalError("Unrecognized remote empty hierarchical entity behaviour: " + grpcEmptyHierarchicalEntityBehaviour); + throw new GenericEvitaInternalError("Unrecognized remote empty hierarchical entity behaviour: " + grpcEmptyHierarchicalEntityBehaviour); }; } @@ -251,7 +252,7 @@ public static StatisticsBase toStatisticsBase(@Nonnull GrpcStatisticsBase grpcSt return switch (grpcStatisticsBase.getNumber()) { case 0 -> StatisticsBase.COMPLETE_FILTER; case 1 -> StatisticsBase.WITHOUT_USER_FILTER; - default -> throw new EvitaInternalError("Unrecognized remote statistics base: " + grpcStatisticsBase); + default -> throw new GenericEvitaInternalError("Unrecognized remote statistics base: " + grpcStatisticsBase); }; } @@ -281,7 +282,7 @@ public static StatisticsType toStatisticsType(@Nonnull GrpcStatisticsType grpcSt return switch (grpcStatisticsType.getNumber()) { case 0 -> StatisticsType.CHILDREN_COUNT; case 1 -> StatisticsType.QUERIED_ENTITY_COUNT; - default -> throw new EvitaInternalError("Unrecognized remote statistics type: " + grpcStatisticsType); + default -> throw new GenericEvitaInternalError("Unrecognized remote statistics type: " + grpcStatisticsType); }; } @@ -297,7 +298,7 @@ public static HistogramBehavior toHistogramBehavior(@Nonnull GrpcHistogramBehavi return switch (grpcHistogramBehavior.getNumber()) { case 0 -> HistogramBehavior.STANDARD; case 1 -> HistogramBehavior.OPTIMIZED; - default -> throw new EvitaInternalError("Unrecognized remote histogram behavior: " + grpcHistogramBehavior); + default -> throw new GenericEvitaInternalError("Unrecognized remote histogram behavior: " + grpcHistogramBehavior); }; } @@ -342,7 +343,7 @@ public static AttributeSpecialValue toAttributeSpecialValue(@Nonnull GrpcAttribu case 0 -> AttributeSpecialValue.NULL; case 1 -> AttributeSpecialValue.NOT_NULL; default -> - throw new EvitaInternalError("Unrecognized remote attribute special value: " + grpcAttributeSpecialValue); + throw new GenericEvitaInternalError("Unrecognized remote attribute special value: " + grpcAttributeSpecialValue); }; } @@ -372,7 +373,7 @@ public static FacetStatisticsDepth toFacetStatisticsDepth(@Nonnull GrpcFacetStat case 0 -> FacetStatisticsDepth.COUNTS; case 1 -> FacetStatisticsDepth.IMPACT; default -> - throw new EvitaInternalError("Unrecognized remote facet statistics depth: " + grpcFacetStatisticsDepth); + throw new GenericEvitaInternalError("Unrecognized remote facet statistics depth: " + grpcFacetStatisticsDepth); }; } @@ -405,7 +406,7 @@ public static PriceInnerRecordHandling toPriceInnerRecordHandling(@Nonnull GrpcP case 2 -> PriceInnerRecordHandling.SUM; case 3 -> PriceInnerRecordHandling.UNKNOWN; default -> - throw new EvitaInternalError("Unrecognized remote price inner record handling: " + grpcPriceInnerRecordHandling); + throw new GenericEvitaInternalError("Unrecognized remote price inner record handling: " + grpcPriceInnerRecordHandling); }; } @@ -440,7 +441,7 @@ public static Cardinality toCardinality(@Nonnull GrpcCardinality grpcCardinality case 2 -> Cardinality.EXACTLY_ONE; case 3 -> Cardinality.ZERO_OR_MORE; case 4 -> Cardinality.ONE_OR_MORE; - default -> throw new EvitaInternalError("Unrecognized remote cardinality: " + grpcCardinality); + default -> throw new GenericEvitaInternalError("Unrecognized remote cardinality: " + grpcCardinality); }; } @@ -474,7 +475,7 @@ public static GrpcCardinality toGrpcCardinality(@Nullable Cardinality cardinalit public static CatalogEvolutionMode toCatalogEvolutionMode(@Nonnull GrpcCatalogEvolutionMode grpcEvolutionMode) { return switch (grpcEvolutionMode.getNumber()) { case 0 -> CatalogEvolutionMode.ADDING_ENTITY_TYPES; - default -> throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); + default -> throw new GenericEvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); }; } @@ -509,7 +510,7 @@ public static EvolutionMode toEvolutionMode(@Nonnull GrpcEvolutionMode grpcEvolu case 5 -> EvolutionMode.ADDING_LOCALES; case 6 -> EvolutionMode.ADDING_CURRENCIES; case 7 -> EvolutionMode.ADDING_HIERARCHY; - default -> throw new EvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); + default -> throw new GenericEvitaInternalError("Unrecognized remote evolution mode: " + grpcEvolutionMode); }; } @@ -564,7 +565,7 @@ public static QueryPhase toQueryPhase(@Nonnull GrpcQueryPhase grpcQueryPhase) { case 18 -> QueryPhase.FETCHING; case 19 -> QueryPhase.FETCHING_REFERENCES; case 20 -> QueryPhase.FETCHING_PARENTS; - default -> throw new EvitaInternalError("Unrecognized remote query phase: " + grpcQueryPhase); + default -> throw new GenericEvitaInternalError("Unrecognized remote query phase: " + grpcQueryPhase); }; } @@ -615,7 +616,7 @@ public static EntityExistence toEntityExistence(@Nonnull GrpcEntityExistence grp case 0 -> EntityExistence.MAY_EXIST; case 1 -> EntityExistence.MUST_NOT_EXIST; case 2 -> EntityExistence.MUST_EXIST; - default -> throw new EvitaInternalError("Unrecognized remote entity existence: " + grpcEntityExistence); + default -> throw new GenericEvitaInternalError("Unrecognized remote entity existence: " + grpcEntityExistence); }; } @@ -650,7 +651,7 @@ public static GrpcAttributeSchemaType toGrpcAttributeSchemaType(@Nonnull Class AttributeUniquenessType.NOT_UNIQUE; case 1 -> AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION; case 2 -> AttributeUniquenessType.UNIQUE_WITHIN_COLLECTION_LOCALE; - default -> throw new EvitaInternalError("Unrecognized remote attribute uniqueness type: " + type); + default -> throw new GenericEvitaInternalError("Unrecognized remote attribute uniqueness type: " + type); }; } @@ -697,7 +698,7 @@ public static GlobalAttributeUniquenessType toGlobalAttributeUniquenessType(@Non case 0 -> GlobalAttributeUniquenessType.NOT_UNIQUE; case 1 -> GlobalAttributeUniquenessType.UNIQUE_WITHIN_CATALOG; case 2 -> GlobalAttributeUniquenessType.UNIQUE_WITHIN_CATALOG_LOCALE; - default -> throw new EvitaInternalError("Unrecognized remote global attribute uniqueness type: " + type); + default -> throw new GenericEvitaInternalError("Unrecognized remote global attribute uniqueness type: " + type); }; } @@ -744,7 +745,7 @@ public static CommitBehavior toCommitBehavior(@Nonnull GrpcCommitBehavior commit case WAIT_FOR_CONFLICT_RESOLUTION -> CommitBehavior.WAIT_FOR_CONFLICT_RESOLUTION; case WAIT_FOR_LOG_PERSISTENCE -> CommitBehavior.WAIT_FOR_WAL_PERSISTENCE; case WAIT_FOR_INDEX_PROPAGATION -> CommitBehavior.WAIT_FOR_INDEX_PROPAGATION; - default -> throw new EvitaInternalError("Unrecognized remote commit behavior: " + commitBehaviour); + default -> throw new GenericEvitaInternalError("Unrecognized remote commit behavior: " + commitBehaviour); }; } diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/ResponseConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/ResponseConverter.java index a69f2e450..c9a4acadf 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/ResponseConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/ResponseConverter.java @@ -51,7 +51,7 @@ import io.evitadb.dataType.DataChunk; import io.evitadb.dataType.PaginatedList; import io.evitadb.dataType.StripList; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.grpc.generated.*; import io.evitadb.externalApi.grpc.generated.GrpcHistogram.GrpcBucket; import io.evitadb.externalApi.grpc.requestResponse.data.EntityConverter; @@ -108,7 +108,7 @@ public static DataChunk convertToDataChunk( converter.apply(grpcRecordPage) ); } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Only PaginatedList or StripList expected, but got none!" ); } @@ -277,7 +277,7 @@ private static FacetGroupStatistics toFacetGroupStatistics( it -> it.getFacetEntity().getPrimaryKey(), Function.identity(), (o, o2) -> { - throw new EvitaInternalError("Duplicate facet statistics for entity " + o.getFacetEntity().getPrimaryKey()); + throw new GenericEvitaInternalError("Duplicate facet statistics for entity " + o.getFacetEntity().getPrimaryKey()); }, LinkedHashMap::new ) diff --git a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/data/EntityConverter.java b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/data/EntityConverter.java index 2fecd3433..4ba84f890 100644 --- a/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/data/EntityConverter.java +++ b/evita_external_api/evita_external_api_grpc/shared/src/main/java/io/evitadb/externalApi/grpc/requestResponse/data/EntityConverter.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ import io.evitadb.api.requestResponse.data.structure.predicate.ReferenceContractSerializablePredicate; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; import io.evitadb.api.requestResponse.schema.SealedEntitySchema; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.grpc.dataType.ComplexDataObjectConverter; import io.evitadb.externalApi.grpc.dataType.EvitaDataTypesConverter; import io.evitadb.externalApi.grpc.generated.*; @@ -141,7 +141,7 @@ public static T toEntity( } if (EntityReference.class.isAssignableFrom(expectedType)) { - throw new EvitaInternalError("EntityReference is not expected in this method!"); + throw new GenericEvitaInternalError("EntityReference is not expected in this method!"); } else { final List references = grpcEntity.getReferencesList() .stream() diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java index ef653450b..dfe59aa52 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java @@ -26,12 +26,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.evitadb.core.Evita; import io.evitadb.core.metric.event.CustomMetricsExecutionEvent; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.exception.UnexpectedIOException; import io.evitadb.externalApi.api.system.model.HealthProblem; import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.CorsFilter; +import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; import io.evitadb.externalApi.http.PathNormalizingHandler; +import io.evitadb.externalApi.observability.agent.ErrorMonitor; import io.evitadb.externalApi.observability.configuration.ObservabilityConfig; import io.evitadb.externalApi.observability.exception.JfRException; import io.evitadb.externalApi.observability.io.ObservabilityExceptionHandler; @@ -58,10 +60,13 @@ import jdk.jfr.Recording; import jdk.jfr.RecordingState; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.Arrays; @@ -69,6 +74,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; /** * This class is used as an orchestrator for all observability-related tasks. It is responsible for starting and stopping @@ -80,6 +86,7 @@ * * @author Tomáš Pozler, FG Forrest a.s. (c) 2024 */ +@Slf4j public class ObservabilityManager { public static final String METRICS_SUFFIX = "metrics"; public static final String METRICS_PATH = "/observability/" + METRICS_SUFFIX; @@ -132,6 +139,28 @@ public class ObservabilityManager { */ @Nonnull @Getter private final ObjectMapper objectMapper = new ObjectMapper(); + static { + ClassLoader classLoader = null; + do { + if (classLoader == null) { + classLoader = MetricHandler.class.getClassLoader(); + } else { + classLoader = classLoader.getParent(); + } + try { + final Class errorMonitorClass = classLoader.loadClass(ErrorMonitor.class.getName()); + final Method setJavaErrorConsumer = errorMonitorClass.getDeclaredMethod("setJavaErrorConsumer", Consumer.class); + setJavaErrorConsumer.invoke(null, (Consumer) ObservabilityManager::javaErrorEvent); + final Method setEvitaErrorConsumer = errorMonitorClass.getDeclaredMethod("setEvitaErrorConsumer", Consumer.class); + setEvitaErrorConsumer.invoke(null, (Consumer) ObservabilityManager::evitaErrorEvent); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InvocationTargetException e) { + // do nothing, the errors won't be monitored + log.error("ErrorMonitor class not found, the Java & evitaDB errors won't be present in metrics."); + } + } while (classLoader.getParent() != null); + } + /** * Method increments the counter of Java errors in the Prometheus metrics. */ @@ -209,6 +238,15 @@ public long getEvitaErrorCount() { return EVITA_ERRORS.get(); } + /** + * Records readiness of the API to the Prometheus metrics. + * @param apiCode the code of the API taken from {@link ExternalApiProviderRegistrar#getExternalApiCode()} + * @param ready true if the API is ready, false otherwise + */ + public void recordReadiness(@Nonnull String apiCode, boolean ready) { + MetricHandler.API_READINESS.labelValues(apiCode).set(ready ? 1 : 0); + } + /** * Records health problem to the Prometheus metrics. * @param healthProblem the health problem to be recorded @@ -310,7 +348,7 @@ private void registerRecordingFileResourceHandler() { }); observabilityRouter.addPrefixPath("/", resourceHandler); } catch (IOException e) { - throw new EvitaInternalError(e.getMessage(), e); + throw new GenericEvitaInternalError(e.getMessage(), e); } } @@ -332,7 +370,7 @@ private void createAndRegisterPrometheusServlet() { try { observabilityRouter.addPrefixPath("/" + METRICS_SUFFIX, servletDeploymentManager.start()); } catch (ServletException e) { - throw new EvitaInternalError("Unable to add routing to Prometheus scraping servlet."); + throw new GenericEvitaInternalError("Unable to add routing to Prometheus scraping servlet."); } } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitor.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitor.java new file mode 100644 index 000000000..bc7e50a20 --- /dev/null +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitor.java @@ -0,0 +1,62 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.externalApi.observability.agent; + +import io.evitadb.externalApi.observability.ObservabilityManager; +import lombok.Setter; + +import javax.annotation.Nonnull; +import java.util.function.Consumer; + +/** + * This simple class serves as a mediator between advices in the ErrorMonitoringAgent and the {@link ObservabilityManager}, + * which registers lambda functions to be called when an error is detected. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public class ErrorMonitor { + @Setter private static Consumer javaErrorConsumer; + @Setter private static Consumer evitaErrorConsumer; + + /** + * Method is called by the ErrorMonitoringAgent advice when a Java error is detected. + * @param errorType the type of the error + */ + public static void registerJavaError(@Nonnull String errorType) { + if (javaErrorConsumer != null) { + javaErrorConsumer.accept(errorType); + } + } + + /** + * Method is called by the ErrorMonitoringAgent advice when an Evita error is detected. + * @param errorType the type of the error + */ + public static void registerEvitaError(@Nonnull String errorType) { + if (evitaErrorConsumer != null) { + evitaErrorConsumer.accept(errorType); + } + } + +} diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java index da2195c47..4d96614e0 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/agent/ErrorMonitoringAgent.java @@ -24,18 +24,20 @@ package io.evitadb.externalApi.observability.agent; import io.evitadb.exception.EvitaInternalError; -import io.evitadb.externalApi.observability.ObservabilityManager; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; -import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.loading.ClassInjector; +import javax.annotation.Nonnull; +import java.io.IOException; +import java.io.InputStream; import java.lang.instrument.Instrumentation; +import java.util.HashMap; +import java.util.Map; -import static net.bytebuddy.matcher.ElementMatchers.isConstructor; -import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.none; +import static net.bytebuddy.matcher.ElementMatchers.*; /** * Agent that intercepts all Error constructors and sends a metric to the MetricHandler. @@ -54,14 +56,14 @@ public static void premain(String agentArgs, Instrumentation inst) { .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .ignore(none()) .ignore(nameStartsWith("net.bytebuddy.")) - .type(isSubTypeOf(Error.class)) + .type(isSubTypeOf(VirtualMachineError.class).and(not(isAbstract()))) .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder .visit( Advice .to(JavaErrorConstructorInterceptAdvice.class) .on(isConstructor()) )) - .type(isSubTypeOf(EvitaInternalError.class)) + .type(isSubTypeOf(EvitaInternalError.class).and(not(isAbstract()))) .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder .visit( Advice @@ -69,6 +71,38 @@ public static void premain(String agentArgs, Instrumentation inst) { .on(isConstructor()) )) .installOn(inst); + + // Inject ErrorMonitoring class into the bootstrap classloader + Map types = new HashMap<>(8); + types.put( + new TypeDescription.ForLoadedType(ErrorMonitor.class), + getClassBytes(ErrorMonitor.class) + ); + ClassInjector.UsingUnsafe.ofBootLoader().inject(types); + } + + /** + * Get the bytes of a particular class from classpath. + * @param clazz Class to get bytes of. + * @return Byte array of the class. + */ + @Nonnull + public static byte[] getClassBytes(@Nonnull Class clazz) { + try { + final String classAsResource = clazz.getName().replace('.', '/') + ".class"; + try (InputStream classStream = ErrorMonitoringAgent.class.getClassLoader().getResourceAsStream(classAsResource)) { + if (classStream == null) { + System.err.println("Class `" + clazz.getName() + "` not found in classpath and is required by ErrorMonitoringAgent."); + System.exit(1); + throw new IllegalStateException("Class `" + clazz.getName() + "` not found in classpath and is required by ErrorMonitoringAgent."); + } + return classStream.readAllBytes(); + } + } catch (IOException e) { + System.err.println("Class `" + clazz.getName() + "` not found in classpath and is required by ErrorMonitoringAgent."); + System.exit(1); + throw new IllegalStateException("Class `" + clazz.getName() + "` not found in classpath and is required by ErrorMonitoringAgent."); + } } /** @@ -76,9 +110,9 @@ public static void premain(String agentArgs, Instrumentation inst) { */ public static class JavaErrorConstructorInterceptAdvice { - @OnMethodEnter - public static boolean before(@Advice.This Object thiz) { - ObservabilityManager.javaErrorEvent(thiz.getClass().getSimpleName()); + @OnMethodExit + public static boolean after(@Advice.This Object thiz) { + ErrorMonitor.registerJavaError(thiz.getClass().getSimpleName()); return true; } @@ -89,9 +123,9 @@ public static boolean before(@Advice.This Object thiz) { */ public static class EvitaDbErrorConstructorInterceptAdvice { - @OnMethodEnter - public static boolean before(@Advice.This Object thiz) { - ObservabilityManager.evitaErrorEvent(thiz.getClass().getSimpleName()); + @OnMethodExit + public static boolean after(@Advice.This Object thiz) { + ErrorMonitor.registerEvitaError(thiz.getClass().getSimpleName()); return true; } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java index 3a9d29f4e..d1c2df52e 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/MetricHandler.java @@ -76,15 +76,20 @@ public class MetricHandler { .help("Total number of internal Java errors") .register(); public static final Counter EVITA_ERRORS_TOTAL = Counter.builder() - .name("evita_errors_total") + .name("io_evitadb_errors_total") .labelNames("error_type") .help("Total number of internal evitaDB errors") .register(); public static final Gauge HEALTH_PROBLEMS = Gauge.builder() - .name("evita_health_problem") + .name("io_evitadb_probe_health_problem") .labelNames("problem_type") .help("Health problems detected in the system") .register(); + public static final Gauge API_READINESS = Gauge.builder() + .name("io_evitadb_probe_api_readiness") + .labelNames("api_type") + .help("Status of the API readiness (internal HTTP call check)") + .register(); static { DEFAULT_JVM_METRICS = Map.of( diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java index 12e209009..ea9a7545d 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -143,12 +143,14 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract @Nonnull @Override public Readiness getReadiness(@Nonnull EvitaContract evitaContract, @Nonnull ExternalApiServer externalApiServer, @Nonnull String... apiCodes) { + final Optional theObservabilityManager = getObservabilityManager(externalApiServer); // check the end-points availability final Collection availableExternalApis = ExternalApiServer.gatherExternalApiProviders(); final Map readiness = CollectionUtils.createHashMap(availableExternalApis.size()); for (String apiCode : apiCodes) { final ExternalApiProvider apiProvider = externalApiServer.getExternalApiProviderByCode(apiCode); readiness.put(apiProvider.getCode(), apiProvider.isReady()); + theObservabilityManager.ifPresent(it -> it.recordReadiness(apiProvider.getCode(), apiProvider.isReady())); } final boolean ready = readiness.values().stream().allMatch(Boolean::booleanValue); if (ready) { diff --git a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/catalog/builder/CatalogRestBuildingContext.java b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/catalog/builder/CatalogRestBuildingContext.java index ed7c71419..66e5397f9 100644 --- a/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/catalog/builder/CatalogRestBuildingContext.java +++ b/evita_external_api/evita_external_api_rest/src/main/java/io/evitadb/externalApi/rest/api/catalog/builder/CatalogRestBuildingContext.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.SealedEntitySchema; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.rest.api.builder.RestBuildingContext; import io.evitadb.externalApi.rest.api.openApi.OpenApiObject; import io.evitadb.externalApi.rest.api.openApi.OpenApiTypeReference; @@ -80,7 +80,7 @@ public CatalogRestBuildingContext(@Nullable String exposedOn, @Nonnull RestConfi final Set schemas = createHashSet(collections.size()); collections.forEach(c -> { final SealedEntitySchema entitySchema = session.getEntitySchema(c) - .orElseThrow(() -> new EvitaInternalError("Schema for `" + c + "` entity type unexpectedly not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for `" + c + "` entity type unexpectedly not found!")); supportedLocales.addAll(entitySchema.getLocales()); supportedCurrencies.addAll(entitySchema.getCurrencies()); schemas.add(entitySchema); diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index 43a1ca8d0..786d421d3 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import io.evitadb.api.requestResponse.system.SystemStatus; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.system.ProbesProvider; import io.evitadb.externalApi.api.system.ProbesProvider.Readiness; import io.evitadb.externalApi.api.system.ProbesProvider.ReadinessState; @@ -75,8 +75,8 @@ public class SystemProviderRegistrar implements ExternalApiProviderRegistrar "\t\t\"" + entry.apiCode() + "\": \"" + (entry.isReady() ? "ready" : "not ready") + "\"") - .collect(Collectors.joining(",\n")) + "\n" + + Arrays.stream(readiness.apiStates()) + .map(entry -> "\t\t\"" + entry.apiCode() + "\": \"" + (entry.isReady() ? "ready" : "not ready") + "\"") + .collect(Collectors.joining(",\n")) + "\n" + "\t}\n" + "}" ); @@ -140,6 +140,23 @@ private static String renderStatus( ); } + /** + * Returns the enabled API endpoints. + * + * @param apiOptions the common settings shared among all the API endpoints + * @return array of codes of the enabled API endpoints + */ + @Nonnull + private static String[] getEnabledApiEndpoints(@Nonnull ApiOptions apiOptions) { + return apiOptions.endpoints() + .entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .filter(entry -> entry.getValue().isEnabled()) + .map(Entry::getKey) + .toArray(String[]::new); + } + @Nonnull @Override public String getExternalApiCode() { @@ -171,7 +188,7 @@ public ExternalApiProvider register( } else { final CertificatePath certificatePath = certificateSettings.custom(); if (certificatePath == null || certificatePath.certificate() == null || certificatePath.privateKey() == null) { - throw new EvitaInternalError("Certificate path is not properly set in the configuration file."); + throw new GenericEvitaInternalError("Certificate path is not properly set in the configuration file."); } final String certificate = certificatePath.certificate(); final int lastSeparatorIndex = certificatePath.certificate().lastIndexOf(File.separator); @@ -230,14 +247,7 @@ public ExternalApiProvider register( } ); - final String[] enabledEndPoints = apiOptions.endpoints() - .entrySet() - .stream() - .filter(entry -> entry.getValue() != null) - .filter(entry -> entry.getValue().isEnabled()) - .map(Entry::getKey) - .toArray(String[]::new); - + final String[] enabledEndPoints = getEnabledApiEndpoints(apiOptions); router.addExactPath( "/" + ENDPOINT_SYSTEM_READINESS, exchange -> { @@ -315,7 +325,7 @@ public ExternalApiProvider register( new String[0] ); } catch (IOException e) { - throw new EvitaInternalError(e.getMessage(), e); + throw new GenericEvitaInternalError(e.getMessage(), e); } } diff --git a/evita_functional_tests/src/test/java/io/evitadb/api/exception/EvitaInternalErrorTest.java b/evita_functional_tests/src/test/java/io/evitadb/api/exception/EvitaInternalErrorTest.java index e8a426266..d9160f5e3 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/api/exception/EvitaInternalErrorTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/api/exception/EvitaInternalErrorTest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ package io.evitadb.api.exception; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -37,7 +38,7 @@ class EvitaInternalErrorTest { @Test void shouldProduceCorrectErrorCode() { - final EvitaInternalError someError = new EvitaInternalError("Whatever"); - assertEquals("84346f4d5323dd1aab59c301c22be909:c44f193800fc6962d8987aa7cd6ed71d:40", someError.getErrorCode()); + final EvitaInternalError someError = new GenericEvitaInternalError("Whatever"); + assertEquals("62663481f4e06623dca9964d207b9599:af7e03290249dcf135c6dd7ad7cd6bc9:84", someError.getErrorCode()); } -} \ No newline at end of file +} diff --git a/evita_functional_tests/src/test/java/io/evitadb/api/query/parser/ValueTest.java b/evita_functional_tests/src/test/java/io/evitadb/api/query/parser/ValueTest.java index 6f19f23e3..310e3f7ec 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/api/query/parser/ValueTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/api/query/parser/ValueTest.java @@ -25,6 +25,7 @@ import io.evitadb.dataType.exception.InconvertibleDataTypeException; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.EvitaInvalidUsageException; import org.junit.jupiter.api.Test; import java.util.List; @@ -67,7 +68,7 @@ void shouldReturnLocale() { @Test void shouldNotReturnLocale() { - assertThrows(EvitaInternalError.class, () -> new Value(1).asLocale()); + assertThrows(EvitaInvalidUsageException.class, () -> new Value(1).asLocale()); assertThrows(InconvertibleDataTypeException.class, () -> new Value("").asLocale()); assertThrows(InconvertibleDataTypeException.class, () -> new Value("zz").asLocale()); } @@ -91,4 +92,4 @@ void shouldReturnLocaleArray() { void shouldNotReturnIntegerArray() { assertThrows(EvitaInternalError.class, () -> new Value(List.of(1, "b")).asIntegerArray()); } -} \ No newline at end of file +} diff --git a/evita_functional_tests/src/test/java/io/evitadb/documentation/JavaDocCopy.java b/evita_functional_tests/src/test/java/io/evitadb/documentation/JavaDocCopy.java index 989a58b51..389a28c06 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/documentation/JavaDocCopy.java +++ b/evita_functional_tests/src/test/java/io/evitadb/documentation/JavaDocCopy.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import io.evitadb.api.query.Constraint; import io.evitadb.api.query.QueryConstraints; import io.evitadb.api.query.descriptor.annotation.ConstraintDefinition; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.test.EvitaTestSupport; import io.evitadb.utils.StringUtils; import org.junit.jupiter.api.Test; @@ -254,20 +254,19 @@ void copyConstraintUserDocsLinksToJavaDocs() throws URISyntaxException, IOExcept } } if (commentStartLine == -1) { - throw new EvitaInternalError("Could not find author line in `" + constraintClass.getName() + "`"); + throw new GenericEvitaInternalError("Could not find author line in `" + constraintClass.getName() + "`"); } final String comment = constraintClass.getComment(); final int commentEndLine = comment.split("\n").length + commentStartLine; - final int originalUserDocsLinkLineNumber = commentEndLine; - final String originalUserDocsLinkLine = constraintSource.get(originalUserDocsLinkLineNumber); + final String originalUserDocsLinkLine = constraintSource.get(commentEndLine); if (originalUserDocsLinkLine.contains("Visit detailed user documentation

"); + constraintSource.set(commentEndLine, " *

Visit detailed user documentation

"); } else { // there is no link, add it - constraintSource.add(originalUserDocsLinkLineNumber + 1, " * Visit detailed user documentation"); - constraintSource.add(originalUserDocsLinkLineNumber + 1, " *"); + constraintSource.add(commentEndLine + 1, " * Visit detailed user documentation"); + constraintSource.add(commentEndLine + 1, " *"); } // rewrite the file with replaced JavaDoc @@ -322,7 +321,7 @@ void exportConstraintDefinitions() throws URISyntaxException, IOException { case "io.evitadb.api.query.filter" -> "FILTER"; case "io.evitadb.api.query.order" -> "ORDER"; case "io.evitadb.api.query.require" -> "REQUIRE"; - default -> throw new EvitaInternalError("Unknown package name: " + constraintClass.getPackageName()); + default -> throw new GenericEvitaInternalError("Unknown package name: " + constraintClass.getPackageName()); }; final String shortDescription = ((String) constraintDefinition.get().getNamedParameter("shortDescription")).replace("\"", ""); final String userDocsLink = "https://evitadb.io" + ((String) constraintDefinition.get().getNamedParameter("userDocsLink")).replace("\"", ""); diff --git a/evita_functional_tests/src/test/java/io/evitadb/documentation/csharp/CShell.java b/evita_functional_tests/src/test/java/io/evitadb/documentation/csharp/CShell.java index f0f18ab02..a1f5b1138 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/documentation/csharp/CShell.java +++ b/evita_functional_tests/src/test/java/io/evitadb/documentation/csharp/CShell.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.evitadb.documentation.Environment; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nonnull; @@ -145,7 +146,7 @@ private static void downloadValidator() { try { Files.createDirectory(VALIDATOR_TEMP_FOLDER_PATH); } catch (IOException e) { - throw new EvitaInternalError("Failed to create temporary folder for C# query validator."); + throw new GenericEvitaInternalError("Failed to create temporary folder for C# query validator."); } } final Path zipPath = Paths.get(VALIDATOR_TEMP_FOLDER_PATH.toString(), "Validator.zip"); @@ -159,7 +160,7 @@ private static void downloadValidator() { setExecutablePermission(); } } catch (IOException ex) { - throw new EvitaInternalError("Failed to download C# query validator.", ex); + throw new GenericEvitaInternalError("Failed to download C# query validator.", ex); } } @@ -204,7 +205,7 @@ private static void unzip(@Nonnull String zipPath, @Nonnull String destDir) thro zis.close(); fis.close(); } catch (IOException e) { - throw new EvitaInternalError("Failed to unzip C# query validator executable.", e); + throw new GenericEvitaInternalError("Failed to unzip C# query validator executable.", e); } } @@ -216,7 +217,7 @@ private static void setExecutablePermission() { ProcessBuilder processBuilder = new ProcessBuilder("chmod", "+x", VALIDATOR_PATH); processBuilder.start(); } catch (IOException e) { - throw new EvitaInternalError("Failed to set executable permission on C# query validator executable."); + throw new GenericEvitaInternalError("Failed to set executable permission on C# query validator executable."); } } @@ -316,14 +317,14 @@ private Map fetchRelease() throws EvitaInternalError { try { releaseResponse = httpClient.send(releaseRequest, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); } catch (IOException | InterruptedException e) { - throw new EvitaInternalError("Could not find GitHub release: ", e); + throw new GenericEvitaInternalError("Could not find GitHub release: ", e); } try { //noinspection unchecked return (Map) objectMapper.readValue(releaseResponse.body(), Map.class); } catch (JsonProcessingException e) { - throw new EvitaInternalError("Could not parse GitHub release: ", e); + throw new GenericEvitaInternalError("Could not parse GitHub release: ", e); } } @@ -332,13 +333,13 @@ private static String findAsset(@Nonnull Map release, @Nonnull S //noinspection unchecked final List> assets = (List>) release.get("assets"); if (assets == null || assets.isEmpty()) { - throw new EvitaInternalError("No assets found in GitHub release."); + throw new GenericEvitaInternalError("No assets found in GitHub release."); } final Map asset = assets.stream() .filter(it -> assetName.equals(it.get("name"))) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Asset not found in GitHub release.")); + .orElseThrow(() -> new GenericEvitaInternalError("Asset not found in GitHub release.")); return (String) asset.get("browser_download_url"); } diff --git a/evita_functional_tests/src/test/java/io/evitadb/externalApi/ExternalApiFunctionTestsSupport.java b/evita_functional_tests/src/test/java/io/evitadb/externalApi/ExternalApiFunctionTestsSupport.java index b47bb392c..798051383 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/externalApi/ExternalApiFunctionTestsSupport.java +++ b/evita_functional_tests/src/test/java/io/evitadb/externalApi/ExternalApiFunctionTestsSupport.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ import io.evitadb.api.requestResponse.data.EntityContract; import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.model.PropertyDescriptor; import io.evitadb.test.Entities; @@ -146,7 +146,7 @@ default int findEntityPk(@Nonnull List originalProductEntities, .filter(filter) .findFirst() .map(SealedEntity::getPrimaryKey) - .orElseThrow(() -> new EvitaInternalError("No entity to test.")); + .orElseThrow(() -> new GenericEvitaInternalError("No entity to test.")); } default int findEntityWithPricePk(@Nonnull List originalProductEntities) { @@ -297,7 +297,7 @@ default List getEntities(@Nonnull Evita evita, assertFalse(entities.isEmpty()); } return entities.stream() - .peek(it -> validator.accept(it)) + .peek(validator::accept) .toList(); } ); @@ -376,12 +376,12 @@ default EvitaResponse queryEntities(@Nonnull Evita e @Nonnull default String serializeStringArrayToQueryString(@Nonnull List items) { - return Arrays.toString(items.stream().map(it -> "\"" + it.toString() + "\"").toArray()); + return Arrays.toString(items.stream().map(it -> "\"" + it + "\"").toArray()); } @Nonnull default String serializeStringArrayToQueryString(@Nonnull String[] items) { - return Arrays.toString(Arrays.stream(items).map(it -> "\"" + it.toString() + "\"").toArray()); + return Arrays.toString(Arrays.stream(items).map(it -> "\"" + it + "\"").toArray()); } @Nonnull diff --git a/evita_functional_tests/src/test/java/io/evitadb/externalApi/api/catalog/mutation/TestMutationResolvingExceptionFactory.java b/evita_functional_tests/src/test/java/io/evitadb/externalApi/api/catalog/mutation/TestMutationResolvingExceptionFactory.java index d3a18066b..bf6b61d94 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/externalApi/api/catalog/mutation/TestMutationResolvingExceptionFactory.java +++ b/evita_functional_tests/src/test/java/io/evitadb/externalApi/api/catalog/mutation/TestMutationResolvingExceptionFactory.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.catalog.resolver.mutation.MutationResolvingExceptionFactory; import javax.annotation.Nonnull; @@ -40,14 +41,14 @@ public class TestMutationResolvingExceptionFactory implements MutationResolvingE @Nonnull @Override public T createInternalError(@Nonnull String message) { - return (T) new EvitaInternalError(message); + return (T) new GenericEvitaInternalError(message); } @SuppressWarnings("unchecked") @Nonnull @Override public T createInternalError(@Nonnull String message, @Nonnull Throwable cause) { - return (T) new EvitaInternalError(message, cause); + return (T) new GenericEvitaInternalError(message, cause); } @SuppressWarnings("unchecked") diff --git a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetEntityQueryFunctionalTest.java b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetEntityQueryFunctionalTest.java index 727985432..21c3d8801 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetEntityQueryFunctionalTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetEntityQueryFunctionalTest.java @@ -28,7 +28,7 @@ import io.evitadb.api.requestResponse.data.ReferenceContract; import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.catalog.dataApi.model.AttributesDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.EntityDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.PriceDescriptor; @@ -1614,7 +1614,7 @@ private SealedEntity findEntity(@Nonnull List originalProductEntit return originalProductEntities.stream() .filter(filter) .findFirst() - .orElseThrow(() -> new EvitaInternalError("No entity to test.")); + .orElseThrow(() -> new GenericEvitaInternalError("No entity to test.")); } @Nonnull diff --git a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetUnknownEntityQueryFunctionalTest.java b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetUnknownEntityQueryFunctionalTest.java index de1175561..ea79174b6 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetUnknownEntityQueryFunctionalTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLGetUnknownEntityQueryFunctionalTest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import io.evitadb.api.requestResponse.data.ReferenceContract; import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.catalog.dataApi.model.AttributesDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.EntityDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.PriceDescriptor; @@ -1190,7 +1190,7 @@ private SealedEntity findEntity(@Nonnull List originalProductEntit return originalProductEntities.stream() .filter(filter) .findFirst() - .orElseThrow(() -> new EvitaInternalError("No entity to test.")); + .orElseThrow(() -> new GenericEvitaInternalError("No entity to test.")); } @Nonnull diff --git a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLListUnknownEntitiesQueryFunctionalTest.java b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLListUnknownEntitiesQueryFunctionalTest.java index fd2fa91b8..1b8831fc2 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLListUnknownEntitiesQueryFunctionalTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/externalApi/graphql/api/catalog/dataApi/CatalogGraphQLListUnknownEntitiesQueryFunctionalTest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,7 @@ import io.evitadb.api.requestResponse.data.ReferenceContract; import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; -import io.evitadb.externalApi.api.catalog.dataApi.model.AssociatedDataDescriptor; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.catalog.dataApi.model.AttributesDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.EntityDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.PriceDescriptor; @@ -80,11 +79,11 @@ void shouldReturnUnknownEntityListByMultipleGloballyUniqueAttribute(GraphQLTeste final SealedEntity entityWithCode1 = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute1)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with code attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with code attribute")); final SealedEntity entityWithCode2 = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute2)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with code attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with code attribute")); tester.test(TEST_CATALOG) .document( @@ -272,11 +271,11 @@ void shouldReturnRichUnknownEntityListByMultipleLocalizedGloballyUniqueAttribute final SealedEntity entityWithUrl1 = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH), urlAttribute1)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with url attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with url attribute")); final SealedEntity entityWithUrl2 = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH), urlAttribute2)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with url attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with url attribute")); tester.test(TEST_CATALOG) .document( @@ -333,12 +332,12 @@ void shouldReturnLocalizedRichUnknownEntityListByMultipleNonLocalizedGloballyUni .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute1) && it.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH) != null) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with url attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with url attribute")); final SealedEntity entityWithCodeAndUrl2 = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute2) && it.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH) != null) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with url attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with url attribute")); tester.test(TEST_CATALOG) .document( @@ -397,7 +396,7 @@ void shouldReturnUnknownEntityWithMultipleDifferentGlobalAttributes(GraphQLTeste final SealedEntity entityWithCode = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with code attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with code attribute")); final String urlAttribute = entityWithCode.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH); tester.test(TEST_CATALOG) @@ -435,12 +434,12 @@ void shouldReturnUnknownEntityListByMultipleDifferentGlobalAttributes(GraphQLTes final SealedEntity entityWithCode = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_CODE), codeAttribute)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with code attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with code attribute")); final String urlAttribute = getRandomAttributeValue(originalProductEntities, ATTRIBUTE_URL, Locale.ENGLISH, 7); final SealedEntity entityWithUrl = originalProductEntities.stream() .filter(it -> Objects.equals(it.getAttribute(ATTRIBUTE_URL, Locale.ENGLISH), urlAttribute)) .findFirst() - .orElseThrow(() -> new EvitaInternalError("Missing entity with url attribute")); + .orElseThrow(() -> new GenericEvitaInternalError("Missing entity with url attribute")); assertNotEquals(entityWithCode.getPrimaryKey(), entityWithUrl.getPrimaryKey()); diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/client/ClientDataFullDatabaseState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/client/ClientDataFullDatabaseState.java index a4a3da399..ec81c777d 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/client/ClientDataFullDatabaseState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/client/ClientDataFullDatabaseState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.api.requestResponse.data.structure.EntityReference; import io.evitadb.api.requestResponse.schema.SealedEntitySchema; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.senesi.SenesiBenchmark; import io.evitadb.utils.StringUtils; import org.openjdk.jmh.annotations.Level; @@ -85,7 +85,7 @@ public void setUp() { for (String entityType : entityTypes) { final long entityProcessingStart = System.nanoTime(); final SealedEntitySchema entitySchema = session.getEntitySchema(entityType) - .orElseThrow(() -> new EvitaInternalError("Schema for entity `" + entityType + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for entity `" + entityType + "` was not found!")); processSchema(entitySchema); EvitaResponse response; diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientPageReadState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientPageReadState.java index 700791d8a..0e3509967 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientPageReadState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientPageReadState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import io.evitadb.api.query.RequireConstraint; import io.evitadb.api.query.require.EntityContentRequire; import io.evitadb.api.requestResponse.data.SealedEntity; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.client.ClientDataFullDatabaseState; import io.evitadb.performance.generators.RandomQueryGenerator; import lombok.Getter; @@ -89,7 +89,7 @@ public void setUp() { super.setUp(); try (final EvitaSessionContract session = this.evita.createReadOnlySession(getCatalogName())) { this.productSchema = session.getEntitySchema(PRODUCT_ENTITY_TYPE) - .orElseThrow(() -> new EvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); } } diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientSingleReadState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientSingleReadState.java index a32355689..7b7e2ccea 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientSingleReadState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientSingleReadState.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import io.evitadb.api.query.Query; import io.evitadb.api.query.require.EntityContentRequire; import io.evitadb.api.requestResponse.data.SealedEntity; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.client.ClientDataFullDatabaseState; import io.evitadb.performance.generators.RandomQueryGenerator; import lombok.Getter; @@ -88,7 +88,7 @@ public void setUp() { super.setUp(); try (final EvitaSessionContract session = this.evita.createReadOnlySession(getCatalogName())) { this.productSchema = session.getEntitySchema(PRODUCT_ENTITY_TYPE) - .orElseThrow(() -> new EvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); } } diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientTransactionalWriteState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientTransactionalWriteState.java index c107ebe14..61ea2e4af 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientTransactionalWriteState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/client/state/ClientTransactionalWriteState.java @@ -26,7 +26,7 @@ import io.evitadb.api.requestResponse.data.EntityEditor.EntityBuilder; import io.evitadb.api.requestResponse.data.SealedEntity; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.client.ClientDataState; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Setup; @@ -147,7 +147,7 @@ public void close() { .iterator();*/ this.productIterator = Collections.emptyIterator(); this.productSchema = session.getEntitySchema(PRODUCT_ENTITY_TYPE) - .orElseThrow(() -> new EvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for entity `" + PRODUCT_ENTITY_TYPE + "` was not found!")); } /** @@ -176,7 +176,7 @@ public void prepareCall() { PRODUCT_ENTITY_TYPE, primaryKey, entityFetchAllContent() - ).orElseThrow(() -> new EvitaInternalError("Entity with id " + primaryKey + " unexpectedly not found!"));; + ).orElseThrow(() -> new GenericEvitaInternalError("Entity with id " + primaryKey + " unexpectedly not found!"));; this.product = this.modificationFunction.apply(existingEntity); this.updateCounter++; diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/graphql/artificial/GraphQLArtificialBenchmarkState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/graphql/artificial/GraphQLArtificialBenchmarkState.java index 67bde45b8..1decc6ffd 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/graphql/artificial/GraphQLArtificialBenchmarkState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/graphql/artificial/GraphQLArtificialBenchmarkState.java @@ -23,7 +23,7 @@ package io.evitadb.performance.externalApi.graphql.artificial; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.artificial.AbstractArtificialBenchmarkState; import io.evitadb.test.client.GraphQLClient; @@ -50,7 +50,7 @@ public GraphQLClient getSession() { true ); } catch (UnknownHostException e) { - throw new EvitaInternalError("Unknown host.", e); + throw new GenericEvitaInternalError("Unknown host.", e); } }); } diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/rest/artificial/RestArtificialBenchmarkState.java b/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/rest/artificial/RestArtificialBenchmarkState.java index 4dbe856a7..d98f1f293 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/rest/artificial/RestArtificialBenchmarkState.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/externalApi/rest/artificial/RestArtificialBenchmarkState.java @@ -23,7 +23,7 @@ package io.evitadb.performance.externalApi.rest.artificial; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.performance.artificial.AbstractArtificialBenchmarkState; import io.evitadb.test.client.RestClient; @@ -46,7 +46,7 @@ public RestClient getSession() { try { return new RestClient("https://" + InetAddress.getByName("localhost").getHostAddress() + ":5555", false, true); } catch (UnknownHostException e) { - throw new EvitaInternalError("Could not create REST API URL:", e); + throw new GenericEvitaInternalError("Could not create REST API URL:", e); } }); } diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/generators/RandomQueryGenerator.java b/evita_performance_tests/src/main/java/io/evitadb/performance/generators/RandomQueryGenerator.java index 322012a08..9fb1f4288 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/generators/RandomQueryGenerator.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/generators/RandomQueryGenerator.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ import io.evitadb.api.requestResponse.schema.AttributeSchemaContract; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.dataType.DateTimeRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; import lombok.Data; @@ -428,7 +428,7 @@ default T getRandomItem(@Nonnull Random random, @Nonnull Collection colle return next; } } - throw new EvitaInternalError("Should not ever happen!"); + throw new GenericEvitaInternalError("Should not ever happen!"); } /** diff --git a/evita_performance_tests/src/main/java/io/evitadb/performance/generators/TestDatasetGenerator.java b/evita_performance_tests/src/main/java/io/evitadb/performance/generators/TestDatasetGenerator.java index fd198924c..355096942 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/performance/generators/TestDatasetGenerator.java +++ b/evita_performance_tests/src/main/java/io/evitadb/performance/generators/TestDatasetGenerator.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaEditor.EntitySchemaBuilder; import io.evitadb.api.requestResponse.schema.SealedEntitySchema; import io.evitadb.core.Evita; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.function.TriConsumer; import io.evitadb.test.Entities; import io.evitadb.test.generator.DataGenerator; @@ -206,7 +206,7 @@ default ReadyReadEvita generateReadTestDataset(@Nonnull DataGenerator dataGenera for (String entityType : entityTypes) { final long entityProcessingStart = System.nanoTime(); final SealedEntitySchema entitySchema = session.getEntitySchema(entityType) - .orElseThrow(() -> new EvitaInternalError("Schema for entity `" + entityType + "` was not found!")); + .orElseThrow(() -> new GenericEvitaInternalError("Schema for entity `" + entityType + "` was not found!")); if (Entities.PRODUCT.equals(entitySchema.getName())) { productSchema.set(entitySchema); diff --git a/evita_performance_tests/src/main/java/io/evitadb/spike/TrieIngestion.java b/evita_performance_tests/src/main/java/io/evitadb/spike/TrieIngestion.java index 511235811..9489c6c07 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/spike/TrieIngestion.java +++ b/evita_performance_tests/src/main/java/io/evitadb/spike/TrieIngestion.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Output; import io.evitadb.dataType.trie.Trie; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.store.service.KryoFactory; import io.evitadb.utils.StringUtils; @@ -152,7 +152,7 @@ private void testAutocompletion(TextInputState state) { final Set autocompletedWords = trie.getWordsStartingWith(randomWord); duration += System.nanoTime() - start; if (autocompletedWords.isEmpty()) { - throw new EvitaInternalError("Indexes doesn't match!"); + throw new GenericEvitaInternalError("Indexes doesn't match!"); } else { autocompleteResults += autocompletedWords.size(); } diff --git a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintCreator.java b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintCreator.java index 8cd464765..2900491bc 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintCreator.java +++ b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintCreator.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,8 @@ package io.evitadb.api.query.descriptor; import io.evitadb.api.query.Constraint; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.ClassUtils; @@ -144,14 +144,14 @@ public Constraint instantiateConstraint(@Nonnull Object[] args, @Nonnull Stri } else if (instantiator instanceof Method method) { return (Constraint) method.invoke(null, args); } else { - throw new EvitaInternalError("Unsupported creator."); + throw new GenericEvitaInternalError("Unsupported creator."); } } catch (Exception e) { if (e instanceof final InvocationTargetException invocationTargetException && invocationTargetException.getTargetException() instanceof final EvitaInvalidUsageException invalidUsageException) { throw invalidUsageException; } - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Could not instantiate constraint `" + parsedName + "` to original constraint `" + instantiator.getDeclaringClass().getName() + "`: " + e.getMessage(), "Could not recreate constraint `" + parsedName + "`.", e @@ -299,6 +299,25 @@ public ConstraintValueStructure valueStructure() { return valueStructure; } + @Override + public String toString() { + return "ConstraintCreator{" + + "instantiator=" + instantiator + + ", suffix='" + suffix + '\'' + + ", parameters=" + parameters + + ", implicitClassifier=" + implicitClassifier + + ", hasClassifierParameter=" + hasClassifierParameter + + ", classifierParameter=" + classifierParameter + + ", hasValueParameters=" + hasValueParameters + + ", valueParameters=" + valueParameters + + ", hasChildParameters=" + hasChildParameters + + ", childParameters=" + childParameters + + ", hasAdditionalChildParameters=" + hasAdditionalChildParameters + + ", additionalChildParameters=" + additionalChildParameters + + ", valueStructure=" + valueStructure + + '}'; + } + /** * Represents classifier that is not specified by client but by system. */ diff --git a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptor.java b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptor.java index 1e3b7bd4d..acdae3844 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptor.java @@ -96,7 +96,7 @@ public ConstraintDescriptor(@Nonnull Class constraintClass, "Constraint `" + fullName + "` is missing short description." ); Assert.isPremiseValid( - !userDocsLink.isEmpty() && userDocsLink.startsWith("/"), + !userDocsLink.isEmpty() && userDocsLink.charAt(0) == '/', "Constraint `" + fullName + "` is missing user documentation link or the link has incorrect format." ); @@ -197,6 +197,21 @@ public int hashCode() { return Objects.hash(type, propertyType, fullName, creator.hasClassifierParameter(), creator.hasImplicitClassifier()); } + @Override + public String toString() { + return "ConstraintDescriptor{" + + "constraintClass=" + constraintClass + + ", type=" + type + + ", propertyType=" + propertyType + + ", fullName='" + fullName + '\'' + + ", shortDescription='" + shortDescription + '\'' + + ", userDocsLink='" + userDocsLink + '\'' + + ", supportedIn=" + supportedIn + + ", supportedValues=" + supportedValues + + ", creator=" + creator + + '}'; + } + @Override public int compareTo(@Nonnull ConstraintDescriptor o) { int result = type().compareTo(o.type()); @@ -229,7 +244,7 @@ public int compareTo(@Nonnull ConstraintDescriptor o) { } @Nonnull - private String constructFullUserDocsLink(@Nonnull String relativeUserDocsLink) { + private static String constructFullUserDocsLink(@Nonnull String relativeUserDocsLink) { return "https://evitadb.io" + relativeUserDocsLink; } diff --git a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptorProvider.java b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptorProvider.java index 7de9bad0d..747c92386 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptorProvider.java +++ b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintDescriptorProvider.java @@ -28,7 +28,7 @@ import io.evitadb.api.query.descriptor.ConstraintCreator.ImplicitClassifier; import io.evitadb.api.query.descriptor.ConstraintCreator.SilentImplicitClassifier; import io.evitadb.dataType.EvitaDataTypes; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -115,7 +115,7 @@ public static Optional getConstraint(@Nonnull ConstraintTy } else if (implicitClassifier.get() instanceof final FixedImplicitClassifier fixedImplicitClassifier) { return fixedImplicitClassifier.classifier().equals(classifier); } else { - throw new EvitaInternalError("Unsupported implicit classifier class."); + throw new GenericEvitaInternalError("Unsupported implicit classifier class."); } } if (creator.hasClassifierParameter()) { @@ -151,7 +151,7 @@ public static ConstraintDescriptor getConstraint(@Nonnull Class - new EvitaInternalError("Unknown constraint `" + constraintClass.getName() + "` with suffix `" + suffix + "`. Is it properly registered?")); + new GenericEvitaInternalError("Unknown constraint `" + constraintClass.getName() + "` with suffix `" + suffix + "`. Is it properly registered?")); } @Nonnull diff --git a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintProcessor.java b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintProcessor.java index 968592c9f..794144dc9 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintProcessor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/descriptor/ConstraintProcessor.java @@ -42,7 +42,7 @@ import io.evitadb.api.query.descriptor.annotation.Creator; import io.evitadb.api.query.descriptor.annotation.Value; import io.evitadb.dataType.EvitaDataTypes; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import io.evitadb.utils.StringUtils; @@ -112,10 +112,10 @@ Set process(@Nonnull Set>> c * Tries to find annotation defining passed constraint. */ @Nonnull - private ConstraintDefinition findConstraintDefAnnotation(@Nonnull Class> constraintClass) { + private static ConstraintDefinition findConstraintDefAnnotation(@Nonnull Class> constraintClass) { final ConstraintDefinition constraintDefinition = constraintClass.getAnnotation(ConstraintDefinition.class); if (constraintDefinition == null) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Constraint `" + constraintClass.getName() + "` has been registered but there is no definition specified." ); } @@ -126,12 +126,12 @@ private ConstraintDefinition findConstraintDefAnnotation(@Nonnull Class constraintClass) { + private static ConstraintType resolveConstraintType(@Nonnull Class constraintClass) { return Arrays.stream(ConstraintType.values()) .filter(t -> t.getRepresentingInterface().isAssignableFrom(constraintClass)) .findFirst() .orElseThrow(() -> - new EvitaInternalError("Constraint `" + constraintClass.getName() + "` has to have defined supported type.") + new GenericEvitaInternalError("Constraint `" + constraintClass.getName() + "` has to have defined supported type.") ); } @@ -139,12 +139,12 @@ private ConstraintType resolveConstraintType(@Nonnull Class constraintClass) * Resolves concrete constraint property type enum from constraint class interfaces. */ @Nonnull - private ConstraintPropertyType resolveConstraintPropertyType(@Nonnull Class> constraintClass) { + private static ConstraintPropertyType resolveConstraintPropertyType(@Nonnull Class> constraintClass) { return Arrays.stream(ConstraintPropertyType.values()) .filter(t -> t.getRepresentingInterface().isAssignableFrom(constraintClass)) .findFirst() .orElseThrow(() -> - new EvitaInternalError("Constraint `" + constraintClass.getName() + "` has to have defined supported property type.") + new GenericEvitaInternalError("Constraint `" + constraintClass.getName() + "` has to have defined supported property type.") ); } @@ -152,7 +152,7 @@ private ConstraintPropertyType resolveConstraintPropertyType(@Nonnull Class resolveCreators(@Nonnull Class> constraintClass, - @Nonnull ConstraintType constraintType) { + private static List resolveCreators(@Nonnull Class> constraintClass, + @Nonnull ConstraintType constraintType) { return findCreators(constraintClass) .stream() .map(creatorTemplate -> { @@ -193,7 +193,7 @@ private List resolveCreators(@Nonnull Class resolveCreators(@Nonnull Class findCreators(@Nonnull Class> constraintClass) { + private static Set findCreators(@Nonnull Class> constraintClass) { final Stream constructors = Arrays.stream(constraintClass.getDeclaredConstructors()) .filter(constructor -> constructor.getAnnotation(Creator.class) != null) .map(it -> it); @@ -244,10 +244,10 @@ private Set findCreators(@Nonnull Class> con * Tries to find creator definition on constructor. */ @Nonnull - private Creator findCreatorAnnotation(@Nonnull Executable creator) { + private static Creator findCreatorAnnotation(@Nonnull Executable creator) { final Creator creatorDef = creator.getAnnotation(Creator.class); if (creatorDef == null) { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Constraint `" + creator.getDeclaringClass().getName() + "` has been registered, creator constructor found but there is no creator definition specified." ); } @@ -258,7 +258,7 @@ private Creator findCreatorAnnotation(@Nonnull Executable creator) { * Transforms creator constructor Java parameters to its corresponding descriptors in same order. */ @Nonnull - private List resolveCreatorParameters(@Nonnull Executable creator, @Nonnull ConstraintType constraintType) { + private static List resolveCreatorParameters(@Nonnull Executable creator, @Nonnull ConstraintType constraintType) { final List parameterDescriptors = new LinkedList<>(); final Parameter[] creatorParameters = creator.getParameters(); @@ -298,7 +298,7 @@ private List resolveCreatorParameters(@Nonnull Executable c continue; } - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Constraint `" + creator.getDeclaringClass().getName() + "` has creator parameter without supported annotation." ); } @@ -309,8 +309,8 @@ private List resolveCreatorParameters(@Nonnull Executable c * Tries to resolve constructor parameter as classifier parameter. */ @Nullable - private ClassifierParameterDescriptor resolveClassifierParameter(@Nonnull Parameter parameter, - @Nonnull Class constraintClass) { + private static ClassifierParameterDescriptor resolveClassifierParameter(@Nonnull Parameter parameter, + @Nonnull Class constraintClass) { final Classifier classifier = parameter.getAnnotation(Classifier.class); if (classifier == null) { return null; @@ -331,7 +331,7 @@ private ClassifierParameterDescriptor resolveClassifierParameter(@Nonnull Parame * Tries to resolve constructor parameter as value parameter. */ @Nonnull - private Optional resolveValueParameter(@Nonnull Parameter parameter) { + private static Optional resolveValueParameter(@Nonnull Parameter parameter) { final Value definition = parameter.getAnnotation(Value.class); //noinspection unchecked final Class parameterType = (Class) parameter.getType(); @@ -367,9 +367,9 @@ private Optional resolveValueParameter(@Nonnull Parame * Tries to resolve constructor parameter as direct child parameter. */ @Nonnull - private Optional resolveChildParameter(@Nonnull Parameter parameter, - @Nonnull Class constraintClass, - @Nonnull ConstraintType constraintType) { + private static Optional resolveChildParameter(@Nonnull Parameter parameter, + @Nonnull Class constraintClass, + @Nonnull ConstraintType constraintType) { final Child definition = parameter.getAnnotation(Child.class); final Class parameterType = parameter.getType(); final Class parameterItemType = parameterType.isArray() ? parameterType.getComponentType() : parameterType; @@ -426,9 +426,9 @@ private Optional resolveChildParameter(@Nonnull Parame * Tries to resolve constructor parameter as additional child parameter. */ @Nonnull - private Optional resolveAdditionalChildParameter(@Nonnull Parameter parameter, - @Nonnull Class constraintClass, - @Nonnull ConstraintType constraintType) { + private static Optional resolveAdditionalChildParameter(@Nonnull Parameter parameter, + @Nonnull Class constraintClass, + @Nonnull ConstraintType constraintType) { final AdditionalChild definition = parameter.getAnnotation(AdditionalChild.class); final Class parameterType = parameter.getType(); diff --git a/evita_query/src/main/java/io/evitadb/api/query/parser/Classifier.java b/evita_query/src/main/java/io/evitadb/api/query/parser/Classifier.java index d94ae6deb..2f9814042 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/parser/Classifier.java +++ b/evita_query/src/main/java/io/evitadb/api/query/parser/Classifier.java @@ -23,7 +23,7 @@ package io.evitadb.api.query.parser; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -87,7 +87,7 @@ public String[] asClassifierArray() { } else { // correct passed type from client should be checked at visitor level, here should be should correct checked type // if everything is correct on parser side - throw new EvitaInternalError("Expected variadic string value but got `" + actualValue.getClass().getName() + "`."); + throw new GenericEvitaInternalError("Expected variadic string value but got `" + actualValue.getClass().getName() + "`."); } } } diff --git a/evita_query/src/main/java/io/evitadb/api/query/parser/ParserExecutor.java b/evita_query/src/main/java/io/evitadb/api/query/parser/ParserExecutor.java index cde3260e3..c832f3d3c 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/parser/ParserExecutor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/parser/ParserExecutor.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ package io.evitadb.api.query.parser; import io.evitadb.api.query.parser.error.EvitaQLInvalidQueryError; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -62,10 +62,10 @@ public static T execute(@Nonnull ParseContext context, @Nonnull Supplier throw evitaQLInvalidQueryError; } else { // probably missed to wrap error with EvitaQL error, therefore it should be checked - throw new EvitaInternalError(cause.getMessage(), "Internal error occurred during query parsing.", cause); + throw new GenericEvitaInternalError(cause.getMessage(), "Internal error occurred during query parsing.", cause); } } catch (Exception e) { - throw new EvitaInternalError(e.getMessage(), "Internal error occurred during query parsing.", e); + throw new GenericEvitaInternalError(e.getMessage(), "Internal error occurred during query parsing.", e); } finally { CONTEXT.remove(); } diff --git a/evita_query/src/main/java/io/evitadb/api/query/parser/Value.java b/evita_query/src/main/java/io/evitadb/api/query/parser/Value.java index 19979297a..8e1e09899 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/parser/Value.java +++ b/evita_query/src/main/java/io/evitadb/api/query/parser/Value.java @@ -27,7 +27,6 @@ import io.evitadb.dataType.DateTimeRange; import io.evitadb.dataType.EvitaDataTypes; import io.evitadb.dataType.LongNumberRange; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; import io.evitadb.utils.Assert; import lombok.EqualsAndHashCode; @@ -35,7 +34,6 @@ import lombok.ToString; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.Serializable; import java.lang.reflect.Array; import java.math.BigDecimal; @@ -201,7 +199,7 @@ public Locale asLocale() { } else { // correct passed type from client should be checked at visitor level, here should be should correct checked type // if everything is correct on parser side - throw new EvitaInternalError("Expected locale or string value but got `" + actualValue.getClass().getName() + "`."); + throw new EvitaInvalidUsageException("Expected locale or string value but got `" + actualValue.getClass().getName() + "`."); } } @@ -269,7 +267,7 @@ public Integer[] asIntegerArray() { } catch (ClassCastException e) { // correct passed type from client should be checked at visitor level, here should be should correct checked type // if everything is correct on parser side - throw new EvitaInternalError("Unexpected type of value array `" + actualValue.getClass().getName() + "`."); + throw new EvitaInvalidUsageException("Unexpected type of value array `" + actualValue.getClass().getName() + "`."); } } @@ -293,30 +291,6 @@ public Locale[] asLocaleArray() { } } - @Nonnull - public > T[] asEnumArray(@Nonnull Class enumType) { - try { - return asArray( - v -> { - if (v instanceof Enum) { - return variadicValueItemAsSpecificType(v, enumType); - } else if (v instanceof EnumWrapper) { - return variadicValueItemAsSpecificType(v, EnumWrapper.class).toEnum(enumType); - } else { - throw new EvitaInvalidUsageException( - "Expected enum value but got `" + v.getClass().getName() + "`." - ); - } - }, - enumType - ); - } catch (ClassCastException e) { - // correct passed type from client should be checked at visitor level, here should be should correct checked type - // if everything is correct on parser side - throw new EvitaInternalError("Unexpected type of value array `" + actualValue.getClass().getName() + "`."); - } - } - private void assertValueIsOfType(@Nonnull Class type) { // correct passed type from client should be checked at visitor level, here should be should correct checked type // if everything is correct on parser side @@ -333,7 +307,7 @@ private T asSpecificType(@Nonnull Class type) { } @Nonnull - private T variadicValueItemAsSpecificType(@Nonnull Object item, @Nonnull Class type) { + private static T variadicValueItemAsSpecificType(@Nonnull Object item, @Nonnull Class type) { // correct passed type from client should be checked at visitor level, here should be should correct checked type // if everything is correct on parser side Assert.isPremiseValid( diff --git a/evita_query/src/main/java/io/evitadb/api/query/parser/visitor/EvitaQLRequireConstraintVisitor.java b/evita_query/src/main/java/io/evitadb/api/query/parser/visitor/EvitaQLRequireConstraintVisitor.java index dfcd603b9..c2f86423f 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/parser/visitor/EvitaQLRequireConstraintVisitor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/parser/visitor/EvitaQLRequireConstraintVisitor.java @@ -35,7 +35,7 @@ import io.evitadb.api.query.parser.grammar.EvitaQLParser.*; import io.evitadb.api.query.parser.grammar.EvitaQLVisitor; import io.evitadb.api.query.require.*; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import javax.annotation.Nonnull; @@ -256,7 +256,7 @@ public RequireConstraint visitAllRefsReferenceContentConstraint(@Nonnull AllRefs } else if (require instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } else { return new ReferenceContent( @@ -288,7 +288,7 @@ public RequireConstraint visitSingleRefReferenceContent1Constraint(SingleRefRefe } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, null, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -333,7 +333,7 @@ public RequireConstraint visitSingleRefReferenceContent3Constraint(SingleRefRefe } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, filterBy, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -380,7 +380,7 @@ public RequireConstraint visitSingleRefReferenceContent5Constraint(SingleRefRefe } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, null, orderBy, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -429,7 +429,7 @@ public RequireConstraint visitSingleRefReferenceContent7Constraint(SingleRefRefe } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, filterBy, orderBy, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -477,7 +477,7 @@ public RequireConstraint visitAllRefsWithAttributesReferenceContent1Constraint(A } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent((AttributeContent) null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -544,7 +544,7 @@ public RequireConstraint visitSingleRefReferenceContentWithAttributes1Constraint } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, null, null, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -621,7 +621,7 @@ public RequireConstraint visitSingleRefReferenceContentWithAttributes4Constraint } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, filterBy, null, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -702,7 +702,7 @@ public RequireConstraint visitSingleRefReferenceContentWithAttributes7Constraint } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, null, orderBy, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -784,7 +784,7 @@ public RequireConstraint visitSingleRefReferenceContentWithAttributes10Constrain } else if (requirement instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifier, filterBy, orderBy, null, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } ); @@ -862,7 +862,7 @@ public RequireConstraint visitMultipleRefsReferenceContentConstraint(@Nonnull Mu } else if (require instanceof final EntityGroupFetch entityGroupFetch) { return new ReferenceContent(classifiers, null, entityGroupFetch); } else { - throw new EvitaInternalError("Should never happen!"); + throw new GenericEvitaInternalError("Should never happen!"); } } else { return new ReferenceContent( diff --git a/evita_query/src/main/java/io/evitadb/api/query/require/EntityContentRequireCombiningCollector.java b/evita_query/src/main/java/io/evitadb/api/query/require/EntityContentRequireCombiningCollector.java index 1111e7be4..a38b3a692 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/require/EntityContentRequireCombiningCollector.java +++ b/evita_query/src/main/java/io/evitadb/api/query/require/EntityContentRequireCombiningCollector.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.api.query.require; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import java.util.Map; import java.util.Set; @@ -60,7 +60,7 @@ public BiConsumer, EntityContentRequir @Override public BinaryOperator, EntityContentRequire>> combiner() { return (entityContentRequireIndex, entityContentRequireIndex2) -> { - throw new EvitaInternalError("Cannot combine multiple content require indexes."); + throw new GenericEvitaInternalError("Cannot combine multiple content require indexes."); }; } diff --git a/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyOfReference.java b/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyOfReference.java index 20792da37..b91a35571 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyOfReference.java +++ b/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyOfReference.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import io.evitadb.api.query.descriptor.annotation.ConstraintDefinition; import io.evitadb.api.query.descriptor.annotation.Creator; import io.evitadb.api.query.order.OrderBy; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; @@ -79,7 +79,7 @@ * - {@link HierarchySiblings} * - {@link HierarchyChildren} * - {@link HierarchyParents} - * + * *

Visit detailed user documentation

* * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2021 @@ -202,7 +202,7 @@ public EmptyHierarchicalEntityBehaviour getEmptyHierarchicalEntityBehaviour() { .filter(EmptyHierarchicalEntityBehaviour.class::isInstance) .map(EmptyHierarchicalEntityBehaviour.class::cast) .findFirst() - .orElseThrow(() -> new EvitaInternalError("EmptyHierarchicalEntityBehaviour is a mandatory argument!")); + .orElseThrow(() -> new GenericEvitaInternalError("EmptyHierarchicalEntityBehaviour is a mandatory argument!")); } /** @@ -249,4 +249,4 @@ public RequireConstraint cloneWithArguments(@Nonnull Serializable[] newArguments ); } -} \ No newline at end of file +} diff --git a/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyStatistics.java b/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyStatistics.java index 8f8d2fa06..a6d55469f 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyStatistics.java +++ b/evita_query/src/main/java/io/evitadb/api/query/require/HierarchyStatistics.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import io.evitadb.api.query.descriptor.ConstraintDomain; import io.evitadb.api.query.descriptor.annotation.ConstraintDefinition; import io.evitadb.api.query.descriptor.annotation.Creator; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; @@ -108,9 +108,11 @@ public HierarchyStatistics( // unnecessary to duplicate the hierarchy prefix super( CONSTRAINT_NAME, - statisticsBase == null - ? StatisticsBase.WITHOUT_USER_FILTER - : statisticsBase + statisticsBase + ); + Assert.isTrue( + statisticsBase != null, + "StatisticsBase is mandatory argument, yet it was not provided!" ); } @@ -124,13 +126,16 @@ public HierarchyStatistics( super( CONSTRAINT_NAME, ArrayUtils.mergeArrays( - statisticsBase == null ? - new Serializable[] {StatisticsBase.WITHOUT_USER_FILTER} : new Serializable[] {statisticsBase}, + new Serializable[] {statisticsBase}, ArrayUtils.isEmpty(statisticsType) ? new StatisticsType[0] : statisticsType ) ); + Assert.isTrue( + statisticsBase != null, + "StatisticsBase is mandatory argument, yet it was not provided!" + ); } /** @@ -144,7 +149,7 @@ public StatisticsBase getStatisticsBase() { return sb; } } - throw new EvitaInternalError("StatisticsBase is mandatory argument, yet it was not found!"); + throw new GenericEvitaInternalError("StatisticsBase is mandatory argument, yet it was not found!"); } /** @@ -181,4 +186,4 @@ public RequireConstraint cloneWithArguments(@Nonnull Serializable[] newArguments return new HierarchyStatistics(newArguments); } -} \ No newline at end of file +} diff --git a/evita_query/src/main/java/io/evitadb/api/query/visitor/ConstraintCloneVisitor.java b/evita_query/src/main/java/io/evitadb/api/query/visitor/ConstraintCloneVisitor.java index ace7c23fe..9528d8f5c 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/visitor/ConstraintCloneVisitor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/visitor/ConstraintCloneVisitor.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import io.evitadb.api.query.ConstraintContainer; import io.evitadb.api.query.ConstraintLeaf; import io.evitadb.api.query.ConstraintVisitor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -50,6 +50,54 @@ public class ConstraintCloneVisitor implements ConstraintVisitor { private final BiFunction, Constraint> constraintTranslator; private Constraint result = null; + public static > T clone(@Nonnull T constraint, @Nullable BiFunction, Constraint> constraintTranslator) { + final ConstraintCloneVisitor visitor = new ConstraintCloneVisitor(constraintTranslator); + constraint.accept(visitor); + //noinspection unchecked + return (T) visitor.getResult(); + } + + /** + * Flattens constraint container if it's not necessary according to {@link ConstraintContainer#isNecessary()} logic. + */ + private static Constraint getFlattenedResult(@Nonnull Constraint constraint) { + if (constraint instanceof final ConstraintContainer constraintContainer) { + if (constraintContainer.isNecessary()) { + return constraint; + } else { + final Constraint[] children = constraintContainer.getChildren(); + if (children.length == 1) { + return children[0]; + } else { + throw new GenericEvitaInternalError( + "Constraint container " + constraintContainer.getName() + " states it's not necessary, " + + "but holds not exactly one child (" + children.length + ")!" + ); + } + } + } else { + return constraint; + } + } + + /** + * Returns true only if array and list contents are same - i.e. have same quantity, and same instances (in terms of + * reference identity). + */ + private static boolean isEqual(@Nonnull Constraint[] constraints, @Nonnull List> comparedConstraints) { + if (constraints.length != comparedConstraints.size()) { + return false; + } + for (int i = 0; i < constraints.length; i++) { + Constraint constraint = constraints[i]; + Constraint comparedConstraint = comparedConstraints.get(i); + if (constraint != comparedConstraint) { + return false; + } + } + return true; + } + private ConstraintCloneVisitor() { this(null); } @@ -58,13 +106,6 @@ private ConstraintCloneVisitor(@Nullable BiFunction constraint); } - public static > T clone(@Nonnull T constraint, @Nullable BiFunction, Constraint> constraintTranslator) { - final ConstraintCloneVisitor visitor = new ConstraintCloneVisitor(constraintTranslator); - constraint.accept(visitor); - //noinspection unchecked - return (T) visitor.getResult(); - } - @Override public void visit(@Nonnull Constraint constraint) { if (constraint instanceof final ConstraintContainer container) { @@ -100,7 +141,8 @@ public void visit(@Nonnull Constraint constraint) { * Method traverses the passed container applying cloning logic of this visitor. The method is expected to be called * from within the {@link #constraintTranslator} lambda. */ - public List> analyseChildren(ConstraintContainer constraint) { + @Nonnull + public List> analyseChildren(@Nonnull ConstraintContainer constraint) { levelConstraints.push(new ArrayList<>(constraint.getChildrenCount())); for (Constraint innerConstraint : constraint) { innerConstraint.accept(this); @@ -108,6 +150,7 @@ public List> analyseChildren(ConstraintContainer constraint) { return levelConstraints.pop(); } + @Nonnull public Constraint getResult() { return result; } @@ -115,10 +158,13 @@ public Constraint getResult() { /** * Creates new immutable container with modified count of children. */ - private > void createNewContainerWithModifiedChildren(ConstraintContainer container, - List> modifiedChildren, - List> modifiedAdditionalChildren) { - //noinspection unchecked + @SuppressWarnings("DuplicatedCode") + private > void createNewContainerWithModifiedChildren( + @Nonnull ConstraintContainer container, + @Nonnull List> modifiedChildren, + @Nonnull List> modifiedAdditionalChildren + ) { + //noinspection unchecked,SuspiciousToArrayCall final T[] newChildren = modifiedChildren.toArray(value -> (T[]) Array.newInstance(container.getType(), 0)); final Constraint[] newAdditionalChildren = modifiedAdditionalChildren.toArray(Constraint[]::new); final Constraint copyWithNewChildren = container.getCopyWithNewChildren(newChildren, newAdditionalChildren); @@ -130,7 +176,7 @@ private > void createNewContainerWithModifiedChildren(Co /** * Adds normalized constraint to the new composition. */ - private void addOnCurrentLevel(Constraint constraint) { + private void addOnCurrentLevel(@Nullable Constraint constraint) { if (constraint != null && constraint.isApplicable()) { if (levelConstraints.isEmpty()) { result = getFlattenedResult(constraint); @@ -139,45 +185,4 @@ private void addOnCurrentLevel(Constraint constraint) { } } } - - /** - * Flattens constraint container if it's not necessary according to {@link ConstraintContainer#isNecessary()} logic. - */ - private Constraint getFlattenedResult(Constraint constraint) { - if (constraint instanceof final ConstraintContainer constraintContainer) { - if (constraintContainer.isNecessary()) { - return constraint; - } else { - final Constraint[] children = constraintContainer.getChildren(); - if (children.length == 1) { - return children[0]; - } else { - throw new EvitaInternalError( - "Constraint container " + constraintContainer.getName() + " states it's not necessary, " + - "but holds not exactly one child (" + children.length + ")!" - ); - } - } - } else { - return constraint; - } - } - - /** - * Returns true only if array and list contents are same - i.e. have same quantity, and same instances (in terms of - * reference identity). - */ - private boolean isEqual(Constraint[] constraints, List> comparedConstraints) { - if (constraints.length != comparedConstraints.size()) { - return false; - } - for (int i = 0; i < constraints.length; i++) { - Constraint constraint = constraints[i]; - Constraint comparedConstraint = comparedConstraints.get(i); - if (constraint != comparedConstraint) { - return false; - } - } - return true; - } } diff --git a/evita_query/src/main/java/io/evitadb/api/query/visitor/QueryPurifierVisitor.java b/evita_query/src/main/java/io/evitadb/api/query/visitor/QueryPurifierVisitor.java index 6fc5afd3d..b5a79e7f1 100644 --- a/evita_query/src/main/java/io/evitadb/api/query/visitor/QueryPurifierVisitor.java +++ b/evita_query/src/main/java/io/evitadb/api/query/visitor/QueryPurifierVisitor.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import io.evitadb.api.query.ConstraintContainer; import io.evitadb.api.query.ConstraintLeaf; import io.evitadb.api.query.ConstraintVisitor; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -65,6 +65,47 @@ public static Constraint purify(@Nonnull Constraint constraint, @Nullable return visitor.getResult(); } + /** + * Flattens constraint container if it's not necessary according to {@link ConstraintContainer#isNecessary()} logic. + */ + private static Constraint getFlattenedResult(@Nonnull Constraint constraint) { + if (constraint instanceof final ConstraintContainer constraintContainer) { + if (constraintContainer.isNecessary()) { + return constraint; + } else { + final Constraint[] children = constraintContainer.getChildren(); + if (children.length == 1) { + return children[0]; + } else { + throw new GenericEvitaInternalError( + "Constraint container " + constraintContainer.getName() + " states it's not necessary, " + + "but holds not exactly one child (" + children.length + ")!" + ); + } + } + } else { + return constraint; + } + } + + /** + * Returns true only if array and list contents are same - i.e. has same count and same instances (in terms of reference + * identity). + */ + private static boolean isEqual(@Nonnull Constraint[] constraints, @Nonnull List> comparedConstraints) { + if (constraints.length != comparedConstraints.size()) { + return false; + } + for (int i = 0; i < constraints.length; i++) { + Constraint constraint = constraints[i]; + Constraint comparedConstraint = comparedConstraints.get(i); + if (constraint != comparedConstraint) { + return false; + } + } + return true; + } + private QueryPurifierVisitor() { this(null); } @@ -102,6 +143,7 @@ public void visit(@Nonnull Constraint constraint) { } } + @Nonnull public Constraint getResult() { return result; } @@ -109,12 +151,13 @@ public Constraint getResult() { /** * Creates new immutable container with reduced count of children. */ + @SuppressWarnings("DuplicatedCode") private > void createNewContainerWithReducedChildren( - ConstraintContainer container, - List> reducedChildren, - List> reducedAdditionalChildren + @Nonnull ConstraintContainer container, + @Nonnull List> reducedChildren, + @Nonnull List> reducedAdditionalChildren ) { - //noinspection unchecked + //noinspection unchecked,SuspiciousToArrayCall final T[] newChildren = reducedChildren.toArray(value -> (T[]) Array.newInstance(container.getType(), 0)); final Constraint[] newAdditionalChildren = reducedAdditionalChildren.toArray(Constraint[]::new); final Constraint copyWithNewChildren = container.getCopyWithNewChildren(newChildren, newAdditionalChildren); @@ -126,7 +169,7 @@ private > void createNewContainerWithReducedChildren( /** * Adds normalized constraint to the new composition. */ - private void addOnCurrentLevel(Constraint constraint) { + private void addOnCurrentLevel(@Nullable Constraint constraint) { if (constraint != null && constraint.isApplicable()) { if (levelConstraints.isEmpty()) { result = getFlattenedResult(constraint); @@ -135,45 +178,4 @@ private void addOnCurrentLevel(Constraint constraint) { } } } - - /** - * Flattens constraint container if it's not necessary according to {@link ConstraintContainer#isNecessary()} logic. - */ - private Constraint getFlattenedResult(Constraint constraint) { - if (constraint instanceof final ConstraintContainer constraintContainer) { - if (constraintContainer.isNecessary()) { - return constraint; - } else { - final Constraint[] children = constraintContainer.getChildren(); - if (children.length == 1) { - return children[0]; - } else { - throw new EvitaInternalError( - "Constraint container " + constraintContainer.getName() + " states it's not necessary, " + - "but holds not exactly one child (" + children.length + ")!" - ); - } - } - } else { - return constraint; - } - } - - /** - * Returns true only if array and list contents are same - i.e. has same count and same instances (in terms of reference - * identity). - */ - private boolean isEqual(Constraint[] constraints, List> comparedConstraints) { - if (constraints.length != comparedConstraints.size()) { - return false; - } - for (int i = 0; i < constraints.length; i++) { - Constraint constraint = constraints[i]; - Constraint comparedConstraint = comparedConstraints.get(i); - if (constraint != comparedConstraint) { - return false; - } - } - return true; - } } diff --git a/evita_store/evita_store_common/src/main/java/io/evitadb/store/dataType/serializer/DateTimeRangeSerializer.java b/evita_store/evita_store_common/src/main/java/io/evitadb/store/dataType/serializer/DateTimeRangeSerializer.java index 91f540bb5..b3c40e20a 100644 --- a/evita_store/evita_store_common/src/main/java/io/evitadb/store/dataType/serializer/DateTimeRangeSerializer.java +++ b/evita_store/evita_store_common/src/main/java/io/evitadb/store/dataType/serializer/DateTimeRangeSerializer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import io.evitadb.dataType.DateTimeRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import java.time.OffsetDateTime; @@ -68,7 +68,7 @@ public DateTimeRange read(Kryo kryo, Input input, Class } else if (to != null) { return DateTimeRange.until(to); } else { - throw new EvitaInternalError("The range have both bounds null. It should have not been created in the first place!"); + throw new GenericEvitaInternalError("The range have both bounds null. It should have not been created in the first place!"); } } diff --git a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/schema/CatalogSchemaStoragePart.java b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/schema/CatalogSchemaStoragePart.java index 9bf17061e..9a3f13af0 100644 --- a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/schema/CatalogSchemaStoragePart.java +++ b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/schema/CatalogSchemaStoragePart.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import io.evitadb.api.requestResponse.schema.CatalogSchemaContract; import io.evitadb.api.requestResponse.schema.dto.CatalogSchema; import io.evitadb.api.requestResponse.schema.dto.EntitySchema; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.store.model.StoragePart; import io.evitadb.store.service.KeyCompressor; import io.evitadb.utils.NamingConvention; @@ -58,7 +58,7 @@ public record CatalogSchemaStoragePart(CatalogSchema catalogSchema) implements S @Nonnull public static CatalogContract getDeserializationContextCatalog() { return ofNullable(CATALOG_ACCESSOR.get()) - .orElseThrow(() -> new EvitaInternalError("Catalog should be already set via CatalogSchemaStoragePart.deserializeWithCatalog")); + .orElseThrow(() -> new GenericEvitaInternalError("Catalog should be already set via CatalogSchemaStoragePart.deserializeWithCatalog")); } /** diff --git a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/serializer/EntitySchemaContext.java b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/serializer/EntitySchemaContext.java index 6fe5f2c6f..ffd94ef3a 100644 --- a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/serializer/EntitySchemaContext.java +++ b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/serializer/EntitySchemaContext.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ package io.evitadb.store.entity.serializer; import io.evitadb.api.requestResponse.schema.dto.EntitySchema; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import javax.annotation.Nonnull; import java.util.Deque; @@ -105,7 +105,7 @@ public static EntitySchema getEntitySchema() { return ofNullable(ENTITY_SCHEMA_SUPPLIER.get()) .filter(it -> !it.isEmpty()) .map(Deque::peek) - .orElseThrow(() -> new EvitaInternalError("Entity schema was not initialized in EntitySchemaContext!")); + .orElseThrow(() -> new GenericEvitaInternalError("Entity schema was not initialized in EntitySchemaContext!")); } private EntitySchemaContext() { diff --git a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/OffsetIndex.java b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/OffsetIndex.java index 9ecc3db99..6fc9f7566 100644 --- a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/OffsetIndex.java +++ b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/OffsetIndex.java @@ -27,7 +27,7 @@ import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.util.Pool; import io.evitadb.api.configuration.StorageOptions; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.exception.UnexpectedIOException; import io.evitadb.store.exception.StorageException; import io.evitadb.store.kryo.ObservableInput; @@ -728,7 +728,7 @@ public FileLocation close() { ); return fileOffsetDescriptor.fileLocation(); } else { - throw new EvitaInternalError("OffsetIndex is already being closed!"); + throw new GenericEvitaInternalError("OffsetIndex is already being closed!"); } } finally { shutdownDownProcedureActive.compareAndExchange(true, false); @@ -1729,7 +1729,7 @@ private NonFlushedValueSet getNonFlushedValues(long catalogVersion) { final long lastCatalogVersion = nv[nv.length - 1]; Assert.isPremiseValid( lastCatalogVersion == -1 || lastCatalogVersion <= catalogVersion, - () -> new EvitaInternalError( + () -> new GenericEvitaInternalError( "You're trying to write to an already completed version `" + catalogVersion + "`, current is `" + lastCatalogVersion + "`!", "You're trying to write to an already completed version!" ) diff --git a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/io/WriteOnlyOffHeapWithFileBackupHandle.java b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/io/WriteOnlyOffHeapWithFileBackupHandle.java index 538a57441..8eb406d96 100644 --- a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/io/WriteOnlyOffHeapWithFileBackupHandle.java +++ b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/io/WriteOnlyOffHeapWithFileBackupHandle.java @@ -25,6 +25,7 @@ import io.evitadb.api.configuration.StorageOptions; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.store.exception.StorageException; import io.evitadb.store.kryo.ObservableInput; import io.evitadb.store.kryo.ObservableOutput; @@ -230,7 +231,7 @@ public OffHeapWithFileBackupReference toReadOffHeapWithFileBackupReference() { this::releaseTemporaryFile ); } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "No content has been written using this write handle!" ); } @@ -472,7 +473,7 @@ private ReadOnlyHandle getDelegate() { ); } } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "No content has been written using source write handle!" ); } diff --git a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/model/OffsetIndexRecordTypeRegistry.java b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/model/OffsetIndexRecordTypeRegistry.java index 3911a6125..0cb403907 100644 --- a/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/model/OffsetIndexRecordTypeRegistry.java +++ b/evita_store/evita_store_key_value/src/main/java/io/evitadb/store/offsetIndex/model/OffsetIndexRecordTypeRegistry.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.store.offsetIndex.model; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.store.model.StoragePart; import io.evitadb.store.offsetIndex.OffsetIndex; import io.evitadb.store.service.StoragePartRegistry; @@ -76,7 +76,7 @@ public void registerFileOffsetIndexType(byte id, Class ty @Nonnull public Class typeFor(byte id) { return Optional.ofNullable(idToTypeIndex.get(id)) - .orElseThrow(() -> new EvitaInternalError("Type id " + id + " cannot be handled by OffsetIndex!")); + .orElseThrow(() -> new GenericEvitaInternalError("Type id " + id + " cannot be handled by OffsetIndex!")); } /** @@ -84,7 +84,7 @@ public Class typeFor(byte id) { */ public byte idFor(@Nonnull Class type) { return Optional.ofNullable(typeToIdIndex.get(type)) - .orElseThrow(() -> new EvitaInternalError("Type " + type + " cannot be handled by OffsetIndex!")); + .orElseThrow(() -> new GenericEvitaInternalError("Type " + type + " cannot be handled by OffsetIndex!")); } } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/cache/serializer/AbstractFlattenedFormulaSerializer.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/cache/serializer/AbstractFlattenedFormulaSerializer.java index d141212cf..0001d77da 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/cache/serializer/AbstractFlattenedFormulaSerializer.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/cache/serializer/AbstractFlattenedFormulaSerializer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ import io.evitadb.core.query.algebra.price.filteredPriceRecords.NonResolvedFilteredPriceRecords; import io.evitadb.core.query.algebra.price.filteredPriceRecords.ResolvedFilteredPriceRecords; import io.evitadb.core.query.algebra.price.termination.PriceEvaluationContext; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.GlobalEntityIndex; import io.evitadb.index.bitmap.BaseBitmap; import io.evitadb.index.bitmap.Bitmap; @@ -134,7 +134,7 @@ protected void writeFilteredPriceRecords(@Nonnull Kryo kryo, @Nonnull Output out writeResolvedPriceRecords(output, combinedPriceRecords.getResolvedFilteredPriceRecords()); writeLazyEvaluatedPriceRecords(kryo, output, combinedPriceRecords.getLazyEvaluatedEntityPriceRecords()); } else { - throw new EvitaInternalError("Unknown type of FilteredPriceRecords " + filteredPriceRecords.getClass().getName()); + throw new GenericEvitaInternalError("Unknown type of FilteredPriceRecords " + filteredPriceRecords.getClass().getName()); } @@ -158,7 +158,7 @@ protected FilteredPriceRecords readFilteredPriceRecords(@Nonnull Kryo kryo, @Non final LazyEvaluatedEntityPriceRecords lazyEvaluatedEntityPriceRecords = readLazyEvaluatedEntityPriceRecords(kryo, input, globalEntityIndexAccessor); return new CombinedPriceRecords(nonResolvedFilteredPriceRecords, lazyEvaluatedEntityPriceRecords); } else { - throw new EvitaInternalError("Unknown type of FilteredPriceRecords: `" + type + "`"); + throw new GenericEvitaInternalError("Unknown type of FilteredPriceRecords: `" + type + "`"); } } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java index fbfbed43b..3fb8d79b6 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java @@ -47,8 +47,8 @@ import io.evitadb.core.buffer.DataStoreIndexChanges; import io.evitadb.dataType.ClassifierType; import io.evitadb.dataType.PaginatedList; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.exception.InvalidClassifierFormatException; import io.evitadb.exception.UnexpectedIOException; import io.evitadb.index.CatalogIndex; @@ -705,7 +705,7 @@ public CatalogOffsetIndexStoragePartPersistenceService getStoragePartPersistence final int lookupIndex = index >= 0 ? index : (-index - 2); Assert.isPremiseValid( lookupIndex >= 0 && lookupIndex < this.catalogPersistenceServiceVersions.length, - () -> new EvitaInternalError("Catalog version " + catalogVersion + " not found in the catalog persistence service versions!") + () -> new GenericEvitaInternalError("Catalog version " + catalogVersion + " not found in the catalog persistence service versions!") ); return this.catalogStoragePartPersistenceService.get( this.catalogPersistenceServiceVersions[lookupIndex] @@ -924,7 +924,7 @@ public Optional flush( ); Assert.isPremiseValid( newPersistenceService.getEntityCollectionHeader().equals(compactedHeader), - () -> new EvitaInternalError("Unexpected header mismatch!") + () -> new GenericEvitaInternalError("Unexpected header mismatch!") ); return of(newPersistenceService); } else { @@ -1081,7 +1081,7 @@ public CatalogPersistenceService replaceWith( final Path filePathForRename = filePath.getParent().resolve(fileNameToRename); Assert.isPremiseValid( it.renameTo(filePathForRename.toFile()), - () -> new EvitaInternalError( + () -> new GenericEvitaInternalError( "Failed to rename `" + it.getAbsolutePath() + "` to `" + filePathForRename.toAbsolutePath() + "`!", "Failed to rename one of the `" + this.catalogName + "` catalog files to target catalog name!" ) @@ -1126,7 +1126,7 @@ public CatalogPersistenceService replaceWith( if (temporaryOriginal != null) { Assert.isPremiseValid( temporaryOriginal.toFile().renameTo(newPath.toFile()), - () -> new EvitaInternalError( + () -> new GenericEvitaInternalError( "Failed to rename the original directory back to `" + newPath.toAbsolutePath() + "` the original catalog will not be available as well!", "Failing to rename the original directory back to the original catalog will not be available as well!", ex @@ -1175,7 +1175,7 @@ public EntityCollectionPersistenceService replaceCollectionWith( if (ex instanceof RuntimeException runtimeException) { throw runtimeException; } else { - throw new EvitaInternalError( + throw new GenericEvitaInternalError( "Unexpected error during the entity collection renaming: " + ex.getMessage(), "Unexpected error during the entity collection renaming!", ex @@ -1575,7 +1575,7 @@ CatalogBootstrap recordBootstrap(long catalogVersion, @Nonnull String newCatalog this.observableOutputKeeper ) ), - () -> new EvitaInternalError("Failed to replace the bootstrap write handle in a critical section!") + () -> new GenericEvitaInternalError("Failed to replace the bootstrap write handle in a critical section!") ); } @@ -1613,7 +1613,7 @@ void trimBootstrapFile(@Nonnull OffsetDateTime toTimestamp) { this.observableOutputKeeper ) ), - () -> new EvitaInternalError("Failed to replace the bootstrap write handle in a critical section!") + () -> new GenericEvitaInternalError("Failed to replace the bootstrap write handle in a critical section!") ); } @@ -1772,7 +1772,7 @@ private void removeCatalogPersistenceServiceForVersion(long catalogVersion) { final int lookupIndex = index >= 0 ? index : (-index - 2); Assert.isPremiseValid( lookupIndex >= 0 && lookupIndex < this.catalogPersistenceServiceVersions.length, - () -> new EvitaInternalError("Catalog version " + catalogVersion + " not found in the catalog persistence service versions!") + () -> new GenericEvitaInternalError("Catalog version " + catalogVersion + " not found in the catalog persistence service versions!") ); final long versionToRemove = this.catalogPersistenceServiceVersions[lookupIndex]; this.catalogPersistenceServiceVersions = ArrayUtils.removeLongFromArrayOnIndex(this.catalogPersistenceServiceVersions, lookupIndex); diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java index c3bd15504..bbdaa0728 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java @@ -54,7 +54,7 @@ import io.evitadb.core.buffer.DataStoreChanges; import io.evitadb.core.buffer.DataStoreIndexChanges; import io.evitadb.core.buffer.DataStoreMemoryBuffer; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.EntityIndex; import io.evitadb.index.EntityIndexKey; import io.evitadb.index.EntityIndexType; @@ -959,7 +959,7 @@ public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonn case CARDINALITY -> fetchCardinalityIndex(catalogVersion, entityIndexId, storagePartPersistenceService, cardinalityIndexes, attributeIndexKey); default -> - throw new EvitaInternalError("Unknown attribute index type: " + attributeIndexKey.indexType()); + throw new GenericEvitaInternalError("Unknown attribute index type: " + attributeIndexKey.indexType()); } } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/index/serializer/PriceListAndCurrencySuperIndexStoragePartSerializer.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/index/serializer/PriceListAndCurrencySuperIndexStoragePartSerializer.java index cf45edeca..b44355afd 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/index/serializer/PriceListAndCurrencySuperIndexStoragePartSerializer.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/index/serializer/PriceListAndCurrencySuperIndexStoragePartSerializer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.index.price.model.PriceIndexKey; import io.evitadb.index.price.model.priceRecord.PriceRecord; import io.evitadb.index.price.model.priceRecord.PriceRecordContract; @@ -77,7 +77,7 @@ public void write(Kryo kryo, Output output, PriceListAndCurrencySuperIndexStorag output.writeInt(priceRecord.priceWithTax(), true); output.writeInt(priceRecord.priceWithoutTax(), true); } else { - throw new EvitaInternalError("Unknown implementation `" + priceRecord.getClass() + "` of PriceRecordContract!"); + throw new GenericEvitaInternalError("Unknown implementation `" + priceRecord.getClass() + "` of PriceRecordContract!"); } } } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/query/serializer/filter/AttributeInRangeSerializer.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/query/serializer/filter/AttributeInRangeSerializer.java index 980621393..be56f650f 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/query/serializer/filter/AttributeInRangeSerializer.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/query/serializer/filter/AttributeInRangeSerializer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import io.evitadb.api.query.filter.AttributeInRange; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.RequiredArgsConstructor; import java.io.Serializable; @@ -57,7 +57,7 @@ public AttributeInRange read(Kryo kryo, Input input, Class { private static final Function IMPOSSIBLE_EXCEPTION_PRODUCER = s -> { - throw new EvitaInternalError("Sanity check!"); + throw new GenericEvitaInternalError("Sanity check!"); }; @Override diff --git a/evita_test_support/src/main/java/io/evitadb/test/EvitaTestSupport.java b/evita_test_support/src/main/java/io/evitadb/test/EvitaTestSupport.java index ccdd91316..dcb12abc3 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/EvitaTestSupport.java +++ b/evita_test_support/src/main/java/io/evitadb/test/EvitaTestSupport.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ package io.evitadb.test; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import org.apache.commons.io.FileUtils; import org.junit.jupiter.params.provider.Arguments; @@ -122,7 +122,7 @@ default void cleanTestDirectoryWithRethrow() { try { cleanTestDirectory(); } catch (IOException e) { - throw new EvitaInternalError("Cannot empty target directory!", e); + throw new GenericEvitaInternalError("Cannot empty target directory!", e); } } @@ -133,7 +133,7 @@ default void cleanTestSubDirectoryWithRethrow(String directory) { try { cleanTestSubDirectory(directory); } catch (IOException e) { - throw new EvitaInternalError("Cannot empty target directory!", e); + throw new GenericEvitaInternalError("Cannot empty target directory!", e); } } @@ -165,7 +165,7 @@ default Path getDataDirectory() { dataPath = Path.of(externallyDefinedPath); } if (!dataPath.toFile().exists()) { - throw new EvitaInternalError("Data directory `" + dataPath + "` does not exist!"); + throw new GenericEvitaInternalError("Data directory `" + dataPath + "` does not exist!"); } return dataPath; } diff --git a/evita_test_support/src/main/java/io/evitadb/test/builder/JsonArrayBuilder.java b/evita_test_support/src/main/java/io/evitadb/test/builder/JsonArrayBuilder.java index 420e2f51d..4232db7b8 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/builder/JsonArrayBuilder.java +++ b/evita_test_support/src/main/java/io/evitadb/test/builder/JsonArrayBuilder.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -82,7 +82,7 @@ public static ArrayNode jsonArray(@Nonnull List items) { } else if (item instanceof Currency value) { builder.add(value); } else { - throw new EvitaInternalError("Unsupported item type."); + throw new GenericEvitaInternalError("Unsupported item type."); } } return builder.build(); diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/ApiClient.java b/evita_test_support/src/main/java/io/evitadb/test/client/ApiClient.java index 24beb7dae..867804812 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/ApiClient.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/ApiClient.java @@ -25,7 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import okhttp3.ResponseBody; @@ -81,12 +81,12 @@ public void checkServerTrusted(X509Certificate[] certs, String authType) {} try { sc = SSLContext.getInstance("SSL"); } catch (NoSuchAlgorithmException e) { - throw new EvitaInternalError("Cannot get SSL context.", e); + throw new GenericEvitaInternalError("Cannot get SSL context.", e); } try { sc.init(null, trustAllCerts, new java.security.SecureRandom()); } catch (KeyManagementException e) { - throw new EvitaInternalError("Cannot init SSL context with custom trust manager.", e); + throw new GenericEvitaInternalError("Cannot init SSL context with custom trust manager.", e); } // Create an all-trusting host verifier diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/GraphQLClient.java b/evita_test_support/src/main/java/io/evitadb/test/client/GraphQLClient.java index f232411ed..0aa62ec60 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/GraphQLClient.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/GraphQLClient.java @@ -25,7 +25,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.graphql.io.GraphQLRequest; import io.evitadb.utils.Assert; import okhttp3.MediaType; @@ -58,7 +58,7 @@ public JsonNode call(@Nonnull String instancePath, @Nonnull String document) { try { request = createRequest(instancePath, document); } catch (IOException e) { - throw new EvitaInternalError("Unexpected error.", e); + throw new GenericEvitaInternalError("Unexpected error.", e); } try (Response response = client.newCall(request).execute()) { @@ -71,12 +71,12 @@ public JsonNode call(@Nonnull String instancePath, @Nonnull String document) { } if (responseCode >= 400 && responseCode <= 499 && responseCode != 404) { final String errorResponseString = response.body() != null ? response.body().string() : "no response body"; - throw new EvitaInternalError("Call to GraphQL instance `" + this.url + instancePath + "` ended with status " + responseCode + ", query was:\n" + document + "\n and response was: \n" + errorResponseString); + throw new GenericEvitaInternalError("Call to GraphQL instance `" + this.url + instancePath + "` ended with status " + responseCode + ", query was:\n" + document + "\n and response was: \n" + errorResponseString); } - throw new EvitaInternalError("Call to GraphQL server ended with status " + responseCode + ", query was:\n" + document); + throw new GenericEvitaInternalError("Call to GraphQL server ended with status " + responseCode + ", query was:\n" + document); } catch (IOException e) { - throw new EvitaInternalError("Unexpected error.", e); + throw new GenericEvitaInternalError("Unexpected error.", e); } } @@ -92,12 +92,10 @@ private Request createRequest(@Nonnull String instancePath, @Nonnull String docu protected String createRequestBody(@Nonnull String document) throws IOException { final GraphQLRequest requestBody = new GraphQLRequest(document, null, null, null); - final String requestBodyJson = objectMapper.writeValueAsString(requestBody); - - return requestBodyJson; + return objectMapper.writeValueAsString(requestBody); } - private void validateResponseBody(@Nonnull JsonNode responseBody) throws JsonProcessingException { + private static void validateResponseBody(@Nonnull JsonNode responseBody) throws JsonProcessingException { final JsonNode errors = responseBody.get("errors"); Assert.isPremiseValid( errors == null || errors.isNull() || errors.isEmpty(), diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/RestClient.java b/evita_test_support/src/main/java/io/evitadb/test/client/RestClient.java index 48669e6ec..3fe1cbada 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/RestClient.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/RestClient.java @@ -24,7 +24,7 @@ package io.evitadb.test.client; import com.fasterxml.jackson.databind.JsonNode; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.Assert; import okhttp3.MediaType; import okhttp3.Request; @@ -64,12 +64,12 @@ public Optional call(@Nonnull String method, @Nonnull String resource, } if (responseCode >= 400 && responseCode <= 499) { final String errorResponseString = response.body() != null ? response.body().string() : "no response body"; - throw new EvitaInternalError("Call to REST server `" + this.url + resource + "` ended with status " + responseCode + " and response: \n" + errorResponseString); + throw new GenericEvitaInternalError("Call to REST server `" + this.url + resource + "` ended with status " + responseCode + " and response: \n" + errorResponseString); } - throw new EvitaInternalError("Call to REST server `" + this.url + resource + "` ended with status " + responseCode); + throw new GenericEvitaInternalError("Call to REST server `" + this.url + resource + "` ended with status " + responseCode); } catch (IOException e) { - throw new EvitaInternalError("Unexpected error.", e); + throw new GenericEvitaInternalError("Unexpected error.", e); } } @@ -84,7 +84,7 @@ private Request createRequest(@Nonnull String method, @Nonnull String resource, } - private void validateResponseBody(@Nonnull JsonNode responseBody) { + private static void validateResponseBody(@Nonnull JsonNode responseBody) { Assert.isPremiseValid( responseBody != null && !responseBody.isNull(), "Call to REST server ended with empty data." diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/query/ConstraintParameterValueResolver.java b/evita_test_support/src/main/java/io/evitadb/test/client/query/ConstraintParameterValueResolver.java index 0881a92ef..b47c462e8 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/query/ConstraintParameterValueResolver.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/query/ConstraintParameterValueResolver.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import io.evitadb.api.query.descriptor.ConstraintCreator; import io.evitadb.api.query.descriptor.ConstraintCreator.ParameterDescriptor; import io.evitadb.api.query.descriptor.annotation.AliasForParameter; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.utils.StringUtils; import javax.annotation.Nonnull; @@ -51,14 +51,14 @@ public Optional resolveParameterValue(@Nonnull Constraint constraint, @Non final Method getter = findGetter(constraintClass.getDeclaredMethods(), parameter.name()) .or(() -> findGetter(constraintClass.getMethods(), parameter.name())) .orElseThrow(() -> - new EvitaInternalError("Could not find getter for parameter `" + parameter.name() + "` in constraint `" + constraintClass.getSimpleName() + "`.")); + new GenericEvitaInternalError("Could not find getter for parameter `" + parameter.name() + "` in constraint `" + constraintClass.getSimpleName() + "`.")); getter.trySetAccessible(); final Object parameterValue; try { parameterValue = getter.invoke(constraint); } catch (IllegalAccessException | InvocationTargetException e) { - throw new EvitaInternalError("Could not invoke getter for parameter `" + parameter.name() + "` in constraint `" + constraintClass.getSimpleName() + "`.", e); + throw new GenericEvitaInternalError("Could not invoke getter for parameter `" + parameter.name() + "` in constraint `" + constraintClass.getSimpleName() + "`.", e); } if (parameterValue instanceof Optional optionalParameterValue) { diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/query/ObjectJsonSerializer.java b/evita_test_support/src/main/java/io/evitadb/test/client/query/ObjectJsonSerializer.java index 7e4774dd7..86ee1239b 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/query/ObjectJsonSerializer.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/query/ObjectJsonSerializer.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import io.evitadb.dataType.Range; import io.evitadb.dataType.data.ComplexDataObjectToJsonConverter; import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.catalog.dataApi.model.PriceDescriptor; import lombok.Getter; @@ -101,7 +102,7 @@ public JsonNode serializeObject(@Nullable Object value) { if (value instanceof PriceContract price) return serialize(price); if (value.getClass().isEnum()) return serialize((Enum) value); - throw new EvitaInternalError("Serialization of value of class: " + value.getClass().getName() + " is not implemented yet."); + throw new GenericEvitaInternalError("Serialization of value of class: " + value.getClass().getName() + " is not implemented yet."); } /** diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/EntityFetchConverter.java b/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/EntityFetchConverter.java index 2b11c87c1..4634419e3 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/EntityFetchConverter.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/EntityFetchConverter.java @@ -35,13 +35,15 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.NamedSchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.ExternalApiNamingConventions; import io.evitadb.externalApi.api.catalog.dataApi.constraint.HierarchyDataLocator; import io.evitadb.externalApi.api.catalog.dataApi.constraint.ReferenceDataLocator; +import io.evitadb.externalApi.api.catalog.dataApi.model.AttributesProviderDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.EntityDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.PriceDescriptor; import io.evitadb.externalApi.api.catalog.dataApi.model.ReferenceDescriptor; +import io.evitadb.externalApi.graphql.api.catalog.dataApi.model.BigDecimalFieldHeaderDescriptor; import io.evitadb.externalApi.graphql.api.catalog.dataApi.model.GraphQLEntityDescriptor; import io.evitadb.externalApi.graphql.api.catalog.dataApi.model.entity.AssociatedDataFieldHeaderDescriptor; import io.evitadb.externalApi.graphql.api.catalog.dataApi.model.entity.AttributesFieldHeaderDescriptor; @@ -74,13 +76,13 @@ public class EntityFetchConverter extends RequireConverter { public EntityFetchConverter(@Nonnull CatalogSchemaContract catalogSchema, - @Nonnull Query query) { + @Nonnull Query query) { super(catalogSchema, query); } public void convert(@Nonnull GraphQLOutputFieldsBuilder fieldsBuilder, @Nullable String entityType, - @Nullable Locale locale, + @Nullable Locale locale, @Nullable EntityFetchRequire entityFetch) { final Optional entitySchema = Optional.ofNullable(entityType).flatMap(catalogSchema::getEntitySchema); @@ -136,7 +138,7 @@ public void convert(@Nonnull GraphQLOutputFieldsBuilder fieldsBuilder, } private void convertHierarchyContent(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, - @Nullable HierarchyContent hierarchyContent, + @Nullable HierarchyContent hierarchyContent, @Nullable Locale locale, @Nonnull EntityFetchRequire entityFetch, @Nonnull EntitySchemaContract entitySchema) { @@ -159,26 +161,25 @@ private void convertHierarchyContent(@Nonnull GraphQLOutputFieldsBuilder entityF private ArgumentSupplier[] getHierarchyContentArguments(@Nonnull HierarchyContent hierarchyContent, @Nonnull EntitySchemaContract entitySchema) { final Optional stopAt = hierarchyContent.getStopAt(); - if (stopAt.isEmpty()) { - return new ArgumentSupplier[0]; - } + return stopAt + .map(requireConstraints -> new ArgumentSupplier[]{ + (offset, multipleArguments) -> new Argument( + ParentsFieldHeaderDescriptor.STOP_AT, + offset, + multipleArguments, + convertRequireConstraint(new HierarchyDataLocator(entitySchema.getName()), requireConstraints) + .orElseThrow() + ) + }) + .orElseGet(() -> new ArgumentSupplier[0]); - return new ArgumentSupplier[] { - (offset, multipleArguments) -> new Argument( - ParentsFieldHeaderDescriptor.STOP_AT, - offset, - multipleArguments, - convertRequireConstraint(new HierarchyDataLocator(entitySchema.getName()), stopAt.get()) - .orElseThrow() - ) - }; } - private void convertAttributeContent(@Nonnull GraphQLOutputFieldsBuilder fieldsBuilder, - @Nullable AttributeContent attributeContent, - @Nullable Locale filterLocale, - @Nullable Set requiredLocales, - @Nonnull AttributeSchemaProvider attributeSchemaProvider) { + private static void convertAttributeContent(@Nonnull GraphQLOutputFieldsBuilder fieldsBuilder, + @Nullable AttributeContent attributeContent, + @Nullable Locale filterLocale, + @Nullable Set requiredLocales, + @Nonnull AttributeSchemaProvider attributeSchemaProvider) { if (attributeContent != null) { final List attributesToFetch; @@ -198,35 +199,35 @@ private void convertAttributeContent(@Nonnull GraphQLOutputFieldsBuilder fieldsB }) .toList(); if (attributesToFetch.isEmpty()) { - throw new EvitaInternalError("There are no attributes to fetch for schema `" + ((NamedSchemaContract)attributeSchemaProvider).getName() + "`. This is strange, this can produce invalid query!"); + throw new GenericEvitaInternalError("There are no attributes to fetch for schema `" + ((NamedSchemaContract) attributeSchemaProvider).getName() + "`. This is strange, this can produce invalid query!"); } } if (requiredLocales == null) { // there will be max one locale from filter fieldsBuilder.addObjectField( - EntityDescriptor.ATTRIBUTES, + AttributesProviderDescriptor.ATTRIBUTES, getAttributesFieldsBuilder(attributesToFetch) ); } else if (requiredLocales.size() == 1) { fieldsBuilder.addObjectField( - EntityDescriptor.ATTRIBUTES, + AttributesProviderDescriptor.ATTRIBUTES, getAttributesFieldsBuilder(attributesToFetch), (offset, multipleArguments) -> new Argument(AttributesFieldHeaderDescriptor.LOCALE, offset, multipleArguments, requiredLocales.iterator().next()) ); } else { final List globalAttributes = attributesToFetch.stream().filter(it -> !it.isLocalized()).toList(); fieldsBuilder.addObjectField( - EntityDescriptor.ATTRIBUTES.name() + "Global", - EntityDescriptor.ATTRIBUTES, + AttributesProviderDescriptor.ATTRIBUTES.name() + "Global", + AttributesProviderDescriptor.ATTRIBUTES, getAttributesFieldsBuilder(globalAttributes) ); final List localizedAttributes = attributesToFetch.stream().filter(AttributeSchemaContract::isLocalized).toList(); for (Locale locale : requiredLocales) { fieldsBuilder.addObjectField( - EntityDescriptor.ATTRIBUTES.name() + StringUtils.toPascalCase(locale.toString()), - EntityDescriptor.ATTRIBUTES, + AttributesProviderDescriptor.ATTRIBUTES.name() + StringUtils.toPascalCase(locale.toString()), + AttributesProviderDescriptor.ATTRIBUTES, getAttributesFieldsBuilder(localizedAttributes), (offset, multipleArguments) -> new Argument(AttributesFieldHeaderDescriptor.LOCALE, offset, multipleArguments, locale) ); @@ -236,7 +237,7 @@ private void convertAttributeContent(@Nonnull GraphQLOutputFieldsBuilder fieldsB } @Nonnull - private Consumer getAttributesFieldsBuilder(@Nonnull List attributes) { + private static Consumer getAttributesFieldsBuilder(@Nonnull List attributes) { return attributesBuilder -> { for (AttributeSchemaContract attribute : attributes) { attributesBuilder.addPrimitiveField(attribute.getNameVariant(ExternalApiNamingConventions.PROPERTY_NAME_NAMING_CONVENTION)); @@ -244,11 +245,11 @@ private Consumer getAttributesFieldsBuilder(@Nonnull }; } - private void convertAssociatedDataContent(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, - @Nullable AssociatedDataContent associatedDataContent, - @Nullable Locale filterLocale, - @Nullable Set requiredLocales, - @Nonnull EntitySchemaContract entitySchema) { + private static void convertAssociatedDataContent(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, + @Nullable AssociatedDataContent associatedDataContent, + @Nullable Locale filterLocale, + @Nullable Set requiredLocales, + @Nonnull EntitySchemaContract entitySchema) { if (associatedDataContent != null) { final List associatedDataToFetch; @@ -303,7 +304,7 @@ private void convertAssociatedDataContent(@Nonnull GraphQLOutputFieldsBuilder en } @Nonnull - private Consumer getAssociatedDataFieldsBuilder(@Nonnull List associatedDataSchemas) { + private static Consumer getAssociatedDataFieldsBuilder(@Nonnull List associatedDataSchemas) { return attributesBuilder -> { for (AssociatedDataSchemaContract associatedDataSchema : associatedDataSchemas) { attributesBuilder.addPrimitiveField(associatedDataSchema.getNameVariant(ExternalApiNamingConventions.PROPERTY_NAME_NAMING_CONVENTION)); @@ -313,7 +314,7 @@ private Consumer getAssociatedDataFieldsBuilder(@Non private void convertPriceContent(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, @Nullable PriceContent priceContent, - @Nullable Locale locale) { + @Nullable Locale locale) { if (priceContent != null) { final PriceContentMode fetchMode = priceContent.getFetchMode(); if (fetchMode == PriceContentMode.NONE) { @@ -421,18 +422,18 @@ private void convertPriceContent(@Nonnull GraphQLOutputFieldsBuilder entityField } ); } else { - throw new EvitaInternalError("Unsupported price content mode `" + fetchMode + "`."); // should never happen + throw new GenericEvitaInternalError("Unsupported price content mode `" + fetchMode + "`."); // should never happen } } } @Nonnull - private ArgumentSupplier[] getPriceValueFieldArguments(@Nullable Locale locale) { + private static ArgumentSupplier[] getPriceValueFieldArguments(@Nullable Locale locale) { if (locale == null) { return new ArgumentSupplier[0]; } - return new ArgumentSupplier[] { - (offset, multipleArguments) -> new Argument(PriceBigDecimalFieldHeaderDescriptor.FORMATTED, offset, multipleArguments, true), + return new ArgumentSupplier[]{ + (offset, multipleArguments) -> new Argument(BigDecimalFieldHeaderDescriptor.FORMATTED, offset, multipleArguments, true), (offset, multipleArguments) -> new Argument(PriceBigDecimalFieldHeaderDescriptor.WITH_CURRENCY, offset, multipleArguments, true) }; } @@ -440,8 +441,8 @@ private ArgumentSupplier[] getPriceValueFieldArguments(@Nullable Locale locale) private void convertReferenceContents(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, @Nonnull List referenceContents, @Nonnull String entityType, - @Nullable Locale filterLocale, - @Nullable Set requiredLocales, + @Nullable Locale filterLocale, + @Nullable Set requiredLocales, @Nonnull EntitySchemaContract entitySchema) { referenceContents.forEach(referenceContent -> { for (String referenceName : referenceContent.getReferenceNames()) { @@ -460,8 +461,8 @@ private void convertReferenceContents(@Nonnull GraphQLOutputFieldsBuilder entity private void convertReferenceContent(@Nonnull GraphQLOutputFieldsBuilder entityFieldsBuilder, @Nonnull String entityType, - @Nullable Locale filterLocale, - @Nullable Set requiredLocales, + @Nullable Locale filterLocale, + @Nullable Set requiredLocales, @Nonnull EntitySchemaContract entitySchema, @Nonnull ReferenceContent referenceContent, @Nonnull String referenceName) { @@ -509,8 +510,8 @@ private void convertReferenceContent(@Nonnull GraphQLOutputFieldsBuilder entityF @Nonnull private ArgumentSupplier[] getReferenceContentArguments(@Nonnull String entityType, - @Nonnull ReferenceContent referenceContent, - @Nonnull String referenceName) { + @Nonnull ReferenceContent referenceContent, + @Nonnull String referenceName) { if (referenceContent.getFilterBy().isEmpty() && referenceContent.getOrderBy().isEmpty()) { return new ArgumentSupplier[0]; } diff --git a/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/FacetSummaryConverter.java b/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/FacetSummaryConverter.java index 707172503..9a4564768 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/FacetSummaryConverter.java +++ b/evita_test_support/src/main/java/io/evitadb/test/client/query/graphql/FacetSummaryConverter.java @@ -30,7 +30,7 @@ import io.evitadb.api.requestResponse.schema.CatalogSchemaContract; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.externalApi.api.ExternalApiNamingConventions; import io.evitadb.externalApi.api.catalog.dataApi.constraint.EntityDataLocator; import io.evitadb.externalApi.api.catalog.dataApi.model.extraResult.ExtraResultsDescriptor; @@ -122,7 +122,7 @@ private FacetSummaryOfReference getFacetSummaryOfReference(@Nonnull ReferenceSch @Nullable FacetSummaryOfReference facetSummaryRequest, @Nullable FacetSummary defaultRequest) { if (facetSummaryRequest == null && defaultRequest == null) { - throw new EvitaInternalError("Either facet summary request or default request must be present!"); + throw new GenericEvitaInternalError("Either facet summary request or default request must be present!"); } return ofNullable(facetSummaryRequest) .map(referenceRequest -> { diff --git a/evita_test_support/src/main/java/io/evitadb/test/generator/DataGenerator.java b/evita_test_support/src/main/java/io/evitadb/test/generator/DataGenerator.java index 3ac45fd57..cdb21145f 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/generator/DataGenerator.java +++ b/evita_test_support/src/main/java/io/evitadb/test/generator/DataGenerator.java @@ -49,7 +49,7 @@ import io.evitadb.dataType.data.DataItem; import io.evitadb.dataType.data.DataItemMap; import io.evitadb.dataType.data.ReflectionCachingBehaviour; -import io.evitadb.exception.EvitaInternalError; +import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.test.Entities; import io.evitadb.utils.Assert; import io.evitadb.utils.ReflectionLookup; @@ -566,7 +566,7 @@ private static void generateAndSetAttribute( } while (value == null && sanityCheck++ < 1000); if (attribute.isSortable() && value == null) { - throw new EvitaInternalError("Cannot generate unique " + attributeName + " even in 1000 iterations!"); + throw new GenericEvitaInternalError("Cannot generate unique " + attributeName + " even in 1000 iterations!"); } generatedValueWriter.accept((T) value); @@ -825,7 +825,7 @@ private static void generateAndSetAssociatedData( (T) defaultConstructor.newInstance(DataItemMap.EMPTY) ); } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - throw new EvitaInternalError("Test associated data class " + defaultConstructor.toGenericString() + " threw exception!", e); + throw new GenericEvitaInternalError("Test associated data class " + defaultConstructor.toGenericString() + " threw exception!", e); } } } From 1e24d479a05cdca88a26249fd201102faf5068dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 13:20:40 +0200 Subject: [PATCH 09/24] fix(#550): Provide health check for Docker image Added documentation. --- documentation/user/en/operate/monitor.md | 92 +++++++++++++++++++ .../api/system/model/HealthProblem.java | 18 ++-- .../observability/ObservabilityManager.java | 9 +- .../metric/ObservabilityProbesDetector.java | 17 ++-- 4 files changed, 112 insertions(+), 24 deletions(-) diff --git a/documentation/user/en/operate/monitor.md b/documentation/user/en/operate/monitor.md index e99f7765a..7ab265a22 100644 --- a/documentation/user/en/operate/monitor.md +++ b/documentation/user/en/operate/monitor.md @@ -98,6 +98,98 @@ The layout is the `io.evitadb.server.log.AppLogJsonLayout` layout to log app log ``` +## Readiness and liveness probes + +The evitaDB server provides endpoints for Kubernetes [readiness and liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). The liveness probe is also c +onfigured as [healthcheck](https://docs.docker.com/reference/dockerfile/#healthcheck) by default in our Docker image. + + + + + +##### Don't change the default system API port when using a Docker image. + + +The healthcheck in the Docker image is configured to use the default system API port, which is `5557`. If you change +the port, the health check will immediately report an unhealthy container because it won't be able to reach the probe +endpoint. + + + +Both probes are available in the `system` API and are accessible at the following endpoints: + +### Readiness probe + +
curl -k "http://localhost:5557/system/readiness" \
+     -H 'Content-Type: application/json'
+
+ +The probe will return `200 OK` if the server is ready to accept traffic, otherwise it will return `503 Service Unavailable`. +Example response: + +```json +{ + "status": "READY", + "apis": { + "rest": "ready", + "system": "ready", + "graphQL": "ready", + "lab": "ready", + "observability": "ready", + "gRPC": "ready" + } +} +``` + +The overall status may be one of the following constants: + +
+
STARTING
+
At least one API is not yet ready.
+
READY
+
The server is ready to serve traffic.
+
STALLING
+
At least one API that was ready is not ready anymore.
+
SHUT_DOWN
+
Server is shutting down. None of the APIs are ready.
+
+ +Each of the enabled APIs has its own status so that you can see which particular API is not ready in case of `STARTING` +or `STALLING` status. + +### Liveness probe + +
curl -k "http://localhost:5557/system/liveness" \
+     -H 'Content-Type: application/json'
+
+ +If the server is healthy, the probe will return `200 OK`. Otherwise, it will return `503 Service Unavailable`. +Example response: + +```json +{ + "status": "healthy", + "problems": [] +} +``` + +If the server is unhealthy, the response will list the problems. + +
+
MEMORY_SHORTAGE
+
Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free + the old generation at least once. This leads to repeated attempts of expensive old generation GC and pressure on + host CPUs.
+
INPUT_QUEUES_OVERLOADED
+
Signalized when the input queues are full and the server is not able to process incoming requests. The problem + is reported when there is ration of rejected tasks to accepted tasks >= 2. This flag is cleared when the rejection + ratio decreases below the specified threshold, which signalizes that server is able to process incoming requests + again.
+
JAVA_INTERNAL_ERRORS
+
Signaled when there are occurrences of Java internal errors. These errors are usually caused by the server + itself and are not related to the client's requests. Java errors signal fatal problems inside the JVM.
+
+ ## Client and request identification In order to monitor which requests each client executes against evitaDB, each client and each request can be identified by diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java index f0691a160..20f99fb5d 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java @@ -32,23 +32,21 @@ public enum HealthProblem { /** * Signalized when the consumed memory never goes below 85% of the maximum heap size and the GC tries to free - * old generation multiple times consuming a log of CPU power. + * old generation at least once (this situation usually leads to repeated attempts of expensive old generation GC + * and pressure on system CPUs). */ MEMORY_SHORTAGE, /** - * Signalized when the input queues are full and the server is not able to process incoming requests. This flag - * is cleared when the server is able to process incoming requests again. + * Signalized when the input queues are full and the server is not able to process incoming requests. The problem + * is reported when there is ration of rejected tasks to accepted tasks >= 2. This flag is cleared when the rejection + * ratio decreases below the specified threshold, which signalizes that server is able to process incoming requests + * again. */ INPUT_QUEUES_OVERLOADED, /** * Signaled when there are occurrences of Java internal errors. These errors are usually caused by the server - * itself and are not related to the client's requests. + * itself and are not related to the client's requests. Java errors signal fatal problems inside the JVM. */ - JAVA_INTERNAL_ERRORS, - /** - * Signaled when there are occurrences of database internal errors. These errors are usually caused by the server - * itself and are not related to the client's requests. - */ - EVITA_DB_INTERNAL_ERRORS + JAVA_INTERNAL_ERRORS } diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java index dfe59aa52..3bac55e2a 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/ObservabilityManager.java @@ -28,7 +28,6 @@ import io.evitadb.core.metric.event.CustomMetricsExecutionEvent; import io.evitadb.exception.GenericEvitaInternalError; import io.evitadb.exception.UnexpectedIOException; -import io.evitadb.externalApi.api.system.model.HealthProblem; import io.evitadb.externalApi.configuration.ApiOptions; import io.evitadb.externalApi.http.CorsFilter; import io.evitadb.externalApi.http.ExternalApiProviderRegistrar; @@ -251,16 +250,16 @@ public void recordReadiness(@Nonnull String apiCode, boolean ready) { * Records health problem to the Prometheus metrics. * @param healthProblem the health problem to be recorded */ - public void recordHealthProblem(@Nonnull HealthProblem healthProblem) { - MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem.name()).set(1); + public void recordHealthProblem(@Nonnull String healthProblem) { + MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem).set(1); } /** * Clears health problem from the Prometheus metrics. * @param healthProblem the health problem to be cleared */ - public void clearHealthProblem(@Nonnull HealthProblem healthProblem) { - MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem.name()).set(0); + public void clearHealthProblem(@Nonnull String healthProblem) { + MetricHandler.HEALTH_PROBLEMS.labelValues(healthProblem).set(0); } /** diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java index ea9a7545d..ea08433b4 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -89,9 +89,9 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract // if the ratio of rejected task to submitted tasks is greater than 2, we could consider queues as overloaded if ((rejectedTaskCount - lastSeenRejectedTaskCount) / (Math.max(submittedTaskCount - lastSeenSubmittedTaskCount, 1)) > 2) { healthProblems.add(HealthProblem.INPUT_QUEUES_OVERLOADED); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED)); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED.name())); } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED)); + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED.name())); } this.lastSeenRejectedTaskCount = rejectedTaskCount; this.lastSeenSubmittedTaskCount = submittedTaskCount; @@ -103,9 +103,9 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract .orElse(0L); if (javaErrorCount > this.lastSeenJavaErrorCount) { healthProblems.add(HealthProblem.JAVA_INTERNAL_ERRORS); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS)); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS.name())); } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS)); + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS.name())); } this.lastSeenJavaErrorCount = javaErrorCount; @@ -114,10 +114,9 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract .map(ObservabilityManager::getEvitaErrorCount) .orElse(0L); if (evitaErrorCount > this.lastSeenEvitaErrorCount) { - healthProblems.add(HealthProblem.EVITA_DB_INTERNAL_ERRORS); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.EVITA_DB_INTERNAL_ERRORS)); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem("EVITA_DB_INTERNAL_ERRORS")); } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.EVITA_DB_INTERNAL_ERRORS)); + theObservabilityManager.ifPresent(it -> it.clearHealthProblem("EVITA_DB_INTERNAL_ERRORS")); } this.lastSeenEvitaErrorCount = evitaErrorCount; @@ -130,9 +129,9 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract final long oldGenerationCollectionCount = garbageCollectorMXBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); if (usedMemory > 0.9f && (javaOOMErrorCount > this.lastSeenJavaOOMErrorCount || oldGenerationCollectionCount > this.lastSeenJavaGarbageCollections)) { healthProblems.add(HealthProblem.MEMORY_SHORTAGE); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.MEMORY_SHORTAGE)); + theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.MEMORY_SHORTAGE.name())); } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.MEMORY_SHORTAGE)); + theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.MEMORY_SHORTAGE.name())); } this.lastSeenJavaOOMErrorCount = javaOOMErrorCount; this.lastSeenJavaGarbageCollections = oldGenerationCollectionCount; From 264d7e8b406b110b4f6f08ab006683908897e433 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 11:23:38 +0000 Subject: [PATCH 10/24] build(deps): bump org.bouncycastle:bcprov-jdk18on from 1.77 to 1.78 Bumps [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.77 to 1.78. - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ebb97ca1b..db0c84b4c 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ 3.5.1.Final 1.18.30 1.62.2 - 1.77 + 1.78 4.1.100.Final 2.2.15 From c40020636edbe516a5e9c0291e2b183e8f8a3faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 13:48:52 +0200 Subject: [PATCH 11/24] doc: updated probes documentation --- documentation/user/en/operate/monitor.md | 11 +++++++---- documentation/user/en/operate/run.md | 17 ++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/documentation/user/en/operate/monitor.md b/documentation/user/en/operate/monitor.md index 7ab265a22..df64271d4 100644 --- a/documentation/user/en/operate/monitor.md +++ b/documentation/user/en/operate/monitor.md @@ -120,11 +120,13 @@ Both probes are available in the `system` API and are accessible at the followin ### Readiness probe -
curl -k "http://localhost:5557/system/readiness" \
+```shell
+curl -k "http://localhost:5557/system/readiness" \
      -H 'Content-Type: application/json'
-
+``` The probe will return `200 OK` if the server is ready to accept traffic, otherwise it will return `503 Service Unavailable`. +Probe internally calls all enabled APIs via HTTP call on the server side to check if they are ready to serve traffic. Example response: ```json @@ -159,9 +161,10 @@ or `STALLING` status. ### Liveness probe -
curl -k "http://localhost:5557/system/liveness" \
+```shell
+curl -k "http://localhost:5557/system/liveness" \
      -H 'Content-Type: application/json'
-
+``` If the server is healthy, the probe will return `200 OK`. Otherwise, it will return `503 Service Unavailable`. Example response: diff --git a/documentation/user/en/operate/run.md b/documentation/user/en/operate/run.md index 7308e8308..3865136f4 100644 --- a/documentation/user/en/operate/run.md +++ b/documentation/user/en/operate/run.md @@ -324,7 +324,10 @@ you will also see the ports configuration here. ### Check statuses of the APIs -You can check statuses of the GraphQL and the REST API by using the `curl` command. +To check the status of all enabled APIs, use the `curl` command and our readiness probe. This probe verifies that APIs +are ready to serve requests via internal HTTP calls and returns their status in a single response. + +You can also check statuses of the GraphQL and the REST API manually by using the `curl` command. #### GraphQL @@ -377,19 +380,15 @@ You need to replace `__path_to_log_file__` with the path to your logback configu The running (and named) container can be stopped and restarted using the following commands: ```shell -# shut the container down -docker stop evitadb -# bring the container up in frontend mode -docker start evitadb -# bring the container up in daemon mode -docker start evitadb -d +# restart the running container +docker restart evitadb ``` Alternatively, you can use `docker ps` to get the ID of a running container and restart it using the [UUID short identifier](https://docs.docker.com/engine/reference/run/#name---name): ```shell -docker start b0c7b140c6a7 +docker restart b0c7b140c6a7 ``` ### Docker Compose @@ -418,4 +417,4 @@ All previously documented options for using Docker apply to Docker Compose: - use [environment variables](#configure-the-evitadb-in-the-container) to configure evitaDB - use [volumes](#configure-database-persistent-storage) to set the data folder -- use [ports](#open--remap-ports) for mapping ports in the docker composition \ No newline at end of file +- use [ports](#open--remap-ports) for mapping ports in the docker composition From 7ff1216b1960a22952078593494a4c05887ccb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 13:51:53 +0200 Subject: [PATCH 12/24] doc: updated probes documentation --- documentation/user/en/operate/run.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/user/en/operate/run.md b/documentation/user/en/operate/run.md index 3865136f4..9cfb2ca0e 100644 --- a/documentation/user/en/operate/run.md +++ b/documentation/user/en/operate/run.md @@ -324,8 +324,9 @@ you will also see the ports configuration here. ### Check statuses of the APIs -To check the status of all enabled APIs, use the `curl` command and our readiness probe. This probe verifies that APIs -are ready to serve requests via internal HTTP calls and returns their status in a single response. +To check the status of all enabled APIs, use the `curl` command and our [readiness probe](./monitor.md#readiness-probe). +This probe verifies that APIs are ready to serve requests via internal HTTP calls and returns their status in a single +response. You can also check statuses of the GraphQL and the REST API manually by using the `curl` command. From 2dfda108eecf8116c8a5cea03f11833d95c33d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 16:43:51 +0200 Subject: [PATCH 13/24] feat(#550): updated probes handling and documentation Added link between readiness and liveness probe. Allow to configure system API port and start delay in Dockerfile using environment variables. --- docker/Dockerfile | 6 +- documentation/user/en/operate/monitor.md | 13 +- .../api/system/ProbesProvider.java | 2 +- .../api/system/model/HealthProblem.java | 5 + .../metric/ObservabilityProbesDetector.java | 219 +++++++++++++----- .../system/SystemProviderRegistrar.java | 2 +- 6 files changed, 186 insertions(+), 61 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 9efe56429..0455ea5aa 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,14 +6,16 @@ ARG VERSION=not_set ARG RELEASE_DATE=not_set ENV EVITA_HOME="/evita" ENV EVITA_JAR_NAME="$EVITA_JAR_NAME" +ENV SYSTEM_API_PORT="5557" +ENV HEALTHCHECK_START_DELAY="30s" # Labels with dynamic information based on environment variables and current date LABEL vendor="FG Forrest, a.s." \ io.evitadb.version="${VERSION}" \ io.evitadb.release-date="${RELEASE_DATE}" -HEALTHCHECK --interval=10s --timeout=2s --retries=3 \ - CMD curl -f http://localhost:5557/system/liveness || exit 1 +HEALTHCHECK --start-period=$HEALTHCHECK_START_DELAY --interval=10s --timeout=2s --retries=3 \ + CMD curl -f http://localhost:$SYSTEM_API_PORT/system/liveness || exit 1 USER root diff --git a/documentation/user/en/operate/monitor.md b/documentation/user/en/operate/monitor.md index df64271d4..8a6720b18 100644 --- a/documentation/user/en/operate/monitor.md +++ b/documentation/user/en/operate/monitor.md @@ -101,18 +101,20 @@ The layout is the `io.evitadb.server.log.AppLogJsonLayout` layout to log app log ## Readiness and liveness probes The evitaDB server provides endpoints for Kubernetes [readiness and liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). The liveness probe is also c -onfigured as [healthcheck](https://docs.docker.com/reference/dockerfile/#healthcheck) by default in our Docker image. +onfigured as [healthcheck](https://docs.docker.com/reference/dockerfile/#healthcheck) by default in our Docker image. By default the health check waits `30s` before it +starts checking the server health, for larger databases you may need to increase this value using environment variable +`HEALTHCHECK_START_DELAY` so that they have enough time to be loaded into memory. -##### Don't change the default system API port when using a Docker image. +##### When you change system API port don't forget to set `SYSTEM_API_PORT` environment variable The healthcheck in the Docker image is configured to use the default system API port, which is `5557`. If you change the port, the health check will immediately report an unhealthy container because it won't be able to reach the probe -endpoint. +endpoint. You need to specify the new port using the `SYSTEM_API_PORT` environment variable of the Docker container. @@ -152,7 +154,7 @@ The overall status may be one of the following constants:
The server is ready to serve traffic.
STALLING
At least one API that was ready is not ready anymore.
-
SHUT_DOWN
+
SHUTDOWN
Server is shutting down. None of the APIs are ready.
@@ -191,6 +193,9 @@ If the server is unhealthy, the response will list the problems.
JAVA_INTERNAL_ERRORS
Signaled when there are occurrences of Java internal errors. These errors are usually caused by the server itself and are not related to the client's requests. Java errors signal fatal problems inside the JVM.
+
EXTERNAL_API_UNAVAILABLE
+
Signalized when the readiness probe signals that at least one external API, that is configured to be enabled + doesn't respond to internal HTTP check call.
## Client and request identification diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java index 468994e6d..b356e04af 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/ProbesProvider.java @@ -112,7 +112,7 @@ enum ReadinessState { /** * Server is shutting down. None of the APIs are ready. */ - SHUT_DOWN, + SHUTDOWN, /** * Unknown state - cannot determine the state of the APIs (should not happen). */ diff --git a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java index 20f99fb5d..30d341d08 100644 --- a/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java +++ b/evita_external_api/evita_external_api_core/src/main/java/io/evitadb/externalApi/api/system/model/HealthProblem.java @@ -36,6 +36,11 @@ public enum HealthProblem { * and pressure on system CPUs). */ MEMORY_SHORTAGE, + /** + * Signalized when the readiness probe signals that at least one external API, that is configured to be enabled + * doesn't respond to internal HTTP check call. + */ + EXTERNAL_API_UNAVAILABLE, /** * Signalized when the input queues are full and the server is not able to process incoming requests. The problem * is reported when there is ration of rejected tasks to accepted tasks >= 2. This flag is cleared when the rejection diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java index ea08433b4..53fdc5033 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -36,6 +36,7 @@ import org.jboss.threads.EnhancedQueueExecutor; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.Collection; @@ -44,6 +45,9 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; /** * This class is responsible for detecting health problems in the system. It monitors: @@ -57,6 +61,7 @@ * 4. The number of Java OutOfMemory errors or Old generation garbage collections. If the current memory usage is above * 90% of the total available memory and the number of errors has increased since the last check, the system is considered * unhealthy. + * 5. The readiness of the external APIs. If at least one external API is not ready, the system is considered unhealthy. */ public class ObservabilityProbesDetector implements ProbesProvider { private static final Set NO_HEALTH_PROBLEMS = EnumSet.noneOf(HealthProblem.class); @@ -69,74 +74,154 @@ public class ObservabilityProbesDetector implements ProbesProvider { .toList(); private ObservabilityManager observabilityManager; - private long lastSeenRejectedTaskCount; - private long lastSeenSubmittedTaskCount; - private long lastSeenJavaErrorCount; - private long lastSeenJavaOOMErrorCount; - private long lastSeenEvitaErrorCount; - private long lastSeenJavaGarbageCollections; - private boolean seenReady; + private final AtomicLong lastSeenRejectedTaskCount = new AtomicLong(0L); + private final AtomicLong lastSeenSubmittedTaskCount = new AtomicLong(0L); + private final AtomicLong lastSeenJavaErrorCount = new AtomicLong(0L); + private final AtomicLong lastSeenJavaOOMErrorCount = new AtomicLong(0L); + private final AtomicLong lastSeenEvitaErrorCount = new AtomicLong(0L); + private final AtomicLong lastSeenJavaGarbageCollections = new AtomicLong(0L); + private final AtomicBoolean seenReady = new AtomicBoolean(); + private final AtomicReference lastReadinessSeen = new AtomicReference<>(); @Nonnull @Override public Set getHealthProblems(@Nonnull EvitaContract evitaContract, @Nonnull ExternalApiServer externalApiServer) { final EnumSet healthProblems = EnumSet.noneOf(HealthProblem.class); - final Optional theObservabilityManager = getObservabilityManager(externalApiServer); + final ObservabilityManager theObservabilityManager = getObservabilityManager(externalApiServer).orElse(null); + if (evitaContract instanceof Evita evita) { - final EnhancedQueueExecutor executor = evita.getExecutor(); - final long rejectedTaskCount = executor.getRejectedTaskCount(); - final long submittedTaskCount = executor.getSubmittedTaskCount(); - // if the ratio of rejected task to submitted tasks is greater than 2, we could consider queues as overloaded - if ((rejectedTaskCount - lastSeenRejectedTaskCount) / (Math.max(submittedTaskCount - lastSeenSubmittedTaskCount, 1)) > 2) { - healthProblems.add(HealthProblem.INPUT_QUEUES_OVERLOADED); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED.name())); - } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.INPUT_QUEUES_OVERLOADED.name())); - } - this.lastSeenRejectedTaskCount = rejectedTaskCount; - this.lastSeenSubmittedTaskCount = submittedTaskCount; + recordResult(checkInputQueues(evita), healthProblems, theObservabilityManager); } - // if the number of errors has increased since the last check, we could consider the system as unhealthy - final long javaErrorCount = theObservabilityManager - .map(ObservabilityManager::getJavaErrorCount) - .orElse(0L); - if (javaErrorCount > this.lastSeenJavaErrorCount) { - healthProblems.add(HealthProblem.JAVA_INTERNAL_ERRORS); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS.name())); - } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.JAVA_INTERNAL_ERRORS.name())); + if (theObservabilityManager != null) { + recordResult(checkEvitaErrors(theObservabilityManager), healthProblems, theObservabilityManager); + recordResult(checkMemoryShortage(theObservabilityManager), healthProblems, theObservabilityManager); + recordResult(checkJavaErrors(theObservabilityManager), healthProblems, theObservabilityManager); } - this.lastSeenJavaErrorCount = javaErrorCount; - // if the number of errors has increased since the last check, we could consider the system as unhealthy - final long evitaErrorCount = theObservabilityManager - .map(ObservabilityManager::getEvitaErrorCount) - .orElse(0L); - if (evitaErrorCount > this.lastSeenEvitaErrorCount) { - theObservabilityManager.ifPresent(it -> it.recordHealthProblem("EVITA_DB_INTERNAL_ERRORS")); + recordResult(checkApiReadiness(), healthProblems, theObservabilityManager); + + return healthProblems.isEmpty() ? NO_HEALTH_PROBLEMS : healthProblems; + } + + /** + * Records the result of the health problem check. + * @param healthProblem the result of the health problem check + * @param healthProblems the set of health problems + * @param theObservabilityManager the observability manager for recording the health problem + */ + private static void recordResult( + @Nonnull HealthProblemCheckResult healthProblem, + @Nonnull Set healthProblems, + @Nullable ObservabilityManager theObservabilityManager + ) { + if (healthProblem.present()) { + if (healthProblem.healthProblem() != null) { + healthProblems.add(healthProblem.healthProblem()); + } + if (theObservabilityManager != null) { + theObservabilityManager.recordHealthProblem(healthProblem.healthProblemName()); + } } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem("EVITA_DB_INTERNAL_ERRORS")); + if (theObservabilityManager != null) { + theObservabilityManager.clearHealthProblem(healthProblem.healthProblemName()); + } } - this.lastSeenEvitaErrorCount = evitaErrorCount; + } + + /** + * Checks the readiness of the external APIs. + * @return the result of the check + */ + @Nonnull + private HealthProblemCheckResult checkApiReadiness() { + final Readiness readiness = this.lastReadinessSeen.get(); + return new HealthProblemCheckResult( + HealthProblem.EXTERNAL_API_UNAVAILABLE, + readiness.state() != ReadinessState.READY + ); + } + /** + * Checks the memory shortage. If the current memory usage is above 90% of the total available memory and the number + * of errors has increased since the last check, the system is considered unhealthy. + * @param theObservabilityManager the observability manager + * @return the result of the check + */ + @Nonnull + private HealthProblemCheckResult checkMemoryShortage(@Nonnull ObservabilityManager theObservabilityManager) { // if the number of errors has increased since the last check, we could consider the system as unhealthy - final long javaOOMErrorCount = theObservabilityManager - .map(ObservabilityManager::getJavaOutOfMemoryErrorCount) - .orElse(0L); + final long javaOOMErrorCount = theObservabilityManager.getJavaOutOfMemoryErrorCount(); // get used memory of the JVM final float usedMemory = 1.0f - ((float) runtime.freeMemory() / (float) runtime.maxMemory()); final long oldGenerationCollectionCount = garbageCollectorMXBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); - if (usedMemory > 0.9f && (javaOOMErrorCount > this.lastSeenJavaOOMErrorCount || oldGenerationCollectionCount > this.lastSeenJavaGarbageCollections)) { - healthProblems.add(HealthProblem.MEMORY_SHORTAGE); - theObservabilityManager.ifPresent(it -> it.recordHealthProblem(HealthProblem.MEMORY_SHORTAGE.name())); - } else { - theObservabilityManager.ifPresent(it -> it.clearHealthProblem(HealthProblem.MEMORY_SHORTAGE.name())); - } - this.lastSeenJavaOOMErrorCount = javaOOMErrorCount; - this.lastSeenJavaGarbageCollections = oldGenerationCollectionCount; + final HealthProblemCheckResult result = new HealthProblemCheckResult( + HealthProblem.MEMORY_SHORTAGE, + usedMemory > 0.9f && + ( + javaOOMErrorCount > this.lastSeenJavaOOMErrorCount.get() || + oldGenerationCollectionCount > this.lastSeenJavaGarbageCollections.get() + ) + ); + this.lastSeenJavaOOMErrorCount.set(javaOOMErrorCount); + this.lastSeenJavaGarbageCollections.set(oldGenerationCollectionCount); + return result; + } - return healthProblems.isEmpty() ? NO_HEALTH_PROBLEMS : healthProblems; + /** + * Checks the number of Java internal errors. + * @param theObservabilityManager the observability manager + * @return the result of the check + */ + @Nonnull + private HealthProblemCheckResult checkJavaErrors(@Nonnull ObservabilityManager theObservabilityManager) { + // if the number of errors has increased since the last check, we could consider the system as unhealthy + final long javaErrorCount = theObservabilityManager.getJavaErrorCount(); + final HealthProblemCheckResult result = new HealthProblemCheckResult( + HealthProblem.JAVA_INTERNAL_ERRORS, + javaErrorCount > this.lastSeenJavaErrorCount.get() + ); + this.lastSeenJavaErrorCount.set(javaErrorCount); + return result; + } + + /** + * Checks the rejection / submission rate of the executor input queues. + * @param evita the Evita instance + * @return the result of the check + */ + @Nonnull + private HealthProblemCheckResult checkInputQueues(@Nonnull Evita evita) { + final EnhancedQueueExecutor executor = evita.getExecutor(); + final long rejectedTaskCount = executor.getRejectedTaskCount(); + final long submittedTaskCount = executor.getSubmittedTaskCount(); + // if the ratio of rejected task to submitted tasks is greater than 2, we could consider queues as overloaded + final HealthProblemCheckResult result = new HealthProblemCheckResult( + HealthProblem.INPUT_QUEUES_OVERLOADED, + (rejectedTaskCount - this.lastSeenRejectedTaskCount.get()) / (Math.max(submittedTaskCount - this.lastSeenSubmittedTaskCount.get(), 1)) > 2 + ); + this.lastSeenRejectedTaskCount.set(rejectedTaskCount); + this.lastSeenSubmittedTaskCount.set(submittedTaskCount); + return result; + } + + /** + * Checks the number of database internal errors. This check doesn't affect the system health, but it's propagated + * to metrics. + * + * @param theObservabilityManager the observability manager + * @return the result of the check + */ + @Nonnull + private HealthProblemCheckResult checkEvitaErrors(@Nonnull ObservabilityManager theObservabilityManager) { + // if the number of errors has increased since the last check, we could consider the system as unhealthy + final long evitaErrorCount = theObservabilityManager.getEvitaErrorCount(); + final HealthProblemCheckResult result = new HealthProblemCheckResult( + "EVITA_DB_INTERNAL_ERRORS", + evitaErrorCount > this.lastSeenEvitaErrorCount.get() + ); + this.lastSeenEvitaErrorCount.set(evitaErrorCount); + return result; } @Nonnull @@ -144,6 +229,7 @@ public Set getHealthProblems(@Nonnull EvitaContract evitaContract public Readiness getReadiness(@Nonnull EvitaContract evitaContract, @Nonnull ExternalApiServer externalApiServer, @Nonnull String... apiCodes) { final Optional theObservabilityManager = getObservabilityManager(externalApiServer); // check the end-points availability + //noinspection rawtypes final Collection availableExternalApis = ExternalApiServer.gatherExternalApiProviders(); final Map readiness = CollectionUtils.createHashMap(availableExternalApis.size()); for (String apiCode : apiCodes) { @@ -153,16 +239,23 @@ public Readiness getReadiness(@Nonnull EvitaContract evitaContract, @Nonnull Ext } final boolean ready = readiness.values().stream().allMatch(Boolean::booleanValue); if (ready) { - this.seenReady = true; + this.seenReady.set(true); } - return new Readiness( - ready ? ReadinessState.READY : (this.seenReady ? ReadinessState.STALLING : ReadinessState.STARTING), + final Readiness currentReadiness = new Readiness( + ready ? ReadinessState.READY : (this.seenReady.get() ? ReadinessState.STALLING : ReadinessState.STARTING), readiness.entrySet().stream() .map(entry -> new ApiState(entry.getKey(), entry.getValue())) .toArray(ApiState[]::new) ); + this.lastReadinessSeen.set(currentReadiness); + return currentReadiness; } + /** + * Returns the observability manager from the external API server. + * @param externalApiServer the external API server + * @return the observability manager or NULL if it is not available + */ @Nonnull private Optional getObservabilityManager(@Nonnull ExternalApiServer externalApiServer) { if (this.observabilityManager == null) { @@ -176,5 +269,25 @@ private Optional getObservabilityManager(@Nonnull External return Optional.ofNullable(this.observabilityManager); } + /** + * This record represents the result of a health problem check. + * @param healthProblem type of health problem + * @param present true if the health problem is present, false otherwise + */ + private record HealthProblemCheckResult( + @Nullable HealthProblem healthProblem, + @Nonnull String healthProblemName, + boolean present + ) { + + public HealthProblemCheckResult(@Nullable HealthProblem healthProblem, boolean present) { + this(healthProblem, healthProblem.name(), present); + } + + public HealthProblemCheckResult(@Nullable String healthProblem, boolean present) { + this(null, healthProblem, present); + } + + } } diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index 786d421d3..9f83bc6e8 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -270,7 +270,7 @@ public ExternalApiProvider register( } } else { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); - exchange.getResponseSender().send("{\"status\": \"" + ReadinessState.SHUT_DOWN.name() + "\"}"); + exchange.getResponseSender().send("{\"status\": \"" + ReadinessState.SHUTDOWN.name() + "\"}"); } } ); From a703872f85cfc049f772dda2dadd5fefb09b5cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 20:22:29 +0200 Subject: [PATCH 14/24] fix: NullPointerException MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan Novotný --- .../observability/metric/ObservabilityProbesDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java index 53fdc5033..fcdacd84b 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -138,7 +138,7 @@ private HealthProblemCheckResult checkApiReadiness() { final Readiness readiness = this.lastReadinessSeen.get(); return new HealthProblemCheckResult( HealthProblem.EXTERNAL_API_UNAVAILABLE, - readiness.state() != ReadinessState.READY + readines == null || readiness.state() != ReadinessState.READY ); } From 720251e07cc6fd1a99096d0e16bc932c4ae08e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 4 May 2024 20:28:57 +0200 Subject: [PATCH 15/24] fix: NullPointerException MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan Novotný --- .../observability/metric/ObservabilityProbesDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java index fcdacd84b..4f88c842b 100644 --- a/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java +++ b/evita_external_api/evita_external_api_observability/src/main/java/io/evitadb/externalApi/observability/metric/ObservabilityProbesDetector.java @@ -138,7 +138,7 @@ private HealthProblemCheckResult checkApiReadiness() { final Readiness readiness = this.lastReadinessSeen.get(); return new HealthProblemCheckResult( HealthProblem.EXTERNAL_API_UNAVAILABLE, - readines == null || readiness.state() != ReadinessState.READY + readiness == null || readiness.state() != ReadinessState.READY ); } From 10848c6d81cb9eb0db1c3435f33de4290a8ab15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sun, 5 May 2024 11:26:20 +0200 Subject: [PATCH 16/24] feat(#550): updated probes handling and documentation Fixed work with service loader and probe instantiation. --- .../system/SystemProviderRegistrar.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java index 9f83bc6e8..d2c593fa3 100644 --- a/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java +++ b/evita_external_api/evita_external_api_system/src/main/java/io/evitadb/externalApi/system/SystemProviderRegistrar.java @@ -55,9 +55,11 @@ import java.io.IOException; import java.time.format.DateTimeFormatter; import java.util.Arrays; +import java.util.List; import java.util.Map.Entry; import java.util.Optional; import java.util.ServiceLoader; +import java.util.ServiceLoader.Provider; import java.util.Set; import java.util.stream.Collectors; @@ -221,13 +223,17 @@ public ExternalApiProvider register( } ); + final List probes = ServiceLoader.load(ProbesProvider.class) + .stream() + .map(Provider::get) + .toList(); router.addExactPath( "/" + ENDPOINT_SYSTEM_LIVENESS, exchange -> { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); - final Set healthProblems = ServiceLoader.load(ProbesProvider.class) + final Set healthProblems = probes .stream() - .flatMap(it -> it.get().getHealthProblems(evita, externalApiServer).stream()) + .flatMap(it -> it.getHealthProblems(evita, externalApiServer).stream()) .collect(Collectors.toSet()); if (healthProblems.isEmpty()) { @@ -253,9 +259,9 @@ public ExternalApiProvider register( exchange -> { exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); if (evita.isActive()) { - final Optional readiness = ServiceLoader.load(ProbesProvider.class) + final Optional readiness = probes .stream() - .map(it -> it.get().getReadiness(evita, externalApiServer, enabledEndPoints)) + .map(it -> it.getReadiness(evita, externalApiServer, enabledEndPoints)) .findFirst(); if (readiness.map(it -> it.state() == ReadinessState.READY).orElse(false)) { From bf5a8bbb75dd61689e8dfada69bbcc758e8b1ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Thu, 9 May 2024 11:16:17 +0200 Subject: [PATCH 17/24] fix: close method needs to be idempotent --- .../grpc/services/EvitaSessionService.java | 19 +++++++++++++++++-- .../ServerSessionInterceptor.java | 3 ++- .../externalApi/grpc/utils/GrpcServer.java | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java index 710d5e8e1..31e0db05a 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/EvitaSessionService.java @@ -24,8 +24,10 @@ package io.evitadb.externalApi.grpc.services; import com.google.protobuf.Empty; +import io.evitadb.api.CatalogContract; import io.evitadb.api.CatalogState; import io.evitadb.api.EvitaSessionContract; +import io.evitadb.api.exception.CatalogNotFoundException; import io.evitadb.api.query.Query; import io.evitadb.api.query.require.EntityContentRequire; import io.evitadb.api.query.require.EntityFetch; @@ -45,6 +47,7 @@ import io.evitadb.api.requestResponse.schema.dto.EntitySchema; import io.evitadb.api.requestResponse.schema.mutation.LocalCatalogSchemaMutation; import io.evitadb.api.requestResponse.schema.mutation.catalog.ModifyEntitySchemaMutation; +import io.evitadb.core.Evita; import io.evitadb.core.EvitaInternalSessionContract; import io.evitadb.dataType.DataChunk; import io.evitadb.dataType.PaginatedList; @@ -103,6 +106,11 @@ public class EvitaSessionService extends EvitaSessionServiceGrpc.EvitaSessionSer private static final EntityMutationConverter ENTITY_MUTATION_CONVERTER = new DelegatingEntityMutationConverter(); + /** + * Instance of Evita upon which will be executed service calls + */ + @Nonnull private final Evita evita; + /** * Produces the {@link CatalogSchema}. */ @@ -743,7 +751,7 @@ public void queryList(GrpcQueryRequest request, StreamObserver re responseObserver.onCompleted(); }); } else { - responseObserver.onCompleted(); + final String catalogName = ServerSessionInterceptor.CATALOG_NAME.get(); + final Optional catalogInstance = catalogName == null ? empty() : evita.getCatalogInstance(catalogName); + if (catalogInstance.isPresent()) { + responseObserver.onNext(GrpcCloseResponse.newBuilder().setCatalogVersion(catalogInstance.get().getVersion()).build()); + responseObserver.onCompleted(); + } else { + responseObserver.onError(new CatalogNotFoundException(catalogName)); + } } }); } diff --git a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/interceptors/ServerSessionInterceptor.java b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/interceptors/ServerSessionInterceptor.java index fe195ab3d..7439ebb33 100644 --- a/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/interceptors/ServerSessionInterceptor.java +++ b/evita_external_api/evita_external_api_grpc/server/src/main/java/io/evitadb/externalApi/grpc/services/interceptors/ServerSessionInterceptor.java @@ -84,6 +84,7 @@ public class ServerSessionInterceptor implements ServerInterceptor { * Context that holds current {@link EvitaSessionContract} session. */ public static final Context.Key SESSION = Context.key(SESSION_ID_HEADER); + public static final Context.Key CATALOG_NAME = Context.key(CATALOG_NAME_HEADER); public static final Context.Key METADATA = Context.key(METADATA_HEADER); /** @@ -125,7 +126,7 @@ public ServerCall.Listener interceptCall(ServerCall Date: Thu, 9 May 2024 12:38:47 +0200 Subject: [PATCH 18/24] fix: start interval cannot be fed from environment variable --- docker/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0455ea5aa..ff4676d55 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,14 +7,13 @@ ARG RELEASE_DATE=not_set ENV EVITA_HOME="/evita" ENV EVITA_JAR_NAME="$EVITA_JAR_NAME" ENV SYSTEM_API_PORT="5557" -ENV HEALTHCHECK_START_DELAY="30s" # Labels with dynamic information based on environment variables and current date LABEL vendor="FG Forrest, a.s." \ io.evitadb.version="${VERSION}" \ io.evitadb.release-date="${RELEASE_DATE}" -HEALTHCHECK --start-period=$HEALTHCHECK_START_DELAY --interval=10s --timeout=2s --retries=3 \ +HEALTHCHECK --start-period=5m --interval=10s --timeout=2s --retries=3 \ CMD curl -f http://localhost:$SYSTEM_API_PORT/system/liveness || exit 1 USER root From 8cd7c80898f1453afdc42e238db28382459a58f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Wed, 8 May 2024 13:26:56 +0200 Subject: [PATCH 19/24] fix(#557): attempt to fix memory leak According to heapdump - current way of switching catalog reference is not safe and there are references to old catalogs trapped in "catalogAccessor" lambda in entity collection. These changes aim to clarify the process of transitioning from one catalog version to another and avoiding the reference leak. According to heapdump - current way of switching catalog reference is not safe and there are references to old catalogs trapped in "catalogAccessor" lambda in entity collection. These changes aim to clarify the process of transitioning from one catalog version to another and avoiding the reference leak. --- .../inspectionProfiles/Evita_DB_Defaults.xml | 2 + .../main/java/io/evitadb/core/Catalog.java | 125 +++++--- .../core/CatalogRelatedDataStructure.java | 70 +++++ .../io/evitadb/core/EntityCollection.java | 294 +++++++++--------- .../src/main/java/io/evitadb/core/Evita.java | 2 +- .../java/io/evitadb/core/SessionRegistry.java | 7 +- .../transaction/TransactionWalFinalizer.java | 2 +- .../java/io/evitadb/index/CatalogIndex.java | 45 +-- .../java/io/evitadb/index/EntityIndex.java | 84 +++-- .../io/evitadb/index/GlobalEntityIndex.java | 13 +- .../io/evitadb/index/ReducedEntityIndex.java | 77 ++++- .../index/ReferencedTypeEntityIndex.java | 13 +- .../index/attribute/GlobalUniqueIndex.java | 58 ++-- .../index/price/AbstractPriceIndex.java | 8 +- .../price/PriceListAndCurrencyPriceIndex.java | 48 ++- .../PriceListAndCurrencyPriceRefIndex.java | 237 +++++++++----- .../PriceListAndCurrencyPriceSuperIndex.java | 73 ++++- .../io/evitadb/index/price/PriceRefIndex.java | 82 +++-- .../EntityCollectionPersistenceService.java | 6 +- .../evitadb/api/EvitaApiFunctionalTest.java | 25 ++ .../api/EvitaBackwardCompatibilityTest.java | 5 + .../mutation/AbstractMutatorTestBase.java | 6 +- .../mutation/ReferenceIndexMutatorTest.java | 19 +- .../DefaultCatalogPersistenceServiceTest.java | 10 +- .../MockPriceListAndCurrencyPriceIndex.java | 12 +- .../DefaultCatalogPersistenceService.java | 4 +- ...ultEntityCollectionPersistenceService.java | 16 +- .../extension/EvitaParameterResolver.java | 4 + 28 files changed, 884 insertions(+), 463 deletions(-) create mode 100644 evita_engine/src/main/java/io/evitadb/core/CatalogRelatedDataStructure.java diff --git a/.idea/inspectionProfiles/Evita_DB_Defaults.xml b/.idea/inspectionProfiles/Evita_DB_Defaults.xml index a0f1fc302..3f98d8373 100644 --- a/.idea/inspectionProfiles/Evita_DB_Defaults.xml +++ b/.idea/inspectionProfiles/Evita_DB_Defaults.xml @@ -16,6 +16,7 @@ + @@ -114,6 +115,7 @@ + diff --git a/evita_engine/src/main/java/io/evitadb/core/Catalog.java b/evita_engine/src/main/java/io/evitadb/core/Catalog.java index b0adc50e9..00a3457fd 100644 --- a/evita_engine/src/main/java/io/evitadb/core/Catalog.java +++ b/evita_engine/src/main/java/io/evitadb/core/Catalog.java @@ -140,6 +140,8 @@ import static io.evitadb.core.Transaction.isTransactionAvailable; import static io.evitadb.utils.CollectionUtils.MAX_POWER_OF_TWO; import static io.evitadb.utils.CollectionUtils.createHashMap; +import static java.util.Optional.empty; +import static java.util.Optional.of; import static java.util.Optional.ofNullable; /** @@ -332,7 +334,8 @@ public Catalog( this.entityTypeSequence = sequenceService.getOrCreateSequence( catalogName, SequenceType.ENTITY_COLLECTION, 0 ); - this.catalogIndex = new CatalogIndex(this); + this.catalogIndex = new CatalogIndex(); + this.catalogIndex.attachToCatalog(null, this); this.proxyFactory = ProxyFactory.createInstance(reflectionLookup); this.storageOptions = storageOptions; this.transactionOptions = transactionOptions; @@ -375,6 +378,7 @@ public Catalog( ); this.schema = new TransactionalReference<>(new CatalogSchemaDecorator(catalogSchema)); this.catalogIndex = this.persistenceService.readCatalogIndex(this); + this.catalogIndex.attachToCatalog(null, this); this.cacheSupervisor = cacheSupervisor; this.dataStoreBuffer = new DataStoreMemoryBuffer<>(this, storagePartPersistenceService); @@ -388,8 +392,8 @@ public Catalog( ); final int entityTypePrimaryKey = entityCollectionHeader.entityTypePrimaryKey(); final EntityCollection collection = new EntityCollection( + catalogName, catalogVersion, - this, entityTypePrimaryKey, entityType, persistenceService, @@ -415,6 +419,11 @@ public Catalog( ), EntityCollection.class, Function.identity() ); + + for (EntityCollection entityCollection : collections.values()) { + entityCollection.attachToCatalog(null, this); + } + this.entitySchemaIndex = new TransactionalMap<>( entityCollections.values() .stream() @@ -438,7 +447,7 @@ public Catalog( long versionId, @Nonnull CatalogState catalogState, @Nonnull CatalogIndex catalogIndex, - @Nonnull Map entityCollections, + @Nonnull Collection entityCollections, @Nonnull Catalog previousCatalogVersion ) { this( @@ -456,7 +465,7 @@ public Catalog( long catalogVersion, @Nonnull CatalogState catalogState, @Nonnull CatalogIndex catalogIndex, - @Nonnull Map entityCollections, + @Nonnull Collection entityCollections, @Nonnull CatalogPersistenceService persistenceService, @Nonnull Catalog previousCatalogVersion, @Nonnull TracingContext tracingContext @@ -475,7 +484,7 @@ public Catalog( this.newCatalogVersionConsumer = previousCatalogVersion.newCatalogVersionConsumer; this.transactionalPipeline = previousCatalogVersion.transactionalPipeline; - catalogIndex.updateReferencesTo(this); + catalogIndex.attachToCatalog(null, this); final StoragePartPersistenceService storagePartPersistenceService = persistenceService.getStoragePartPersistenceService(catalogVersion); final CatalogSchema catalogSchema = CatalogSchemaStoragePart.deserializeWithCatalog( this, @@ -490,16 +499,20 @@ public Catalog( final Map newEntityCollections = CollectionUtils.createHashMap(entityCollections.size()); final Map newEntityCollectionsIndex = CollectionUtils.createHashMap(entityCollections.size()); final Map newEntitySchemaIndex = CollectionUtils.createHashMap(entityCollections.size()); - for (EntityCollection entityCollection : entityCollections.values()) { - entityCollection.updateReferenceToCatalog(this); + for (EntityCollection entityCollection : entityCollections) { newEntityCollections.put(entityCollection.getEntityType(), entityCollection); newEntityCollectionsIndex.put(entityCollection.getEntityTypePrimaryKey(), entityCollection); - newEntitySchemaIndex.put(entityCollection.getEntityType(), entityCollection.getSchema()); } this.entityCollections = new TransactionalMap<>(newEntityCollections, EntityCollection.class, Function.identity()); this.entityCollectionsByPrimaryKey = new TransactionalMap<>(newEntityCollectionsIndex, EntityCollection.class, Function.identity()); this.entitySchemaIndex = new TransactionalMap<>(newEntitySchemaIndex); this.lastPersistedSchemaVersion = previousCatalogVersion.lastPersistedSchemaVersion; + // finally attach every collection to this instance of the catalog + for (EntityCollection entityCollection : entityCollections) { + entityCollection.attachToCatalog(null, this); + // when the collection is attached to the catalog, we can access its schema and put it into the schema index + newEntitySchemaIndex.put(entityCollection.getEntityType(), entityCollection.getSchema()); + } } @Override @@ -705,37 +718,32 @@ public CatalogContract replace(@Nonnull CatalogSchemaContract updatedSchema, @No CatalogSchema._internalBuild(updatedSchema) ); final long catalogVersionAfterRename = newIoService.getLastCatalogVersion(); - final Map newCollections = entityCollections + final List newCollections = this.entityCollections .values() .stream() - .collect( - Collectors.toMap( - EntityCollection::getEntityType, - it -> new EntityCollection( - catalogVersionAfterRename, - this, - it.getEntityTypePrimaryKey(), - it.getEntityType(), - newIoService, - cacheSupervisor, - sequenceService, - tracingContext - ) + .map( + it -> new EntityCollection( + updatedSchema.getName(), + catalogVersionAfterRename, + it.getEntityTypePrimaryKey(), + it.getEntityType(), + newIoService, + this.cacheSupervisor, + this.sequenceService, + this.tracingContext ) - ); + ).toList(); advanceVersion(catalogVersionAfterRename); - final Catalog catalogAfterRename = new Catalog( + return new Catalog( catalogVersionAfterRename, getCatalogState(), - catalogIndex, + this.catalogIndex, newCollections, newIoService, this, - tracingContext + this.tracingContext ); - newCollections.values().forEach(it -> it.updateReferenceToCatalog(catalogAfterRename)); - return catalogAfterRename; } @Nonnull @@ -755,25 +763,31 @@ public Optional getEntitySchema(@Nonnull String entityType) public boolean goLive() { try { Assert.isTrue( - goingLive.compareAndSet(false, true), + this.goingLive.compareAndSet(false, true), "Concurrent call of `goLive` method is not supported!" ); - Assert.isTrue(state == CatalogState.WARMING_UP, "Catalog has already alive state!"); + Assert.isTrue(this.state == CatalogState.WARMING_UP, "Catalog has already alive state!"); flush(); - newCatalogVersionConsumer.accept( - new Catalog( - 1L, - CatalogState.ALIVE, - this.catalogIndex, - this.entityCollections, - this.persistenceService, - this, - this.tracingContext - ) + final List newCollections = this.entityCollections + .values() + .stream() + .map(EntityCollection::createCopyForNewCatalogAttachment) + .toList(); + + final Catalog newCatalog = new Catalog( + 1L, + CatalogState.ALIVE, + this.catalogIndex, + newCollections, + this.persistenceService, + this, + this.tracingContext ); + this.newCatalogVersionConsumer.accept(newCatalog); + return true; } finally { goingLive.set(false); @@ -868,16 +882,25 @@ public CatalogIndex getCatalogIndex() { * Returns {@link EntitySchema} for passed `entityType` or throws {@link IllegalArgumentException} if schema for * this type is not yet known. */ - @Nullable - public EntityIndex getEntityIndexIfExists(@Nonnull String entityType, @Nonnull EntityIndexKey indexKey) { - final EntityCollection targetCollection = ofNullable(entityCollections.get(entityType)) - .orElseThrow(() -> new IllegalArgumentException("Entity collection of type " + entityType + " doesn't exist!")); - return targetCollection.getIndexByKeyIfExists(indexKey); + @Nonnull + public Optional getEntityIndexIfExists(@Nonnull String entityType, @Nonnull EntityIndexKey indexKey, @Nonnull Class expectedType) { + final EntityCollection targetCollection = ofNullable(this.entityCollections.get(entityType)) + .orElseThrow(() -> new CollectionNotFoundException(entityType)); + final EntityIndex entityIndex = targetCollection.getIndexByKeyIfExists(indexKey); + if (entityIndex == null) { + return empty(); + } else if (expectedType.isInstance(entityIndex)) { + //noinspection unchecked + return of((T)entityIndex); + } else { + throw new IllegalArgumentException("Expected index of type " + expectedType.getName() + " but got " + entityIndex.getClass().getName()); + } } /** * Returns internally held {@link CatalogSchema}. */ + @Nonnull public CatalogSchema getInternalSchema() { return schema.get().getDelegate(); } @@ -933,6 +956,7 @@ public Catalog createCopyWithMergedTransactionalMemory( final MapChanges collectionChanges = transactionalLayer.getTransactionalMemoryLayerIfExists(this.entityCollections); Map updatedServiceCollections = null; if (collectionChanges != null) { + // recognize renamed collections final Map originalCollectionContents = collectionChanges.getMapDelegate(); final ObjectObjectIdentityHashMap originalCollections = new ObjectObjectIdentityHashMap<>(collectionChanges.getRemovedKeys().size()); for (String removedKey : collectionChanges.getRemovedKeys()) { @@ -968,7 +992,7 @@ public Catalog createCopyWithMergedTransactionalMemory( updatedServiceCollections.forEach((entityType, newPersistenceService) -> { possiblyUpdatedCollections.compute( entityType, - (entityTypeKey, entityCollection) -> entityCollection.createCopyWithNewPersistenceService(newPersistenceService) + (entityTypeKey, entityCollection) -> entityCollection.createCopyWithNewPersistenceService(newCatalogVersionId, newPersistenceService) ); }); } @@ -995,7 +1019,7 @@ public Catalog createCopyWithMergedTransactionalMemory( newCatalogVersionId, getCatalogState(), possiblyUpdatedCatalogIndex, - possiblyUpdatedCollections, + possiblyUpdatedCollections.values(), this ); } else { @@ -1009,7 +1033,7 @@ public Catalog createCopyWithMergedTransactionalMemory( newCatalogVersionId, getCatalogState(), possiblyUpdatedCatalogIndex, - possiblyUpdatedCollections, + possiblyUpdatedCollections.values(), this ); } else { @@ -1360,8 +1384,8 @@ private CatalogSchemaContract createEntitySchema( createEntitySchemaMutation.getName() ); final EntityCollection newCollection = new EntityCollection( + this.getName(), this.getVersion(), - this, this.entityTypeSequence.incrementAndGet(), createEntitySchemaMutation.getName(), persistenceService, @@ -1371,6 +1395,7 @@ private CatalogSchemaContract createEntitySchema( ); this.entityCollectionsByPrimaryKey.put(newCollection.getEntityTypePrimaryKey(), newCollection); this.entityCollections.put(newCollection.getEntityType(), newCollection); + newCollection.attachToCatalog(null, this); final CatalogSchema newSchema = CatalogSchema._internalBuildWithUpdatedVersion( catalogSchema, getEntitySchemaAccessor() @@ -1541,7 +1566,7 @@ private void doReplaceEntityCollectionInternal( ); this.entityCollections.put( entityCollectionNameToBeReplaced, - entityCollectionToBeReplacedWith.createCopyWithNewPersistenceService(newPersistenceService) + entityCollectionToBeReplacedWith.createCopyWithNewPersistenceService(catalogVersion, newPersistenceService) ); // store catalog with a new file pointer this.flush(); diff --git a/evita_engine/src/main/java/io/evitadb/core/CatalogRelatedDataStructure.java b/evita_engine/src/main/java/io/evitadb/core/CatalogRelatedDataStructure.java new file mode 100644 index 000000000..e7cc04460 --- /dev/null +++ b/evita_engine/src/main/java/io/evitadb/core/CatalogRelatedDataStructure.java @@ -0,0 +1,70 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb.core; + +import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; +import io.evitadb.core.transaction.memory.TransactionalLayerProducer; +import io.evitadb.exception.EvitaInternalError; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * This interface needs to be implemented by all data structures that are working with current {@link Catalog} instance + * or objects that are related to the actual catalog. The catalog instance is constantly changing during its + * transactional updates because it represents the root of the instance graph that aggregates all other objects in + * the particular version of the catalog. + * + * Beware!!! Late initialization data structures must always create new instance in + * {@link TransactionalLayerProducer#createCopyWithMergedTransactionalMemory(Object, TransactionalLayerMaintainer)} + * method in order not to share the same instance of {@link Catalog} related data between different versions of + * the catalog. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public interface CatalogRelatedDataStructure> { + + /** + * This method attaches the data structure to the particular catalog instance (version). + * It is responsible iterating over its "children" objects and propagating the attachment to them as well. + * The implementation must check that the attachment happens exactly once and throws an exception if not. + * + * @param entityType the type of the entity in which context the call is made + * @param catalog the catalog instance to which the data structure should be attached + * @throws EvitaInternalError if the data structure is already attached to a catalog + */ + void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) throws EvitaInternalError; + + /** + * This method creates new instance of the data structure that keeps the same state as the original one but is + * not attached to any catalog at all - including all "children" objects. This method is used for reattaching + * the data structure to the new version of the catalog, but leaving the original instance attached to the previous + * one. + * + * @return the new instance of the data structure that is not attached to any catalog + */ + @Nonnull + T createCopyForNewCatalogAttachment(); + +} diff --git a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java index df9e98525..1e3ab61f0 100644 --- a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java +++ b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java @@ -105,8 +105,6 @@ import io.evitadb.index.map.TransactionalMap; import io.evitadb.index.mutation.ContainerizedLocalMutationExecutor; import io.evitadb.index.mutation.EntityIndexLocalMutationExecutor; -import io.evitadb.index.price.PriceRefIndex; -import io.evitadb.index.price.PriceSuperIndex; import io.evitadb.index.reference.ReferenceChanges; import io.evitadb.index.reference.TransactionalReference; import io.evitadb.store.entity.model.schema.EntitySchemaStoragePart; @@ -134,7 +132,6 @@ import java.util.OptionalInt; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -156,7 +153,11 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2021 */ -public final class EntityCollection implements TransactionalLayerProducer, EntityCollection>, EntityCollectionContract { +public final class EntityCollection implements + TransactionalLayerProducer, EntityCollection>, + EntityCollectionContract, + CatalogRelatedDataStructure +{ @Getter private final long id = TransactionalObjectVersion.SEQUENCE.nextId(); /** @@ -177,21 +178,9 @@ public final class EntityCollection implements TransactionalLayerProducer schema; - /** - * This field contains reference to the CURRENT {@link Catalog} instance allowing to access {@link EntityCollection} - * for any of entity types that are known to the catalog this collection is part of. Reference to other collections - * is used to access their schema or their indexes from this collection. - * - * The reference pointer is used because when transaction is committed and new catalog is created to atomically swap - * changes and left old readers finish with old catalog, the entity collection copy is created, and we need to init - * the reference to this function lazily when new catalog is instantiated (existence of the new collection precedes - * the creation of the catalog copy). + * Contains entity schema in the form it was initialized during creation. */ - private final AtomicReference catalogAccessor; + private final EntitySchema initialSchema; /** * Contains sequence that allows automatic assigning monotonic primary keys to the entities. */ @@ -239,6 +228,22 @@ public final class EntityCollection implements TransactionalLayerProducer schema; /** * Retrieves the primary key of the given entity or throws an unified exception. @@ -306,8 +311,8 @@ private static void processMutations( } public EntityCollection( + @Nonnull String catalogName, long catalogVersion, - @Nonnull Catalog catalog, int entityTypePrimaryKey, @Nonnull String entityType, @Nonnull CatalogPersistenceService catalogPersistenceService, @@ -327,31 +332,27 @@ public EntityCollection( final EntityCollectionHeader entityHeader = entityCollectionPersistenceService.getEntityCollectionHeader(); this.pkSequence = sequenceService.getOrCreateSequence( - catalog.getName(), SequenceType.ENTITY, entityType, entityHeader.lastPrimaryKey() + catalogName, SequenceType.ENTITY, entityType, entityHeader.lastPrimaryKey() ); this.indexPkSequence = sequenceService.getOrCreateSequence( - catalog.getName(), SequenceType.INDEX, entityType, entityHeader.lastEntityIndexPrimaryKey() + catalogName, SequenceType.INDEX, entityType, entityHeader.lastEntityIndexPrimaryKey() ); - this.catalogAccessor = new AtomicReference<>(catalog); // initialize container buffer final StoragePartPersistenceService storagePartPersistenceService = this.persistenceService.getStoragePartPersistenceService(); this.dataStoreBuffer = new DataStoreMemoryBuffer<>(this, storagePartPersistenceService); // initialize schema - still in constructor - this.schema = new TransactionalReference<>( - ofNullable(storagePartPersistenceService.getStoragePart(catalog.getVersion(), 1, EntitySchemaStoragePart.class)) - .map(EntitySchemaStoragePart::entitySchema) - .map(it -> new EntitySchemaDecorator(catalog::getSchema, it)) - .orElseGet(() -> { - if (this.persistenceService.isNew()) { - final EntitySchema newEntitySchema = EntitySchema._internalBuild(entityType); - this.dataStoreBuffer.update(catalog.getVersion(), new EntitySchemaStoragePart(newEntitySchema)); - return new EntitySchemaDecorator(catalog::getSchema, newEntitySchema); - } else { - throw new SchemaNotFoundException(catalog.getName(), entityHeader.entityType()); - } - }) - ); + this.initialSchema = ofNullable(storagePartPersistenceService.getStoragePart(catalogVersion, 1, EntitySchemaStoragePart.class)) + .map(EntitySchemaStoragePart::entitySchema) + .orElseGet(() -> { + if (this.persistenceService.isNew()) { + final EntitySchema newEntitySchema = EntitySchema._internalBuild(entityType); + this.dataStoreBuffer.update(catalogVersion, new EntitySchemaStoragePart(newEntitySchema)); + return newEntitySchema; + } else { + throw new SchemaNotFoundException(catalog.getName(), entityHeader.entityType()); + } + }); // init entity indexes if (entityHeader.globalEntityIndexId() == null) { Assert.isPremiseValid( @@ -364,18 +365,18 @@ public EntityCollection( it -> (EntityIndex) it ); } else { - this.indexes = loadIndexes(entityHeader); + this.indexes = loadIndexes(catalogVersion, entityHeader); } // sanity check whether we deserialized the file offset index we expect to Assert.isTrue( - entityHeader.entityType().equals(getSchema().getName()), - "Deserialized schema name differs from expected entity type - expected " + entityHeader.entityType() + " got " + getSchema().getName() + entityHeader.entityType().equals(this.initialSchema.getName()), + () -> "Deserialized schema name differs from expected entity type - expected " + entityHeader.entityType() + " got " + this.initialSchema.getName() ); - this.emptyOnStart = isEmpty(); + this.emptyOnStart = this.persistenceService.isEmpty(catalogVersion, dataStoreBuffer); } private EntityCollection( - @Nonnull Catalog catalog, + long catalogVersion, int entityTypePrimaryKey, @Nonnull EntitySchema entitySchema, @Nonnull AtomicInteger pkSequence, @@ -389,19 +390,15 @@ private EntityCollection( this.tracingContext = tracingContext; this.entityType = entitySchema.getName(); this.entityTypePrimaryKey = entityTypePrimaryKey; - this.schema = new TransactionalReference<>(new EntitySchemaDecorator(() -> getCatalog().getSchema(), entitySchema)); - this.catalogAccessor = new AtomicReference<>(catalog); + this.initialSchema = entitySchema; this.pkSequence = pkSequence; this.catalogPersistenceService = catalogPersistenceService; this.persistenceService = persistenceService; this.indexPkSequence = indexPkSequence; this.dataStoreBuffer = new DataStoreMemoryBuffer<>(this, persistenceService.getStoragePartPersistenceService()); this.indexes = new TransactionalMap<>(indexes, it -> (EntityIndex) it); - for (EntityIndex entityIndex : this.indexes.values()) { - entityIndex.updateReferencesTo(this); - } this.cacheSupervisor = cacheSupervisor; - this.emptyOnStart = isEmpty(); + this.emptyOnStart = this.persistenceService.isEmpty(catalogVersion, dataStoreBuffer); } @Override @@ -421,7 +418,7 @@ public > T getEntities(@Nonnu @Override @Nonnull public Optional getBinaryEntity(int primaryKey, @Nonnull EvitaRequest evitaRequest, @Nonnull EvitaSessionContract session) { - final long catalogVersion = catalogAccessor.get().getVersion(); + final long catalogVersion = catalog.getVersion(); final Optional entity = cacheSupervisor.analyse( session, primaryKey, @@ -433,7 +430,7 @@ public Optional getBinaryEntity(int primaryKey, @Nonnull EvitaRequ evitaRequest, session, entityType -> entityType.equals(getEntityType()) ? - this : getCatalog().getCollectionForEntityOrThrowException(entityType), + this : catalog.getCollectionForEntityOrThrowException(entityType), dataStoreBuffer ), binaryEntity -> this.persistenceService.enrichEntity( @@ -704,12 +701,12 @@ public SealedEntity[] deleteEntitiesAndReturnThem(@Nonnull EvitaRequest evitaReq @Override public boolean isEmpty() { - return this.persistenceService.isEmpty(getCatalog().getVersion(), dataStoreBuffer); + return this.persistenceService.isEmpty(catalog.getVersion(), dataStoreBuffer); } @Override public int size() { - return this.persistenceService.countEntities(getCatalog().getVersion(), dataStoreBuffer); + return this.persistenceService.countEntities(catalog.getVersion(), dataStoreBuffer); } @Override @@ -764,7 +761,7 @@ public SealedEntitySchema updateSchema(@Nonnull CatalogSchemaContract catalogSch final EntitySchema updatedInternalSchema = (EntitySchema) updatedSchema; final EntitySchemaDecorator originalSchemaBeforeExchange = this.schema.compareAndExchange( this.schema.get(), - new EntitySchemaDecorator(() -> getCatalog().getSchema(), updatedInternalSchema) + new EntitySchemaDecorator(() -> this.catalog.getSchema(), updatedInternalSchema) ); Assert.isTrue( originalSchemaBeforeExchange.version() == originalSchema.version(), @@ -773,17 +770,17 @@ public SealedEntitySchema updateSchema(@Nonnull CatalogSchemaContract catalogSch } } catch (RuntimeException ex) { // revert all changes in the schema (for current transaction) if anything failed - final EntitySchemaDecorator decorator = new EntitySchemaDecorator(() -> getCatalog().getSchema(), originalSchema); + final EntitySchemaDecorator decorator = new EntitySchemaDecorator(() -> this.catalog.getSchema(), originalSchema); this.schema.set(decorator); throw ex; } finally { // finally, store the updated catalog schema to disk final EntitySchema updatedInternalSchema = getInternalSchema(); - this.dataStoreBuffer.update(getCatalog().getVersion(), new EntitySchemaStoragePart(updatedInternalSchema)); + this.dataStoreBuffer.update(this.catalog.getVersion(), new EntitySchemaStoragePart(updatedInternalSchema)); } final SealedEntitySchema schemaResult = getSchema(); - this.catalogAccessor.get().entitySchemaUpdated(schemaResult); + this.catalog.entitySchemaUpdated(schemaResult); return schemaResult; } @@ -891,7 +888,7 @@ public EntityDecorator enrichEntity( return Entity.decorate( // load all missing data according to current evita request this.persistenceService.enrichEntity( - getCatalog().getVersion(), + this.catalog.getVersion(), internalSchema, // use all data from existing entity partiallyLoadedEntity, @@ -949,7 +946,7 @@ public T ensureReferencesFetched(@Nonnull T entity) return (T) Entity.decorate( // load references if missing this.persistenceService.enrichEntity( - getCatalog().getVersion(), + this.catalog.getVersion(), getInternalSchema(), // use all data from existing entity partiallyLoadedEntity, @@ -958,7 +955,7 @@ public T ensureReferencesFetched(@Nonnull T entity) partiallyLoadedEntity.getAssociatedDataPredicate(), new ReferenceContractSerializablePredicate(true), partiallyLoadedEntity.getPricePredicate(), - dataStoreBuffer + this.dataStoreBuffer ), // use original schema getInternalSchema(), @@ -993,7 +990,7 @@ public T ensureReferencesFetched(@Nonnull T entity) * Returns internally held {@link EntitySchema}. */ public EntitySchema getInternalSchema() { - return schema.get().getDelegate(); + return this.schema == null ? this.initialSchema : this.schema.get().getDelegate(); } /** @@ -1021,15 +1018,6 @@ public EntityIndex getIndexByKeyIfExists(EntityIndexKey entityIndexKey) { return this.dataStoreBuffer.getIndexIfExists(entityIndexKey, this.indexes::get); } - /** - * Method returns {@link PriceSuperIndex}. This method is used when deserializing {@link PriceRefIndex} which - * looks up for prices in super index in order to save memory consumption. - */ - @Nonnull - public PriceSuperIndex getPriceSuperIndex() { - return getGlobalIndex().getPriceIndex(); - } - /** * Method returns {@link GlobalEntityIndex} or throws an exception if it hasn't yet exist. */ @@ -1062,16 +1050,15 @@ public QueryContext createQueryContext( @Nonnull EvitaRequest evitaRequest, @Nonnull EvitaSessionContract session ) { - final Catalog catalog = getCatalog(); return new QueryContext( queryContext, - catalog, + this.catalog, this, new ReadOnlyEntityStorageContainerAccessor(catalog.getVersion(), this.dataStoreBuffer, this::getInternalSchema), session, evitaRequest, queryContext.getCurrentStep(), - indexes, - cacheSupervisor + this.indexes, + this.cacheSupervisor ); } @@ -1084,15 +1071,14 @@ public QueryContext createQueryContext( */ @Nonnull public QueryContext createQueryContext(@Nonnull EvitaRequest evitaRequest, @Nonnull EvitaSessionContract session) { - final Catalog catalog = getCatalog(); return new QueryContext( - catalog, + this.catalog, this, new ReadOnlyEntityStorageContainerAccessor(catalog.getVersion(), this.dataStoreBuffer, this::getInternalSchema), session, evitaRequest, evitaRequest.isQueryTelemetryRequested() ? new QueryTelemetry(QueryPhase.OVERALL) : null, - indexes, - cacheSupervisor + this.indexes, + this.cacheSupervisor ); } @@ -1115,13 +1101,13 @@ public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer @Nonnull @Override public EntityCollection createCopyWithMergedTransactionalMemory(@Nullable DataStoreChanges layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { + final long catalogVersion = this.catalog.getVersion(); final DataStoreChanges transactionalChanges = transactionalLayer.getTransactionalMemoryLayerIfExists(this); if (transactionalChanges != null) { // when we register all storage parts for persisting we can now release transactional memory transactionalLayer.removeTransactionalMemoryLayer(this); - final Catalog catalog = this.catalogAccessor.get(); return new EntityCollection( - catalog, + catalogVersion, this.entityTypePrimaryKey, transactionalLayer.getStateCopyWithCommittedChanges(this.schema) .map(EntitySchemaDecorator::getDelegate) @@ -1130,7 +1116,7 @@ public EntityCollection createCopyWithMergedTransactionalMemory(@Nullable DataSt this.indexPkSequence, this.catalogPersistenceService, this.catalogPersistenceService.getOrCreateEntityCollectionPersistenceService( - catalog.getVersion(), this.entityType, this.entityTypePrimaryKey + catalogVersion, this.entityType, this.entityTypePrimaryKey ), transactionalLayer.getStateCopyWithCommittedChanges(this.indexes), this.cacheSupervisor, @@ -1149,8 +1135,8 @@ public EntityCollection createCopyWithMergedTransactionalMemory(@Nullable DataSt transactionalLayer.getTransactionalMemoryLayerIfExists(this.indexes) == null, "Indexes are unexpectedly modified!" ); - // no changes present we can return self - return this; + // no changes were present - we return shallow copy + return createCopyForNewCatalogAttachment(); } } @@ -1161,11 +1147,11 @@ public EntityCollection createCopyWithMergedTransactionalMemory(@Nullable DataSt * @return a new EntityCollection object with the updated persistence service */ @Nonnull - public EntityCollection createCopyWithNewPersistenceService(@Nonnull EntityCollectionPersistenceService newPersistenceService) { - return new EntityCollection( - this.catalogAccessor.get(), + public EntityCollection createCopyWithNewPersistenceService(long catalogVersion, @Nonnull EntityCollectionPersistenceService newPersistenceService) { + final EntityCollection entityCollection = new EntityCollection( + catalogVersion, this.entityTypePrimaryKey, - getInternalSchema(), + this.getInternalSchema(), this.pkSequence, this.indexPkSequence, this.catalogPersistenceService, @@ -1174,6 +1160,40 @@ public EntityCollection createCopyWithNewPersistenceService(@Nonnull EntityColle this.cacheSupervisor, this.tracingContext ); + // the catalog remains the same here + entityCollection.catalog = this.catalog; + entityCollection.schema = this.schema; + return entityCollection; + } + + /** + * Creates a new copy of the Entity collection with the same state as the current one. + * @return a new EntityCollection object with the same state as the current one + */ + @Nonnull + public EntityCollection createCopyForNewCatalogAttachment() { + //noinspection unchecked + return new EntityCollection( + this.catalog.getVersion(), + this.entityTypePrimaryKey, + this.getInternalSchema(), + this.pkSequence, + this.indexPkSequence, + this.catalogPersistenceService, + this.persistenceService, + this.indexes.entrySet() + .stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + it -> it.getValue() instanceof CatalogRelatedDataStructure ? + ((CatalogRelatedDataStructure) it.getValue()).createCopyForNewCatalogAttachment() : + it.getValue() + ) + ), + this.cacheSupervisor, + this.tracingContext + ); } /** @@ -1201,20 +1221,24 @@ EntityCollectionHeader flush(long catalogVersion) { .orElseGet(this::getEntityCollectionHeader); } + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + this.catalog = catalog; + this.schema = new TransactionalReference<>( + new EntitySchemaDecorator(catalog::getSchema, this.initialSchema) + ); + for (EntityIndex entityIndex : indexes.values()) { + entityIndex.useSchema(this::getInternalSchema); + if (entityIndex instanceof CatalogRelatedDataStructure catalogRelatedEntityIndex) { + catalogRelatedEntityIndex.attachToCatalog(this.entityType, this.catalog); + } + } + } + /* PRIVATE METHODS */ - /** - * This method replaces references in current instance that needs to work with information outside this entity - * collection. When transaction is committed new catalog instance is created after entity collection instances are - * recreated to encapsulate them. That means that all entity collections still point to the old catalog and when - * new one encapsulating them is created, all of them needs to update their "pointers". - */ - void updateReferenceToCatalog(@Nonnull Catalog catalog) { - this.catalogAccessor.set(catalog); - } - /** * Generates new UNIQUE primary key for the entity. Calling this * @@ -1225,50 +1249,35 @@ private int getNextPrimaryKey() { return pkSequence.incrementAndGet(); } - /** - * Returns reference to current {@link Catalog} instance. - */ - @Nonnull - private Catalog getCatalog() { - return catalogAccessor.get(); - } - /** * Method loads all indexes mentioned in {@link EntityCollectionHeader#globalEntityIndexId()} and * {@link EntityCollectionHeader#usedEntityIndexIds()} into a transactional map indexed by their * {@link EntityIndex#getIndexKey()}. */ - private TransactionalMap loadIndexes(@Nonnull EntityCollectionHeader entityHeader) { + @Nonnull + private TransactionalMap loadIndexes(long catalogVersion, @Nonnull EntityCollectionHeader entityHeader) { // we need to load global index first, this is the only one index containing all data - final long catalogVersion = getCatalog().getVersion(); final GlobalEntityIndex globalIndex = (GlobalEntityIndex) this.persistenceService.readEntityIndex( catalogVersion, entityHeader.globalEntityIndexId(), - this::getInternalSchema, - () -> { - throw new GenericEvitaInternalError("Global index is currently loading!"); - }, - this::getPriceSuperIndex + this.initialSchema + ); + globalIndex.useSchema(this::getInternalSchema); + Assert.isPremiseValid( + globalIndex != null, + () -> "Global index must never be null for entity type `" + this.initialSchema.getName() + "`!" ); - Assert.isPremiseValid(globalIndex != null, "Global index must never be null for entity type `" + getSchema().getName() + "`!"); return new TransactionalMap<>( // now join global index with all other reduced indexes into single key-value index Stream.concat( Stream.of(globalIndex), entityHeader.usedEntityIndexIds() .stream() - .map(eid -> - this.persistenceService.readEntityIndex( - catalogVersion, - eid, - this::getInternalSchema, - // this method is used just for `readEntityIndex` method to access global index until - // it's available by `this::getPriceSuperIndex` (constructor must be finished first) - globalIndex::getPriceIndex, - // this method needs to be used from now on to access the super index - this::getPriceSuperIndex - ) - ) + .map(eid -> { + final EntityIndex entityIndex = this.persistenceService.readEntityIndex(catalogVersion, eid, this.initialSchema); + entityIndex.useSchema(this::getInternalSchema); + return entityIndex; + }) ) .collect( Collectors.toMap( @@ -1306,7 +1315,7 @@ private Entity getFullEntityById(int primaryKey) { @Nullable private Entity getEntityById(int primaryKey, @Nonnull EvitaRequest evitaRequest) { return this.persistenceService.readEntity( - getCatalog().getVersion(), + catalog.getVersion(), primaryKey, evitaRequest, getInternalSchema(), @@ -1376,12 +1385,12 @@ private int upsertEntityInternal(@Nonnull EntityMutation entityMutation) { // it was already executed when mutation was created, but there are two reasons to do it again // - we don't trust clients - in future it may be some external JS application // - schema may have changed between entity was provided to the client and the moment upsert was called - final SealedCatalogSchema catalogSchema = getCatalog().getSchema(); + final SealedCatalogSchema catalogSchema = this.catalog.getSchema(); entityMutation.verifyOrEvolveSchema(catalogSchema, getSchema(), emptyOnStart && isEmpty()) .ifPresent( it -> { // we need to call apply mutation on the catalog level in order to insert the mutations to the WAL - getCatalog().applyMutation( + this.catalog.applyMutation( new ModifyEntitySchemaMutation(getEntityType(), it) ); } @@ -1478,8 +1487,8 @@ private void applyMutations( final EntityRemoveMutation entityRemoveMutation = entityMutation instanceof EntityRemoveMutation erm ? erm : null; final ContainerizedLocalMutationExecutor changeCollector = new ContainerizedLocalMutationExecutor( - dataStoreBuffer, - getCatalog().getVersion(), + this.dataStoreBuffer, + this.catalog.getVersion(), entityMutation.getEntityPrimaryKey(), entityMutation.expects(), this::getInternalSchema, @@ -1490,10 +1499,9 @@ private void applyMutations( changeCollector, entityMutation.getEntityPrimaryKey(), this.entityIndexCreator, - this.getCatalog().getCatalogIndexMaintainer(), + this.catalog.getCatalogIndexMaintainer(), this::getInternalSchema, - entityType -> this.catalogAccessor.get() - .getCollectionForEntityOrThrowException(entityType).getInternalSchema(), + entityType -> this.catalog.getCollectionForEntityOrThrowException(entityType).getInternalSchema(), undoOnError ); @@ -1539,12 +1547,11 @@ private void assertAllReferencedEntitiesExist(@Nonnull EntitySchema newSchema) { ) .distinct() .forEach(it -> { - final Catalog catalog = this.catalogAccessor.get(); Assert.isTrue( catalog.getCollectionForEntity(it).isPresent(), () -> new InvalidMutationException( "Entity schema `" + newSchema.getName() + "` references entity `" + it + "`," + - " but such entity is not known in catalog `" + catalog.getName() + "`." + " but such entity is not known in catalog `" + this.catalog.getName() + "`." ) ); }); @@ -1593,9 +1600,10 @@ public EntityIndex getOrCreateIndex(@Nonnull EntityIndexKey entityIndexKey) { EntityCollection.this.indexes.computeIfAbsent( eik, eikAgain -> { + final EntityIndex entityIndex; // if index doesn't exist even there create new one if (eikAgain.getType() == EntityIndexType.GLOBAL) { - return new GlobalEntityIndex(indexPkSequence.incrementAndGet(), eikAgain, EntityCollection.this::getInternalSchema); + entityIndex = new GlobalEntityIndex(indexPkSequence.incrementAndGet(), entityType, eikAgain); } else { final EntityIndex globalIndex = getIndexIfExists(new EntityIndexKey(EntityIndexType.GLOBAL)); Assert.isPremiseValid( @@ -1603,18 +1611,22 @@ public EntityIndex getOrCreateIndex(@Nonnull EntityIndexKey entityIndexKey) { "When reduced index is created global one must already exist!" ); if (eikAgain.getType() == EntityIndexType.REFERENCED_ENTITY_TYPE) { - return new ReferencedTypeEntityIndex( - indexPkSequence.incrementAndGet(), eikAgain, - EntityCollection.this::getInternalSchema + entityIndex = new ReferencedTypeEntityIndex( + indexPkSequence.incrementAndGet(), entityType, eikAgain ); } else { - return new ReducedEntityIndex( - indexPkSequence.incrementAndGet(), eikAgain, - EntityCollection.this::getInternalSchema, - ((GlobalEntityIndex) globalIndex)::getPriceIndex + entityIndex = new ReducedEntityIndex( + indexPkSequence.incrementAndGet(), entityType, eikAgain ); } } + + entityIndex.useSchema(EntityCollection.this::getInternalSchema); + if (entityIndex instanceof CatalogRelatedDataStructure lateInitializationIndex) { + lateInitializationIndex.attachToCatalog(EntityCollection.this.entityType, EntityCollection.this.catalog); + } + + return entityIndex; } ) ); diff --git a/evita_engine/src/main/java/io/evitadb/core/Evita.java b/evita_engine/src/main/java/io/evitadb/core/Evita.java index 19322fc63..508fc4560 100644 --- a/evita_engine/src/main/java/io/evitadb/core/Evita.java +++ b/evita_engine/src/main/java/io/evitadb/core/Evita.java @@ -833,7 +833,7 @@ private CreatedSession createSessionInternal(@Nonnull SessionTraits sessionTrait final SessionRegistry sessionRegistry = activeSessions.computeIfAbsent( sessionTraits.catalogName(), - theCatalogName -> new SessionRegistry(tracingContext, catalog) + theCatalogName -> new SessionRegistry(tracingContext, () -> (Catalog) this.catalogs.get(sessionTraits.catalogName())) ); final NonTransactionalCatalogDescriptor nonTransactionalCatalogDescriptor = diff --git a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java index 27810f103..558879353 100644 --- a/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java +++ b/evita_engine/src/main/java/io/evitadb/core/SessionRegistry.java @@ -74,7 +74,7 @@ final class SessionRegistry { /** * Reference to {@link Catalog} this instance is bound to. */ - private final Catalog catalog; + private final Supplier catalog; /** * Keeps information about currently active sessions. */ @@ -163,7 +163,8 @@ public void removeSession(@Nonnull EvitaSessionContract session) { .unregisterSessionConsumingCatalogInVersion(session.getCatalogVersion()); if (finalizationResult.lastReader()) { // notify listeners that the catalog version is no longer used - this.catalog.catalogVersionBeyondTheHorizon( + final Catalog theCatalog = this.catalog.get(); + theCatalog.catalogVersionBeyondTheHorizon( finalizationResult.minimalActiveCatalogVersion() ); } @@ -235,7 +236,7 @@ public Object invoke(Object proxy, Method method, Object[] args) { if (method.isAnnotationPresent(Traced.class)) { return tracingContext.executeWithinBlockIfParentContextAvailable( "session call - " + method.getName(), - invocation::get, + invocation, () -> { final Parameter[] parameters = method.getParameters(); final SpanAttribute[] spanAttributes = new SpanAttribute[1 + parameters.length]; diff --git a/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionWalFinalizer.java b/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionWalFinalizer.java index 9ed93a8fb..41f0ce0c0 100644 --- a/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionWalFinalizer.java +++ b/evita_engine/src/main/java/io/evitadb/core/transaction/TransactionWalFinalizer.java @@ -134,7 +134,7 @@ public void commit(@Nonnull TransactionalLayerMaintainer transactionalLayer) { // copying them to the shared WAL if (walPersistenceService != null) { // this invokes the asynchronous action of copying the isolated WAL to the shared one - catalog.commitWal( + this.catalog.commitWal( transactionId, commitBehaviour, walPersistenceService, diff --git a/evita_engine/src/main/java/io/evitadb/index/CatalogIndex.java b/evita_engine/src/main/java/io/evitadb/index/CatalogIndex.java index 04d2440cb..2725905ee 100644 --- a/evita_engine/src/main/java/io/evitadb/index/CatalogIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/CatalogIndex.java @@ -29,6 +29,7 @@ import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.GlobalAttributeSchemaContract; import io.evitadb.core.Catalog; +import io.evitadb.core.CatalogRelatedDataStructure; import io.evitadb.core.Transaction; import io.evitadb.core.transaction.memory.TransactionalContainerChanges; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; @@ -38,7 +39,6 @@ import io.evitadb.index.attribute.GlobalUniqueIndex; import io.evitadb.index.attribute.UniqueIndex; import io.evitadb.index.bool.TransactionalBoolean; -import io.evitadb.index.map.MapChanges; import io.evitadb.index.map.TransactionalMap; import io.evitadb.store.model.StoragePart; import io.evitadb.store.spi.model.storageParts.index.CatalogIndexStoragePart; @@ -47,7 +47,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; @@ -70,7 +69,11 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2021 */ -public class CatalogIndex implements Index, TransactionalLayerProducer, IndexDataStructure { +public class CatalogIndex implements + Index, TransactionalLayerProducer, + IndexDataStructure, + CatalogRelatedDataStructure +{ @Getter private final long id = TransactionalObjectVersion.SEQUENCE.nextId(); /** * This is internal flag that tracks whether the index contents became dirty and needs to be persisted. @@ -92,29 +95,35 @@ public class CatalogIndex implements Index, TransactionalLayerP */ private Catalog catalog; - public CatalogIndex(@Nonnull Catalog catalog) { + public CatalogIndex() { this.version = 1; this.dirty = new TransactionalBoolean(); - this.catalog = catalog; this.uniqueIndex = new TransactionalMap<>(new HashMap<>(), GlobalUniqueIndex.class, Function.identity()); } - public CatalogIndex(@Nonnull Catalog catalog, int version, @Nonnull Map uniqueIndex) { + public CatalogIndex(int version, @Nonnull Map uniqueIndex) { this.version = version; this.dirty = new TransactionalBoolean(); - this.catalog = catalog; this.uniqueIndex = new TransactionalMap<>(uniqueIndex, GlobalUniqueIndex.class, Function.identity()); } - /** - * Replaces reference to the new catalog object. This needs to be done when transaction is - * committed and new GlobalUniqueIndex is created with link to the original transactional Catalog but finally - * new {@link Catalog} is created and the new indexes linking old collection needs to be - * migrated to new catalog instance. - */ - public void updateReferencesTo(@Nonnull Catalog newCatalog) { - this.catalog = newCatalog; - this.uniqueIndex.values().forEach(it -> it.updateReferencesTo(newCatalog)); + private CatalogIndex(int version, @Nonnull TransactionalMap uniqueIndex) { + this.version = version; + this.dirty = new TransactionalBoolean(); + this.uniqueIndex = uniqueIndex; + } + + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + this.catalog = catalog; + } + + @Nonnull + @Override + public CatalogIndex createCopyForNewCatalogAttachment() { + return new CatalogIndex( + this.version, this.uniqueIndex + ); } @Nonnull @@ -229,7 +238,7 @@ public CatalogIndexChanges createLayer() { public CatalogIndex createCopyWithMergedTransactionalMemory(@Nullable CatalogIndexChanges layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { final Boolean wasDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); final CatalogIndex newCatalogIndex = new CatalogIndex( - catalog, version + (wasDirty ? 1 : 0), transactionalLayer.getStateCopyWithCommittedChanges(uniqueIndex) + version + (wasDirty ? 1 : 0), transactionalLayer.getStateCopyWithCommittedChanges(uniqueIndex) ); ofNullable(layer).ifPresent(it -> it.clean(transactionalLayer)); return newCatalogIndex; @@ -248,7 +257,7 @@ public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer * This class collects changes in {@link #uniqueIndex} transactional maps. */ public static class CatalogIndexChanges { - private final TransactionalContainerChanges, Map, TransactionalMap>, GlobalUniqueIndex, GlobalUniqueIndex> uniqueIndexChanges = new TransactionalContainerChanges<>(); + private final TransactionalContainerChanges uniqueIndexChanges = new TransactionalContainerChanges<>(); public void addCreatedItem(@Nonnull GlobalUniqueIndex uniqueIndex) { uniqueIndexChanges.addCreatedItem(uniqueIndex); diff --git a/evita_engine/src/main/java/io/evitadb/index/EntityIndex.java b/evita_engine/src/main/java/io/evitadb/index/EntityIndex.java index e38d4b115..632caefa7 100644 --- a/evita_engine/src/main/java/io/evitadb/index/EntityIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/EntityIndex.java @@ -26,7 +26,6 @@ import io.evitadb.api.requestResponse.data.Versioned; import io.evitadb.api.requestResponse.schema.EvolutionMode; import io.evitadb.api.requestResponse.schema.dto.EntitySchema; -import io.evitadb.core.EntityCollection; import io.evitadb.core.query.algebra.Formula; import io.evitadb.core.query.algebra.base.ConstantFormula; import io.evitadb.core.query.algebra.base.EmptyFormula; @@ -139,55 +138,54 @@ public abstract class EntityIndex implements * transaction is committed and anything in this index was changed). */ protected final int version; - /** - * Lambda that provides access to the current schema. - * Beware this reference changes with each entity collection exchange during transactional commit. - */ - protected Supplier schemaAccessor; /** * This field captures the original state of the hierarchy index when this index was created. * This information is used along with {@link #dirty} flag to determine whether {@link EntityIndexStoragePart} * should be persisted. */ - private final boolean originalHierarchyIndexEmpty; + protected final boolean originalHierarchyIndexEmpty; /** * This field captures the original state of the price id sequence when this index was created. * This information is used along with {@link #dirty} flag to determine whether {@link EntityIndexStoragePart} * should be persisted. */ - private final Integer originalInternalPriceIdSequence; + protected final Integer originalInternalPriceIdSequence; /** * This field captures the original state of the attribute index when this index was created. * This information is used along with {@link #dirty} flag to determine whether {@link EntityIndexStoragePart} * should be persisted. */ - private final Set originalAttributeIndexes; + protected final Set originalAttributeIndexes; /** * This field captures the original state of the price indexes when this index was created. * This information is used along with {@link #dirty} flag to determine whether {@link EntityIndexStoragePart} * should be persisted. */ - private final Set originalPriceIndexes; + protected final Set originalPriceIndexes; /** * This field captures the original state of the facet indexes when this index was created. * This information is used along with {@link #dirty} flag to determine whether {@link EntityIndexStoragePart} * should be persisted. */ - private final Set originalFacetIndexes; + protected final Set originalFacetIndexes; + /** + * Lambda that provides access to the current schema. + * Beware this reference changes with each entity collection exchange during transactional commit. + */ + protected Supplier schemaAccessor; protected EntityIndex( int primaryKey, - @Nonnull EntityIndexKey indexKey, - @Nonnull Supplier schemaAccessor + @Nonnull String entityType, + @Nonnull EntityIndexKey indexKey ) { this.primaryKey = primaryKey; this.version = 1; this.dirty = new TransactionalBoolean(); this.indexKey = indexKey; - this.schemaAccessor = schemaAccessor; this.entityIds = new TransactionalBitmap(); this.entityIdsByLanguage = new TransactionalMap<>(new HashMap<>(), TransactionalBitmap.class, TransactionalBitmap::new); - this.attributeIndex = new AttributeIndex(schemaAccessor.get().getName()); + this.attributeIndex = new AttributeIndex(entityType); this.hierarchyIndex = new HierarchyIndex(); this.facetIndex = new FacetIndex(); this.originalHierarchyIndexEmpty = true; @@ -201,7 +199,6 @@ protected EntityIndex( int primaryKey, @Nonnull EntityIndexKey indexKey, int version, - @Nonnull Supplier schemaAccessor, @Nonnull Bitmap entityIds, @Nonnull Map entityIdsByLanguage, @Nonnull AttributeIndex attributeIndex, @@ -212,7 +209,6 @@ protected EntityIndex( this.primaryKey = primaryKey; this.indexKey = indexKey; this.version = version; - this.schemaAccessor = schemaAccessor; this.dirty = new TransactionalBoolean(); this.entityIds = new TransactionalBitmap(entityIds); @@ -231,6 +227,48 @@ protected EntityIndex( this.originalFacetIndexes = getFacetIndexReferencedEntities(); } + protected EntityIndex( + int primaryKey, + @Nonnull EntityIndexKey indexKey, + int version, + @Nonnull TransactionalBitmap entityIds, + @Nonnull TransactionalMap entityIdsByLanguage, + @Nonnull AttributeIndex attributeIndex, + @Nonnull HierarchyIndex hierarchyIndex, + @Nonnull FacetIndex facetIndex, + boolean originalHierarchyIndexEmpty, + @Nonnull Integer originalInternalPriceIdSequence, + @Nonnull Set originalAttributeIndexes, + @Nonnull Set originalPriceIndexes, + @Nonnull Set originalFacetIndexes + ) { + this.primaryKey = primaryKey; + this.indexKey = indexKey; + this.version = version; + this.dirty = new TransactionalBoolean(); + this.entityIds = entityIds; + this.entityIdsByLanguage = entityIdsByLanguage; + this.attributeIndex = attributeIndex; + this.hierarchyIndex = hierarchyIndex; + this.facetIndex = facetIndex; + this.originalHierarchyIndexEmpty = originalHierarchyIndexEmpty; + this.originalInternalPriceIdSequence = originalInternalPriceIdSequence; + this.originalAttributeIndexes = originalAttributeIndexes; + this.originalPriceIndexes = originalPriceIndexes; + this.originalFacetIndexes = originalFacetIndexes; + } + + /** + * Initializes accessor that allows accessing current entity schema. This accessor needs to be initialized in + * {@link io.evitadb.core.EntityCollection} constructors and everytime new schema is created within entity + * collection existence. + * + * @param entitySchemaAccessor lambda that provides access to the current schema + */ + public void useSchema(@Nonnull Supplier entitySchemaAccessor) { + this.schemaAccessor = entitySchemaAccessor; + } + /** * Registers new entity primary key to the superset of entity ids of this entity index. */ @@ -274,16 +312,6 @@ public Bitmap getAllPrimaryKeys() { return entityIds; } - /** - * Replaces reference to the schema accessor lambda in new collection. This needs to be done when transaction is - * committed and new EntityIndex is created with link to the original transactional EntityIndex but finally - * new {@link EntityCollection} is created and the new indexes linking old collection needs to be - * migrated to new entity collection. - */ - public void updateReferencesTo(@Nonnull EntityCollection newCollection) { - this.schemaAccessor = newCollection::getInternalSchema; - } - /** * Provides access to the entity schema via passed lambda. */ @@ -484,7 +512,7 @@ private static Set getPriceIndexKeys(@Nonnull PriceIndexContract * @return the internal price ID sequence if the PriceIndex is an instance of PriceSuperIndex, null otherwise */ @Nullable - private Integer getInternalPriceIdSequence(@Nonnull PriceIndexContract priceIndex) { + private static Integer getInternalPriceIdSequence(@Nonnull PriceIndexContract priceIndex) { return priceIndex instanceof PriceSuperIndex ? ((PriceSuperIndex) priceIndex).getLastAssignedInternalPriceId() : null; } diff --git a/evita_engine/src/main/java/io/evitadb/index/GlobalEntityIndex.java b/evita_engine/src/main/java/io/evitadb/index/GlobalEntityIndex.java index f9f55ebae..0615e1de0 100644 --- a/evita_engine/src/main/java/io/evitadb/index/GlobalEntityIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/GlobalEntityIndex.java @@ -23,7 +23,6 @@ package io.evitadb.index; -import io.evitadb.api.requestResponse.schema.dto.EntitySchema; import io.evitadb.core.EntityCollection; import io.evitadb.core.query.algebra.Formula; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; @@ -44,7 +43,6 @@ import java.util.Collection; import java.util.Locale; import java.util.Map; -import java.util.function.Supplier; /** * Global entity index contains complete set of indexed data including their bodies. It contains data for all entities @@ -67,10 +65,10 @@ public class GlobalEntityIndex extends EntityIndex public GlobalEntityIndex( int primaryKey, - @Nonnull EntityIndexKey entityIndexKey, - @Nonnull Supplier schemaAccessor + @Nonnull String entityType, + @Nonnull EntityIndexKey entityIndexKey ) { - super(primaryKey, entityIndexKey, schemaAccessor); + super(primaryKey, entityType, entityIndexKey); this.priceIndex = new PriceSuperIndex(); } @@ -78,7 +76,6 @@ public GlobalEntityIndex( int primaryKey, @Nonnull EntityIndexKey entityIndexKey, int version, - @Nonnull Supplier schemaAccessor, @Nonnull Bitmap entityIds, @Nonnull Map entityIdsByLanguage, @Nonnull AttributeIndex attributeIndex, @@ -87,7 +84,7 @@ public GlobalEntityIndex( @Nonnull FacetIndex facetIndex ) { super( - primaryKey, entityIndexKey, version, schemaAccessor, + primaryKey, entityIndexKey, version, entityIds, entityIdsByLanguage, attributeIndex, hierarchyIndex, facetIndex, priceIndex ); @@ -126,7 +123,7 @@ public GlobalEntityIndex createCopyWithMergedTransactionalMemory( // we can safely throw away dirty flag now final Boolean wasDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); return new GlobalEntityIndex( - primaryKey, indexKey, version + (wasDirty ? 1 : 0), schemaAccessor, + this.primaryKey, this.indexKey, this.version + (wasDirty ? 1 : 0), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIds), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIdsByLanguage), transactionalLayer.getStateCopyWithCommittedChanges(this.attributeIndex), diff --git a/evita_engine/src/main/java/io/evitadb/index/ReducedEntityIndex.java b/evita_engine/src/main/java/io/evitadb/index/ReducedEntityIndex.java index 47b4d5ae9..7374cacd2 100644 --- a/evita_engine/src/main/java/io/evitadb/index/ReducedEntityIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/ReducedEntityIndex.java @@ -24,8 +24,8 @@ package io.evitadb.index; import io.evitadb.api.requestResponse.data.ReferenceContract; -import io.evitadb.api.requestResponse.schema.dto.EntitySchema; -import io.evitadb.core.EntityCollection; +import io.evitadb.core.Catalog; +import io.evitadb.core.CatalogRelatedDataStructure; import io.evitadb.core.query.algebra.Formula; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.VoidTransactionMemoryProducer; @@ -34,10 +34,12 @@ import io.evitadb.index.bitmap.TransactionalBitmap; import io.evitadb.index.facet.FacetIndex; import io.evitadb.index.hierarchy.HierarchyIndex; +import io.evitadb.index.map.TransactionalMap; import io.evitadb.index.price.PriceIndexContract; import io.evitadb.index.price.PriceRefIndex; -import io.evitadb.index.price.PriceSuperIndex; +import io.evitadb.index.price.model.PriceIndexKey; import io.evitadb.store.model.StoragePart; +import io.evitadb.store.spi.model.storageParts.index.AttributeIndexStorageKey; import lombok.Getter; import lombok.experimental.Delegate; @@ -46,7 +48,7 @@ import java.util.Collection; import java.util.Locale; import java.util.Map; -import java.util.function.Supplier; +import java.util.Set; /** * Reduced entity index is a "helper" index that maintains primarily bitmaps of primary keys that are connected to @@ -60,7 +62,7 @@ * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2022 */ public class ReducedEntityIndex extends EntityIndex - implements VoidTransactionMemoryProducer { + implements VoidTransactionMemoryProducer, CatalogRelatedDataStructure { /** * This part of index collects information about prices of the entities. It provides data that are necessary for * constructing {@link Formula} tree for the constraints related to the prices. @@ -70,19 +72,17 @@ public class ReducedEntityIndex extends EntityIndex public ReducedEntityIndex( int primaryKey, - @Nonnull EntityIndexKey entityIndexKey, - @Nonnull Supplier schemaAccessor, - @Nonnull Supplier superIndexAccessor + @Nonnull String entityType, + @Nonnull EntityIndexKey entityIndexKey ) { - super(primaryKey, entityIndexKey, schemaAccessor); - this.priceIndex = new PriceRefIndex(superIndexAccessor); + super(primaryKey, entityType, entityIndexKey); + this.priceIndex = new PriceRefIndex(); } public ReducedEntityIndex( int primaryKey, @Nonnull EntityIndexKey entityIndexKey, int version, - @Nonnull Supplier schemaAccessor, @Nonnull Bitmap entityIds, @Nonnull Map entityIdsByLanguage, @Nonnull AttributeIndex attributeIndex, @@ -91,16 +91,59 @@ public ReducedEntityIndex( @Nonnull FacetIndex facetIndex ) { super( - primaryKey, entityIndexKey, version, schemaAccessor, + primaryKey, entityIndexKey, version, entityIds, entityIdsByLanguage, - attributeIndex, hierarchyIndex, facetIndex, priceIndex); + attributeIndex, hierarchyIndex, facetIndex, priceIndex + ); + this.priceIndex = priceIndex; + } + + public ReducedEntityIndex( + int primaryKey, + @Nonnull EntityIndexKey indexKey, + int version, + @Nonnull TransactionalBitmap entityIds, + @Nonnull TransactionalMap entityIdsByLanguage, + @Nonnull AttributeIndex attributeIndex, + @Nonnull HierarchyIndex hierarchyIndex, + @Nonnull FacetIndex facetIndex, + boolean originalHierarchyIndexEmpty, + @Nonnull Integer originalInternalPriceIdSequence, + @Nonnull Set originalAttributeIndexes, + @Nonnull Set originalPriceIndexes, + @Nonnull Set originalFacetIndexes, + @Nonnull PriceRefIndex priceIndex + ) { + super( + primaryKey, indexKey, version, entityIds, + entityIdsByLanguage, attributeIndex, hierarchyIndex, facetIndex, + originalHierarchyIndexEmpty, originalInternalPriceIdSequence, + originalAttributeIndexes, originalPriceIndexes, originalFacetIndexes + ); this.priceIndex = priceIndex; } @Override - public void updateReferencesTo(@Nonnull EntityCollection newCollection) { - super.updateReferencesTo(newCollection); - this.priceIndex.updateReferencesTo(newCollection::getPriceSuperIndex); + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + this.priceIndex.attachToCatalog(entityType, catalog); + } + + @Nonnull + @Override + public ReducedEntityIndex createCopyForNewCatalogAttachment() { + return new ReducedEntityIndex( + this.primaryKey, this.indexKey, this.version, + this.entityIds, this.entityIdsByLanguage, + this.attributeIndex, + this.hierarchyIndex, + this.facetIndex, + this.originalHierarchyIndexEmpty, + this.originalInternalPriceIdSequence, + this.originalAttributeIndexes, + this.originalPriceIndexes, + this.originalFacetIndexes, + this.priceIndex.createCopyForNewCatalogAttachment() + ); } @Override @@ -134,7 +177,7 @@ public ReducedEntityIndex createCopyWithMergedTransactionalMemory(@Nullable Void // we can safely throw away dirty flag now final Boolean wasDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); return new ReducedEntityIndex( - primaryKey, indexKey, version + (wasDirty ? 1 : 0), schemaAccessor, + this.primaryKey, this.indexKey, this.version + (wasDirty ? 1 : 0), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIds), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIdsByLanguage), transactionalLayer.getStateCopyWithCommittedChanges(this.attributeIndex), diff --git a/evita_engine/src/main/java/io/evitadb/index/ReferencedTypeEntityIndex.java b/evita_engine/src/main/java/io/evitadb/index/ReferencedTypeEntityIndex.java index f71d8d9bd..b7b6a0c1e 100644 --- a/evita_engine/src/main/java/io/evitadb/index/ReferencedTypeEntityIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/ReferencedTypeEntityIndex.java @@ -27,7 +27,6 @@ import io.evitadb.api.requestResponse.schema.AttributeSchemaContract; import io.evitadb.api.requestResponse.schema.EntitySchemaContract; import io.evitadb.api.requestResponse.schema.SortableAttributeCompoundSchemaContract; -import io.evitadb.api.requestResponse.schema.dto.EntitySchema; import io.evitadb.core.Transaction; import io.evitadb.core.transaction.memory.TransactionalContainerChanges; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; @@ -62,7 +61,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Stream; import static io.evitadb.core.Transaction.isTransactionAvailable; @@ -107,10 +105,10 @@ public class ReferencedTypeEntityIndex extends EntityIndex implements public ReferencedTypeEntityIndex( int primaryKey, - @Nonnull EntityIndexKey entityIndexKey, - @Nonnull Supplier schemaAccessor + @Nonnull String entityType, + @Nonnull EntityIndexKey entityIndexKey ) { - super(primaryKey, entityIndexKey, schemaAccessor); + super(primaryKey, entityType, entityIndexKey); this.primaryKeyCardinality = new CardinalityIndex(Integer.class); this.cardinalityIndexes = new TransactionalMap<>(new HashMap<>(), CardinalityIndex.class, Function.identity()); } @@ -119,7 +117,6 @@ public ReferencedTypeEntityIndex( int primaryKey, @Nonnull EntityIndexKey entityIndexKey, int version, - @Nonnull Supplier schemaAccessor, @Nonnull Bitmap entityIds, @Nonnull Map entityIdsByLanguage, @Nonnull AttributeIndex attributeIndex, @@ -129,7 +126,7 @@ public ReferencedTypeEntityIndex( @Nonnull Map cardinalityIndexes ) { super( - primaryKey, entityIndexKey, version, schemaAccessor, + primaryKey, entityIndexKey, version, entityIds, entityIdsByLanguage, attributeIndex, hierarchyIndex, facetIndex, VoidPriceIndex.INSTANCE ); @@ -365,7 +362,7 @@ public ReferencedTypeEntityIndex createCopyWithMergedTransactionalMemory(Referen // we can safely throw away dirty flag now final Boolean wasDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); final ReferencedTypeEntityIndex referencedTypeEntityIndex = new ReferencedTypeEntityIndex( - primaryKey, indexKey, version + (wasDirty ? 1 : 0), schemaAccessor, + this.primaryKey, this.indexKey, this.version + (wasDirty ? 1 : 0), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIds), transactionalLayer.getStateCopyWithCommittedChanges(this.entityIdsByLanguage), transactionalLayer.getStateCopyWithCommittedChanges(this.attributeIndex), diff --git a/evita_engine/src/main/java/io/evitadb/index/attribute/GlobalUniqueIndex.java b/evita_engine/src/main/java/io/evitadb/index/attribute/GlobalUniqueIndex.java index f795a1915..6344900df 100644 --- a/evita_engine/src/main/java/io/evitadb/index/attribute/GlobalUniqueIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/attribute/GlobalUniqueIndex.java @@ -27,17 +27,17 @@ import io.evitadb.api.requestResponse.data.AttributesContract.AttributeKey; import io.evitadb.api.requestResponse.data.structure.EntityReference; import io.evitadb.core.Catalog; +import io.evitadb.core.CatalogRelatedDataStructure; import io.evitadb.core.EntityCollection; -import io.evitadb.core.transaction.memory.TransactionalContainerChanges; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; -import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; +import io.evitadb.core.transaction.memory.VoidTransactionMemoryProducer; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.bool.TransactionalBoolean; -import io.evitadb.index.map.MapChanges; import io.evitadb.index.map.TransactionalMap; import io.evitadb.store.model.StoragePart; import io.evitadb.store.spi.model.storageParts.index.GlobalUniqueIndexStoragePart; +import io.evitadb.utils.Assert; import lombok.Getter; import javax.annotation.Nonnull; @@ -53,7 +53,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import static io.evitadb.core.Transaction.isTransactionAvailable; import static io.evitadb.index.attribute.UniqueIndex.verifyValue; import static io.evitadb.index.attribute.UniqueIndex.verifyValueArray; import static io.evitadb.utils.Assert.isTrue; @@ -69,7 +68,7 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2019 */ -public class GlobalUniqueIndex implements TransactionalLayerProducer, Map, TransactionalMap>, GlobalUniqueIndex>, IndexDataStructure { +public class GlobalUniqueIndex implements VoidTransactionMemoryProducer, IndexDataStructure, CatalogRelatedDataStructure { @Getter private final long id = TransactionalObjectVersion.SEQUENCE.nextId(); /** * Constant representing the attribute has no locale assigned. @@ -152,14 +151,37 @@ public GlobalUniqueIndex( ); } - /** - * Replaces reference to the new catalog object. This needs to be done when transaction is - * committed and new GlobalUniqueIndex is created with link to the original transactional Catalog but finally - * new {@link Catalog} is created and the new indexes linking old collection needs to be - * migrated to new catalog instance. - */ - public void updateReferencesTo(@Nonnull Catalog newCatalog) { - this.catalog = newCatalog; + private GlobalUniqueIndex( + @Nonnull AttributeKey attributeKey, + @Nonnull Class attributeType, + @Nonnull TransactionalMap uniqueValueToEntityTuple, + @Nonnull TransactionalMap localeToIdIndex, + @Nonnull TransactionalMap idToLocaleIndex + ) { + this.attributeKey = attributeKey; + this.type = attributeType; + this.dirty = new TransactionalBoolean(); + this.uniqueValueToEntityTuple = uniqueValueToEntityTuple; + this.localeToIdIndex = localeToIdIndex; + this.idToLocaleIndex = idToLocaleIndex; + } + + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + Assert.isPremiseValid(this.catalog == null, "Catalog was already attached to this index!"); + this.catalog = catalog; + } + + @Nonnull + @Override + public GlobalUniqueIndex createCopyForNewCatalogAttachment() { + return new GlobalUniqueIndex( + this.attributeKey, + this.type, + this.uniqueValueToEntityTuple, + this.localeToIdIndex, + this.idToLocaleIndex + ); } /** @@ -232,15 +254,9 @@ public void resetDirty() { this.dirty.reset(); } - @Nullable - @Override - public TransactionalContainerChanges, Map, TransactionalMap> createLayer() { - return isTransactionAvailable() ? new TransactionalContainerChanges<>() : null; - } - @Nonnull @Override - public GlobalUniqueIndex createCopyWithMergedTransactionalMemory(@Nullable TransactionalContainerChanges, Map, TransactionalMap> layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { + public GlobalUniqueIndex createCopyWithMergedTransactionalMemory(@Nullable Void layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { final GlobalUniqueIndex uniqueKeyIndex = new GlobalUniqueIndex( attributeKey, type, catalog, transactionalLayer.getStateCopyWithCommittedChanges(this.uniqueValueToEntityTuple), @@ -248,8 +264,6 @@ public GlobalUniqueIndex createCopyWithMergedTransactionalMemory(@Nullable Trans ); transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); transactionalLayer.removeTransactionalMemoryLayerIfExists(this.localeToIdIndex); - // we can safely throw away dirty flag now - ofNullable(layer).ifPresent(it -> it.clean(transactionalLayer)); return uniqueKeyIndex; } diff --git a/evita_engine/src/main/java/io/evitadb/index/price/AbstractPriceIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/AbstractPriceIndex.java index 5737c861d..f401e1fdc 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/AbstractPriceIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/AbstractPriceIndex.java @@ -127,7 +127,8 @@ public void priceRemove( @Nonnull PriceInnerRecordHandling innerRecordHandling, @Nullable Integer innerRecordId, @Nullable DateTimeRange validity, - int priceWithoutTax, int priceWithTax + int priceWithoutTax, + int priceWithTax ) { final PriceIndexKey lookupKey = new PriceIndexKey(priceKey.priceList(), priceKey.currency(), innerRecordHandling); final T priceListIndex = this.getPriceIndexes().get(lookupKey); @@ -139,7 +140,7 @@ public void priceRemove( validity, priceWithoutTax, priceWithTax ); - if (priceListIndex.isEmpty()) { + if (!priceListIndex.isTerminated() && priceListIndex.isEmpty()) { removeExistingIndex(lookupKey, priceListIndex); } } @@ -189,7 +190,8 @@ public void resetDirty() { protected abstract T createNewPriceListAndCurrencyIndex(@Nonnull PriceIndexKey lookupKey); protected void removeExistingIndex(@Nonnull PriceIndexKey lookupKey, @Nonnull T priceListIndex) { - getPriceIndexes().remove(lookupKey); + final T removedIndex = getPriceIndexes().remove(lookupKey); + removedIndex.terminate(); } protected abstract PriceInternalIdContainer addPrice( diff --git a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java index 6348f0502..465ac441a 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceIndex.java @@ -41,6 +41,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.io.Serial; import java.io.Serializable; import java.time.OffsetDateTime; import java.util.Arrays; @@ -65,19 +66,19 @@ public interface PriceListAndCurrencyPriceIndex extends IndexD * Returns bitmap of all indexed records of this combination of price list and currency. */ @Nonnull - Bitmap getIndexedPriceEntityIds(); + Bitmap getIndexedPriceEntityIds() throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns bitmap of all indexed price ids. */ @Nonnull - int[] getIndexedPriceIds(); + int[] getIndexedPriceIds() throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns array of {@link PriceRecordContract} for passed bitmap of ids. */ @Nonnull - default PriceRecordContract[] getPriceRecords(@Nonnull Bitmap priceIds) { + default PriceRecordContract[] getPriceRecords(@Nonnull Bitmap priceIds) throws PriceListAndCurrencyPriceIndexTerminated { return getPriceRecords( priceIds, priceRecordContract -> { @@ -96,7 +97,7 @@ default PriceRecordContract[] getPriceRecords( @Nonnull Bitmap priceIds, @Nonnull Consumer priceFoundCallback, @Nonnull IntConsumer priceIdNotFoundCallback - ) { + ) throws PriceListAndCurrencyPriceIndexTerminated { // TOBEDONE JNO - there is also an issue https://github.com/RoaringBitmap/RoaringBitmap/issues/562 that could make this algorithm faster final CompositeObjectArray filteredPriceRecords = new CompositeObjectArray<>(PriceRecordContract.class); final int[] buffer = SharedBufferPool.INSTANCE.obtain(); @@ -187,50 +188,73 @@ default PriceRecordContract[] getPriceRecords( * Returns formula that computes all indexed records of this combination of price list and currency. */ @Nonnull - Formula getIndexedPriceEntityIdsFormula(); + Formula getIndexedPriceEntityIdsFormula() throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns formula that computes all indexed records of this combination of price list and currency that are valid * at the passed moment. */ @Nonnull - PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime theMoment); + PriceIdContainerFormula getIndexedRecordIdsValidInFormula(@Nonnull OffsetDateTime theMoment) throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns array of all {@link PriceRecord#internalPriceId()} of the entity. */ @Nullable - int[] getInternalPriceIdsForEntity(int entityId); + int[] getInternalPriceIdsForEntity(int entityId) throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns array of the lowest prices distinct by {@link PriceRecordContract#innerRecordId()} that exists in this * index and that belong to the particular entity sorted by price id. */ @Nullable - PriceRecordContract[] getLowestPriceRecordsForEntity(int entityId); + PriceRecordContract[] getLowestPriceRecordsForEntity(int entityId) throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns array of all prices in this index ordered by price id in ascending order. */ @Nonnull - PriceRecordContract[] getPriceRecords(); + PriceRecordContract[] getPriceRecords() throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns formula that computes all indexed records in this index. Depending on the type of the index it returns * either entity ids or inner record ids. */ @Nonnull - Formula createPriceIndexFormulaWithAllRecords(); + Formula createPriceIndexFormulaWithAllRecords() throws PriceListAndCurrencyPriceIndexTerminated; /** * Returns true if index is empty. */ - boolean isEmpty(); + boolean isEmpty() throws PriceListAndCurrencyPriceIndexTerminated; /** * Method creates container for storing any of price related indexes from memory to the persistent storage. */ @Nullable - StoragePart createStoragePart(int entityIndexPrimaryKey); + StoragePart createStoragePart(int entityIndexPrimaryKey) throws PriceListAndCurrencyPriceIndexTerminated; + + /** + * Returns true if index is terminated. + * @return true if index is terminated + */ + boolean isTerminated(); + + /** + * Sets price index as terminated. + */ + void terminate(); + + /** + * Exception is thrown when outside logic tries to work with terminated price index. + */ + class PriceListAndCurrencyPriceIndexTerminated extends RuntimeException { + @Serial private static final long serialVersionUID = 1551590894819222190L; + + public PriceListAndCurrencyPriceIndexTerminated(@Nonnull String message) { + super(message); + } + + } } diff --git a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceRefIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceRefIndex.java index 3ae8b3b7e..e27b8f309 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceRefIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceRefIndex.java @@ -25,7 +25,8 @@ import io.evitadb.api.requestResponse.data.PriceContract; import io.evitadb.api.requestResponse.data.structure.Entity; -import io.evitadb.core.EntityCollection; +import io.evitadb.core.Catalog; +import io.evitadb.core.CatalogRelatedDataStructure; import io.evitadb.core.query.algebra.Formula; import io.evitadb.core.query.algebra.base.ConstantFormula; import io.evitadb.core.query.algebra.base.EmptyFormula; @@ -35,6 +36,10 @@ import io.evitadb.core.transaction.memory.TransactionalObjectVersion; import io.evitadb.core.transaction.memory.VoidTransactionMemoryProducer; import io.evitadb.dataType.DateTimeRange; +import io.evitadb.exception.GenericEvitaInternalError; +import io.evitadb.index.EntityIndexKey; +import io.evitadb.index.EntityIndexType; +import io.evitadb.index.GlobalEntityIndex; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.array.TransactionalObjArray; import io.evitadb.index.bitmap.Bitmap; @@ -47,6 +52,7 @@ import io.evitadb.index.range.RangeIndex; import io.evitadb.store.model.StoragePart; import io.evitadb.store.spi.model.storageParts.index.PriceListAndCurrencyRefIndexStoragePart; +import io.evitadb.utils.Assert; import lombok.Getter; import javax.annotation.Nonnull; @@ -57,7 +63,6 @@ import java.util.Comparator; import java.util.Iterator; import java.util.Objects; -import java.util.function.Function; import static io.evitadb.core.Transaction.isTransactionAvailable; @@ -67,12 +72,16 @@ * minimize the working set by separating price indexes by this combination. * * RefIndex attempts to store minimal data set in order to save memory on heap. For memory expensive objects such as - * {@link PriceRecord} and {@link EntityPrices} it looks up via {@link #superIndexAccessor} lambda to - * {@link PriceListAndCurrencyPriceSuperIndex} where the records are located. + * {@link PriceRecord} and {@link EntityPrices} it looks up via {@link #superIndex} where the records are located. * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2021 */ -public class PriceListAndCurrencyPriceRefIndex implements VoidTransactionMemoryProducer, IndexDataStructure, Serializable, PriceListAndCurrencyPriceIndex { +public class PriceListAndCurrencyPriceRefIndex implements + VoidTransactionMemoryProducer, + CatalogRelatedDataStructure, + IndexDataStructure, + Serializable, + PriceListAndCurrencyPriceIndex { @Serial private static final long serialVersionUID = 182980639981206272L; @Getter private final long id = TransactionalObjectVersion.SEQUENCE.nextId(); /** @@ -83,10 +92,6 @@ public class PriceListAndCurrencyPriceRefIndex implements VoidTransactionMemoryP * Unique identification of this index - contains price list name and currency combination. */ @Getter private final PriceIndexKey priceIndexKey; - /** - * Bitmap contains all entity ids known to this index. This bitmap represents superset of all inner bitmaps. - */ - private final TransactionalBitmap indexedPriceEntityIds; /** * Field contains condensed bitmap of all {@link #priceRecords} {@link PriceRecordContract#internalPriceId()} * for the sake of the faster search for appropriate {@link PriceRecordContract} by the internal price id. @@ -97,88 +102,135 @@ public class PriceListAndCurrencyPriceRefIndex implements VoidTransactionMemoryP * the {@link io.evitadb.api.query.filter.PriceValidIn} filtering query. */ private final RangeIndex validityIndex; + /** + * Reference to the main {@link PriceListAndCurrencyPriceSuperIndex} that keeps memory expensive objects, which + * is initialized in {@link #attachToCatalog(String, Catalog)} callback. + */ + private PriceListAndCurrencyPriceSuperIndex superIndex; + /** + * Bitmap contains all entity ids known to this index. This bitmap represents superset of all inner bitmaps. + */ + private TransactionalBitmap indexedPriceEntityIds; /** * Array contains complete information about prices sorted by {@link PriceContract#priceId()} allowing translation * of price id to {@link Entity#getPrimaryKey()} using binary search algorithm. */ - private final TransactionalObjArray priceRecords; + private TransactionalObjArray priceRecords; /** - * Lambda providing access to the main {@link PriceListAndCurrencyPriceSuperIndex} that keeps memory expensive - * objects. + * Contains flags that makes the index terminated and unusable. */ - private Function superIndexAccessor; + private final TransactionalBoolean terminated; /** * Contains cached result of {@link TransactionalBitmap#getArray()} call. */ private int[] memoizedIndexedPriceIds; public PriceListAndCurrencyPriceRefIndex( - @Nonnull PriceIndexKey priceIndexKey, - @Nonnull Function superIndexAccessor + @Nonnull PriceIndexKey priceIndexKey ) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.indexedPriceEntityIds = new TransactionalBitmap(); this.indexedPriceIds = new TransactionalBitmap(); this.priceIndexKey = priceIndexKey; this.validityIndex = new RangeIndex(); this.priceRecords = new TransactionalObjArray<>(new PriceRecordContract[0], Comparator.naturalOrder()); - this.superIndexAccessor = superIndexAccessor; } public PriceListAndCurrencyPriceRefIndex( @Nonnull PriceIndexKey priceIndexKey, @Nonnull RangeIndex validityIndex, - @Nonnull int[] priceIds, - @Nonnull Function superIndexAccessor + @Nonnull int[] priceIds ) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.priceIndexKey = priceIndexKey; this.validityIndex = validityIndex; - this.superIndexAccessor = superIndexAccessor; this.indexedPriceIds = new TransactionalBitmap(priceIds); this.memoizedIndexedPriceIds = priceIds; - final PriceRecordContract[] priceRecords = superIndexAccessor.apply(priceIndexKey).getPriceRecords(indexedPriceIds); - this.priceRecords = new TransactionalObjArray<>(priceRecords, Comparator.naturalOrder()); - - final int[] entityIds = new int[priceRecords.length]; - for (int i = 0; i < priceRecords.length; i++) { - final PriceRecordContract priceRecord = priceRecords[i]; - entityIds[i] = priceRecord.entityPrimaryKey(); - } - this.indexedPriceEntityIds = new TransactionalBitmap(entityIds); } private PriceListAndCurrencyPriceRefIndex( @Nonnull PriceIndexKey priceIndexKey, @Nonnull Bitmap indexedPriceEntityIds, @Nonnull Bitmap priceIds, - @Nonnull RangeIndex validityIndex, - @Nonnull Function superIndexAccessor + @Nonnull RangeIndex validityIndex ) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.priceIndexKey = priceIndexKey; this.indexedPriceEntityIds = new TransactionalBitmap(indexedPriceEntityIds); this.indexedPriceIds = new TransactionalBitmap(priceIds); this.validityIndex = validityIndex; - this.superIndexAccessor = superIndexAccessor; - final PriceRecordContract[] priceRecords = getPriceSuperIndex().getPriceRecords(this.indexedPriceIds); + } + + private PriceListAndCurrencyPriceRefIndex( + @Nonnull PriceIndexKey priceIndexKey, + @Nonnull TransactionalBitmap indexedPriceEntityIds, + @Nonnull TransactionalBitmap priceIds, + @Nonnull RangeIndex validityIndex + ) { + this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); + this.priceIndexKey = priceIndexKey; + this.indexedPriceEntityIds = indexedPriceEntityIds; + this.indexedPriceIds = priceIds; + this.validityIndex = validityIndex; + } + + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + assertNotTerminated(); + Assert.isPremiseValid(this.superIndex == null, "Catalog was already attached to this index!"); + final PriceListAndCurrencyPriceIndex superIndex = catalog.getEntityIndexIfExists( + entityType, new EntityIndexKey(EntityIndexType.GLOBAL), GlobalEntityIndex.class + ) + .map(it -> it.getPriceIndex(priceIndexKey)) + .orElse(null); + Assert.isPremiseValid( + superIndex instanceof PriceListAndCurrencyPriceSuperIndex, + () -> new GenericEvitaInternalError( + "PriceListAndCurrencyPriceRefIndex can only be initialized with PriceListAndCurrencyPriceSuperIndex, " + + "actual instance is `" + (superIndex == null ? "NULL" : superIndex.getClass().getName()) + "`", + "PriceListAndCurrencyPriceRefIndex can only be initialized with PriceListAndCurrencyPriceSuperIndex" + ) + ); + this.superIndex = (PriceListAndCurrencyPriceSuperIndex) superIndex; + final PriceRecordContract[] priceRecords = superIndex.getPriceRecords(indexedPriceIds); this.priceRecords = new TransactionalObjArray<>(priceRecords, Comparator.naturalOrder()); + + final int[] entityIds = new int[priceRecords.length]; + for (int i = 0; i < priceRecords.length; i++) { + final PriceRecordContract priceRecord = priceRecords[i]; + entityIds[i] = priceRecord.entityPrimaryKey(); + } + this.indexedPriceEntityIds = new TransactionalBitmap(entityIds); } - /** - * This method replaces super index accessor with new one. This needs to be done when transaction is committed and - * PriceListAndCurrencyPriceRefIndex is created with link to the original transactional - * {@link PriceListAndCurrencyPriceSuperIndex} but finally new {@link EntityCollection} is created - * along with new {@link PriceSuperIndex} and reference needs to be exchanged. - */ - public void updateReferencesTo(@Nonnull Function superIndexAccessor) { - this.superIndexAccessor = superIndexAccessor; + @Nonnull + @Override + public PriceListAndCurrencyPriceRefIndex createCopyForNewCatalogAttachment() { + assertNotTerminated(); + return new PriceListAndCurrencyPriceRefIndex( + this.priceIndexKey, + this.indexedPriceEntityIds, + this.indexedPriceIds, + this.validityIndex + ); } /** * Indexes inner record id or entity primary key into the price index with passed values. */ - public void addPrice(@Nonnull PriceRecordContract priceRecord, @Nullable DateTimeRange validity) { + @Nonnull + public PriceRecordContract addPrice( + @Nonnull Integer internalPriceId, + @Nullable DateTimeRange validity + ) { + assertNotTerminated(); + final int ipId = Objects.requireNonNull(internalPriceId); + final PriceRecordContract priceRecord = this.superIndex.getPriceRecord(ipId); + // index the presence of the record this.indexedPriceEntityIds.add(priceRecord.entityPrimaryKey()); this.indexedPriceIds.add(priceRecord.internalPriceId()); @@ -195,19 +247,30 @@ public void addPrice(@Nonnull PriceRecordContract priceRecord, @Nullable DateTim if (!isTransactionAvailable()) { this.memoizedIndexedPriceIds = null; } + + return priceRecord; } /** * Removes inner record id or entity primary key of passed values from the price index. */ - public void removePrice(@Nonnull PriceRecordContract priceRecord, @Nullable DateTimeRange validity, @Nonnull EntityPrices updatedEntityPrices) { + @Nonnull + public PriceRecordContract removePrice( + @Nonnull Integer internalPriceId, + @Nullable DateTimeRange validity + ) { + assertNotTerminated(); + final int ipId = Objects.requireNonNull(internalPriceId); + final PriceRecordContract priceRecord = this.superIndex.getPriceRecord(ipId); + final EntityPrices entityPrices = this.superIndex.getEntityPrices(priceRecord.entityPrimaryKey()); + // remove price to the translation triple this.priceRecords.remove(priceRecord); // remove the presence of the record this.indexedPriceIds.remove(priceRecord.internalPriceId()); - if (!updatedEntityPrices.containsAnyOf(this.priceRecords.getArray())) { + if (!entityPrices.containsAnyOf(this.priceRecords.getArray())) { // remove the presence of the record this.indexedPriceEntityIds.remove(priceRecord.entityPrimaryKey()); } @@ -222,6 +285,15 @@ public void removePrice(@Nonnull PriceRecordContract priceRecord, @Nullable Date if (!isTransactionAvailable()) { this.memoizedIndexedPriceIds = null; } + + return priceRecord; + } + + @Nonnull + @Override + public Bitmap getIndexedPriceEntityIds() { + assertNotTerminated(); + return indexedPriceEntityIds; } /** @@ -230,6 +302,7 @@ public void removePrice(@Nonnull PriceRecordContract priceRecord, @Nullable Date */ @Nonnull public int[] getIndexedPriceIds() { + assertNotTerminated(); // if there is transaction open, there might be changes in the histogram data, and we can't easily use cache if (isTransactionAvailable() && this.dirty.isTrue()) { return this.indexedPriceIds.getArray(); @@ -241,15 +314,10 @@ public int[] getIndexedPriceIds() { } } - @Nonnull - @Override - public Bitmap getIndexedPriceEntityIds() { - return indexedPriceEntityIds; - } - @Nonnull @Override public Formula getIndexedPriceEntityIdsFormula() { + assertNotTerminated(); if (indexedPriceEntityIds.isEmpty()) { return EmptyFormula.INSTANCE; } else { @@ -259,7 +327,8 @@ public Formula getIndexedPriceEntityIdsFormula() { @Nonnull @Override - public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime theMoment) { + public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(@Nonnull OffsetDateTime theMoment) { + assertNotTerminated(); final long thePoint = DateTimeRange.toComparableLong(theMoment); return new PriceIdContainerFormula( this, this.validityIndex.getRecordsEnvelopingInclusive(thePoint) @@ -269,32 +338,47 @@ public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime @Nullable @Override public int[] getInternalPriceIdsForEntity(int entityId) { - return getPriceSuperIndex().getInternalPriceIdsForEntity(entityId); + assertNotTerminated(); + return this.superIndex.getInternalPriceIdsForEntity(entityId); } @Override @Nullable public PriceRecordContract[] getLowestPriceRecordsForEntity(int entityId) { - return getPriceSuperIndex().getLowestPriceRecordsForEntity(entityId); + assertNotTerminated(); + return this.superIndex.getLowestPriceRecordsForEntity(entityId); } @Nonnull @Override public PriceRecordContract[] getPriceRecords() { + assertNotTerminated(); return this.priceRecords.getArray(); } @Nonnull @Override public Formula createPriceIndexFormulaWithAllRecords() { + assertNotTerminated(); return new PriceIndexContainerFormula(this, this.getIndexedPriceEntityIdsFormula()); } @Override public boolean isEmpty() { + assertNotTerminated(); return this.indexedPriceEntityIds.isEmpty(); } + @Override + public boolean isTerminated() { + return this.terminated.isTrue(); + } + + @Override + public void terminate() { + this.terminated.setToTrue(); + } + @Nullable @Override public StoragePart createStoragePart(int entityIndexPrimaryKey) { @@ -314,16 +398,6 @@ public StoragePart createStoragePart(int entityIndexPrimaryKey) { } } - /** - * Method returns reference to the {@link PriceListAndCurrencyPriceSuperIndex} that maintains the original, full - * {@link PriceRecordContract} data. Because the price data are quite big the only place where we store them is - * the {@link PriceListAndCurrencyPriceSuperIndex} and other indexes only reference the existing objects in it. - */ - @Nonnull - public PriceListAndCurrencyPriceSuperIndex getPriceSuperIndex() { - return Objects.requireNonNull(superIndexAccessor.apply(priceIndexKey)); - } - @Override public String toString() { return priceIndexKey.toString(); @@ -331,6 +405,7 @@ public String toString() { @Override public void resetDirty() { + assertNotTerminated(); this.dirty.reset(); } @@ -342,29 +417,41 @@ public void resetDirty() { @Override public PriceListAndCurrencyPriceRefIndex createCopyWithMergedTransactionalMemory(@Nullable Void layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { // we can safely throw away dirty flag now - final Boolean isDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); - if (isDirty) { - transactionalLayer.removeTransactionalMemoryLayerIfExists(this.priceRecords); - return new PriceListAndCurrencyPriceRefIndex( - priceIndexKey, - transactionalLayer.getStateCopyWithCommittedChanges(this.indexedPriceEntityIds), - transactionalLayer.getStateCopyWithCommittedChanges(this.indexedPriceIds), - transactionalLayer.getStateCopyWithCommittedChanges(this.validityIndex), - this.superIndexAccessor - ); - } else { - return this; - } + this.dirty.removeLayer(transactionalLayer); + this.terminated.removeLayer(transactionalLayer); + this.priceRecords.removeLayer(transactionalLayer); + return new PriceListAndCurrencyPriceRefIndex( + this.priceIndexKey, + transactionalLayer.getStateCopyWithCommittedChanges(this.indexedPriceEntityIds), + transactionalLayer.getStateCopyWithCommittedChanges(this.indexedPriceIds), + transactionalLayer.getStateCopyWithCommittedChanges(this.validityIndex) + ); } @Override public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer) { transactionalLayer.removeTransactionalMemoryLayerIfExists(this); this.dirty.removeLayer(transactionalLayer); + this.terminated.removeLayer(transactionalLayer); this.priceRecords.removeLayer(transactionalLayer); this.indexedPriceEntityIds.removeLayer(transactionalLayer); this.indexedPriceIds.removeLayer(transactionalLayer); this.validityIndex.removeLayer(transactionalLayer); } + /* + PRIVATE METHODS + */ + + /** + * Verifies that the index is not terminated. + */ + private void assertNotTerminated() { + if (terminated.isTrue()) { + throw new PriceListAndCurrencyPriceIndexTerminated( + "Price list and currency index " + priceIndexKey + " is terminated!" + ); + } + } + } diff --git a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceSuperIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceSuperIndex.java index 38a2e8ef4..8e58f88d2 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceSuperIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/PriceListAndCurrencyPriceSuperIndex.java @@ -110,6 +110,10 @@ public class PriceListAndCurrencyPriceSuperIndex implements VoidTransactionMemor * of price id to {@link Entity#getPrimaryKey()} using binary search algorithm. */ private final TransactionalObjArray priceRecords; + /** + * Contains flags that makes the index terminated and unusable. + */ + private final TransactionalBoolean terminated; /** * Contains cached result of {@link TransactionalBitmap#getArray()} call. */ @@ -117,6 +121,7 @@ public class PriceListAndCurrencyPriceSuperIndex implements VoidTransactionMemor public PriceListAndCurrencyPriceSuperIndex(@Nonnull PriceIndexKey priceIndexKey) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.indexedPriceEntityIds = new TransactionalBitmap(); this.indexedPriceIds = new TransactionalBitmap(); this.priceIndexKey = priceIndexKey; @@ -131,6 +136,7 @@ public PriceListAndCurrencyPriceSuperIndex( @Nonnull PriceRecordContract[] priceRecords ) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.priceIndexKey = priceIndexKey; this.validityIndex = validityIndex; this.priceRecords = new TransactionalObjArray<>(priceRecords, Comparator.naturalOrder()); @@ -159,6 +165,7 @@ private PriceListAndCurrencyPriceSuperIndex( @Nonnull PriceRecordContract[] priceRecords ) { this.dirty = new TransactionalBoolean(); + this.terminated = new TransactionalBoolean(); this.priceIndexKey = priceIndexKey; this.indexedPriceEntityIds = new TransactionalBitmap(indexedPriceEntityIds); this.indexedPriceIds = new TransactionalBitmap(priceIds); @@ -171,6 +178,7 @@ private PriceListAndCurrencyPriceSuperIndex( * Indexes inner record id or entity primary key into the price index with passed values. */ public void addPrice(@Nonnull PriceRecordContract priceRecord, @Nullable DateTimeRange validity) { + assertNotTerminated(); if (isPriceRecordKnown(priceRecord.entityPrimaryKey(), priceRecord.priceId())) { throw new PriceAlreadyAssignedToEntityException( priceRecord.priceId(), @@ -202,6 +210,7 @@ public void addPrice(@Nonnull PriceRecordContract priceRecord, @Nullable DateTim * Removes inner record id or entity primary key of passed values from the price index. */ public void removePrice(int entityPrimaryKey, int internalPriceId, @Nullable DateTimeRange validity) { + assertNotTerminated(); final PriceRecordContract priceRecord = getPriceRecord(internalPriceId); this.priceRecords.remove(priceRecord); @@ -231,12 +240,20 @@ public void removePrice(int entityPrimaryKey, int internalPriceId, @Nullable Dat } } + @Nonnull + @Override + public Bitmap getIndexedPriceEntityIds() { + assertNotTerminated(); + return indexedPriceEntityIds; + } + /** * Method returns condensed bitmap of all {@link #priceRecords} {@link PriceRecordContract#internalPriceId()} * that can be used for the faster search for appropriate {@link PriceRecordContract} by the internal price id. */ @Nonnull public int[] getIndexedPriceIds() { + assertNotTerminated(); // if there is transaction open, there might be changes in the histogram data, and we can't easily use cache if (isTransactionAvailable() && this.dirty.isTrue()) { return this.indexedPriceIds.getArray(); @@ -248,15 +265,10 @@ public int[] getIndexedPriceIds() { } } - @Nonnull - @Override - public Bitmap getIndexedPriceEntityIds() { - return indexedPriceEntityIds; - } - @Nonnull @Override public Formula getIndexedPriceEntityIdsFormula() { + assertNotTerminated(); if (indexedPriceEntityIds.isEmpty()) { return EmptyFormula.INSTANCE; } else { @@ -266,7 +278,8 @@ public Formula getIndexedPriceEntityIdsFormula() { @Nonnull @Override - public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime theMoment) { + public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(@Nonnull OffsetDateTime theMoment) { + assertNotTerminated(); final long thePoint = DateTimeRange.toComparableLong(theMoment); return new PriceIdContainerFormula( this, this.validityIndex.getRecordsEnvelopingInclusive(thePoint) @@ -276,35 +289,41 @@ public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime @Nullable @Override public int[] getInternalPriceIdsForEntity(int entityId) { + assertNotTerminated(); return ofNullable(this.entityPrices.get(entityId)).map(EntityPrices::getInternalPriceIds).orElse(null); } @Nullable @Override public PriceRecordContract[] getLowestPriceRecordsForEntity(int entityId) { + assertNotTerminated(); return ofNullable(this.entityPrices.get(entityId)).map(EntityPrices::getLowestPriceRecords).orElse(null); } @Nonnull @Override public PriceRecordContract[] getPriceRecords() { + assertNotTerminated(); return this.priceRecords.getArray(); } @Nonnull @Override public Formula createPriceIndexFormulaWithAllRecords() { + assertNotTerminated(); return new PriceIndexContainerFormula(this, this.getIndexedPriceEntityIdsFormula()); } @Override public boolean isEmpty() { + assertNotTerminated(); return this.indexedPriceEntityIds.isEmpty(); } @Nullable @Override public StoragePart createStoragePart(int entityIndexPrimaryKey) { + assertNotTerminated(); if (this.dirty.isTrue()) { return new PriceListAndCurrencySuperIndexStoragePart( entityIndexPrimaryKey, priceIndexKey, validityIndex, priceRecords.getArray() @@ -319,6 +338,7 @@ public StoragePart createStoragePart(int entityIndexPrimaryKey) { */ @Nonnull public PriceRecordContract getPriceRecord(int internalPriceId) { + assertNotTerminated(); final PriceRecordContract[] priceRecords = this.priceRecords.getArray(); final int position = this.indexedPriceIds.indexOf(internalPriceId); Assert.isTrue(position >= 0, "Price id `" + internalPriceId + "` was not found in the price super index!"); @@ -330,11 +350,22 @@ public PriceRecordContract getPriceRecord(int internalPriceId) { */ @Nonnull public EntityPrices getEntityPrices(int entityPrimaryKey) { + assertNotTerminated(); final EntityPrices theEntityPrices = this.entityPrices.get(entityPrimaryKey); Assert.isPremiseValid(theEntityPrices != null, "Entity prices for " + entityPrimaryKey + " unexpectedly not found!"); return theEntityPrices; } + @Override + public boolean isTerminated() { + return this.terminated.isTrue(); + } + + @Override + public void terminate() { + this.terminated.setToTrue(); + } + @Override public String toString() { return priceIndexKey.toString(); @@ -342,18 +373,17 @@ public String toString() { @Override public void resetDirty() { + assertNotTerminated(); this.dirty.reset(); } - /* - TransactionalLayerCreator implementation - */ - @Nonnull @Override public PriceListAndCurrencyPriceSuperIndex createCopyWithMergedTransactionalMemory(@Nullable Void layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { + assertNotTerminated(); // we can safely throw away dirty flag now final Boolean isDirty = transactionalLayer.getStateCopyWithCommittedChanges(this.dirty); + this.terminated.removeLayer(transactionalLayer); if (isDirty) { final PriceRecordContract[] newTriples = transactionalLayer.getStateCopyWithCommittedChanges(this.priceRecords); return new PriceListAndCurrencyPriceSuperIndex( @@ -369,10 +399,15 @@ public PriceListAndCurrencyPriceSuperIndex createCopyWithMergedTransactionalMemo } } + /* + TransactionalLayerCreator implementation + */ + @Override public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer) { transactionalLayer.removeTransactionalMemoryLayerIfExists(this); this.dirty.removeLayer(transactionalLayer); + this.terminated.removeLayer(transactionalLayer); this.indexedPriceEntityIds.removeLayer(transactionalLayer); this.indexedPriceIds.removeLayer(transactionalLayer); this.entityPrices.removeLayer(transactionalLayer); @@ -384,6 +419,17 @@ public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer PRIVATE METHODS */ + /** + * Verifies that the index is not terminated. + */ + private void assertNotTerminated() { + if (terminated.isTrue()) { + throw new PriceListAndCurrencyPriceIndexTerminated( + "Price list and currency index " + priceIndexKey + " is terminated!" + ); + } + } + private boolean isPriceRecordKnown(int entityPrimaryKey, int priceId) { final EntityPrices theEntityPrices = this.entityPrices.get(entityPrimaryKey); if (theEntityPrices != null) { @@ -392,7 +438,7 @@ private boolean isPriceRecordKnown(int entityPrimaryKey, int priceId) { return false; } - private void addEntityPrice(PriceRecordContract priceRecord) { + private void addEntityPrice(@Nonnull PriceRecordContract priceRecord) { this.entityPrices.compute( priceRecord.entityPrimaryKey(), (entityId, existingPriceRecords) -> { @@ -405,7 +451,8 @@ private void addEntityPrice(PriceRecordContract priceRecord) { ); } - private EntityPrices removeEntityPrice(PriceRecordContract priceRecord) { + @Nonnull + private EntityPrices removeEntityPrice(@Nonnull PriceRecordContract priceRecord) { return this.entityPrices.computeIfPresent( priceRecord.entityPrimaryKey(), (entityId, existingPriceRecords) -> EntityPrices.removePrice(existingPriceRecords, priceRecord) diff --git a/evita_engine/src/main/java/io/evitadb/index/price/PriceRefIndex.java b/evita_engine/src/main/java/io/evitadb/index/price/PriceRefIndex.java index c929b0b2f..6569d1d8d 100644 --- a/evita_engine/src/main/java/io/evitadb/index/price/PriceRefIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/price/PriceRefIndex.java @@ -25,13 +25,15 @@ import io.evitadb.api.query.order.PriceNatural; import io.evitadb.api.requestResponse.data.PriceContract; -import io.evitadb.core.EntityCollection; +import io.evitadb.core.Catalog; +import io.evitadb.core.CatalogRelatedDataStructure; import io.evitadb.core.Transaction; import io.evitadb.core.transaction.memory.TransactionalContainerChanges; import io.evitadb.core.transaction.memory.TransactionalLayerMaintainer; import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.dataType.DateTimeRange; import io.evitadb.index.map.TransactionalMap; +import io.evitadb.index.price.PriceListAndCurrencyPriceIndex.PriceListAndCurrencyPriceIndexTerminated; import io.evitadb.index.price.PriceRefIndex.PriceIndexChanges; import io.evitadb.index.price.model.PriceIndexKey; import io.evitadb.index.price.model.entityPrices.EntityPrices; @@ -39,6 +41,7 @@ import io.evitadb.index.price.model.priceRecord.PriceRecordContract; import io.evitadb.store.entity.model.entity.price.MinimalPriceInternalIdContainer; import io.evitadb.store.entity.model.entity.price.PriceInternalIdContainer; +import io.evitadb.utils.Assert; import lombok.Getter; import javax.annotation.Nonnull; @@ -46,9 +49,9 @@ import java.io.Serial; import java.util.HashMap; import java.util.Map; -import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; +import java.util.stream.Collectors; import static java.util.Optional.ofNullable; @@ -68,7 +71,10 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2022 */ -public class PriceRefIndex extends AbstractPriceIndex implements TransactionalLayerProducer { +public class PriceRefIndex extends AbstractPriceIndex implements + TransactionalLayerProducer, + CatalogRelatedDataStructure +{ @Serial private static final long serialVersionUID = 7596276815836027747L; /** * Map of {@link PriceListAndCurrencyPriceSuperIndex indexes} that contains prices that relates to specific price-list @@ -76,32 +82,42 @@ public class PriceRefIndex extends AbstractPriceIndex priceIndexes; /** - * Lambda providing access to the main {@link PriceSuperIndex} that keeps memory expensive - * objects. + * Lambda that manages initialization of new price list indexes. These indexes needs to locate their + * {@link PriceListAndCurrencyPriceSuperIndex} from the catalog data and use it for locating shared price records + * instances. */ - private Supplier superIndexAccessor; + private Consumer initCallback; - public PriceRefIndex(@Nonnull Supplier superIndexAccessor) { + public PriceRefIndex() { this.priceIndexes = new TransactionalMap<>(new HashMap<>(), PriceListAndCurrencyPriceRefIndex.class, Function.identity()); - this.superIndexAccessor = superIndexAccessor; } - public PriceRefIndex(@Nonnull Map priceIndexes, @Nonnull Supplier superIndexAccessor) { + public PriceRefIndex(@Nonnull Map priceIndexes) { this.priceIndexes = new TransactionalMap<>(priceIndexes, PriceListAndCurrencyPriceRefIndex.class, Function.identity()); - this.superIndexAccessor = superIndexAccessor; } - /** - * This method replaces super index accessor with new one. This needs to be done when transaction is committed and - * PriceRefIndex is created with link to the original transactional {@link PriceSuperIndex} but finally new - * {@link EntityCollection} is created along with new {@link PriceSuperIndex} and reference needs - * to be exchanged. - */ - public void updateReferencesTo(@Nonnull Supplier priceSuperIndexAccessor) { - this.superIndexAccessor = priceSuperIndexAccessor; - for (PriceListAndCurrencyPriceRefIndex index : priceIndexes.values()) { - index.updateReferencesTo(priceIndexKey -> superIndexAccessor.get().getPriceIndex(priceIndexKey)); - } + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + Assert.isPremiseValid(this.initCallback == null, "Catalog was already attached to this index!"); + this.initCallback = priceListAndCurrencyPriceRefIndex -> priceListAndCurrencyPriceRefIndex.attachToCatalog(entityType, catalog); + // delegate call to price list indexes + this.priceIndexes.values().forEach(it -> it.attachToCatalog(entityType, catalog)); + } + + @Nonnull + @Override + public PriceRefIndex createCopyForNewCatalogAttachment() { + return new PriceRefIndex( + this.priceIndexes + .entrySet() + .stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + it -> it.getValue().createCopyForNewCatalogAttachment() + ) + ) + ); } /* @@ -117,8 +133,7 @@ public PriceIndexChanges createLayer() { @Override public PriceRefIndex createCopyWithMergedTransactionalMemory(@Nullable PriceIndexChanges layer, @Nonnull TransactionalLayerMaintainer transactionalLayer) { final PriceRefIndex priceIndex = new PriceRefIndex( - transactionalLayer.getStateCopyWithCommittedChanges(this.priceIndexes), - this.superIndexAccessor + transactionalLayer.getStateCopyWithCommittedChanges(this.priceIndexes) ); ofNullable(layer).ifPresent(it -> it.clean(transactionalLayer)); return priceIndex; @@ -137,9 +152,8 @@ public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer @Nonnull protected PriceListAndCurrencyPriceRefIndex createNewPriceListAndCurrencyIndex(@Nonnull PriceIndexKey lookupKey) { - final PriceListAndCurrencyPriceRefIndex newPriceListIndex = new PriceListAndCurrencyPriceRefIndex( - lookupKey, priceIndexKey -> this.superIndexAccessor.get().getPriceIndex(priceIndexKey) - ); + final PriceListAndCurrencyPriceRefIndex newPriceListIndex = new PriceListAndCurrencyPriceRefIndex(lookupKey); + initCallback.accept(newPriceListIndex); ofNullable(Transaction.getOrCreateTransactionalMemoryLayer(this)) .ifPresent(it -> it.addCreatedItem(newPriceListIndex)); return newPriceListIndex; @@ -159,9 +173,7 @@ protected PriceInternalIdContainer addPrice( @Nullable Integer innerRecordId, @Nullable DateTimeRange validity, int priceWithoutTax, int priceWithTax ) { - final PriceListAndCurrencyPriceSuperIndex superIndex = Objects.requireNonNull(superIndexAccessor.get().getPriceIndex(priceListIndex.getPriceIndexKey())); - final PriceRecordContract priceRecord = superIndex.getPriceRecord(Objects.requireNonNull(internalPriceId)); - priceListIndex.addPrice(priceRecord, validity); + final PriceRecordContract priceRecord = priceListIndex.addPrice(internalPriceId, validity); return new MinimalPriceInternalIdContainer(priceRecord.internalPriceId()); } @@ -172,10 +184,12 @@ protected void removePrice( @Nullable Integer innerRecordId, @Nullable DateTimeRange validity, int priceWithoutTax, int priceWithTax ) { - final PriceListAndCurrencyPriceSuperIndex superIndex = Objects.requireNonNull(superIndexAccessor.get().getPriceIndex(priceListIndex.getPriceIndexKey())); - final PriceRecordContract priceRecord = superIndex.getPriceRecord(internalPriceId); - final EntityPrices entityPrices = superIndex.getEntityPrices(priceRecord.entityPrimaryKey()); - priceListIndex.removePrice(priceRecord, validity, entityPrices); + try { + priceListIndex.removePrice(internalPriceId, validity); + } catch (PriceListAndCurrencyPriceIndexTerminated ex) { + // when super index was removed the referencing index must be removed as well + removeExistingIndex(priceListIndex.getPriceIndexKey(), priceListIndex); + } } /** diff --git a/evita_engine/src/main/java/io/evitadb/store/spi/EntityCollectionPersistenceService.java b/evita_engine/src/main/java/io/evitadb/store/spi/EntityCollectionPersistenceService.java index d858b7ff3..cba946be7 100644 --- a/evita_engine/src/main/java/io/evitadb/store/spi/EntityCollectionPersistenceService.java +++ b/evita_engine/src/main/java/io/evitadb/store/spi/EntityCollectionPersistenceService.java @@ -42,7 +42,6 @@ import io.evitadb.core.buffer.DataStoreMemoryBuffer; import io.evitadb.index.EntityIndex; import io.evitadb.index.EntityIndexKey; -import io.evitadb.index.price.PriceSuperIndex; import io.evitadb.store.model.PersistentStorageDescriptor; import io.evitadb.store.model.StoragePart; import io.evitadb.store.spi.model.EntityCollectionHeader; @@ -51,7 +50,6 @@ import javax.annotation.Nullable; import java.nio.file.Path; import java.util.function.Function; -import java.util.function.Supplier; /** * This interface represents a link between {@link io.evitadb.api.EntityCollectionContract} and its persistent storage. @@ -172,9 +170,7 @@ BinaryEntity enrichEntity( EntityIndex readEntityIndex( long catalogVersion, int entityIndexId, - @Nonnull Supplier schemaSupplier, - @Nonnull Supplier temporalIndexAccessor, - @Nonnull Supplier superIndexAccessor + @Nonnull EntitySchema entitySchema ); /** diff --git a/evita_functional_tests/src/test/java/io/evitadb/api/EvitaApiFunctionalTest.java b/evita_functional_tests/src/test/java/io/evitadb/api/EvitaApiFunctionalTest.java index a7ecdd2ff..e86211de6 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/api/EvitaApiFunctionalTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/api/EvitaApiFunctionalTest.java @@ -352,6 +352,31 @@ void shouldUpdateExistingEntityAndReturnItWithFilteredPrices(Evita evita) { ); } + @DisplayName("Existing entity can be altered and updated back to evitaDB, returning the new contents respecting removed prices") + @Test + void shouldUpdateExistingEntityAndReturnItWithoutRemovedPrice(Evita evita) { + // open new session + evita.updateCatalog( + TEST_CATALOG, + session -> { + final EntityReference brand = createBrand(session, 1).upsertVia(session); + final EntityReference category = createCategory(session, 1).upsertVia(session); + final EntityReference productRef = createProduct(session, 1, brand, category).upsertVia(session); + // select existing entity + final SealedEntity product = getProductById(session, 1).orElseThrow(); + // modify the entity contents + final EntityBuilder updatedProduct = product.openForWrite() + .removePrice(2, "basic", EUR); + // store entity back to the database + final SealedEntity updatedEntity = session.upsertAndFetchEntity(updatedProduct, priceContentAll()); + assertNotNull(updatedEntity); + + assertFalse(updatedEntity.getPrices().isEmpty()); + assertTrue(updatedEntity.getPrices().stream().noneMatch(it -> it.currency() == EUR && it.priceList().equals("basic"))); + } + ); + } + @DisplayName("Existing entity should accept price handling strategy change") @Test void shouldUpdateEntityPriceInnerHandlingStrategy(Evita evita) { diff --git a/evita_functional_tests/src/test/java/io/evitadb/api/EvitaBackwardCompatibilityTest.java b/evita_functional_tests/src/test/java/io/evitadb/api/EvitaBackwardCompatibilityTest.java index 744fe950f..cedae4476 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/api/EvitaBackwardCompatibilityTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/api/EvitaBackwardCompatibilityTest.java @@ -29,6 +29,7 @@ import io.evitadb.api.requestResponse.system.SystemStatus; import io.evitadb.core.Evita; import io.evitadb.test.EvitaTestSupport; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; @@ -51,6 +52,7 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 */ +@Slf4j public class EvitaBackwardCompatibilityTest implements EvitaTestSupport { private static final String DIRECTORY = "evita-backward-compatibility"; private Path mainDirectory; @@ -69,6 +71,8 @@ void setUp() throws MalformedURLException { void verifyBackwardCompatibilityTo_2024_5() throws IOException { final Path directory_2024_5 = mainDirectory.resolve("2024.5"); if (!directory_2024_5.toFile().exists()) { + log.info("Downloading and unzipping evita-demo-dataset_2024.5.zip"); + directory_2024_5.toFile().mkdirs(); // first download the file from https://evitadb.io/test/evita-demo-dataset_2024.5.zip to tmp folder and unzip it try (final InputStream is = new URL("https://evitadb.io/download/test/evita-demo-dataset_2024.5.zip").openStream()) { @@ -95,6 +99,7 @@ void verifyBackwardCompatibilityTo_2024_5() throws IOException { } } + log.info("Starting Evita with backward compatibility to 2024.5"); final Evita evita = new Evita( EvitaConfiguration.builder() .server( diff --git a/evita_functional_tests/src/test/java/io/evitadb/index/mutation/AbstractMutatorTestBase.java b/evita_functional_tests/src/test/java/io/evitadb/index/mutation/AbstractMutatorTestBase.java index 77a393b87..4b9a0e497 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/index/mutation/AbstractMutatorTestBase.java +++ b/evita_functional_tests/src/test/java/io/evitadb/index/mutation/AbstractMutatorTestBase.java @@ -94,7 +94,8 @@ public Optional getEntitySchema(@Nonnull String entityType alterCatalogSchema(catalogSchemaBuilder); catalogSchema = (CatalogSchema) catalogSchemaBuilder.toInstance(); sealedCatalogSchema = new CatalogSchemaDecorator(catalogSchema); - catalogIndex = new CatalogIndex(catalog); + catalogIndex = new CatalogIndex(); + catalogIndex.attachToCatalog(null, catalog); final EvitaSession mockSession = Mockito.mock(EvitaSession.class); Mockito.when(catalog.getSchema()).thenReturn(sealedCatalogSchema); @@ -107,7 +108,8 @@ public Optional getEntitySchema(@Nonnull String entityType AbstractMutatorTestBase.this::alterProductSchema ) ); - productIndex = new GlobalEntityIndex(1, new EntityIndexKey(EntityIndexType.GLOBAL), () -> productSchema); + productIndex = new GlobalEntityIndex(1, productSchema.getName(), new EntityIndexKey(EntityIndexType.GLOBAL)); + productIndex.useSchema(() -> productSchema); executor = new EntityIndexLocalMutationExecutor( containerAccessor, 1, new MockEntityIndexCreator<>(productIndex), diff --git a/evita_functional_tests/src/test/java/io/evitadb/index/mutation/ReferenceIndexMutatorTest.java b/evita_functional_tests/src/test/java/io/evitadb/index/mutation/ReferenceIndexMutatorTest.java index 4c5ab296b..c3482a0d3 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/index/mutation/ReferenceIndexMutatorTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/index/mutation/ReferenceIndexMutatorTest.java @@ -32,6 +32,7 @@ import io.evitadb.api.requestResponse.schema.CatalogSchemaEditor; import io.evitadb.api.requestResponse.schema.EntitySchemaEditor; import io.evitadb.api.requestResponse.schema.dto.AttributeSchema; +import io.evitadb.api.requestResponse.schema.dto.EntitySchema; import io.evitadb.index.EntityIndex; import io.evitadb.index.EntityIndexKey; import io.evitadb.index.EntityIndexType; @@ -41,6 +42,7 @@ import org.junit.jupiter.api.Test; import java.util.function.Consumer; +import java.util.function.Supplier; import static io.evitadb.index.mutation.ReferenceIndexMutator.attributeUpdate; import static io.evitadb.index.mutation.ReferenceIndexMutator.referenceInsert; @@ -59,8 +61,15 @@ class ReferenceIndexMutatorTest extends AbstractMutatorTestBase { private static final String ATTRIBUTE_CHAR_ARRAY = "charArray"; public static final Consumer DO_NOTHING_CONSUMER = runnable -> { }; - private final EntityIndex entityIndex = new GlobalEntityIndex(1, new EntityIndexKey(EntityIndexType.GLOBAL), () -> productSchema); - private final ReferencedTypeEntityIndex referenceTypesIndex = new ReferencedTypeEntityIndex(1, new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY_TYPE, Entities.BRAND), () -> productSchema); + private final Supplier schemaSupplier = () -> productSchema; + private final EntityIndex entityIndex = new GlobalEntityIndex(1, productSchema.getName(), new EntityIndexKey(EntityIndexType.GLOBAL)); + private final ReferencedTypeEntityIndex referenceTypesIndex = new ReferencedTypeEntityIndex(1, productSchema.getName(), new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY_TYPE, Entities.BRAND)); + + { + entityIndex.useSchema(schemaSupplier); + referenceTypesIndex.useSchema(schemaSupplier); + + } @Override protected void alterCatalogSchema(CatalogSchemaEditor.CatalogSchemaBuilder schema) { @@ -84,7 +93,8 @@ protected void alterProductSchema(EntitySchemaEditor.EntitySchemaBuilder schema) @Test void shouldInsertNewReference() { final ReferenceKey referenceKey = new ReferenceKey(Entities.BRAND, 10); - final EntityIndex referenceIndex = new GlobalEntityIndex(2, new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY, referenceKey), () -> productSchema); + final EntityIndex referenceIndex = new GlobalEntityIndex(2, productSchema.getName(), new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY, referenceKey)); + referenceIndex.useSchema(() -> productSchema); referenceInsert( 1, ENTITY_NAME, executor, entityIndex, referenceTypesIndex, referenceIndex, referenceKey, DO_NOTHING_CONSUMER ); @@ -95,7 +105,8 @@ void shouldInsertNewReference() { @Test void shouldIndexAttributes() { final ReferenceKey referenceKey = new ReferenceKey(Entities.BRAND, 10); - final EntityIndex referenceIndex = new GlobalEntityIndex(2, new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY, referenceKey), () -> productSchema); + final EntityIndex referenceIndex = new GlobalEntityIndex(2, productSchema.getName(), new EntityIndexKey(EntityIndexType.REFERENCED_ENTITY, referenceKey)); + referenceIndex.useSchema(() -> productSchema); referenceInsert( 1, ENTITY_NAME, executor, entityIndex, referenceTypesIndex, referenceIndex, referenceKey, DO_NOTHING_CONSUMER ); diff --git a/evita_functional_tests/src/test/java/io/evitadb/store/catalog/DefaultCatalogPersistenceServiceTest.java b/evita_functional_tests/src/test/java/io/evitadb/store/catalog/DefaultCatalogPersistenceServiceTest.java index 0d31a4314..05780ac89 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/store/catalog/DefaultCatalogPersistenceServiceTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/store/catalog/DefaultCatalogPersistenceServiceTest.java @@ -108,6 +108,7 @@ import static io.evitadb.store.spi.CatalogPersistenceService.CATALOG_FILE_SUFFIX; import static io.evitadb.store.spi.CatalogPersistenceService.getCatalogDataStoreFileNamePattern; import static io.evitadb.test.Assertions.assertExactlyEquals; +import static java.util.Optional.empty; import static java.util.Optional.of; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.any; @@ -157,7 +158,7 @@ private static Catalog getMockCatalog(SealedCatalogSchema catalogSchema, @Nonnul final Catalog mockCatalog = mock(Catalog.class); when(mockCatalog.getSchema()).thenReturn(catalogSchema); when(mockCatalog.getEntitySchema(schema.getName())).thenReturn(of(schema)); - when(mockCatalog.getEntityIndexIfExists(Mockito.eq(schema.getName()), any(EntityIndexKey.class))).thenReturn(null); + when(mockCatalog.getEntityIndexIfExists(Mockito.eq(schema.getName()), any(EntityIndexKey.class), any(Class.class))).thenReturn(empty()); return mockCatalog; } @@ -607,8 +608,8 @@ private EntityCollection constructEntityCollectionWithSomeEntities(@Nonnull Cata final Catalog mockCatalog = getMockCatalog(catalogSchema, entitySchema); final CatalogSchemaContract catalogSchemaContract = Mockito.mock(CatalogSchemaContract.class); final EntityCollection entityCollection = new EntityCollection( + catalogSchema.getName(), 0L, - mockCatalog, entityTypePrimaryKey, entitySchema.getName(), ioService, @@ -616,7 +617,7 @@ private EntityCollection constructEntityCollectionWithSomeEntities(@Nonnull Cata sequenceService, DefaultTracingContext.INSTANCE ); - + entityCollection.attachToCatalog(null, mockCatalog); final ArgumentCaptor mutationCaptor = ArgumentCaptor.forClass(Mutation.class); @@ -654,8 +655,8 @@ private void assertEntityCollectionsHaveIdenticalContent( final SealedEntitySchema schema = entityCollection.getSchema(); final EntityCollection collection = new EntityCollection( + catalogSchema.getName(), 0L, - getMockCatalog(catalogSchema, schema), entityCollection.getEntityTypePrimaryKey(), schema.getName(), ioService, @@ -663,6 +664,7 @@ private void assertEntityCollectionsHaveIdenticalContent( sequenceService, DefaultTracingContext.INSTANCE ); + collection.attachToCatalog(null, getMockCatalog(catalogSchema, schema)); final EvitaSession mockSession = mock(EvitaSession.class); for (Integer primaryKey : entityCollection.getGlobalIndex().getAllPrimaryKeys()) { diff --git a/evita_performance_tests/src/main/java/io/evitadb/spike/mock/MockPriceListAndCurrencyPriceIndex.java b/evita_performance_tests/src/main/java/io/evitadb/spike/mock/MockPriceListAndCurrencyPriceIndex.java index 05498b81e..ce1f30e91 100644 --- a/evita_performance_tests/src/main/java/io/evitadb/spike/mock/MockPriceListAndCurrencyPriceIndex.java +++ b/evita_performance_tests/src/main/java/io/evitadb/spike/mock/MockPriceListAndCurrencyPriceIndex.java @@ -117,7 +117,7 @@ public Formula getIndexedPriceEntityIdsFormula() { @Nonnull @Override - public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(OffsetDateTime theMoment) { + public PriceIdContainerFormula getIndexedRecordIdsValidInFormula(@Nonnull OffsetDateTime theMoment) { throw new UnsupportedOperationException(); } @@ -156,6 +156,16 @@ public boolean isEmpty() { throw new UnsupportedOperationException(); } + @Override + public boolean isTerminated() { + return false; + } + + @Override + public void terminate() { + + } + @Nullable @Override public StoragePart createStoragePart(int entityIndexPrimaryKey) { diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java index 3fb8d79b6..2c7480b51 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultCatalogPersistenceService.java @@ -753,7 +753,7 @@ public CatalogIndex readCatalogIndex(@Nonnull Catalog catalog) { final CatalogStoragePartPersistenceService storagePartPersistenceService = getStoragePartPersistenceService(catalogVersion); final CatalogIndexStoragePart catalogIndexStoragePart = storagePartPersistenceService.getStoragePart(catalogVersion, 1, CatalogIndexStoragePart.class); if (catalogIndexStoragePart == null) { - return new CatalogIndex(catalog); + return new CatalogIndex(); } else { final Set sharedAttributeUniqueIndexes = catalogIndexStoragePart.getSharedAttributeUniqueIndexes(); final Map sharedUniqueIndexes = CollectionUtils.createHashMap(sharedAttributeUniqueIndexes.size()); @@ -775,7 +775,7 @@ public CatalogIndex readCatalogIndex(@Nonnull Catalog catalog) { ) ); } - return new CatalogIndex(catalog, catalogIndexStoragePart.getVersion(), sharedUniqueIndexes); + return new CatalogIndex(catalogIndexStoragePart.getVersion(), sharedUniqueIndexes); } } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java index bbdaa0728..17b79ed5f 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java @@ -466,8 +466,7 @@ private static Map fetchPriceR long catalogVersion, int entityIndexId, @Nonnull Set priceIndexes, - @Nonnull StoragePartPersistenceService persistenceService, - @Nonnull Supplier superIndexAccessor + @Nonnull StoragePartPersistenceService persistenceService ) { final Map priceRefIndexes = CollectionUtils.createHashMap(priceIndexes.size()); for (PriceIndexKey priceIndexKey : priceIndexes) { @@ -482,8 +481,7 @@ private static Map fetchPriceR new PriceListAndCurrencyPriceRefIndex( priceIndexKey, priceIndexCnt.getValidityIndex(), - priceIndexCnt.getPriceIds(), - pik -> superIndexAccessor.get().getPriceIndex(pik) + priceIndexCnt.getPriceIds() ) ); } @@ -912,7 +910,7 @@ public BinaryEntity enrichEntity(long catalogVersion, @Nonnull EntitySchema enti } @Override - public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonnull Supplier schemaSupplier, @Nonnull Supplier temporalIndexAccessor, @Nonnull Supplier superIndexAccessor) { + public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonnull EntitySchema entitySchema) { final EntityIndexStoragePart entityIndexCnt = this.storagePartPersistenceService.getStoragePart(catalogVersion, entityIndexId, EntityIndexStoragePart.class); isPremiseValid( entityIndexCnt != null, @@ -926,7 +924,6 @@ public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonn final Map cardinalityIndexes = new HashMap<>(); /* TOBEDONE #538 - REMOVE IN FUTURE VERSIONS */ - final EntitySchema entitySchema = schemaSupplier.get(); final Function attributeTypeFetcher; final EntityIndexKey entityIndexKey = entityIndexCnt.getEntityIndexKey(); if (entityIndexKey.getType() == EntityIndexType.GLOBAL) { @@ -976,7 +973,6 @@ public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonn entityIndexCnt.getPrimaryKey(), entityIndexKey, entityIndexCnt.getVersion(), - schemaSupplier, entityIndexCnt.getEntityIds(), entityIndexCnt.getEntityIdsByLanguage(), new AttributeIndex( @@ -995,7 +991,6 @@ public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonn entityIndexCnt.getPrimaryKey(), entityIndexKey, entityIndexCnt.getVersion(), - schemaSupplier, entityIndexCnt.getEntityIds(), entityIndexCnt.getEntityIdsByLanguage(), new AttributeIndex( @@ -1009,19 +1004,18 @@ public EntityIndex readEntityIndex(long catalogVersion, int entityIndexId, @Nonn ); } else { final Map priceIndexes = fetchPriceRefIndexes( - catalogVersion, entityIndexId, entityIndexCnt.getPriceIndexes(), storagePartPersistenceService, temporalIndexAccessor + catalogVersion, entityIndexId, entityIndexCnt.getPriceIndexes(), storagePartPersistenceService ); return new ReducedEntityIndex( entityIndexCnt.getPrimaryKey(), entityIndexKey, entityIndexCnt.getVersion(), - schemaSupplier, entityIndexCnt.getEntityIds(), entityIndexCnt.getEntityIdsByLanguage(), new AttributeIndex( entitySchema.getName(), uniqueIndexes, filterIndexes, sortIndexes, chainIndexes ), - new PriceRefIndex(priceIndexes, superIndexAccessor), + new PriceRefIndex(priceIndexes), hierarchyIndex, facetIndex ); diff --git a/evita_test_support/src/main/java/io/evitadb/test/extension/EvitaParameterResolver.java b/evita_test_support/src/main/java/io/evitadb/test/extension/EvitaParameterResolver.java index 414263313..386df3f15 100644 --- a/evita_test_support/src/main/java/io/evitadb/test/extension/EvitaParameterResolver.java +++ b/evita_test_support/src/main/java/io/evitadb/test/extension/EvitaParameterResolver.java @@ -1175,6 +1175,10 @@ public void destroy( log.info("Closing Evita instance for data set `{}`", dataSetName); evitaInstance.close(); + // close the client + ofNullable(client.get()) + .ifPresent(EvitaClient::close); + // close the server instance and free ports ofNullable(evitaServerInstance) .ifPresent(it -> { From 9369b1fefb97b31d01fe966a5f6d1315c0a6b5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Fri, 10 May 2024 13:17:49 +0200 Subject: [PATCH 20/24] perf(#559): Entity fetching optimization When the entity is fetched from the data store, it's entity body storage part is always fetched twice. This becomes a problem when a large number of entities are fetched from the data store, and when the OS file cache is cold, it can mean twice the latency. This fetching is not entirely necessary if we refactor the `enrichEntity` method into two separate ones and call the second one only conditionally. --- .../io/evitadb/core/EntityCollection.java | 181 ++++++++++++------ .../io/evitadb/core/query/QueryContext.java | 36 +--- .../core/query/ReferencedEntityFetcher.java | 17 +- 3 files changed, 139 insertions(+), 95 deletions(-) diff --git a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java index 1e3ab61f0..bde6f42cb 100644 --- a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java +++ b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java @@ -156,8 +156,7 @@ public final class EntityCollection implements TransactionalLayerProducer, EntityCollection>, EntityCollectionContract, - CatalogRelatedDataStructure -{ + CatalogRelatedDataStructure { @Getter private final long id = TransactionalObjectVersion.SEQUENCE.nextId(); /** @@ -487,9 +486,9 @@ public EntityDecorator enrichEntity(@Nonnull EntityContract entity, @Nonnull Evi ); } - return enrichEntity( - referenceFetcher.initReferenceIndex((EntityDecorator) entity, this), - evitaRequest, referenceFetcher + return applyReferenceFetcher( + enrichEntity(entity, evitaRequest), + referenceFetcher ); } @@ -806,8 +805,8 @@ public void terminate() { public Optional getEntity(int primaryKey, @Nonnull EvitaRequest evitaRequest, @Nonnull EvitaSessionContract session, @Nonnull ReferenceFetcher referenceFetcher) { // retrieve current version of entity return getEntityDecorator(primaryKey, evitaRequest, session) - .map(it -> enrichEntity(referenceFetcher.initReferenceIndex(it, this), evitaRequest, referenceFetcher)) - .map(it -> limitEntity(it, evitaRequest, session)); + .map(it -> limitEntity(it, evitaRequest, session)) + .map(it -> applyReferenceFetcher(it, referenceFetcher)); } @Nonnull @@ -818,11 +817,10 @@ public List getEntities(@Nonnull int[] primaryKeys, @Nonnull Evita .filter(Optional::isPresent) .map(Optional::get) .toList(); - return referenceFetcher.initReferenceIndex(entityDecorators, this) - .stream() - .map(it -> enrichEntity(it, evitaRequest, referenceFetcher)) - .map(it -> limitEntity(it, evitaRequest, session)) - .toList(); + return applyReferenceFetcher( + entityDecorators.stream().map(it -> limitEntity(it, evitaRequest, session)).toList(), + referenceFetcher + ); } @Nonnull @@ -849,17 +847,15 @@ public Optional getEntityDecorator(int primaryKey, @Nonnull Evi ); } }, - theEntity -> enrichEntity(theEntity, evitaRequest, ReferenceFetcher.NO_IMPLEMENTATION) + theEntity -> enrichEntity(theEntity, evitaRequest) ); } @Nonnull public EntityDecorator enrichEntity( - @Nonnull SealedEntity sealedEntity, - @Nonnull EvitaRequest evitaRequest, - @Nonnull ReferenceFetcher referenceFetcher - ) - throws EntityAlreadyRemovedException { + @Nonnull EntityContract sealedEntity, + @Nonnull EvitaRequest evitaRequest + ) throws EntityAlreadyRemovedException { final EntityDecorator partiallyLoadedEntity = (EntityDecorator) sealedEntity; // return decorator that hides information not requested by original query final LocaleSerializablePredicate newLocalePredicate = partiallyLoadedEntity.createLocalePredicateRicherCopyWith(evitaRequest); @@ -868,22 +864,7 @@ public EntityDecorator enrichEntity( final AssociatedDataValueSerializablePredicate newAssociatedDataPredicate = partiallyLoadedEntity.createAssociatedDataPredicateRicherCopyWith(evitaRequest); final ReferenceContractSerializablePredicate newReferenceContractPredicate = partiallyLoadedEntity.createReferencePredicateRicherCopyWith(evitaRequest); final PriceContractSerializablePredicate newPriceContractPredicate = partiallyLoadedEntity.createPricePredicateRicherCopyWith(evitaRequest); - // fetch parents if requested - final EntityClassifierWithParent parentEntity; final EntitySchema internalSchema = getInternalSchema(); - if (internalSchema.isWithHierarchy() && newHierarchyPredicate.isRequiresHierarchy()) { - if (partiallyLoadedEntity.getParentEntityWithoutCheckingPredicate().map(it -> it instanceof SealedEntity).orElse(false)) { - parentEntity = partiallyLoadedEntity.getParentEntityWithoutCheckingPredicate().get(); - } else { - final OptionalInt theParent = partiallyLoadedEntity.getDelegate().getParent(); - parentEntity = theParent.isPresent() ? - ofNullable(referenceFetcher.getParentEntityFetcher()) - .map(it -> it.apply(theParent.getAsInt())) - .orElse(null) : null; - } - } else { - parentEntity = null; - } return Entity.decorate( // load all missing data according to current evita request @@ -902,7 +883,7 @@ public EntityDecorator enrichEntity( // use original schema internalSchema, // fetch parents if requested - parentEntity, + null, // show / hide locales the entity is fetched in newLocalePredicate, // show / hide parent information @@ -918,6 +899,93 @@ public EntityDecorator enrichEntity( // propagate original date time partiallyLoadedEntity.getAlignedNow(), // recursive entity loader + ReferenceFetcher.NO_IMPLEMENTATION + ); + } + + @Nonnull + public EntityDecorator applyReferenceFetcher( + @Nonnull SealedEntity sealedEntity, + @Nonnull ReferenceFetcher referenceFetcher + ) throws EntityAlreadyRemovedException { + if (referenceFetcher == ReferenceFetcher.NO_IMPLEMENTATION) { + return (EntityDecorator) sealedEntity; + } else { + referenceFetcher.initReferenceIndex(sealedEntity, this); + return applyReferenceFetcherInternal((EntityDecorator) sealedEntity, referenceFetcher); + } + } + + @Nonnull + public List applyReferenceFetcher( + @Nonnull List sealedEntities, + @Nonnull ReferenceFetcher referenceFetcher + ) throws EntityAlreadyRemovedException { + if (referenceFetcher == ReferenceFetcher.NO_IMPLEMENTATION) { + return sealedEntities; + } else { + referenceFetcher.initReferenceIndex(sealedEntities, this); + return sealedEntities.stream() + .map(it -> applyReferenceFetcherInternal((EntityDecorator) it, referenceFetcher)) + .map(SealedEntity.class::cast) + .toList(); + } + } + + private @Nonnull EntityDecorator applyReferenceFetcherInternal( + @Nonnull EntityDecorator sealedEntity, + @Nonnull ReferenceFetcher referenceFetcher + ) { + // fetch parents if requested + final EntityClassifierWithParent parentEntity; + final EntitySchema internalSchema = getInternalSchema(); + if (internalSchema.isWithHierarchy() && sealedEntity.getHierarchyPredicate().isRequiresHierarchy()) { + if (sealedEntity.getParentEntityWithoutCheckingPredicate().map(it -> it instanceof SealedEntity).orElse(false)) { + parentEntity = sealedEntity.getParentEntityWithoutCheckingPredicate().get(); + } else { + final OptionalInt theParent = sealedEntity.getDelegate().getParent(); + parentEntity = theParent.isPresent() ? + ofNullable(referenceFetcher.getParentEntityFetcher()) + .map(it -> it.apply(theParent.getAsInt())) + .orElse(null) : null; + } + } else { + parentEntity = null; + } + + return Entity.decorate( + // load all missing data according to current evita request + this.persistenceService.enrichEntity( + this.catalog.getVersion(), + internalSchema, + // use all data from existing entity + sealedEntity, + sealedEntity.getHierarchyPredicate(), + sealedEntity.getAttributePredicate(), + sealedEntity.getAssociatedDataPredicate(), + sealedEntity.getReferencePredicate(), + sealedEntity.getPricePredicate(), + dataStoreBuffer + ), + // use original schema + internalSchema, + // fetch parents if requested + parentEntity, + // show / hide locales the entity is fetched in + sealedEntity.getLocalePredicate(), + // show / hide parent information + sealedEntity.getHierarchyPredicate(), + // show / hide attributes information + sealedEntity.getAttributePredicate(), + // show / hide associated data information + sealedEntity.getAssociatedDataPredicate(), + // show / hide references information + sealedEntity.getReferencePredicate(), + // show / hide price information + sealedEntity.getPricePredicate(), + // propagate original date time + sealedEntity.getAlignedNow(), + // recursive entity loader referenceFetcher ); } @@ -1002,10 +1070,10 @@ public EntitySchema getInternalSchema() { public EntityCollectionHeader flush() { this.persistenceService.flushTrappedUpdates(0L, this.dataStoreBuffer.getTrappedIndexChanges()); return this.catalogPersistenceService.flush( - 0L, - this.headerInfoSupplier, - this.persistenceService.getEntityCollectionHeader() - ) + 0L, + this.headerInfoSupplier, + this.persistenceService.getEntityCollectionHeader() + ) .map(EntityCollectionPersistenceService::getEntityCollectionHeader) .orElseGet(this::getEntityCollectionHeader); } @@ -1166,8 +1234,23 @@ public EntityCollection createCopyWithNewPersistenceService(long catalogVersion, return entityCollection; } + @Override + public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { + this.catalog = catalog; + this.schema = new TransactionalReference<>( + new EntitySchemaDecorator(catalog::getSchema, this.initialSchema) + ); + for (EntityIndex entityIndex : indexes.values()) { + entityIndex.useSchema(this::getInternalSchema); + if (entityIndex instanceof CatalogRelatedDataStructure catalogRelatedEntityIndex) { + catalogRelatedEntityIndex.attachToCatalog(this.entityType, this.catalog); + } + } + } + /** * Creates a new copy of the Entity collection with the same state as the current one. + * * @return a new EntityCollection object with the same state as the current one */ @Nonnull @@ -1213,28 +1296,14 @@ EntityCollectionHeader getEntityCollectionHeader() { EntityCollectionHeader flush(long catalogVersion) { this.persistenceService.flushTrappedUpdates(catalogVersion, this.dataStoreBuffer.getTrappedIndexChanges()); return this.catalogPersistenceService.flush( - catalogVersion, - this.headerInfoSupplier, - this.persistenceService.getEntityCollectionHeader() - ) + catalogVersion, + this.headerInfoSupplier, + this.persistenceService.getEntityCollectionHeader() + ) .map(EntityCollectionPersistenceService::getEntityCollectionHeader) .orElseGet(this::getEntityCollectionHeader); } - @Override - public void attachToCatalog(@Nullable String entityType, @Nonnull Catalog catalog) { - this.catalog = catalog; - this.schema = new TransactionalReference<>( - new EntitySchemaDecorator(catalog::getSchema, this.initialSchema) - ); - for (EntityIndex entityIndex : indexes.values()) { - entityIndex.useSchema(this::getInternalSchema); - if (entityIndex instanceof CatalogRelatedDataStructure catalogRelatedEntityIndex) { - catalogRelatedEntityIndex.attachToCatalog(this.entityType, this.catalog); - } - } - } - /* PRIVATE METHODS */ diff --git a/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java b/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java index 96a583298..b4b240845 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/QueryContext.java @@ -391,11 +391,13 @@ public List fetchEntities(int... entityPrimaryKey) { (entityCollection, entityPrimaryKeys, requestToUse) -> entityCollection.getEntities(entityPrimaryKeys, evitaRequest, evitaSession), (entityCollection, prefetchedEntities, requestToUse) -> - entityFetcher.initReferenceIndex(prefetchedEntities, entityCollection) - .stream() - .map(it -> entityCollection.enrichEntity(it, requestToUse, entityFetcher)) - .map(it -> entityCollection.limitEntity(it, requestToUse, evitaSession)) - .toList() + entityCollection.applyReferenceFetcher( + prefetchedEntities.stream() + .map(it -> entityCollection.enrichEntity(it, requestToUse, evitaSession)) + .map(it -> entityCollection.limitEntity(it, requestToUse, evitaSession)) + .toList(), + entityFetcher + ) ); } } @@ -884,30 +886,6 @@ public boolean isRequiresBinaryForm() { return evitaSession.isBinaryFormat(); } - /** - * Method allows to enrich / limit the referenced entity according to passed evita request. - */ - public SealedEntity enrichOrLimitReferencedEntity( - @Nonnull SealedEntity sealedEntity, - @Nonnull EvitaRequest evitaRequest, - @Nonnull ReferenceFetcher referenceFetcher - ) { - final EntityCollection theCollection = getEntityCollectionOrThrowException(sealedEntity.getType(), "enriching reference"); - return theCollection.limitEntity( - theCollection.enrichEntity( - sealedEntity, - evitaRequest, - referenceFetcher - ), - evitaRequest, - evitaSession - ); - } - - /* - PRIVATE METHODS - */ - /** * Method returns appropriate {@link EntityCollection} for the {@link #evitaRequest} or empty value. */ diff --git a/evita_engine/src/main/java/io/evitadb/core/query/ReferencedEntityFetcher.java b/evita_engine/src/main/java/io/evitadb/core/query/ReferencedEntityFetcher.java index 0c6defbc2..258220a4f 100644 --- a/evita_engine/src/main/java/io/evitadb/core/query/ReferencedEntityFetcher.java +++ b/evita_engine/src/main/java/io/evitadb/core/query/ReferencedEntityFetcher.java @@ -323,16 +323,13 @@ private static Map fetchEntitiesByIdsIntoIndex( // enrich and limit entities them appropriately if (!entities.isEmpty()) { - referenceFetcher.initReferenceIndex(entities, entityCollection); - entities.forEach( - // if so, enrich or limit the existing entity for desired scope - previouslyFetchedEntity -> { - final SealedEntity refEntity = queryContext.enrichOrLimitReferencedEntity( - previouslyFetchedEntity, fetchRequest, referenceFetcher - ); - entityIndex.put(refEntity.getPrimaryKey(), refEntity); - } - ); + entityCollection.applyReferenceFetcher( + entities.stream() + .map(it -> entityCollection.enrichEntity(it, fetchRequest)) + .map(it -> entityCollection.limitEntity(it, fetchRequest, queryContext.getEvitaSession())) + .toList(), + referenceFetcher + ).forEach(refEntity -> entityIndex.put(refEntity.getPrimaryKey(), refEntity)); } return entityIndex; } From 1656b4e7a3f9cddb77ae311c88afcc0fe6a1ccaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 11 May 2024 09:53:40 +0200 Subject: [PATCH 21/24] fix(#531): Random errors and NullPointerExceptions in ChainIndex We need to write better fuzzy tests that would allow us to exterminate the edge cases in the ChainIndex. It seems that the real production systems may behave sometimes chaotically till they return back to consistent state. --- .../ConsistencySensitiveDataStructure.java | 78 +++ .../evitadb/index/attribute/ChainIndex.java | 488 ++++++++++++------ .../index/attribute/ChainIndexTest.java | 184 ++++++- 3 files changed, 593 insertions(+), 157 deletions(-) create mode 100644 evita_engine/src/main/java/io/evitadb/ConsistencySensitiveDataStructure.java diff --git a/evita_engine/src/main/java/io/evitadb/ConsistencySensitiveDataStructure.java b/evita_engine/src/main/java/io/evitadb/ConsistencySensitiveDataStructure.java new file mode 100644 index 000000000..4cedd4da1 --- /dev/null +++ b/evita_engine/src/main/java/io/evitadb/ConsistencySensitiveDataStructure.java @@ -0,0 +1,78 @@ +/* + * + * _ _ ____ ____ + * _____ _(_) |_ __ _| _ \| __ ) + * / _ \ \ / / | __/ _` | | | | _ \ + * | __/\ V /| | || (_| | |_| | |_) | + * \___| \_/ |_|\__\__,_|____/|____/ + * + * Copyright (c) 2024 + * + * Licensed under the Business Source License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/FgForrest/evitaDB/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.evitadb; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * All {@link io.evitadb.index.Index} and {@link io.evitadb.index.IndexDataStructure} that are sensitive to data + * consistency should implement this interface. The interface provides a method to check the consistency of the index + * and return a report about the state of the index. + * + * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2024 + */ +public interface ConsistencySensitiveDataStructure { + + /** + * Returns up-to-date consistency report of the index instance. + * + * @return consistency report of the index + */ + @Nonnull + ConsistencyReport getConsistencyReport(); + + /** + * Describes the consistency state of the index. + */ + enum ConsistencyState { + /** + * All data in the index are in consistent state. + */ + CONSISTENT, + /** + * Index is operating ok, but some data are not consistent from the user prospective. This state is usually + * temporary and should be eventually corrected by new data arriving to the index. + */ + INCONSISTENT, + /** + * Index internal state is broken. This state may never happen and if it does, it usually means error in + * evitaDB implementation. The only solution is to rebuild the index from scratch. + */ + BROKEN + } + + /** + * Describes the consistency state of the index. + * + * @param state enum signaling the state of the index + * @param report textual description of the state in English / Markdown format + */ + record ConsistencyReport( + @Nonnull ConsistencyState state, + @Nullable String report + ) { + } + +} diff --git a/evita_engine/src/main/java/io/evitadb/index/attribute/ChainIndex.java b/evita_engine/src/main/java/io/evitadb/index/attribute/ChainIndex.java index 5484c447b..8970afa1a 100644 --- a/evita_engine/src/main/java/io/evitadb/index/attribute/ChainIndex.java +++ b/evita_engine/src/main/java/io/evitadb/index/attribute/ChainIndex.java @@ -23,6 +23,7 @@ package io.evitadb.index.attribute; +import io.evitadb.ConsistencySensitiveDataStructure; import io.evitadb.api.requestResponse.data.AttributesContract.AttributeKey; import io.evitadb.core.Catalog; import io.evitadb.core.Transaction; @@ -31,7 +32,6 @@ import io.evitadb.core.transaction.memory.TransactionalLayerProducer; import io.evitadb.core.transaction.memory.TransactionalObjectVersion; import io.evitadb.dataType.Predecessor; -import io.evitadb.exception.EvitaInternalError; import io.evitadb.exception.EvitaInvalidUsageException; import io.evitadb.index.IndexDataStructure; import io.evitadb.index.array.TransactionalUnorderedIntArray; @@ -40,6 +40,7 @@ import io.evitadb.index.map.TransactionalMap; import io.evitadb.store.model.StoragePart; import io.evitadb.store.spi.model.storageParts.index.ChainIndexStoragePart; +import io.evitadb.utils.ArrayUtils; import io.evitadb.utils.Assert; import lombok.Getter; @@ -53,6 +54,7 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.OptionalInt; +import java.util.PrimitiveIterator.OfInt; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -78,7 +80,12 @@ * * @author Jan Novotný (novotny@fg.cz), FG Forrest a.s. (c) 2023 */ -public class ChainIndex implements SortedRecordsSupplierFactory, TransactionalLayerProducer, IndexDataStructure, Serializable { +public class ChainIndex implements + IndexDataStructure, + ConsistencySensitiveDataStructure, + SortedRecordsSupplierFactory, + TransactionalLayerProducer, + Serializable { @Serial private static final long serialVersionUID = 6633952268102524794L; /** * Index contains tuples of entity primary key and its predecessor primary key. The conflicting primary key is @@ -150,8 +157,6 @@ private ChainIndex( this.dirty = new TransactionalBoolean(); this.chains = new TransactionalMap<>(chains, TransactionalUnorderedIntArray.class, TransactionalUnorderedIntArray::new); this.elementStates = new TransactionalMap<>(elementStates); - // TOBEDONE #531 - REMOVE WHEN THE ISSUE GETS FIXED - checkForCorruption(); } /** @@ -213,26 +218,23 @@ public UnorderedLookup getUnorderedLookup() { * @param primaryKey primary key of the element */ public void upsertPredecessor(@Nonnull Predecessor predecessor, int primaryKey) { - try { - Assert.isTrue( - primaryKey != predecessor.predecessorId(), - "An entity that is its own predecessor doesn't have sense!" - ); - final ChainElementState existingState = this.elementStates.get(primaryKey); + Assert.isTrue( + primaryKey != predecessor.predecessorId(), + "An entity that is its own predecessor doesn't have sense!" + ); + final ChainElementState existingState = this.elementStates.get(primaryKey); + if (existingState == null) { // if existing state is not found - we need to insert new one - if (existingState == null) { - insertPredecessor(primaryKey, predecessor); - } else { - updatePredecessor(primaryKey, predecessor, existingState); - } - this.dirty.setToTrue(); - getOrCreateChainIndexChanges().reset(); - } catch (RuntimeException ex) { - throw new ChainUpdateFailedException( - "State of the index: " + this.toString(), - ex - ); + insertPredecessor(primaryKey, predecessor); + } else if (existingState.predecessorPrimaryKey() == predecessor.predecessorId()) { + // the predecessor is the same - nothing to do + return; + } else { + // otherwise we need to perform update + updatePredecessor(primaryKey, predecessor, existingState); } + this.dirty.setToTrue(); + getOrCreateChainIndexChanges().reset(); } /** @@ -241,22 +243,15 @@ public void upsertPredecessor(@Nonnull Predecessor predecessor, int primaryKey) * @param primaryKey primary key of the element */ public void removePredecessor(int primaryKey) { - try { - final ChainElementState existingState = this.elementStates.remove(primaryKey); - isTrue( - existingState != null, - "Value `" + primaryKey + "` is not present in the chain element index!" - ); - // if existing state is not found - we need to insert new one - removePredecessorFromChain(primaryKey, existingState); - this.dirty.setToTrue(); - getOrCreateChainIndexChanges().reset(); - } catch (RuntimeException ex) { - throw new ChainUpdateFailedException( - "State of the index: " + this.toString(), - ex - ); - } + final ChainElementState existingState = this.elementStates.remove(primaryKey); + isTrue( + existingState != null, + "Value `" + primaryKey + "` is not present in the chain element index!" + ); + // if existing state is not found - we need to insert new one + removePredecessorFromChain(primaryKey, existingState); + this.dirty.setToTrue(); + getOrCreateChainIndexChanges().reset(); } /** @@ -266,6 +261,172 @@ public boolean isEmpty() { return this.elementStates.isEmpty(); } + @Nonnull + @Override + public SortedRecordsSupplier getAscendingOrderRecordsSupplier() { + return getOrCreateChainIndexChanges().getAscendingOrderRecordsSupplier(); + } + + @Nonnull + @Override + public SortedRecordsSupplier getDescendingOrderRecordsSupplier() { + return getOrCreateChainIndexChanges().getDescendingOrderRecordsSupplier(); + } + + /** + * This method verifies internal consistency of the data-structure. It checks whether the chains are correctly + * ordered and whether the element states are correctly set. + */ + @Nonnull + @Override + public ConsistencyReport getConsistencyReport() { + final StringBuilder errors = new StringBuilder(512); + + int overallCount = 0; + for (Entry entry : chains.entrySet()) { + overallCount += entry.getValue().getLength(); + final int[] unorderedList = entry.getValue().getArray(); + if (unorderedList.length <= 0) { + errors.append("\nThe chain with head `") + .append(entry.getKey()) + .append("` is empty!"); + } + int headElementId = unorderedList[0]; + if (headElementId != entry.getKey()) { + errors.append("\nThe head of the chain `") + .append(headElementId).append("` doesn't match the chain head `") + .append(entry.getKey()) + .append("`!"); + } + + int previousElementId = headElementId; + for (int i = 0; i < unorderedList.length; i++) { + int elementId = unorderedList[i]; + final ChainElementState state = elementStates.get(elementId); + if (state == null) { + errors.append("\nThe element `") + .append(elementId) + .append("` is not present in the element states!"); + } + if (i > 0) { + if (state.state() == ElementState.HEAD) { + errors.append("\nThe element `") + .append(elementId) + .append("` must not be a head of the chain!"); + } + if (state.inChainOfHeadWithPrimaryKey() != headElementId) { + errors.append("\nThe element `") + .append(elementId) + .append("` is not in the chain with head `") + .append(headElementId) + .append("`!"); + } + if (state.predecessorPrimaryKey() != previousElementId) { + errors.append("\nThe predecessor of the element `") + .append(elementId) + .append("` doesn't match the previous element!"); + } + } + previousElementId = elementId; + } + + final Integer headId = entry.getKey(); + final ChainElementState headState = this.elementStates.get(headId); + if (headState.state() == ElementState.CIRCULAR) { + // the chain must contain element the head refers to + if (Arrays.stream(entry.getValue().getArray()).noneMatch(it -> it == headState.predecessorPrimaryKey())) { + errors.append("\nThe chain with CIRCULAR head `") + .append(headId) + .append("` doesn't contain element `") + .append(headState.predecessorPrimaryKey()) + .append("` the head refers to!"); + } + } else { + // the chain must not contain element the head refers to + if (Arrays.stream(entry.getValue().getArray()).anyMatch(it -> it == headState.predecessorPrimaryKey())) { + errors.append("\nThe chain with head `") + .append(headId) + .append("` contain element `") + .append(headState.predecessorPrimaryKey()) + .append("` the head refers to and is not marked as CIRCULAR!"); + } + } + } + + for (Entry entry : elementStates.entrySet()) { + final int chainHeadPk = entry.getValue().inChainOfHeadWithPrimaryKey(); + if (entry.getValue().state() == ElementState.SUCCESSOR) { + final TransactionalUnorderedIntArray chain = this.chains.get(chainHeadPk); + if (chain == null) { + errors.append("\nThe referenced chain with head `") + .append(chainHeadPk) + .append("` referenced by ") + .append(entry.getValue().state()) + .append("` element `") + .append(entry.getKey()) + .append("` doesn't exist!"); + } else if (chain.indexOf(entry.getKey()) < 0) { + errors.append("\nThe `") + .append(entry.getValue().state()) + .append("` element `") + .append(entry.getKey()) + .append("` is not in the chain with head `") + .append(chainHeadPk) + .append("`!"); + } + } else if (chainHeadPk != entry.getKey()) { + errors.append("\nThe `") + .append(entry.getValue().state()) + .append("` element `") + .append(entry.getKey()) + .append("` is not in the chain with head `") + .append(chainHeadPk) + .append("`!"); + } + } + + if (overallCount != elementStates.size()) { + errors.append("\nThe number of elements in chains doesn't match " + + "the number of elements in element states!"); + } + + final ConsistencyState state; + if (!errors.isEmpty()) { + state = ConsistencyState.BROKEN; + } else if (isConsistent()) { + state = ConsistencyState.CONSISTENT; + } else { + state = ConsistencyState.INCONSISTENT; + } + + final String chainsListing = chains.values() + .stream() + .map(it -> { + final StringBuilder sb = new StringBuilder("\t- "); + final OfInt pkIt = it.iterator(); + int counter = 0; + while (pkIt.hasNext() && sb.length() < 80) { + if (counter > 0) { + sb.append(", "); + } + sb.append(pkIt.nextInt()); + counter++; + } + if (counter < it.getLength()) { + sb.append("... (") + .append(it.getLength() - counter) + .append(" more)"); + } + return sb.toString(); + }) + .collect(Collectors.joining("\n")); + return new ConsistencyReport( + state, + "## Chains\n\n" + chainsListing + "\n\n" + + (errors.isEmpty() ? "## No errors detected." : "## Errors detected\n\n" + errors) + ); + } + /** * Method creates container for storing chain index from memory to the persistent storage. */ @@ -293,16 +454,15 @@ public void resetDirty() { this.dirty.reset(); } + /* + Implementation of TransactionalLayerProducer + */ + @Override public ChainIndexChanges createLayer() { return new ChainIndexChanges(this); } - - /* - Implementation of TransactionalLayerProducer - */ - @Override public void removeLayer(@Nonnull TransactionalLayerMaintainer transactionalLayer) { transactionalLayer.removeTransactionalMemoryLayerIfExists(this); @@ -332,49 +492,9 @@ public String toString() { " - elementStates:\n" + elementStates.entrySet().stream().map(it -> " - " + it.getKey() + ": " + it.getValue()).collect(Collectors.joining("\n")); } - @Nonnull - @Override - public SortedRecordsSupplier getAscendingOrderRecordsSupplier() { - return getOrCreateChainIndexChanges().getAscendingOrderRecordsSupplier(); - } - - @Nonnull - @Override - public SortedRecordsSupplier getDescendingOrderRecordsSupplier() { - return getOrCreateChainIndexChanges().getDescendingOrderRecordsSupplier(); - } - - /** - * This method verifies internal consistency of the data-structure. It checks whether the chains are correctly - * ordered and whether the element states are correctly set. + /* + PRIVATE METHODS */ - private void checkForCorruption() { - int overallCount = 0; - for (Entry entry : chains.entrySet()) { - overallCount += entry.getValue().getLength(); - final int[] unorderedList = entry.getValue().getArray(); - Assert.isPremiseValid(unorderedList.length > 0, () -> new ChainIndexCorruptedException("Corruption detected! The chain with head `" + entry.getKey() + "` is empty!")); - int headElementId = unorderedList[0]; - Assert.isPremiseValid(headElementId == entry.getKey(), () -> new ChainIndexCorruptedException("Corruption detected! The head of the chain `" + headElementId + "` doesn't match the chain head `" + entry.getKey() + "`!")); - - int previousElementId = headElementId; - for (int i = 0; i < unorderedList.length; i++) { - int elementId = unorderedList[i]; - final ChainElementState state = elementStates.get(elementId); - Assert.isPremiseValid(state != null, () -> new ChainIndexCorruptedException("Corruption detected! The element `" + elementId + "` is not present in the element states!")); - if (i > 0) { - Assert.isPremiseValid(state.state() != ElementState.HEAD, () -> new ChainIndexCorruptedException("Corruption detected! The element `" + elementId + "` must not be a head of the chain!")); - Assert.isPremiseValid(state.inChainOfHeadWithPrimaryKey() == headElementId, () -> new ChainIndexCorruptedException("Corruption detected! The element `" + elementId + "` is not in the chain with head `" + headElementId + "`!")); - Assert.isPremiseValid(state.predecessorPrimaryKey() == previousElementId, () -> new ChainIndexCorruptedException("Corruption detected! The predecessor of the element `" + elementId + "` doesn't match the previous element!")); - } - previousElementId = elementId; - } - } - Assert.isPremiseValid( - overallCount == elementStates.size(), - () -> new ChainIndexCorruptedException("Corruption detected! The number of elements in chains doesn't match the number of elements in element states!") - ); - } /** * Retrieves or creates temporary data structure. When transaction exists it's created in the transactional memory @@ -454,6 +574,7 @@ private OptionalInt removeSuccessorElement(int primaryKey, int chainHeadPk) { // if the primary key is successor of its chain final TransactionalUnorderedIntArray chain = ofNullable(this.chains.get(chainHeadPk)) .orElseThrow(() -> new EvitaInvalidUsageException("Chain with head `" + chainHeadPk + "` is not present in the index!")); + final ChainElementState existingStateHeadState = this.elementStates.get(chainHeadPk); final int index = chain.indexOf(primaryKey); // sanity check - the primary key must be present in the chain according to the state information @@ -470,9 +591,19 @@ private OptionalInt removeSuccessorElement(int primaryKey, int chainHeadPk) { final int[] subChainWithoutRemovedElement = Arrays.copyOfRange(subChain, 1, subChain.length); this.chains.put(subChainWithoutRemovedElement[0], new TransactionalUnorderedIntArray(subChainWithoutRemovedElement)); reclassifyChain(subChainWithoutRemovedElement[0], subChainWithoutRemovedElement); + + // verify whether the head of chain was not in circular conflict and if so + verifyIfCircularDependencyExistsAndIsBroken(existingStateHeadState); } else { // just remove it from the chain chain.removeRange(index, chain.getLength()); + // if the primary key was the perpetrator of the circular dependency, remove the status + if (existingStateHeadState.state() == ElementState.CIRCULAR && existingStateHeadState.predecessorPrimaryKey() == primaryKey) { + this.elementStates.put( + chainHeadPk, + new ChainElementState(existingStateHeadState, ElementState.SUCCESSOR) + ); + } } return OptionalInt.of(chainHeadPk); } else { @@ -507,6 +638,14 @@ private void insertPredecessor(int primaryKey, @Nonnull Predecessor predecessor) // we may safely append the primary key to it predecessorChain.add(predecessor.predecessorId(), primaryKey); this.elementStates.put(primaryKey, new ChainElementState(chainHeadId, predecessor, ElementState.SUCCESSOR)); + // if the head of the chain refers to the appended primary key - we have circular dependency + final ChainElementState chainHeadState = this.elementStates.get(chainHeadId); + if (chainHeadState.predecessorPrimaryKey() == primaryKey) { + this.elementStates.put( + chainHeadId, + new ChainElementState(chainHeadState, ElementState.CIRCULAR) + ); + } } else { // we have to create new "split" chain for the primary key introduceNewSuccessorChain(primaryKey, predecessor); @@ -619,40 +758,56 @@ private void updateElementWithinExistingChain( final int movedChainHeadPk; final ChainElementState predecessorState = this.elementStates.get(predecessor.predecessorId()); - final TransactionalUnorderedIntArray predecessorChain = this.chains.get(predecessorState.inChainOfHeadWithPrimaryKey()); - // if we append the sub-chain to after the last record of the predecessor chain - if (predecessor.predecessorId() == predecessorChain.getLastRecordId()) { - // we can merge both chains together - movedChainHeadPk = predecessorState.inChainOfHeadWithPrimaryKey(); + if (predecessorState == null) { + // the predecessor doesn't exist in current index if (index > 0) { - // the element is in the body of the chain, we need to split the chain - movedChain = existingChain.removeRange(index, existingChain.getLength()); - // append only the sub-chain to the predecessor chain - predecessorChain.appendAll(movedChain); - } else { - // the element is the head of the chain, discard the chain completely - final TransactionalUnorderedIntArray removedChain = this.chains.remove(existingState.inChainOfHeadWithPrimaryKey()); - // and fully merge with predecessor chain - movedChain = existingChain.getArray(); - predecessorChain.appendAll(movedChain); - // if there was transactional memory associated with the chain, discard it - removedChain.removeLayer(); - } - } else { - // if the element is in the body of the chain and is already successor of the predecessor - if (index > 0 && predecessor.predecessorId() == existingChain.get(index - 1)) { - // do nothing - the update doesn't affect anything - return; - } else if (index > 0) { - // the element is in the body of the chain, we need to split the chain - we have a situation with - // multiple successors of the single predecessor + // we need to split the chain and make new successor chain movedChain = existingChain.removeRange(index, existingChain.getLength()); movedChainHeadPk = primaryKey; this.chains.put(primaryKey, new TransactionalUnorderedIntArray(movedChain)); } else { - // leave the current chain be - we need to switch the state to SUCCESSOR only + // but we tackle the head of the chain - so we don't have to do anything + movedChainHeadPk = primaryKey; movedChain = null; - movedChainHeadPk = existingState.inChainOfHeadWithPrimaryKey(); + } + } else { + final TransactionalUnorderedIntArray predecessorChain = this.chains.get(predecessorState.inChainOfHeadWithPrimaryKey()); + // if we append the sub-chain to after the last record of the predecessor chain + if (predecessor.predecessorId() == predecessorChain.getLastRecordId()) { + // we can merge both chains together + movedChainHeadPk = predecessorState.inChainOfHeadWithPrimaryKey(); + if (index > 0) { + // the element is in the body of the chain, we need to split the chain + movedChain = existingChain.removeRange(index, existingChain.getLength()); + // append only the sub-chain to the predecessor chain + predecessorChain.appendAll(movedChain); + // if the appended chain contains primary key the head of predecessor chain refers to - we have circular dependency + examineCircularConflictInNewlyAppendedChain(predecessorState.inChainOfHeadWithPrimaryKey(), movedChain); + } else { + // the element is the head of the chain, discard the chain completely + final TransactionalUnorderedIntArray removedChain = this.chains.remove(existingState.inChainOfHeadWithPrimaryKey()); + // and fully merge with predecessor chain + movedChain = existingChain.getArray(); + predecessorChain.appendAll(movedChain); + // if there was transactional memory associated with the chain, discard it + removedChain.removeLayer(); + } + } else { + // if the element is in the body of the chain and is already successor of the predecessor + if (index > 0 && predecessor.predecessorId() == existingChain.get(index - 1)) { + // do nothing - the update doesn't affect anything + return; + } else if (index > 0) { + // the element is in the body of the chain, we need to split the chain - we have a situation with + // multiple successors of the single predecessor + movedChain = existingChain.removeRange(index, existingChain.getLength()); + movedChainHeadPk = primaryKey; + this.chains.put(primaryKey, new TransactionalUnorderedIntArray(movedChain)); + } else { + // leave the current chain be - we need to switch the state to SUCCESSOR only + movedChain = null; + movedChainHeadPk = existingState.inChainOfHeadWithPrimaryKey(); + } } } @@ -660,6 +815,9 @@ private void updateElementWithinExistingChain( this.elementStates.put(primaryKey, new ChainElementState(movedChainHeadPk, predecessor, ElementState.SUCCESSOR)); // if there was a chain split if (movedChain != null) { + // check whether the new head doesn't introduce circular dependency with new chain + examineCircularConflictInNewlyAppendedChain(movedChainHeadPk, movedChain); + // then we need to update the chain head for all other elements in the split chain // (except the element itself which was already updated by previous statement) for (int i = 1; i < movedChain.length; i++) { @@ -682,6 +840,24 @@ private void updateElementWithinExistingChain( } } + /** + * Method check whether there is circular dependency between chain head predecessor and the contents of the appended + * chain. If so, the chain head is marked as circular. + * + * @param chainHeadPrimaryKey primary key of the chain head + * @param appendedChain chain that was appended to the chain head + */ + private void examineCircularConflictInNewlyAppendedChain(int chainHeadPrimaryKey, int[] appendedChain) { + final ChainElementState predecessorChainHeadState = this.elementStates.get(chainHeadPrimaryKey); + final boolean newCircularConflict = ArrayUtils.indexOf(predecessorChainHeadState.predecessorPrimaryKey(), appendedChain) >= 0; + if (newCircularConflict) { + this.elementStates.put( + chainHeadPrimaryKey, + new ChainElementState(predecessorChainHeadState, ElementState.CIRCULAR) + ); + } + } + /** * Method updates existing element that is known to introduce circular dependency - i.e. it depends on one of its * successors. @@ -728,12 +904,20 @@ private void verifyIfCircularDependencyExistsAndIsBroken( // if original chain head is in circular dependency if (originalChainHeadState.state() == ElementState.CIRCULAR) { final TransactionalUnorderedIntArray originalHeadChain = this.chains.get(originalChainHeadState.inChainOfHeadWithPrimaryKey()); - // verify it is still in circular dependency - if (originalHeadChain.indexOf(originalChainHeadState.predecessorPrimaryKey()) < 0) { - // the circular dependency was broken - this.elementStates.put( + if (originalHeadChain != null) { + // verify it is still in circular dependency + if (originalHeadChain.indexOf(originalChainHeadState.predecessorPrimaryKey()) < 0) { + // the circular dependency was broken + this.elementStates.put( + originalChainHeadState.inChainOfHeadWithPrimaryKey(), + new ChainElementState(originalChainHeadState, ElementState.SUCCESSOR) + ); + } + } else { + // the circular dependency was broken - the chain is now part of another chain + this.elementStates.compute( originalChainHeadState.inChainOfHeadWithPrimaryKey(), - new ChainElementState(originalChainHeadState, ElementState.SUCCESSOR) + (k, newState) -> new ChainElementState(newState, ElementState.SUCCESSOR) ); } } @@ -746,8 +930,10 @@ private void verifyIfCircularDependencyExistsAndIsBroken( */ private void reclassifyChain(int inChainOfHeadWithPrimaryKey, @Nonnull int[] primaryKeys) { for (int splitPk : primaryKeys) { - final ChainElementState movedPkState = this.elementStates.get(splitPk); - this.elementStates.put(splitPk, new ChainElementState(inChainOfHeadWithPrimaryKey, movedPkState)); + this.elementStates.compute( + splitPk, + (key, movedPkState) -> new ChainElementState(inChainOfHeadWithPrimaryKey, movedPkState) + ); } } @@ -810,6 +996,8 @@ private Integer mergeSuccessorChainToElementChainIfPossible(@Nonnull ChainElemen removedChain.removeLayer(); predecessorChain.appendAll(movedChain); reclassifyChain(predecessorState.inChainOfHeadWithPrimaryKey(), movedChain); + // if the moved chain contains primary key the new head refers to - we have circular dependency + examineCircularConflictInNewlyAppendedChain(predecessorState.inChainOfHeadWithPrimaryKey(), movedChain); return predecessorState.inChainOfHeadWithPrimaryKey(); } } @@ -831,30 +1019,49 @@ private Integer findFirstSuccessorChainAndMergeToElementChain(@Nonnull ChainElem final int lastRecordId = chain.getLastRecordId(); final Optional collapsableChain = this.chains.keySet() .stream() - .filter(pk -> { - final ChainElementState theState = this.elementStates.get(pk); - return theState.state() == ElementState.SUCCESSOR && theState.predecessorPrimaryKey() == lastRecordId; - }) + .filter(pk -> this.elementStates.get(pk).predecessorPrimaryKey() == lastRecordId) .findFirst(); if (collapsableChain.isPresent()) { - final ChainElementState headChainState = this.elementStates.get(collapsableChain.get()); - final TransactionalUnorderedIntArray chainToBeCollapsed = this.chains.get(collapsableChain.get()); - if (chainToBeCollapsed.indexOf(headChainState.predecessorPrimaryKey()) >= 0) { + final Integer collapsableHeadPk = collapsableChain.get(); + final ChainElementState collapsableHeadChainState = this.elementStates.get(collapsableHeadPk); + final TransactionalUnorderedIntArray chainToBeCollapsed = this.chains.get(collapsableHeadPk); + if (chainToBeCollapsed.indexOf(collapsableHeadChainState.predecessorPrimaryKey()) >= 0) { // we have circular dependency - we can't collapse the chain this.elementStates.put( - collapsableChain.get(), - new ChainElementState(headChainState, ElementState.CIRCULAR) + collapsableHeadPk, + new ChainElementState(collapsableHeadChainState, ElementState.CIRCULAR) ); return null; } else { // we may append the chain to the predecessor chain - final TransactionalUnorderedIntArray removedChain = this.chains.remove(collapsableChain.get()); + final TransactionalUnorderedIntArray removedChain = this.chains.remove(collapsableHeadPk); final int[] movedChain = removedChain.getArray(); // if there was transactional memory associated with the chain, discard it removedChain.removeLayer(); chain.appendAll(movedChain); reclassifyChain(chainHeadElement, movedChain); - return collapsableChain.get(); + // this collapse introduced new circular dependency + final ChainElementState chainHeadElementState = this.elementStates.get(chainHeadElement); + if (ArrayUtils.indexOf(chainHeadElementState.predecessorPrimaryKey(), movedChain) >= 0) { + this.elementStates.put( + chainHeadElement, + new ChainElementState( + chainHeadElement, + chainHeadState.predecessorPrimaryKey(), + ElementState.CIRCULAR + ) + ); + } else if (collapsableHeadChainState.state() == ElementState.CIRCULAR) { + this.elementStates.put( + collapsableHeadPk, + new ChainElementState( + chainHeadElement, + collapsableHeadChainState.predecessorPrimaryKey(), + ElementState.SUCCESSOR + ) + ); + } + return collapsableHeadPk; } } return null; @@ -940,23 +1147,4 @@ public String toString() { } } - public static class ChainIndexCorruptedException extends EvitaInternalError { - @Serial private static final long serialVersionUID = 8701958569974008862L; - - public ChainIndexCorruptedException(@Nonnull String publicMessage) { - super(publicMessage); - } - - } - - /* TOBEDONE #531 - remove when fixed */ - public static class ChainUpdateFailedException extends EvitaInternalError { - @Serial private static final long serialVersionUID = 8701958569974008862L; - - public ChainUpdateFailedException(@Nonnull String publicMessage, @Nonnull RuntimeException cause) { - super(publicMessage, cause); - } - - } - } diff --git a/evita_functional_tests/src/test/java/io/evitadb/index/attribute/ChainIndexTest.java b/evita_functional_tests/src/test/java/io/evitadb/index/attribute/ChainIndexTest.java index 6ca08d528..f443b4896 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/index/attribute/ChainIndexTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/index/attribute/ChainIndexTest.java @@ -6,7 +6,7 @@ * | __/\ V /| | || (_| | |_| | |_) | * \___| \_/ |_|\__\__,_|____/|____/ * - * Copyright (c) 2023 + * Copyright (c) 2023-2024 * * Licensed under the Business Source License, Version 1.1 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ package io.evitadb.index.attribute; +import io.evitadb.ConsistencySensitiveDataStructure.ConsistencyState; import io.evitadb.api.requestResponse.data.AttributesContract.AttributeKey; import io.evitadb.dataType.Predecessor; import io.evitadb.test.duration.TimeArgumentProvider; @@ -40,10 +41,12 @@ import javax.annotation.Nonnull; import java.util.Arrays; import java.util.Deque; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -51,10 +54,7 @@ import static io.evitadb.test.TestConstants.LONG_RUNNING_TEST; import static io.evitadb.utils.AssertionUtils.assertStateAfterCommit; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * This test verifies the contract of {@link ChainIndex} implementation. @@ -295,6 +295,21 @@ void shouldExecuteOperationsInTransactionAndStayConsistent() { ); } + @Test + void shouldGenerateConsistencyReport() { + shouldExecuteOperationsInTransactionAndStayConsistent(); + + assertEquals( + """ + ## Chains + + - 23, 26, 8, 3, 2, 4, 7, 6, 9, 10, 5, 11 + + ## No errors detected.""", + index.getConsistencyReport().report() + ); + } + @ParameterizedTest(name = "ChainIndex should survive generational randomized test applying modifications on it") @Tag(LONG_RUNNING_TEST) @ArgumentsSource(TimeArgumentProvider.class) @@ -400,6 +415,8 @@ void generationalProofTest(GenerationalTestInput input) { assertArrayEquals(desiredOrder.get(), finalArray); assertTrue(original.isConsistent()); assertTrue(committed.isConsistent()); + assertEquals(ConsistencyState.CONSISTENT, original.getConsistencyReport().state()); + assertEquals(ConsistencyState.CONSISTENT, committed.getConsistencyReport().state()); originalOrder.set(finalArray); transactionalIndex.set(committed); @@ -417,6 +434,159 @@ void generationalProofTest(GenerationalTestInput input) { ); } + /** + * This test will insert to a and remove from the data chaotically. In the final stage it reorder them in + * a consistent way and checks if the final state is consistent. + * + * @param input input for the test + */ + @ParameterizedTest(name = "ChainIndex should survive generational randomized test with garbage") + @Tag(LONG_RUNNING_TEST) + @ArgumentsSource(TimeArgumentProvider.class) + void generationalAllTimeBrokenProofTest(GenerationalTestInput input) { + final int initialCount = 30; + final Random theRandom = new Random(input.randomSeed()); + final int[] initialState = generateInitialChain(theRandom, initialCount); + final AtomicReference originalOrder = new AtomicReference<>(new int[0]); + final AtomicReference transactionalIndex = new AtomicReference<>(this.index); + + runFor( + input, + 100, + new StringBuilder(), + (random, codeBuffer) -> { + final int[] originalState = originalOrder.get(); + + final ChainIndex index = transactionalIndex.get(); + codeBuffer.append("\nSTART: ") + .append( + "int[] initialState = {" + Arrays.stream(index.getUnorderedLookup().getArray()).mapToObj(String::valueOf).collect(Collectors.joining(", ")) + "};\n" + + "\t\tfor (int i = 0; i < initialState.length; i++) {\n" + + "\t\t\tint pk = initialState[i];\n" + + "\t\t\tfinal Predecessor predecessor = i == 0 ? new Predecessor() : new Predecessor(initialState[i - 1]);\n" + + "\t\t\tindex.upsertPredecessor(predecessor, pk);\n" + + "\t\t}" + ) + .append("\n"); + + assertStateAfterCommit( + index, + original -> { + final Deque removedPrimaryKeys = new LinkedList<>(); + for (int pk : originalState) { + if (originalState.length - removedPrimaryKeys.size() > initialCount * 0.8 && random.nextInt(5) == 0) { + removedPrimaryKeys.push(pk); + } + } + + try { + + final Set processedPks = new HashSet<>(removedPrimaryKeys); + for (int i = 0; i < initialCount * 0.5; i++) { + final int randomPreviousIndex = random.nextInt(initialState.length); + final int previousPk = initialState[randomPreviousIndex]; + + int randomPk; + do { + randomPk = initialState[random.nextInt(initialState.length)]; + } while (processedPks.contains(randomPk) || randomPk == previousPk); + + processedPks.add(randomPk); + final Predecessor predecessor = randomPreviousIndex == 0 ? Predecessor.HEAD : new Predecessor(previousPk); + + // change order + codeBuffer.append("index.upsertPredecessor(") + .append("new Predecessor(").append(predecessor.predecessorId()).append("), ") + .append(randomPk).append(");\n"); + original.upsertPredecessor(predecessor, randomPk); + + // remove the element randomly + if (!removedPrimaryKeys.isEmpty() && random.nextInt(5) == 0) { + final Integer pkToRemove = removedPrimaryKeys.pop(); + codeBuffer.append("index.removePredecessor(") + .append(pkToRemove).append(");\n"); + original.removePredecessor(pkToRemove); + } + } + + while (!removedPrimaryKeys.isEmpty()) { + final Integer pkToRemove = removedPrimaryKeys.pop(); + codeBuffer.append("index.removePredecessor(") + .append(pkToRemove).append(");\n"); + original.removePredecessor(pkToRemove); + } + + codeBuffer.append("\n"); + + } catch (Exception ex) { + System.out.println(codeBuffer); + throw ex; + } + }, + (original, committed) -> { + try { + final int[] originalArray = original.getUnorderedLookup().getArray(); + assertArrayEquals(originalOrder.get(), originalArray); + final int[] finalArray = committed.getUnorderedLookup().getArray(); + assertNotEquals(ConsistencyState.BROKEN, committed.getConsistencyReport().state()); + + originalOrder.set(finalArray); + transactionalIndex.set(committed); + } catch (Throwable ex) { + System.out.println(codeBuffer); + throw ex; + } + } + ); + + return new StringBuilder(); + } + ); + + final StringBuilder codeBuffer = new StringBuilder(); + final int[] originalState = originalOrder.get(); + final AtomicReference desiredOrder = new AtomicReference<>(initialState); + defineTargetState(theRandom, originalState, initialCount, desiredOrder); + assertStateAfterCommit( + index, + original -> { + final int[] targetState = desiredOrder.get(); + try { + for (int i = 0; i < targetState.length; i++) { + final int pk = targetState[i]; + final Predecessor predecessor = i <= 0 ? Predecessor.HEAD : new Predecessor(targetState[i - 1]); + + // change order + codeBuffer.append("index.upsertPredecessor(") + .append("new Predecessor(").append(predecessor.predecessorId()).append("), ") + .append(pk).append(");\n"); + original.upsertPredecessor(predecessor, pk); + } + + codeBuffer.append("\n"); + + } catch (Exception ex) { + System.out.println(codeBuffer); + throw ex; + } + }, + (original, committed) -> { + try { + final int[] finalArray = committed.getUnorderedLookup().getArray(); + assertArrayEquals(desiredOrder.get(), finalArray); + assertTrue(committed.isConsistent()); + assertEquals(ConsistencyState.CONSISTENT, committed.getConsistencyReport().state()); + + originalOrder.set(finalArray); + transactionalIndex.set(committed); + } catch (Throwable ex) { + System.out.println(codeBuffer); + throw ex; + } + } + ); + } + private static void defineTargetState(@Nonnull Random random, @Nonnull int[] originalState, int initialCount, @Nonnull AtomicReference desiredOrder) { // collect the pks to next generation - leave out some of existing and add some new final int[] targetState = IntStream.concat( @@ -439,7 +609,7 @@ private static void defineTargetState(@Nonnull Random random, @Nonnull int[] ori * @param initialCount number of elements in the chain * @return array of primary keys */ - private int[] generateInitialChain(@Nonnull Random random, int initialCount) { + private static int[] generateInitialChain(@Nonnull Random random, int initialCount) { final int[] initialState = new int[initialCount]; for (int i = 0; i < initialCount; i++) { initialState[i] = i + 1; @@ -492,4 +662,4 @@ private static void swap(@Nonnull int[] nums, int i, int j) { nums[j] = temp; } -} \ No newline at end of file +} From 95b2e42be643f9657880361e3e455024f5aa586f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 11 May 2024 14:50:25 +0200 Subject: [PATCH 22/24] fix(#556): Add support for comparing OffsetDateTime against DateTimeRange data type If the client uses `attributeGreaterThan("validity", OffsetDateTime.now())`, the exception of a different type will be thrown. He/she needs to update the constraint to `attributeGreaterThan("validity", DateTimeRange.since(OffsetDateTime.now()))` or `attributeGreaterThan("validity", DateTimeRange.between(OffsetDateTime.now(), OffsetDateTime.now()))`, both of which work fine, but this approach is counterintuitive. We should handle the passing of OffsetDateTime and automatically convert it to a time (i.e. the `between` variant) internally to avoid confusion for developers. --- .../io/evitadb/dataType/EvitaDataTypes.java | 9 +++++ ...ityByAttributeFilteringFunctionalTest.java | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java b/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java index 47fb3469c..56fe0b379 100644 --- a/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java +++ b/evita_common/src/main/java/io/evitadb/dataType/EvitaDataTypes.java @@ -192,6 +192,15 @@ public class EvitaDataTypes { private static final BiFunction, Serializable, DateTimeRange> DATE_TIME_RANGE_FUNCTION = (requestedType, unknownObject) -> { if (unknownObject instanceof DateTimeRange) { return (DateTimeRange) unknownObject; + } else if (unknownObject instanceof OffsetDateTime offsetDateTime) { + return DateTimeRange.between(offsetDateTime, offsetDateTime); + } else if (unknownObject instanceof LocalDateTime localDateTime) { + return DateTimeRange.between(localDateTime.atOffset(ZoneOffset.UTC), localDateTime.atOffset(ZoneOffset.UTC)); + } else if (unknownObject instanceof LocalDate localDate) { + return DateTimeRange.between( + localDate.atStartOfDay(ZoneOffset.UTC).toOffsetDateTime(), + localDate.atStartOfDay(ZoneOffset.UTC).toOffsetDateTime().plusHours(23).plusMinutes(59).plusSeconds(59).plusNanos(999999999) + ); } else { final String value = unknownObject.toString(); final String[] parsedResult = DateTimeRange.PARSE_FCT.apply(value); diff --git a/evita_functional_tests/src/test/java/io/evitadb/api/EntityByAttributeFilteringFunctionalTest.java b/evita_functional_tests/src/test/java/io/evitadb/api/EntityByAttributeFilteringFunctionalTest.java index 76fc455cf..1534916a5 100644 --- a/evita_functional_tests/src/test/java/io/evitadb/api/EntityByAttributeFilteringFunctionalTest.java +++ b/evita_functional_tests/src/test/java/io/evitadb/api/EntityByAttributeFilteringFunctionalTest.java @@ -2653,6 +2653,46 @@ void shouldReturnEntitiesByAttributeDateTimeInRange(Evita evita, List originalProductEntities) { + evita.queryCatalog( + TEST_CATALOG, + session -> { + final List allValidities = originalProductEntities.stream() + .map(it -> it.getAttribute(ATTRIBUTE_VALIDITY, DateTimeRange.class)) + .filter(Objects::nonNull) + .map(DateTimeRange::getPreciseFrom) + .distinct() + .sorted() + .toList(); + final OffsetDateTime theMoment = allValidities.get(allValidities.size() / 2); + final EvitaResponse result = session.query( + query( + collection(Entities.PRODUCT), + filterBy( + attributeGreaterThan(ATTRIBUTE_VALIDITY, theMoment) + ), + require( + page(1, Integer.MAX_VALUE), + debug(DebugMode.VERIFY_ALTERNATIVE_INDEX_RESULTS, DebugMode.VERIFY_POSSIBLE_CACHING_TREES) + ) + ), + EntityReference.class + ); + assertResultIs( + originalProductEntities, + sealedEntity -> ofNullable((DateTimeRange) sealedEntity.getAttribute(ATTRIBUTE_VALIDITY)) + .map(it -> it.compareTo(DateTimeRange.between(theMoment, theMoment)) > 0) + .orElse(false), + result.getRecordData() + ); + return null; + } + ); + } + @DisplayName("Should return entities by date time range (now)") @UseDataSet(HUNDRED_PRODUCTS) @Test From 2dd48ac2d7c87fbfcb154cf2ade4bb9ba504d541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 11 May 2024 21:18:47 +0200 Subject: [PATCH 23/24] perf(#559): Entity fetching optimization Fixes revealed by documentation tests. Entities used wrong predicates when prefetched. --- .../data/structure/EntityDecorator.java | 30 ++++++++++++++++ .../io/evitadb/core/EntityCollection.java | 36 +------------------ 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java index 309901df0..316f4c2a7 100644 --- a/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java +++ b/evita_api/src/main/java/io/evitadb/api/requestResponse/data/structure/EntityDecorator.java @@ -282,6 +282,36 @@ public EntityDecorator( ) : Collections.emptyMap(); } + /** + * Creates wrapper around {@link Entity} that filters existing data according passed predicates (which are constructed + * to match query that is used to retrieve the decorator). + * + * @param entity fully or partially loaded entity - it's usually wider than decorator (may be even complete), decorator + * might be obtained from shared global cache + * @param parentEntity object of the parentEntity + * @param referenceFetcher fetcher that can be used for fetching, filtering and ordering referenced + * entities / groups + */ + public EntityDecorator( + @Nonnull EntityDecorator entity, + @Nullable EntityClassifierWithParent parentEntity, + @Nonnull ReferenceFetcher referenceFetcher + ) { + this( + entity.getDelegate(), + entity.getSchema(), + parentEntity, + entity.localePredicate, + entity.hierarchyPredicate, + entity.attributePredicate, + entity.associatedDataPredicate, + entity.referencePredicate, + entity.pricePredicate, + entity.alignedNow, + referenceFetcher + ); + } + /** * Creates wrapper around {@link Entity} that filters existing data according passed predicates (which are constructed * to match query that is used to retrieve the decorator). diff --git a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java index bde6f42cb..6294213a2 100644 --- a/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java +++ b/evita_engine/src/main/java/io/evitadb/core/EntityCollection.java @@ -953,41 +953,7 @@ public List applyReferenceFetcher( parentEntity = null; } - return Entity.decorate( - // load all missing data according to current evita request - this.persistenceService.enrichEntity( - this.catalog.getVersion(), - internalSchema, - // use all data from existing entity - sealedEntity, - sealedEntity.getHierarchyPredicate(), - sealedEntity.getAttributePredicate(), - sealedEntity.getAssociatedDataPredicate(), - sealedEntity.getReferencePredicate(), - sealedEntity.getPricePredicate(), - dataStoreBuffer - ), - // use original schema - internalSchema, - // fetch parents if requested - parentEntity, - // show / hide locales the entity is fetched in - sealedEntity.getLocalePredicate(), - // show / hide parent information - sealedEntity.getHierarchyPredicate(), - // show / hide attributes information - sealedEntity.getAttributePredicate(), - // show / hide associated data information - sealedEntity.getAssociatedDataPredicate(), - // show / hide references information - sealedEntity.getReferencePredicate(), - // show / hide price information - sealedEntity.getPricePredicate(), - // propagate original date time - sealedEntity.getAlignedNow(), - // recursive entity loader - referenceFetcher - ); + return new EntityDecorator(sealedEntity, parentEntity, referenceFetcher); } /** From 34874aaf445bb076ed62c52a887953f3a96d7cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Novotn=C3=BD?= Date: Sat, 11 May 2024 21:54:45 +0200 Subject: [PATCH 24/24] doc: Corrected documentation examples --- .../facet-groups-negation.evitaql.string.md | 20 +- .../facet-groups-negation.graphql.json.md | 40 +- .../facet/facet-groups-negation.rest.json.md | 40 +- .../faceted-search.evitaql.string.md | 754 +++++++++--------- 4 files changed, 427 insertions(+), 427 deletions(-) diff --git a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.evitaql.string.md b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.evitaql.string.md index 9890f5c12..ce2be523b 100644 --- a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.evitaql.string.md +++ b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.evitaql.string.md @@ -1,15 +1,15 @@ ```md Facet summary: parameterValues: 'ram-memory' [525]: - [ ] 'ram-memory-24' (4214) 0 - [ ] 'ram-memory-64' (4118) 0 - [ ] 'ram-memory-4' (3820) 0 - [ ] 'ram-memory-48' (4223) 0 + [ ] 'ram-memory-24' (4214) -11 + [ ] 'ram-memory-64' (4118) -107 + [ ] 'ram-memory-4' (3820) -405 + [ ] 'ram-memory-48' (4223) -2 parameterValues: 'rom-memory' [1527]: - [ ] 'rom-memory-64' (267) 0 - [ ] 'rom-memory-4' (83) 0 - [ ] 'rom-memory-0-064' (6) 0 - [ ] '1024' (912) 0 - [ ] '4094' (20) 0 - [ ] '2048' (365) 0 + [ ] 'rom-memory-64' (267) -3958 + [ ] 'rom-memory-4' (83) -4142 + [ ] 'rom-memory-0-064' (6) -4219 + [ ] '1024' (912) -3313 + [ ] '4094' (20) -4205 + [ ] '2048' (365) -3860 ``` \ No newline at end of file diff --git a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.graphql.json.md b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.graphql.json.md index a621ee978..796535dc7 100644 --- a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.graphql.json.md +++ b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.graphql.json.md @@ -14,8 +14,8 @@ "requested": false, "count": 4214, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -11, + "matchCount": 4214, "hasSense": true }, "facetEntity": { @@ -29,8 +29,8 @@ "requested": false, "count": 4118, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -107, + "matchCount": 4118, "hasSense": true }, "facetEntity": { @@ -44,8 +44,8 @@ "requested": false, "count": 3820, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -405, + "matchCount": 3820, "hasSense": true }, "facetEntity": { @@ -59,8 +59,8 @@ "requested": false, "count": 4223, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -2, + "matchCount": 4223, "hasSense": true }, "facetEntity": { @@ -85,8 +85,8 @@ "requested": false, "count": 267, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3958, + "matchCount": 267, "hasSense": true }, "facetEntity": { @@ -100,8 +100,8 @@ "requested": false, "count": 83, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4142, + "matchCount": 83, "hasSense": true }, "facetEntity": { @@ -115,8 +115,8 @@ "requested": false, "count": 6, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4219, + "matchCount": 6, "hasSense": true }, "facetEntity": { @@ -130,8 +130,8 @@ "requested": false, "count": 912, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3313, + "matchCount": 912, "hasSense": true }, "facetEntity": { @@ -145,8 +145,8 @@ "requested": false, "count": 20, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4205, + "matchCount": 20, "hasSense": true }, "facetEntity": { @@ -160,8 +160,8 @@ "requested": false, "count": 365, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3860, + "matchCount": 365, "hasSense": true }, "facetEntity": { diff --git a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.rest.json.md b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.rest.json.md index e4202331a..1f10a054a 100644 --- a/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.rest.json.md +++ b/documentation/user/en/query/requirements/examples/facet/facet-groups-negation.rest.json.md @@ -21,8 +21,8 @@ "requested": false, "count": 4214, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -11, + "matchCount": 4214, "hasSense": true }, "facetEntity": { @@ -43,8 +43,8 @@ "requested": false, "count": 4118, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -107, + "matchCount": 4118, "hasSense": true }, "facetEntity": { @@ -65,8 +65,8 @@ "requested": false, "count": 3820, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -405, + "matchCount": 3820, "hasSense": true }, "facetEntity": { @@ -87,8 +87,8 @@ "requested": false, "count": 4223, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -2, + "matchCount": 4223, "hasSense": true }, "facetEntity": { @@ -127,8 +127,8 @@ "requested": false, "count": 267, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3958, + "matchCount": 267, "hasSense": true }, "facetEntity": { @@ -149,8 +149,8 @@ "requested": false, "count": 83, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4142, + "matchCount": 83, "hasSense": true }, "facetEntity": { @@ -171,8 +171,8 @@ "requested": false, "count": 6, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4219, + "matchCount": 6, "hasSense": true }, "facetEntity": { @@ -193,8 +193,8 @@ "requested": false, "count": 912, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3313, + "matchCount": 912, "hasSense": true }, "facetEntity": { @@ -215,8 +215,8 @@ "requested": false, "count": 20, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -4205, + "matchCount": 20, "hasSense": true }, "facetEntity": { @@ -237,8 +237,8 @@ "requested": false, "count": 365, "impact": { - "difference": 0, - "matchCount": 4225, + "difference": -3860, + "matchCount": 365, "hasSense": true }, "facetEntity": { diff --git a/documentation/user/en/solve/examples/filtering-products-in-category/faceted-search.evitaql.string.md b/documentation/user/en/solve/examples/filtering-products-in-category/faceted-search.evitaql.string.md index f19a9f3d2..eba00f1a2 100644 --- a/documentation/user/en/solve/examples/filtering-products-in-category/faceted-search.evitaql.string.md +++ b/documentation/user/en/solve/examples/filtering-products-in-category/faceted-search.evitaql.string.md @@ -1,406 +1,406 @@ ```md Facet summary: parameterValues: 'Color' [187]: - [ ] 'Titanium' (8) 0 - [ ] 'Stainless steel' (2) 0 - [ ] 'aluminum alloy' (2) 0 - [ ] 'classic edition' (2) 0 - [ ] 'sport edition' (2) 0 - [ ] 'Brown leather' (2) 0 - [ ] 'Copper Rose' (4) 0 - [ ] 'Gunmetal' (2) 0 - [ ] 'Blue' (12) 0 - [ ] 'Silver' (34) 0 - [ ] 'Red' (2) 0 - [ ] 'Black' (70) 0 - [ ] 'Gold' (36) 0 - [ ] 'White' (20) 0 - [ ] 'Pink' (22) 0 - [ ] 'Purple' (2) 0 - [ ] 'Graphite' (16) 0 - [ ] 'Midnight' (6) 0 + [ ] 'Titanium' (8) -261 + [ ] 'Stainless steel' (2) -267 + [ ] 'aluminum alloy' (2) -267 + [ ] 'classic edition' (2) -267 + [ ] 'sport edition' (2) -267 + [ ] 'Brown leather' (2) -267 + [ ] 'Copper Rose' (4) -265 + [ ] 'Gunmetal' (2) -267 + [ ] 'Blue' (12) -257 + [ ] 'Silver' (34) -235 + [ ] 'Red' (2) -267 + [ ] 'Black' (70) -199 + [ ] 'Gold' (36) -233 + [ ] 'White' (20) -249 + [ ] 'Pink' (22) -247 + [ ] 'Purple' (2) -267 + [ ] 'Graphite' (16) -253 + [ ] 'Midnight' (6) -263 parameterValues: 'Material' [260]: - [ ] 'Stainless steel' (108) 0 - [ ] 'Titanium' (99) 0 - [ ] 'Ceramic' (4) 0 - [ ] 'Aluminium' (38) 0 - [ ] 'Metal' (11) 0 + [ ] 'Stainless steel' (108) -161 + [ ] 'Titanium' (99) -170 + [ ] 'Ceramic' (4) -265 + [ ] 'Aluminium' (38) -231 + [ ] 'Metal' (11) -258 parameterValues: 'Operating system compatibility' [268]: - [ ] 'iOS' (268) 0 - [ ] 'Android' (183) 0 + [ ] 'iOS' (268) -1 + [ ] 'Android' (183) -86 parameterValues: 'Processor' [203]: - [ ] 'Exynos 9110' (34) 0 - [ ] 'Exynos 9000' (43) 0 - [ ] 'Exynos 9100' (11) 0 - [ ] 'Apple S5' (28) 0 - [ ] 'Apple S8' (28) 0 - [ ] 'Apple S6' (14) 0 - [ ] 'Apple S7' (14) 0 - [ ] 'STM32L151C6' (19) 0 - [ ] 'Exynos 9110 SoC' (4) 0 - [ ] 'Kirin A1' (6) 0 - [ ] 'Cortex-A55' (2) 0 + [ ] 'Exynos 9110' (34) -235 + [ ] 'Exynos 9000' (43) -226 + [ ] 'Exynos 9100' (11) -258 + [ ] 'Apple S5' (28) -241 + [ ] 'Apple S8' (28) -241 + [ ] 'Apple S6' (14) -255 + [ ] 'Apple S7' (14) -255 + [ ] 'STM32L151C6' (19) -250 + [ ] 'Exynos 9110 SoC' (4) -265 + [ ] 'Kirin A1' (6) -263 + [ ] 'Cortex-A55' (2) -267 parameterValues: 'RAM memory' [212]: - [ ] '1.5' (2) 0 - [ ] '2' (58) 0 - [ ] '0.5' (12) 0 - [ ] '1' (140) 0 + [ ] '1.5' (2) -267 + [ ] '2' (58) -211 + [ ] '0.5' (12) -257 + [ ] '1' (140) -129 parameterValues: 'ROM memory' [218]: - [ ] '4' (83) 0 - [ ] '0.064' (6) 0 - [ ] '32' (91) 0 - [ ] '16' (2) 0 - [ ] '8' (36) 0 + [ ] '4' (83) -186 + [ ] '0.064' (6) -263 + [ ] '32' (91) -178 + [ ] '16' (2) -267 + [ ] '8' (36) -233 parameterValues: 'Display size' [218]: - [ ] '1.69' (22) 0 - [ ] '1.28' (11) 0 - [ ] '1.43' (10) 0 - [ ] '1.39' (25) 0 - [ ] '1.45' (3) 0 - [ ] '1.65' (19) 0 - [ ] '1.55' (8) 0 - [ ] '1.75' (9) 0 - [ ] '1.3' (16) 0 - [ ] '1.57' (28) 0 - [ ] '1.78' (28) 0 - [ ] '1.9' (14) 0 - [ ] '1.58' (15) 0 - [ ] '1.2' (8) 0 - [ ] '1.4' (2) 0 - [ ] '1.32' (2) 0 + [ ] '1.69' (22) -247 + [ ] '1.28' (11) -258 + [ ] '1.43' (10) -259 + [ ] '1.39' (25) -244 + [ ] '1.45' (3) -266 + [ ] '1.65' (19) -250 + [ ] '1.55' (8) -261 + [ ] '1.75' (9) -260 + [ ] '1.3' (16) -253 + [ ] '1.57' (28) -241 + [ ] '1.78' (28) -241 + [ ] '1.9' (14) -255 + [ ] '1.58' (15) -254 + [ ] '1.2' (8) -261 + [ ] '1.4' (2) -267 + [ ] '1.32' (2) -267 parameterValues: 'Display resolution' [267]: - [ ] '240 x 280' (8) 0 - [ ] '176 x 176' (5) 0 - [ ] '178 x 178' (4) 0 - [ ] '302 x 320' (8) 0 - [ ] '416 x 416' (7) 0 - [ ] '454 x 454' (31) 0 - [ ] '480 x 480' (3) 0 - [ ] '348 x 442' (14) 0 - [ ] '306 x 354' (8) 0 - [ ] '390 x 450' (9) 0 - [ ] '336 x 384' (5) 0 - [ ] '320 x 320' (2) 0 - [ ] '360 x 360' (8) 0 - [ ] '324 x 394' (27) 0 - [ ] '368 x 448' (28) 0 - [ ] '352 x 430' (14) 0 - [ ] '396 x 484' (14) 0 - [ ] '410 x 502' (1) 0 - [ ] '336 x 336' (15) 0 - [ ] '300 x 300' (4) 0 - [ ] '240 x 240' (12) 0 - [ ] '260 x 260' (5) 0 - [ ] '280 x 280' (2) 0 - [ ] '208 x 208' (2) 0 - [ ] '128 x 128' (1) 0 - [ ] '201 x 240' (1) 0 - [ ] '390 x 390' (1) 0 - [ ] '320 x 360' (3) 0 - [ ] '218 x 218' (1) 0 - [ ] '132 x 64' (2) 0 - [ ] '72 x 154' (1) 0 - [ ] '450 x 450' (7) 0 - [ ] '466 x 466' (8) 0 - [ ] '280 x 456' (3) 0 - [ ] '336 x 480' (3) 0 + [ ] '240 x 280' (8) -261 + [ ] '176 x 176' (5) -264 + [ ] '178 x 178' (4) -265 + [ ] '302 x 320' (8) -261 + [ ] '416 x 416' (7) -262 + [ ] '454 x 454' (31) -238 + [ ] '480 x 480' (3) -266 + [ ] '348 x 442' (14) -255 + [ ] '306 x 354' (8) -261 + [ ] '390 x 450' (9) -260 + [ ] '336 x 384' (5) -264 + [ ] '320 x 320' (2) -267 + [ ] '360 x 360' (8) -261 + [ ] '324 x 394' (27) -242 + [ ] '368 x 448' (28) -241 + [ ] '352 x 430' (14) -255 + [ ] '396 x 484' (14) -255 + [ ] '410 x 502' (1) -268 + [ ] '336 x 336' (15) -254 + [ ] '300 x 300' (4) -265 + [ ] '240 x 240' (12) -257 + [ ] '260 x 260' (5) -264 + [ ] '280 x 280' (2) -267 + [ ] '208 x 208' (2) -267 + [ ] '128 x 128' (1) -268 + [ ] '201 x 240' (1) -268 + [ ] '390 x 390' (1) -268 + [ ] '320 x 360' (3) -266 + [ ] '218 x 218' (1) -268 + [ ] '132 x 64' (2) -267 + [ ] '72 x 154' (1) -268 + [ ] '450 x 450' (7) -262 + [ ] '466 x 466' (8) -261 + [ ] '280 x 456' (3) -266 + [ ] '336 x 480' (3) -266 parameterValues: 'Display type' [268]: - [ ] 'Retina OLED' (85) 0 - [ ] 'MIP' (18) 0 - [ ] 'Liquid crystal' (2) 0 - [ ] 'TFT' (11) 0 - [ ] 'OLED' (28) 0 - [ ] 'Super AMOLED' (3) 0 - [ ] 'AMOLED' (121) 0 + [ ] 'Retina OLED' (85) -184 + [ ] 'MIP' (18) -251 + [ ] 'Liquid crystal' (2) -267 + [ ] 'TFT' (11) -258 + [ ] 'OLED' (28) -241 + [ ] 'Super AMOLED' (3) -266 + [ ] 'AMOLED' (121) -148 parameterValues: 'Battery life' [268]: - [ ] '14' (54) 0 - [ ] '40' (5) 0 - [ ] '30' (4) 0 - [ ] '9' (10) 0 - [ ] '12' (14) 0 - [ ] '11' (7) 0 - [ ] '24' (4) 0 - [ ] '21' (3) 0 - [ ] '7' (9) 0 - [ ] '8' (6) 0 - [ ] '15' (6) 0 - [ ] '18' (5) 0 - [ ] '20' (3) 0 - [ ] '0.75' (84) 0 - [ ] '1.5' (1) 0 - [ ] '6' (17) 0 - [ ] '4' (4) 0 - [ ] '10' (11) 0 - [ ] '16' (5) 0 - [ ] '22' (2) 0 - [ ] '37' (2) 0 - [ ] '5' (9) 0 - [ ] '2' (2) 0 - [ ] '3.5' (1) 0 + [ ] '14' (54) -215 + [ ] '40' (5) -264 + [ ] '30' (4) -265 + [ ] '9' (10) -259 + [ ] '12' (14) -255 + [ ] '11' (7) -262 + [ ] '24' (4) -265 + [ ] '21' (3) -266 + [ ] '7' (9) -260 + [ ] '8' (6) -263 + [ ] '15' (6) -263 + [ ] '18' (5) -264 + [ ] '20' (3) -266 + [ ] '0.75' (84) -185 + [ ] '1.5' (1) -268 + [ ] '6' (17) -252 + [ ] '4' (4) -265 + [ ] '10' (11) -258 + [ ] '16' (5) -264 + [ ] '22' (2) -267 + [ ] '37' (2) -267 + [ ] '5' (9) -260 + [ ] '2' (2) -267 + [ ] '3.5' (1) -268 parameterValues: 'Connector' [183]: - [ ] 'usb-c' (183) 0 + [ ] 'usb-c' (183) -86 parameterValues: 'Cellular' [218]: - [ ] 'True' (54) 0 - [ ] 'False' (176) 0 + [ ] 'True' (54) -215 + [ ] 'False' (176) -93 parameterValues: 'Height' [209]: - [ ] '44.12' (4) 0 - [ ] '42' (16) 0 - [ ] '49.4' (1) 0 - [ ] '46.4' (6) 0 - [ ] '46.5' (5) 0 - [ ] '45.8' (3) 0 - [ ] '46' (9) 0 - [ ] '43.25' (6) 0 - [ ] '42.8' (8) 0 - [ ] '40.5' (12) 0 - [ ] '42.4' (9) 0 - [ ] '47.7' (4) 0 - [ ] '43' (3) 0 - [ ] '44' (57) 0 - [ ] '45' (30) 0 - [ ] '49' (1) 0 - [ ] '40.4' (12) 0 - [ ] '47' (8) 0 - [ ] '51' (2) 0 - [ ] '34.5' (1) 0 - [ ] '43.2' (1) 0 - [ ] '43.6' (1) 0 - [ ] '45.1' (1) 0 - [ ] '41' (5) 0 - [ ] '46.2' (1) 0 - [ ] '38' (1) 0 - [ ] '42.9' (1) 0 - [ ] '53.3' (1) 0 + [ ] '44.12' (4) -265 + [ ] '42' (16) -253 + [ ] '49.4' (1) -268 + [ ] '46.4' (6) -263 + [ ] '46.5' (5) -264 + [ ] '45.8' (3) -266 + [ ] '46' (9) -260 + [ ] '43.25' (6) -263 + [ ] '42.8' (8) -261 + [ ] '40.5' (12) -257 + [ ] '42.4' (9) -260 + [ ] '47.7' (4) -265 + [ ] '43' (3) -266 + [ ] '44' (57) -212 + [ ] '45' (30) -239 + [ ] '49' (1) -268 + [ ] '40.4' (12) -257 + [ ] '47' (8) -261 + [ ] '51' (2) -267 + [ ] '34.5' (1) -268 + [ ] '43.2' (1) -268 + [ ] '43.6' (1) -268 + [ ] '45.1' (1) -268 + [ ] '41' (5) -264 + [ ] '46.2' (1) -268 + [ ] '38' (1) -268 + [ ] '42.9' (1) -268 + [ ] '53.3' (1) -268 parameterValues: 'Width' [73]: - [ ] '45.8' (3) 0 - [ ] '46' (4) 0 - [ ] '35.6' (8) 0 - [ ] '35.8' (8) 0 - [ ] '36' (9) 0 - [ ] '36.66' (5) 0 - [ ] '47.7' (4) 0 - [ ] '44' (2) 0 - [ ] '40' (6) 0 - [ ] '47' (8) 0 - [ ] '42' (7) 0 - [ ] '45' (2) 0 - [ ] '43.2' (1) 0 - [ ] '41' (4) 0 - [ ] '48' (1) 0 - [ ] '35' (1) 0 + [ ] '45.8' (3) -266 + [ ] '46' (4) -265 + [ ] '35.6' (8) -261 + [ ] '35.8' (8) -261 + [ ] '36' (9) -260 + [ ] '36.66' (5) -264 + [ ] '47.7' (4) -265 + [ ] '44' (2) -267 + [ ] '40' (6) -263 + [ ] '47' (8) -261 + [ ] '42' (7) -262 + [ ] '45' (2) -267 + [ ] '43.2' (1) -268 + [ ] '41' (4) -265 + [ ] '48' (1) -268 + [ ] '35' (1) -268 parameterValues: 'Thickness' [268]: - [ ] '12.7' (1) 0 - [ ] '10.5' (1) 0 - [ ] '9.4' (6) 0 - [ ] '10.1' (1) 0 - [ ] '9.6' (4) 0 - [ ] '14.4' (1) 0 - [ ] '9.65' (4) 0 - [ ] '11.4' (20) 0 - [ ] '13' (1) 0 - [ ] '10.75' (4) 0 - [ ] '10.7' (75) 0 - [ ] '10.9' (5) 0 - [ ] '10.8' (11) 0 - [ ] '9.7' (4) 0 - [ ] '8.95' (8) 0 - [ ] '13.4' (1) 0 - [ ] '12.6' (4) 0 - [ ] '10.74' (14) 0 - [ ] '10.4' (14) 0 - [ ] '12.4' (8) 0 - [ ] '12.35' (4) 0 - [ ] '12' (4) 0 - [ ] '12.3' (8) 0 - [ ] '11.7' (4) 0 - [ ] '14.5' (7) 0 - [ ] '15.1' (1) 0 - [ ] '14.1' (3) 0 - [ ] '14.9' (2) 0 - [ ] '11.6' (1) 0 - [ ] '15.3' (1) 0 - [ ] '14.7' (1) 0 - [ ] '12.2' (2) 0 - [ ] '12.1' (1) 0 - [ ] '11.5' (2) 0 - [ ] '11.1' (2) 0 - [ ] '12.8' (1) 0 - [ ] '11.3' (1) 0 - [ ] '11.9' (2) 0 - [ ] '11' (8) 0 - [ ] '14' (1) 0 - [ ] '10' (1) 0 - [ ] '11.8' (1) 0 - [ ] '9.9' (9) 0 - [ ] '13.6' (1) 0 - [ ] '13.5' (4) 0 - [ ] '8.8' (4) 0 - [ ] '9.1' (5) 0 + [ ] '12.7' (1) -268 + [ ] '10.5' (1) -268 + [ ] '9.4' (6) -263 + [ ] '10.1' (1) -268 + [ ] '9.6' (4) -265 + [ ] '14.4' (1) -268 + [ ] '9.65' (4) -265 + [ ] '11.4' (20) -249 + [ ] '13' (1) -268 + [ ] '10.75' (4) -265 + [ ] '10.7' (75) -194 + [ ] '10.9' (5) -264 + [ ] '10.8' (11) -258 + [ ] '9.7' (4) -265 + [ ] '8.95' (8) -261 + [ ] '13.4' (1) -268 + [ ] '12.6' (4) -265 + [ ] '10.74' (14) -255 + [ ] '10.4' (14) -255 + [ ] '12.4' (8) -261 + [ ] '12.35' (4) -265 + [ ] '12' (4) -265 + [ ] '12.3' (8) -261 + [ ] '11.7' (4) -265 + [ ] '14.5' (7) -262 + [ ] '15.1' (1) -268 + [ ] '14.1' (3) -266 + [ ] '14.9' (2) -267 + [ ] '11.6' (1) -268 + [ ] '15.3' (1) -268 + [ ] '14.7' (1) -268 + [ ] '12.2' (2) -267 + [ ] '12.1' (1) -268 + [ ] '11.5' (2) -267 + [ ] '11.1' (2) -267 + [ ] '12.8' (1) -268 + [ ] '11.3' (1) -268 + [ ] '11.9' (2) -267 + [ ] '11' (8) -261 + [ ] '14' (1) -268 + [ ] '10' (1) -268 + [ ] '11.8' (1) -268 + [ ] '9.9' (9) -260 + [ ] '13.6' (1) -268 + [ ] '13.5' (4) -265 + [ ] '8.8' (4) -265 + [ ] '9.1' (5) -264 parameterValues: 'Weight' [83]: - [ ] '64' (1) 0 - [ ] '39' (6) 0 - [ ] '24.7' (4) 0 - [ ] '19.5' (8) 0 - [ ] '24.4' (4) 0 - [ ] '27' (6) 0 - [ ] '19' (5) 0 - [ ] '59.4' (4) 0 - [ ] '40' (6) 0 - [ ] '61.3' (1) 0 - [ ] '48.2' (4) 0 - [ ] '38' (6) 0 - [ ] '76' (4) 0 - [ ] '63' (3) 0 - [ ] '58' (1) 0 - [ ] '37' (1) 0 - [ ] '36' (6) 0 - [ ] '46.3' (1) 0 - [ ] '38.2' (1) 0 - [ ] '46.1' (1) 0 - [ ] '25.5' (1) 0 - [ ] '40.9' (1) 0 - [ ] '50' (1) 0 - [ ] '35' (3) 0 - [ ] '48' (4) 0 + [ ] '64' (1) -268 + [ ] '39' (6) -263 + [ ] '24.7' (4) -265 + [ ] '19.5' (8) -261 + [ ] '24.4' (4) -265 + [ ] '27' (6) -263 + [ ] '19' (5) -264 + [ ] '59.4' (4) -265 + [ ] '40' (6) -263 + [ ] '61.3' (1) -268 + [ ] '48.2' (4) -265 + [ ] '38' (6) -263 + [ ] '76' (4) -265 + [ ] '63' (3) -266 + [ ] '58' (1) -268 + [ ] '37' (1) -268 + [ ] '36' (6) -263 + [ ] '46.3' (1) -268 + [ ] '38.2' (1) -268 + [ ] '46.1' (1) -268 + [ ] '25.5' (1) -268 + [ ] '40.9' (1) -268 + [ ] '50' (1) -268 + [ ] '35' (3) -266 + [ ] '48' (4) -265 parameterValues: 'Battery capacity' [268]: - [ ] '280' (8) 0 - [ ] '200' (9) 0 - [ ] '225' (4) 0 - [ ] '230' (5) 0 - [ ] '500' (1) 0 - [ ] '410' (5) 0 - [ ] '471' (10) 0 - [ ] '450' (6) 0 - [ ] '220' (14) 0 - [ ] '246' (8) 0 - [ ] '250' (4) 0 - [ ] '270' (5) 0 - [ ] '390' (7) 0 - [ ] '340' (1) 0 - [ ] '296' (42) 0 - [ ] '304' (14) 0 - [ ] '309' (28) 0 - [ ] '542' (1) 0 - [ ] '266' (27) 0 - [ ] '275' (28) 0 - [ ] '294' (4) 0 - [ ] '455' (17) 0 - [ ] '120' (1) 0 - [ ] '790' (2) 0 - [ ] '180' (4) 0 - [ ] '361' (1) 0 - [ ] '590' (1) 0 - [ ] '420' (1) 0 - [ ] '470' (2) 0 - [ ] '262' (1) 0 - [ ] '300' (7) 0 + [ ] '280' (8) -261 + [ ] '200' (9) -260 + [ ] '225' (4) -265 + [ ] '230' (5) -264 + [ ] '500' (1) -268 + [ ] '410' (5) -264 + [ ] '471' (10) -259 + [ ] '450' (6) -263 + [ ] '220' (14) -255 + [ ] '246' (8) -261 + [ ] '250' (4) -265 + [ ] '270' (5) -264 + [ ] '390' (7) -262 + [ ] '340' (1) -268 + [ ] '296' (42) -227 + [ ] '304' (14) -255 + [ ] '309' (28) -241 + [ ] '542' (1) -268 + [ ] '266' (27) -242 + [ ] '275' (28) -241 + [ ] '294' (4) -265 + [ ] '455' (17) -252 + [ ] '120' (1) -268 + [ ] '790' (2) -267 + [ ] '180' (4) -265 + [ ] '361' (1) -268 + [ ] '590' (1) -268 + [ ] '420' (1) -268 + [ ] '470' (2) -267 + [ ] '262' (1) -268 + [ ] '300' (7) -262 parameterValues: 'GPS' [218]: - [ ] 'False' (16) 0 - [ ] 'True' (202) 0 + [ ] 'False' (16) -253 + [ ] 'True' (202) -67 parameterValues: 'Solar' [218]: - [ ] 'False' (218) 0 + [ ] 'False' (218) -51 parameterValues: 'NFC' [268]: - [ ] 'False' (113) 0 - [ ] 'True' (155) 0 + [ ] 'False' (113) -156 + [ ] 'True' (155) -114 parameterValues: 'Water resistance' [268]: - [ ] 'True' (268) 0 + [ ] 'True' (268) -1 parameterValues: 'Color' [54]: - [ ] 'Cream' (2) 0 - [ ] 'Vermillion Orange' (2) 0 - [ ] 'Terra Rosa' (2) 0 - [ ] 'Autumn Brown' (2) 0 - [ ] 'Carbon' (4) 0 - [ ] 'Platinum' (4) 0 - [ ] 'Flax Brown' (2) 0 - [ ] 'Starlight' (6) 0 - [ ] 'Green' (10) 0 - [ ] 'Grey' (24) 0 + [ ] 'Cream' (2) -267 + [ ] 'Vermillion Orange' (2) -267 + [ ] 'Terra Rosa' (2) -267 + [ ] 'Autumn Brown' (2) -267 + [ ] 'Carbon' (4) -265 + [ ] 'Platinum' (4) -265 + [ ] 'Flax Brown' (2) -267 + [ ] 'Starlight' (6) -263 + [ ] 'Green' (10) -259 + [ ] 'Grey' (24) -245 parameterValues: 'Height' [59]: - [ ] '44.1' (4) 0 - [ ] '40.9' (8) 0 - [ ] '47.2' (4) 0 - [ ] '41.8' (5) 0 - [ ] '48.6' (1) 0 - [ ] '45.9' (13) 0 - [ ] '40' (6) 0 - [ ] '43.4' (4) 0 - [ ] '45.4' (1) 0 - [ ] '40.6' (4) 0 - [ ] '39' (1) 0 - [ ] '49.6' (1) 0 - [ ] '53' (1) 0 - [ ] '46.7' (2) 0 - [ ] '45.5' (2) 0 - [ ] '47.3' (1) 0 - [ ] '41.2' (1) 0 + [ ] '44.1' (4) -265 + [ ] '40.9' (8) -261 + [ ] '47.2' (4) -265 + [ ] '41.8' (5) -264 + [ ] '48.6' (1) -268 + [ ] '45.9' (13) -256 + [ ] '40' (6) -263 + [ ] '43.4' (4) -265 + [ ] '45.4' (1) -268 + [ ] '40.6' (4) -265 + [ ] '39' (1) -268 + [ ] '49.6' (1) -268 + [ ] '53' (1) -268 + [ ] '46.7' (2) -267 + [ ] '45.5' (2) -267 + [ ] '47.3' (1) -268 + [ ] '41.2' (1) -268 parameterValues: 'Width' [195]: - [ ] '36.49' (4) 0 - [ ] '36.4' (4) 0 - [ ] '35.3' (10) 0 - [ ] '35.5' (8) 0 - [ ] '47.2' (5) 0 - [ ] '46.4' (6) 0 - [ ] '46.5' (6) 0 - [ ] '36.25' (6) 0 - [ ] '48.6' (1) 0 - [ ] '43' (3) 0 - [ ] '45.9' (14) 0 - [ ] '38' (84) 0 - [ ] '40.4' (12) 0 - [ ] '40.5' (4) 0 - [ ] '43.4' (4) 0 - [ ] '51' (3) 0 - [ ] '34.5' (1) 0 - [ ] '45.4' (1) 0 - [ ] '43.6' (1) 0 - [ ] '37' (4) 0 - [ ] '45.1' (1) 0 - [ ] '39' (1) 0 - [ ] '46.2' (1) 0 - [ ] '30' (2) 0 - [ ] '33.5' (3) 0 - [ ] '46.8' (1) 0 - [ ] '46.7' (2) 0 - [ ] '42.9' (1) 0 - [ ] '45.5' (2) 0 + [ ] '36.49' (4) -265 + [ ] '36.4' (4) -265 + [ ] '35.3' (10) -259 + [ ] '35.5' (8) -261 + [ ] '47.2' (5) -264 + [ ] '46.4' (6) -263 + [ ] '46.5' (6) -263 + [ ] '36.25' (6) -263 + [ ] '48.6' (1) -268 + [ ] '43' (3) -266 + [ ] '45.9' (14) -255 + [ ] '38' (84) -185 + [ ] '40.4' (12) -257 + [ ] '40.5' (4) -265 + [ ] '43.4' (4) -265 + [ ] '51' (3) -266 + [ ] '34.5' (1) -268 + [ ] '45.4' (1) -268 + [ ] '43.6' (1) -268 + [ ] '37' (4) -265 + [ ] '45.1' (1) -268 + [ ] '39' (1) -268 + [ ] '46.2' (1) -268 + [ ] '30' (2) -267 + [ ] '33.5' (3) -266 + [ ] '46.8' (1) -268 + [ ] '46.7' (2) -267 + [ ] '42.9' (1) -268 + [ ] '45.5' (2) -267 parameterValues: 'Weigth' [185]: - [ ] '33.2' (8) 0 - [ ] '31' (13) 0 - [ ] '30' (7) 0 - [ ] '32' (14) 0 - [ ] '24.8' (6) 0 - [ ] '25' (4) 0 - [ ] '40.4' (1) 0 - [ ] '43.8' (3) 0 - [ ] '36.2' (14) 0 - [ ] '33' (14) 0 - [ ] '36.7' (14) 0 - [ ] '36.5' (14) 0 - [ ] '38.8' (28) 0 - [ ] '46' (4) 0 - [ ] '43' (5) 0 - [ ] '85' (1) 0 - [ ] '79' (2) 0 - [ ] '73' (1) 0 - [ ] '89' (1) 0 - [ ] '96' (1) 0 - [ ] '52' (4) 0 - [ ] '24' (1) 0 - [ ] '95' (1) 0 - [ ] '49' (1) 0 - [ ] '51' (1) 0 - [ ] '37.6' (2) 0 - [ ] '50.5' (1) 0 - [ ] '38.6' (1) 0 - [ ] '55.5' (1) 0 - [ ] '33.8' (1) 0 - [ ] '41' (9) 0 - [ ] '54' (1) 0 - [ ] '21' (1) 0 - [ ] '42.6' (3) 0 - [ ] '33.5' (1) 0 - [ ] '46.5' (1) 0 + [ ] '33.2' (8) -261 + [ ] '31' (13) -256 + [ ] '30' (7) -262 + [ ] '32' (14) -255 + [ ] '24.8' (6) -263 + [ ] '25' (4) -265 + [ ] '40.4' (1) -268 + [ ] '43.8' (3) -266 + [ ] '36.2' (14) -255 + [ ] '33' (14) -255 + [ ] '36.7' (14) -255 + [ ] '36.5' (14) -255 + [ ] '38.8' (28) -241 + [ ] '46' (4) -265 + [ ] '43' (5) -264 + [ ] '85' (1) -268 + [ ] '79' (2) -267 + [ ] '73' (1) -268 + [ ] '89' (1) -268 + [ ] '96' (1) -268 + [ ] '52' (4) -265 + [ ] '24' (1) -268 + [ ] '95' (1) -268 + [ ] '49' (1) -268 + [ ] '51' (1) -268 + [ ] '37.6' (2) -267 + [ ] '50.5' (1) -268 + [ ] '38.6' (1) -268 + [ ] '55.5' (1) -268 + [ ] '33.8' (1) -268 + [ ] '41' (9) -260 + [ ] '54' (1) -268 + [ ] '21' (1) -268 + [ ] '42.6' (3) -266 + [ ] '33.5' (1) -268 + [ ] '46.5' (1) -268 parameterValues: 'Connector' [268]: - [ ] 'Lightning' (85) 0 - [ ] 'USB-C' (183) 0 + [ ] 'Lightning' (85) -184 + [ ] 'USB-C' (183) -86 ``` \ No newline at end of file