Search in sources :

Example 6 with IndexEntryConflictException

use of org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException in project neo4j by neo4j.

the class ConstraintIndexConcurrencyTest method shouldNotAllowConcurrentViolationOfConstraint.

@Test
void shouldNotAllowConcurrentViolationOfConstraint() throws Exception {
    // Given
    Label label = label("Foo");
    String propertyKey = "bar";
    String conflictingValue = "baz";
    String constraintName = "MyConstraint";
    // a constraint
    try (Transaction tx = db.beginTx()) {
        tx.schema().constraintFor(label).assertPropertyIsUnique(propertyKey).withName(constraintName).create();
        tx.commit();
    }
    // When
    try (Transaction tx = db.beginTx()) {
        KernelTransaction ktx = ((InternalTransaction) tx).kernelTransaction();
        int labelId = ktx.tokenRead().nodeLabel(label.name());
        int propertyKeyId = ktx.tokenRead().propertyKey(propertyKey);
        Read read = ktx.dataRead();
        try (NodeValueIndexCursor cursor = ktx.cursors().allocateNodeValueIndexCursor(ktx.cursorContext(), ktx.memoryTracker())) {
            IndexDescriptor index = ktx.schemaRead().indexGetForName(constraintName);
            IndexReadSession indexSession = ktx.dataRead().indexReadSession(index);
            read.nodeIndexSeek(indexSession, cursor, unconstrained(), PropertyIndexQuery.exact(propertyKeyId, "The value is irrelevant, we just want to perform some sort of lookup against this " + "index"));
        }
        // then let another thread come in and create a node
        threads.execute(db -> {
            try (Transaction transaction = db.beginTx()) {
                transaction.createNode(label).setProperty(propertyKey, conflictingValue);
                transaction.commit();
            }
            return null;
        }, db).get();
        // before we create a node with the same property ourselves - using the same statement that we have
        // already used for lookup against that very same index
        long node = ktx.dataWrite().nodeCreate();
        ktx.dataWrite().nodeAddLabel(node, labelId);
        var e = assertThrows(UniquePropertyValueValidationException.class, () -> ktx.dataWrite().nodeSetProperty(node, propertyKeyId, Values.of(conflictingValue)));
        assertEquals(ConstraintDescriptorFactory.uniqueForLabel(labelId, propertyKeyId), e.constraint());
        IndexEntryConflictException conflict = Iterators.single(e.conflicts().iterator());
        assertEquals(Values.stringValue(conflictingValue), conflict.getSinglePropertyValue());
        tx.commit();
    }
}
Also used : Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) Label(org.neo4j.graphdb.Label) IndexReadSession(org.neo4j.internal.kernel.api.IndexReadSession) InternalTransaction(org.neo4j.kernel.impl.coreapi.InternalTransaction) ConstraintDescriptorFactory(org.neo4j.internal.schema.constraints.ConstraintDescriptorFactory) Values(org.neo4j.values.storable.Values) ImpermanentDbmsExtension(org.neo4j.test.extension.ImpermanentDbmsExtension) IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Inject(org.neo4j.test.extension.Inject) ThreadingExtension(org.neo4j.test.rule.concurrent.ThreadingExtension) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Transaction(org.neo4j.graphdb.Transaction) NodeValueIndexCursor(org.neo4j.internal.kernel.api.NodeValueIndexCursor) Read(org.neo4j.internal.kernel.api.Read) Iterators(org.neo4j.internal.helpers.collection.Iterators) Label.label(org.neo4j.graphdb.Label.label) GraphDatabaseAPI(org.neo4j.kernel.internal.GraphDatabaseAPI) Test(org.junit.jupiter.api.Test) IndexQueryConstraints.unconstrained(org.neo4j.internal.kernel.api.IndexQueryConstraints.unconstrained) KernelTransaction(org.neo4j.kernel.api.KernelTransaction) UniquePropertyValueValidationException(org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException) ThreadingRule(org.neo4j.test.rule.concurrent.ThreadingRule) PropertyIndexQuery(org.neo4j.internal.kernel.api.PropertyIndexQuery) IndexDescriptor(org.neo4j.internal.schema.IndexDescriptor) KernelTransaction(org.neo4j.kernel.api.KernelTransaction) NodeValueIndexCursor(org.neo4j.internal.kernel.api.NodeValueIndexCursor) Label(org.neo4j.graphdb.Label) InternalTransaction(org.neo4j.kernel.impl.coreapi.InternalTransaction) IndexDescriptor(org.neo4j.internal.schema.IndexDescriptor) IndexReadSession(org.neo4j.internal.kernel.api.IndexReadSession) Read(org.neo4j.internal.kernel.api.Read) InternalTransaction(org.neo4j.kernel.impl.coreapi.InternalTransaction) Transaction(org.neo4j.graphdb.Transaction) KernelTransaction(org.neo4j.kernel.api.KernelTransaction) IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException) Test(org.junit.jupiter.api.Test)

