FgForrest/evitaDB

Issues applying partial rollback in atomic entity mutation

Opened this issue · 0 comments

Since we need to respect the atomicity update of the entity mutation, we try to undo the local action that happened on the transactional index that was already executed since the start of the entity mutation beginning until the problematic local mutation. Obviously, this doesn't always work, which leads to compromising the state of a particular transaction. We should add more tests that would verify as many possible scenarios as possible.

One of the problematic scenarios is represented by the following stack from production:

SEVERE: Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed@3ac174bd
io.evitadb.api.exception.UniqueValueViolationException: Unique value is already present for entity `Product` `url` key: `/elektronicky-darkovy-poukaz-500-kc` in locale `sk` (existing: 40791, new: 41233)!
        at io.evitadb.index.attribute.UniqueIndex.assertUniqueKeyIsFree(UniqueIndex.java:347)
        at io.evitadb.index.attribute.UniqueIndex.registerUniqueKeyValue(UniqueIndex.java:303)
        at io.evitadb.index.attribute.UniqueIndex.registerUniqueKeyValue(UniqueIndex.java:291)
        at io.evitadb.index.attribute.UniqueIndex.registerUniqueKey(UniqueIndex.java:145)
        at io.evitadb.index.attribute.AttributeIndex.insertUniqueAttribute(AttributeIndex.java:230)
        at io.evitadb.index.EntityIndex.insertUniqueAttribute(EntityIndex.java:101)
        at io.evitadb.index.mutation.AttributeIndexMutator.executeAttributeUpsert(AttributeIndexMutator.java:123)
        at io.evitadb.index.mutation.EntityIndexLocalMutationExecutor.updateAttributes(EntityIndexLocalMutationExecutor.java:391)
        at io.evitadb.index.mutation.EntityIndexLocalMutationExecutor.lambda$applyMutation$7(EntityIndexLocalMutationExecutor.java:249)
        at io.evitadb.index.mutation.EntityIndexLocalMutationExecutor.applyMutation(EntityIndexLocalMutationExecutor.java:258)
        at io.evitadb.core.EntityCollection.processMutations(EntityCollection.java:276)
        at io.evitadb.core.EntityCollection.lambda$applyMutations$41(EntityCollection.java:1543)
        at io.evitadb.store.entity.serializer.EntitySchemaContext.executeWithSchemaContext(EntitySchemaContext.java:90)
        at io.evitadb.core.EntityCollection.applyMutations(EntityCollection.java:1541)
        at io.evitadb.core.EntityCollection.upsertEntityInternal(EntityCollection.java:1434)
        at io.evitadb.core.EntityCollection.upsertEntity(EntityCollection.java:542)
        at io.evitadb.core.EvitaSession.lambda$upsertEntity$37(EvitaSession.java:874)
        at io.evitadb.core.EvitaSession.lambda$executeInTransactionIfPossible$58(EvitaSession.java:1386)
        at io.evitadb.core.Transaction.executeInTransactionIfProvided(Transaction.java:149)
        at io.evitadb.core.EvitaSession.executeInTransactionIfPossible(EvitaSession.java:1384)
        at io.evitadb.core.EvitaSession.upsertEntity(EvitaSession.java:872)
        at jdk.internal.reflect.GeneratedMethodAccessor58.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.evitadb.core.SessionRegistry$EvitaSessionProxy.lambda$invoke$0(SessionRegistry.java:198)
        at io.evitadb.core.SessionRegistry$EvitaSessionProxy.lambda$invoke$2(SessionRegistry.java:260)
        at io.evitadb.core.Transaction.executeInTransactionIfProvided(Transaction.java:149)
        at io.evitadb.core.SessionRegistry$EvitaSessionProxy.invoke(SessionRegistry.java:193)
        at jdk.proxy2/jdk.proxy2.$Proxy68.upsertEntity(Unknown Source)
        at io.evitadb.externalApi.grpc.services.EvitaSessionService.upsertEntity(EvitaSessionService.java:366)
        at io.evitadb.externalApi.grpc.generated.EvitaSessionServiceGrpc$MethodHandlers.invoke(EvitaSessionServiceGrpc.java:2217)
        at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182)
        at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
        at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
        at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
        at io.grpc.Contexts$ContextualizedServerCallListener.onHalfClose(Contexts.java:86)
        at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
        at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
        at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
        at io.evitadb.externalApi.grpc.services.interceptors.GlobalExceptionHandlerInterceptor$ExceptionHandler.onHalfClose(GlobalExceptionHandlerInterceptor.java:72)
        at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
        at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
        at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
        at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:351)
        at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:861)
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at java.base/java.lang.Thread.run(Thread.java:840)
        Suppressed: io.evitadb.exception.EvitaInvalidUsageException: Facet `12` not found in index (group: `null`)!
                at io.evitadb.utils.Assert.notNull(Assert.java:54)
                at io.evitadb.index.facet.FacetReferenceIndex.removeFacet(FacetReferenceIndex.java:194)
                at io.evitadb.index.facet.FacetIndex.removeFacet(FacetIndex.java:166)
                at io.evitadb.index.EntityIndex.removeFacet(EntityIndex.java:123)
                at io.evitadb.index.mutation.ReferenceIndexMutator.lambda$addFacetToIndex$21(ReferenceIndexMutator.java:696)
                at io.evitadb.index.mutation.EntityIndexLocalMutationExecutor.rollback(EntityIndexLocalMutationExecutor.java:304)
                at io.evitadb.core.EntityCollection.processMutations(EntityCollection.java:293)
                ... 40 more