use of org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException in project neo4j by neo4j.
the class Operations method assertNoBlockingSchemaRulesExists.
private void assertNoBlockingSchemaRulesExists(ConstraintDescriptor constraint) throws EquivalentSchemaRuleAlreadyExistsException, IndexWithNameAlreadyExistsException, ConstraintWithNameAlreadyExistsException, AlreadyConstrainedException, AlreadyIndexedException {
final String name = constraint.getName();
if (name == null) {
throw new IllegalStateException("Expected constraint to always have a name by this point");
}
// Equivalent constraint
final List<ConstraintDescriptor> constraintsWithSameSchema = Iterators.asList(allStoreHolder.constraintsGetForSchema(constraint.schema()));
for (ConstraintDescriptor constraintWithSameSchema : constraintsWithSameSchema) {
if (constraint.equals(constraintWithSameSchema) && constraint.getName().equals(constraintWithSameSchema.getName())) {
throw new EquivalentSchemaRuleAlreadyExistsException(constraintWithSameSchema, CONSTRAINT_CREATION, token);
}
}
// Name conflict with other schema rule
assertSchemaRuleWithNameDoesNotExist(name);
// Already constrained
for (ConstraintDescriptor constraintWithSameSchema : constraintsWithSameSchema) {
final boolean creatingExistenceConstraint = constraint.type() == ConstraintType.EXISTS;
final boolean existingIsExistenceConstraint = constraintWithSameSchema.type() == ConstraintType.EXISTS;
if (creatingExistenceConstraint == existingIsExistenceConstraint) {
throw new AlreadyConstrainedException(constraintWithSameSchema, CONSTRAINT_CREATION, token);
}
}
// Already indexed
if (constraint.type() != ConstraintType.EXISTS) {
Iterator<IndexDescriptor> existingIndexes = allStoreHolder.index(constraint.schema());
if (existingIndexes.hasNext()) {
IndexDescriptor existingIndex = existingIndexes.next();
throw new AlreadyIndexedException(existingIndex.schema(), CONSTRAINT_CREATION, token);
}
}
// is being populated we will end up with two indexes on the same schema.
if (constraint.isIndexBackedConstraint() && ktx.hasTxStateWithChanges()) {
for (ConstraintDescriptor droppedConstraint : ktx.txState().constraintsChanges().getRemoved()) {
// If dropped and new constraint have similar backing index we cannot allow this constraint creation
if (droppedConstraint.isIndexBackedConstraint() && constraint.schema().equals(droppedConstraint.schema())) {
throw new UnsupportedOperationException(format("Trying to create constraint '%s' in same transaction as dropping '%s'. " + "This is not supported because they are both backed by similar indexes. " + "Please drop constraint in a separate transaction before creating the new one.", constraint.getName(), droppedConstraint.getName()));
}
}
}
}
use of org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException 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);
}
}
}
}
use of org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException in project neo4j by neo4j.
the class AbstractConstraintCreationIT method shouldNotCreateConstraintThatAlreadyExists.
@Test
public void shouldNotCreateConstraintThatAlreadyExists() throws Exception {
// given
{
SchemaWriteOperations statement = schemaWriteOperationsInNewTransaction();
createConstraint(statement, descriptor);
commit();
}
// when
try {
SchemaWriteOperations statement = schemaWriteOperationsInNewTransaction();
createConstraint(statement, descriptor);
fail("Should not have validated");
}// then
catch (AlreadyConstrainedException e) {
// good
}
}
use of org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException in project neo4j by neo4j.
the class PlainOperationsTest method shouldReleaseAcquiredSchemaWriteLockIfNodePropertyExistenceConstraintCreationFails.
@Test
void shouldReleaseAcquiredSchemaWriteLockIfNodePropertyExistenceConstraintCreationFails() throws Exception {
// given
NodeExistenceConstraintDescriptor constraint = existsForSchema(schema);
storageReaderWithConstraints(constraint);
int labelId = schema.getLabelId();
int propertyId = schema.getPropertyId();
when(tokenHolders.labelTokens().getTokenById(labelId)).thenReturn(new NamedToken("Label", labelId));
when(tokenHolders.propertyKeyTokens().getTokenById(propertyId)).thenReturn(new NamedToken("prop", labelId));
// when
try {
operations.nodePropertyExistenceConstraintCreate(schema, "constraint name");
fail("Expected an exception because this schema should already be constrained.");
} catch (AlreadyConstrainedException ignore) {
// Good.
}
// then
order.verify(locks).acquireExclusive(LockTracer.NONE, ResourceTypes.LABEL, labelId);
order.verify(storageReader).constraintsGetForSchema(schema);
order.verify(locks).releaseExclusive(ResourceTypes.LABEL, labelId);
}
use of org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException in project neo4j by neo4j.
the class PlainOperationsTest method shouldReleaseAcquiredSchemaWriteLockIfConstraintCreationFails.
@Test
void shouldReleaseAcquiredSchemaWriteLockIfConstraintCreationFails() throws Exception {
// given
UniquenessConstraintDescriptor constraint = uniqueForSchema(schema);
storageReaderWithConstraints(constraint);
int labelId = schema.getLabelId();
int propertyId = schema.getPropertyId();
when(tokenHolders.labelTokens().getTokenById(labelId)).thenReturn(new NamedToken("Label", labelId));
when(tokenHolders.propertyKeyTokens().getTokenById(propertyId)).thenReturn(new NamedToken("prop", labelId));
// when
try {
operations.uniquePropertyConstraintCreate(IndexPrototype.uniqueForSchema(schema).withName("constraint name"));
fail("Expected an exception because this schema should already be constrained.");
} catch (AlreadyConstrainedException ignore) {
// Good.
}
// then
order.verify(locks).acquireExclusive(LockTracer.NONE, ResourceTypes.LABEL, labelId);
order.verify(storageReader).constraintsGetForSchema(schema);
order.verify(locks).releaseExclusive(ResourceTypes.LABEL, labelId);
}
Aggregations