Example 7 with IndexEntryConflictException

use of org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException in project neo4j by neo4j.

the class UniquePropertyValueValidationException method getUserMessage.

@Override
public String getUserMessage(TokenNameLookup tokenNameLookup) {
    SchemaDescriptor schema = constraint.schema();
    StringBuilder message = new StringBuilder();
    for (Iterator<IndexEntryConflictException> iterator = conflicts.iterator(); iterator.hasNext(); ) {
        IndexEntryConflictException conflict = iterator.next();
        message.append(conflict.evidenceMessage(tokenNameLookup, schema));
        if (iterator.hasNext()) {
            message.append(System.lineSeparator());
        }
    }
    return message.toString();
}
Also used : SchemaDescriptor(org.neo4j.internal.schema.SchemaDescriptor) IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException)

Example 8 with IndexEntryConflictException

use of org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException in project neo4j by neo4j.

the class TentativeConstraintIndexProxy method verifyDeferredConstraints.

@Override
public void verifyDeferredConstraints(NodePropertyAccessor accessor) throws IndexEntryConflictException, IOException {
    // If we've seen constraint violation failures in here when updates came in then fail immediately with those
    if (!failures.isEmpty()) {
        Iterator<IndexEntryConflictException> failureIterator = failures.iterator();
        IndexEntryConflictException conflict = failureIterator.next();
        failureIterator.forEachRemaining(conflict::addSuppressed);
        throw conflict;
    }
    // Otherwise consolidate the usual verification
    super.verifyDeferredConstraints(accessor);
}
Also used : IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException)

Example 9 with IndexEntryConflictException

use of org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException in project neo4j by neo4j.

the class ConstraintIndexCreator method createUniquenessConstraintIndex.

/**
 * You MUST hold a label write lock before you call this method.
 * However the label write lock is temporarily released while populating the index backing the constraint.
 * It goes a little like this:
 * <ol>
 * <li>Prerequisite: Getting here means that there's an open schema transaction which has acquired the
 * LABEL WRITE lock.</li>
 * <li>Index schema rule which is backing the constraint is created in a nested mini-transaction
 * which doesn't acquire any locking, merely adds tx state and commits so that the index rule is applied
 * to the store, which triggers the index population</li>
 * <li>Release the LABEL WRITE lock</li>
 * <li>Await index population to complete</li>
 * <li>Acquire the LABEL WRITE lock (effectively blocking concurrent transactions changing
 * data related to this constraint, and it so happens, most other transactions as well) and verify
 * the uniqueness of the built index</li>
 * <li>Leave this method, knowing that the uniqueness constraint rule will be added to tx state
 * and this tx committed, which will create the uniqueness constraint</li>
 * </ol>
 */
public IndexDescriptor createUniquenessConstraintIndex(KernelTransactionImplementation transaction, IndexBackedConstraintDescriptor constraint, IndexPrototype prototype) throws TransactionFailureException, CreateConstraintFailureException, UniquePropertyValueValidationException, AlreadyConstrainedException {
    String constraintString = constraint.userDescription(transaction.tokenRead());
    log.info("Starting constraint creation: %s.", constraintString);
    IndexDescriptor index;
    SchemaRead schemaRead = transaction.schemaRead();
    try {
        index = checkAndCreateConstraintIndex(schemaRead, transaction.tokenRead(), constraint, prototype);
    } catch (AlreadyConstrainedException e) {
        throw e;
    } catch (KernelException e) {
        throw new CreateConstraintFailureException(constraint, e);
    }
    boolean success = false;
    boolean reacquiredLabelLock = false;
    Client locks = transaction.lockClient();
    ResourceType keyType = constraint.schema().keyType();
    long[] lockingKeys = constraint.schema().lockingKeys();
    try {
        locks.acquireShared(transaction.lockTracer(), keyType, lockingKeys);
        IndexProxy proxy = indexingService.getIndexProxy(index);
        // Release the LABEL WRITE lock during index population.
        // At this point the integrity of the constraint to be created was checked
        // while holding the lock and the index rule backing the soon-to-be-created constraint
        // has been created. Now it's just the population left, which can take a long time
        locks.releaseExclusive(keyType, lockingKeys);
        awaitConstraintIndexPopulation(constraint, proxy, transaction);
        log.info("Constraint %s populated, starting verification.", constraintString);
        // Index population was successful, but at this point we don't know if the uniqueness constraint holds.
        // Acquire LABEL WRITE lock and verify the constraints here in this user transaction
        // and if everything checks out then it will be held until after the constraint has been
        // created and activated.
        locks.acquireExclusive(transaction.lockTracer(), keyType, lockingKeys);
        reacquiredLabelLock = true;
        try (NodePropertyAccessor propertyAccessor = new DefaultNodePropertyAccessor(transaction.newStorageReader(), transaction.cursorContext(), transaction.memoryTracker())) {
            indexingService.getIndexProxy(index).verifyDeferredConstraints(propertyAccessor);
        }
        log.info("Constraint %s verified.", constraintString);
        success = true;
        return index;
    } catch (IndexNotFoundKernelException e) {
        String indexString = index.userDescription(transaction.tokenRead());
        throw new TransactionFailureException(format("Index (%s) that we just created does not exist.", indexString), e);
    } catch (IndexEntryConflictException e) {
        throw new UniquePropertyValueValidationException(constraint, VERIFICATION, e, transaction.tokenRead());
    } catch (InterruptedException | IOException e) {
        throw new CreateConstraintFailureException(constraint, e);
    } finally {
        if (!success) {
            if (!reacquiredLabelLock) {
                locks.acquireExclusive(transaction.lockTracer(), keyType, lockingKeys);
            }
            if (indexStillExists(schemaRead, index)) {
                dropUniquenessConstraintIndex(index);
            }
        }
    }
}
Also used : UniquePropertyValueValidationException(org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException) SchemaRead(org.neo4j.internal.kernel.api.SchemaRead) ResourceType(org.neo4j.lock.ResourceType) IndexNotFoundKernelException(org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException) IOException(java.io.IOException) IndexDescriptor(org.neo4j.internal.schema.IndexDescriptor) NodePropertyAccessor(org.neo4j.storageengine.api.NodePropertyAccessor) DefaultNodePropertyAccessor(org.neo4j.kernel.impl.transaction.state.storeview.DefaultNodePropertyAccessor) DefaultNodePropertyAccessor(org.neo4j.kernel.impl.transaction.state.storeview.DefaultNodePropertyAccessor) TransactionFailureException(org.neo4j.internal.kernel.api.exceptions.TransactionFailureException) AlreadyConstrainedException(org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException) IndexProxy(org.neo4j.kernel.impl.api.index.IndexProxy) IndexNotFoundKernelException(org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException) IndexPopulationFailedKernelException(org.neo4j.kernel.api.exceptions.index.IndexPopulationFailedKernelException) KernelException(org.neo4j.exceptions.KernelException) Client(org.neo4j.kernel.impl.locking.Locks.Client) IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException) CreateConstraintFailureException(org.neo4j.internal.kernel.api.exceptions.schema.CreateConstraintFailureException)

Example 10 with IndexEntryConflictException

use of org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException in project neo4j by neo4j.

the class ConstraintIndexCreatorTest method shouldDropIndexIfPopulationFails.

@Test
void shouldDropIndexIfPopulationFails() throws Exception {
    // given
    IndexingService indexingService = mock(IndexingService.class);
    IndexProxy indexProxy = mock(IndexProxy.class);
    when(indexingService.getIndexProxy(index)).thenReturn(indexProxy);
    when(indexProxy.getDescriptor()).thenReturn(index);
    when(schemaRead.indexGetForName(constraint.getName())).thenReturn(IndexDescriptor.NO_INDEX, index);
    IndexEntryConflictException cause = new IndexEntryConflictException(2, 1, Values.of("a"));
    doThrow(new IndexPopulationFailedKernelException("some index", cause)).when(indexProxy).awaitStoreScanCompleted(anyLong(), any());
    when(schemaRead.index(any(SchemaDescriptor.class))).thenReturn(// first claim it doesn't exist, because it doesn't... so
    Iterators.emptyResourceIterator()).thenReturn(// then after it failed claim it does exist
    Iterators.iterator(index));
    ConstraintIndexCreator creator = new ConstraintIndexCreator(() -> kernel, indexingService, logProvider);
    // when
    KernelTransactionImplementation transaction = createTransaction();
    UniquePropertyValueValidationException exception = assertThrows(UniquePropertyValueValidationException.class, () -> creator.createUniquenessConstraintIndex(transaction, constraint, prototype));
    assertEquals("Existing data does not satisfy Constraint( name='constraint', type='UNIQUENESS', schema=(:Label {prop}) ): " + "Both node 2 and node 1 share the property value ( String(\"a\") )", exception.getMessage());
    assertEquals(2, kernel.transactions.size());
    KernelTransactionImplementation tx1 = kernel.transactions.get(0);
    verify(tx1).indexUniqueCreate(prototype);
    verify(schemaRead, times(2)).indexGetForName(constraint.getName());
    verifyNoMoreInteractions(schemaRead);
    KernelTransactionImplementation kti2 = kernel.transactions.get(1);
    verify(kti2).addIndexDoDropToTxState(index);
}
Also used : ConstraintIndexCreator(org.neo4j.kernel.impl.api.state.ConstraintIndexCreator) UniquePropertyValueValidationException(org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException) IndexingService(org.neo4j.kernel.impl.api.index.IndexingService) IndexPopulationFailedKernelException(org.neo4j.kernel.api.exceptions.index.IndexPopulationFailedKernelException) KernelTransactionImplementation(org.neo4j.kernel.impl.api.KernelTransactionImplementation) IndexProxy(org.neo4j.kernel.impl.api.index.IndexProxy) IndexEntryConflictException(org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException) Test(org.junit.jupiter.api.Test)

Aggregations

IndexEntryConflictException (org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException)21 IOException (java.io.IOException)9 Test (org.junit.jupiter.api.Test)5 IndexDescriptor (org.neo4j.internal.schema.IndexDescriptor)5 UniquePropertyValueValidationException (org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException)5 TermQuery (org.apache.lucene.search.TermQuery)4 IndexUpdater (org.neo4j.kernel.api.index.IndexUpdater)4 NewIndexDescriptor (org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor)4 ArrayList (java.util.ArrayList)3 Arrays (java.util.Arrays)3 Collection (java.util.Collection)3 List (java.util.List)3 Test (org.junit.Test)3 Collections (java.util.Collections)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Iterator (java.util.Iterator)2 Map (java.util.Map)2 Set (java.util.Set)2 Query (org.apache.lucene.search.Query)2