diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Locks.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Locks.java index a7e0813a4ce8a..620009752b96d 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Locks.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Locks.java @@ -55,4 +55,16 @@ public interface Locks void releaseSharedExplicitIndexLock( long... ids ); void releaseSharedLabelLock( long... ids ); + + /** + * Shared token locks are held when new tokens are created. + */ + void acquireSharedTokenLock(); + + /** + * Exclusive token locks are used to hold back the creation of new tokens. + * + * This is useful for when a lock on "all tokens" are needed. + */ + void acquireExclusiveTokenLock(); } diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/schema/SchemaDescriptor.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/schema/SchemaDescriptor.java index d23e8285962ee..b715bb3a3c08f 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/schema/SchemaDescriptor.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/schema/SchemaDescriptor.java @@ -114,6 +114,18 @@ static boolean isAnyEntityTokenSchema( SchemaDescriptor schema ) return Arrays.equals(schema.getEntityTokenIds(), ANY_ENTITY_TOKEN ); } + static long[] schemaTokenLockingIds( SchemaDescriptor schema ) + { + // TODO make getEntityTokenIds produce a long array directly, and avoid this extra copying. + int[] entityTokenIds = schema.getEntityTokenIds(); + long[] lockingIds = new long[entityTokenIds.length]; + for ( int i = 0; i < lockingIds.length; i++ ) + { + lockingIds[i] = entityTokenIds[i]; + } + return lockingIds; + } + /** * Returns true if any of the given entity token ids are part of this schema unit. * @param entityTokenIds entity token ids to check against. diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema/MultiTokenSchemaDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema/MultiTokenSchemaDescriptor.java index ea853bdbeeb30..c984eddb87a68 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema/MultiTokenSchemaDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema/MultiTokenSchemaDescriptor.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Objects; -import org.neo4j.hashing.HashFunction; import org.neo4j.internal.kernel.api.TokenNameLookup; import org.neo4j.internal.kernel.api.schema.SchemaComputer; import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; @@ -39,32 +38,15 @@ public class MultiTokenSchemaDescriptor implements SchemaDescriptor { - private static final HashFunction HASH_FUNCTION = HashFunction.incrementalXXH64(); private final int[] entityTokens; private final EntityType entityType; private final int[] propertyIds; - private final int key; MultiTokenSchemaDescriptor( int[] entityTokens, EntityType entityType, int[] propertyIds ) { this.entityTokens = entityTokens; - Arrays.sort( this.entityTokens ); this.entityType = entityType; this.propertyIds = propertyIds; - Arrays.sort( this.propertyIds ); - - long hash = HASH_FUNCTION.initialise( 0x0123456789abcdefL + entityType.ordinal() ); - for ( int entityToken : this.entityTokens ) - { - hash = HASH_FUNCTION.update( hash, entityToken ); - } - for ( int propertyId : this.propertyIds ) - { - hash = HASH_FUNCTION.update( hash, propertyId ); - } - - hash = HASH_FUNCTION.finalise( hash ); - key = HASH_FUNCTION.toInt( hash ); } @Override @@ -126,13 +108,13 @@ public int[] getEntityTokenIds() @Override public int keyId() { - return key; + throw new UnsupportedOperationException( this + " does not have a single keyId." ); } @Override public ResourceType keyType() { - return ResourceTypes.SCHEMA; + return entityType == EntityType.NODE ? ResourceTypes.LABEL : ResourceTypes.RELATIONSHIP_TYPE; } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SchemaCache.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SchemaCache.java index 2b57bc3e25ceb..df250f72ee0d2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SchemaCache.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SchemaCache.java @@ -310,7 +310,7 @@ else if ( rule instanceof StoreIndexDescriptor ) for ( int entityTokenId : schemaDescriptor.getEntityTokenIds() ) { Set forLabel = - indexDescriptorsByLabel.getIfAbsent( entityTokenId, HashSet::new ); + indexDescriptorsByLabel.getIfAbsentPut( entityTokenId, HashSet::new ); forLabel.add( index ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/IsolatedTransactionTokenCreator.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/IsolatedTransactionTokenCreator.java index 8f4f06452a0c6..0ce279f3c5d8a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/IsolatedTransactionTokenCreator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/IsolatedTransactionTokenCreator.java @@ -48,10 +48,11 @@ abstract class IsolatedTransactionTokenCreator implements TokenCreator public synchronized int createToken( String name ) throws KernelException { Kernel kernel = kernelSupplier.get(); - try ( Transaction transaction = kernel.beginTransaction( Type.implicit, LoginContext.AUTH_DISABLED ) ) + try ( Transaction tx = kernel.beginTransaction( Type.implicit, LoginContext.AUTH_DISABLED ) ) { - int id = createKey( transaction, name ); - transaction.success(); + tx.locks().acquireSharedTokenLock(); + int id = createKey( tx, name ); + tx.success(); return id; } } @@ -62,6 +63,7 @@ public synchronized void createTokens( String[] names, int[] ids, IntPredicate f Kernel kernel = kernelSupplier.get(); try ( Transaction tx = kernel.beginTransaction( Type.implicit, LoginContext.AUTH_DISABLED ) ) { + tx.locks().acquireSharedTokenLock(); for ( int i = 0; i < ids.length; i++ ) { if ( filter.test( i ) ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/ResourceTypes.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/ResourceTypes.java index 898d457a75fd5..2218c9da206e0 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/ResourceTypes.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/ResourceTypes.java @@ -42,7 +42,7 @@ public enum ResourceTypes implements ResourceType EXPLICIT_INDEX( 5, LockWaitStrategies.INCREMENTAL_BACKOFF ), LABEL( 6, LockWaitStrategies.INCREMENTAL_BACKOFF ), RELATIONSHIP_TYPE( 7, LockWaitStrategies.INCREMENTAL_BACKOFF ), - SCHEMA( 8, LockWaitStrategies.INCREMENTAL_BACKOFF ); + TOKEN_CREATE( 8, LockWaitStrategies.INCREMENTAL_BACKOFF ); private static final boolean useStrongHashing = FeatureToggles.flag( ResourceTypes.class, "useStrongHashing", false ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java index 826cae3970e11..acb9db0036655 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java @@ -410,7 +410,8 @@ public Iterator indexesGetForLabel( int labelId ) { iterator = ktx.txState().indexDiffSetsByLabel( labelId ).apply( iterator ); } - return (Iterator)iterator; + //noinspection unchecked + return (Iterator) iterator; } @Override @@ -440,7 +441,7 @@ public IndexReference indexGetForName( String name ) { return IndexReference.NO_INDEX; } - sharedOptimisticLock( index.schema().keyType(), index.schema().keyId() ); + acquireSharedSchemaLock( index.schema() ); return index; } @@ -457,7 +458,7 @@ public Iterator indexesGetAll() return Iterators.map( indexDescriptor -> { - sharedOptimisticLock( indexDescriptor.schema().keyType(), indexDescriptor.schema().keyId() ); + acquireSharedSchemaLock( indexDescriptor.schema() ); return indexDescriptor; }, iterator ); } @@ -466,7 +467,7 @@ public Iterator indexesGetAll() public InternalIndexState indexGetState( IndexReference index ) throws IndexNotFoundKernelException { assertValidIndex( index ); - sharedOptimisticLock( index.schema().keyType(), index.schema().keyId() ); + acquireSharedSchemaLock( index.schema() ); ktx.assertOpen(); return indexGetState( (IndexDescriptor) index ); } @@ -476,7 +477,7 @@ public PopulationProgress indexGetPopulationProgress( IndexReference index ) throws IndexNotFoundKernelException { assertValidIndex( index ); - sharedOptimisticLock( index.schema().keyType(), index.schema().keyId() ); + acquireSharedSchemaLock( index.schema() ); ktx.assertOpen(); if ( ktx.hasTxStateWithChanges() ) @@ -493,7 +494,7 @@ public PopulationProgress indexGetPopulationProgress( IndexReference index ) @Override public Long indexGetOwningUniquenessConstraintId( IndexReference index ) { - sharedOptimisticLock( index.schema().keyType(), index.schema().keyId() ); + acquireSharedSchemaLock( index.schema() ); ktx.assertOpen(); if ( index instanceof StoreIndexDescriptor ) { @@ -508,7 +509,7 @@ public Long indexGetOwningUniquenessConstraintId( IndexReference index ) @Override public long indexGetCommittedId( IndexReference index ) throws SchemaRuleNotFoundException { - sharedOptimisticLock( index.schema().keyType(), index.schema().keyId() ); + acquireSharedSchemaLock( index.schema() ); ktx.assertOpen(); if ( index instanceof StoreIndexDescriptor ) { @@ -532,7 +533,7 @@ public double indexUniqueValuesSelectivity( IndexReference index ) throws IndexN { assertValidIndex( index ); SchemaDescriptor schema = index.schema(); - sharedOptimisticLock( schema.keyType(), schema.keyId() ); + acquireSharedSchemaLock( schema ); ktx.assertOpen(); return storageReader.indexUniqueValuesPercentage( schema ); } @@ -542,7 +543,7 @@ public long indexSize( IndexReference index ) throws IndexNotFoundKernelExceptio { assertValidIndex( index ); SchemaDescriptor schema = index.schema(); - sharedOptimisticLock( schema.keyType(), schema.keyId() ); + acquireSharedSchemaLock( schema ); ktx.assertOpen(); return storageReader.indexSize( schema ); } @@ -631,7 +632,7 @@ IndexDescriptor indexGetForSchema( SchemaDescriptor descriptor ) { indexes = filter( SchemaDescriptor.equalTo( descriptor ), - ktx.txState().indexDiffSetsByLabel( descriptor.keyId() ).apply( indexes ) ); + ktx.txState().indexDiffSetsBySchema( descriptor ).apply( indexes ) ); } return singleOrNull( indexes ); } @@ -654,7 +655,7 @@ private boolean checkIndexState( IndexDescriptor index, ReadableDiffSets constraintsGetForSchema( SchemaDescriptor descriptor ) { - sharedOptimisticLock( descriptor.keyType(), descriptor.keyId() ); + acquireSharedSchemaLock( descriptor ); ktx.assertOpen(); Iterator constraints = storageReader.constraintsGetForSchema( descriptor ); if ( ktx.hasTxStateWithChanges() ) @@ -668,7 +669,7 @@ public Iterator constraintsGetForSchema( SchemaDescriptor public boolean constraintExists( ConstraintDescriptor descriptor ) { SchemaDescriptor schema = descriptor.schema(); - sharedOptimisticLock( schema.keyType(), schema.keyId() ); + acquireSharedSchemaLock( schema ); ktx.assertOpen(); boolean inStore = storageReader.constraintExists( descriptor ); if ( ktx.hasTxStateWithChanges() ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NodeSchemaMatcher.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NodeSchemaMatcher.java index 5251b5f0f325d..221d7ec8cf5d1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NodeSchemaMatcher.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NodeSchemaMatcher.java @@ -72,7 +72,7 @@ static { SUPPLIER schemaSupplier = schemaSuppliers.next(); SchemaDescriptor schema = schemaSupplier.schema(); - if ( node.hasLabel( schema.keyId() ) ) + if ( schema.isAffected( node.labels().all() ) ) { if ( nodePropertyIds == null ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java index 90af09db615ea..897cea4fb9871 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java @@ -35,6 +35,7 @@ import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.IndexReference; import org.neo4j.internal.kernel.api.Locks; +import org.neo4j.internal.kernel.api.NamedToken; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; import org.neo4j.internal.kernel.api.Procedures; import org.neo4j.internal.kernel.api.Read; @@ -85,6 +86,7 @@ import org.neo4j.kernel.impl.api.state.ConstraintIndexCreator; import org.neo4j.kernel.impl.constraints.ConstraintSemantics; import org.neo4j.kernel.impl.index.IndexEntityType; +import org.neo4j.kernel.impl.locking.LockTracer; import org.neo4j.kernel.impl.locking.ResourceTypes; import org.neo4j.storageengine.api.EntityType; import org.neo4j.storageengine.api.StorageReader; @@ -96,6 +98,8 @@ import static java.lang.Math.min; import static org.neo4j.internal.kernel.api.exceptions.schema.ConstraintValidationException.Phase.VALIDATION; import static org.neo4j.internal.kernel.api.exceptions.schema.SchemaKernelException.OperationContext.CONSTRAINT_CREATION; + +import static org.neo4j.internal.kernel.api.schema.SchemaDescriptor.schemaTokenLockingIds; import static org.neo4j.internal.kernel.api.schema.SchemaDescriptorPredicates.hasProperty; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_NODE; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_PROPERTY_KEY; @@ -886,7 +890,7 @@ public IndexReference indexCreate( SchemaDescriptor descriptor, Optional provider, Optional name ) throws SchemaKernelException { - exclusiveSchemaLock( descriptor.keyType(), descriptor.keyId() ); + exclusiveSchemaLock( descriptor ); ktx.assertOpen(); assertValidDescriptor( descriptor, SchemaKernelException.OperationContext.INDEX_CREATION ); assertIndexDoesNotExist( SchemaKernelException.OperationContext.INDEX_CREATION, descriptor, name ); @@ -916,7 +920,7 @@ public void indexDrop( IndexReference indexReference ) throws SchemaKernelExcept IndexDescriptor index = (IndexDescriptor) indexReference; SchemaDescriptor schema = index.schema(); - exclusiveSchemaLock( schema.keyType(), schema.keyId() ); + exclusiveSchemaLock( schema ); ktx.assertOpen(); try { @@ -954,7 +958,7 @@ public ConstraintDescriptor uniquePropertyConstraintCreate( SchemaDescriptor des throws SchemaKernelException { //Lock - exclusiveSchemaLock( descriptor.keyType(), descriptor.keyId() ); + exclusiveSchemaLock( descriptor ); ktx.assertOpen(); //Check data integrity @@ -979,7 +983,7 @@ public ConstraintDescriptor nodeKeyConstraintCreate( LabelSchemaDescriptor descr public ConstraintDescriptor nodeKeyConstraintCreate( LabelSchemaDescriptor descriptor, Optional provider ) throws SchemaKernelException { //Lock - exclusiveSchemaLock( ResourceTypes.LABEL, descriptor.getLabelId() ); + exclusiveSchemaLock( descriptor ); ktx.assertOpen(); //Check data integrity @@ -1006,7 +1010,7 @@ public ConstraintDescriptor nodePropertyExistenceConstraintCreate( LabelSchemaDe throws SchemaKernelException { //Lock - exclusiveSchemaLock( ResourceTypes.LABEL, descriptor.getLabelId() ); + exclusiveSchemaLock( descriptor ); ktx.assertOpen(); //verify data integrity @@ -1032,7 +1036,7 @@ public ConstraintDescriptor relationshipPropertyExistenceConstraintCreate( Relat throws SchemaKernelException { //Lock - exclusiveSchemaLock( ResourceTypes.RELATIONSHIP_TYPE, descriptor.getRelTypeId() ); + exclusiveSchemaLock( descriptor ); ktx.assertOpen(); //verify data integrity @@ -1148,9 +1152,46 @@ private void sharedSchemaLock( ResourceType type, int tokenId ) ktx.statementLocks().optimistic().acquireShared( ktx.lockTracer(), type, tokenId ); } - private void exclusiveSchemaLock( ResourceType type, int tokenId ) + private void exclusiveSchemaLock( SchemaDescriptor schema ) + { + long[] lockingIds = schemaTokenLockingIds( schema ); + ktx.statementLocks().optimistic().acquireExclusive( ktx.lockTracer(), schema.keyType(), lockingIds ); + + if ( SchemaDescriptor.isAnyEntityTokenSchema( schema ) ) + { + exclusiveAnyEntityTokenSchema( schema ); + } + } + + private void exclusiveAnyEntityTokenSchema( SchemaDescriptor schema ) { - ktx.statementLocks().optimistic().acquireExclusive( ktx.lockTracer(), type, tokenId ); + // After we get the exclusive token lock, no new tokens can be created. This allows us to grab a lock on all + // the existing tokens, and be sure that we won't miss any updates. + allStoreHolder.acquireExclusiveTokenLock(); + ResourceType resourceType; + long[] tokens; + Iterator itr; + if ( schema.entityType() == EntityType.NODE ) + { + resourceType = ResourceTypes.LABEL; + tokens = new long[token.labelCount()]; + itr = token.labelsGetAllTokens(); + } + else + { + resourceType = ResourceTypes.RELATIONSHIP_TYPE; + tokens = new long[token.relationshipTypeCount()]; + itr = token.relationshipTypesGetAllTokens(); + } + + int i = 0; + while ( itr.hasNext() ) + { + tokens[i] = itr.next().id(); + } + + LockTracer lockTracer = ktx.lockTracer(); + ktx.statementLocks().optimistic().acquireExclusive( lockTracer, resourceType, tokens ); } private void lockRelationshipNodes( long startNodeId, long endNodeId ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java index 68e697ee64f2a..7525ca63fd092 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java @@ -36,6 +36,7 @@ import org.neo4j.internal.kernel.api.Scan; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.explicitindex.ExplicitIndexNotFoundKernelException; +import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; import org.neo4j.internal.kernel.api.security.AccessMode; import org.neo4j.kernel.api.AssertOpen; import org.neo4j.kernel.api.ExplicitIndex; @@ -59,6 +60,7 @@ import org.neo4j.values.storable.Values; import static java.lang.String.format; +import static org.neo4j.internal.kernel.api.schema.SchemaDescriptor.schemaTokenLockingIds; import static org.neo4j.kernel.impl.locking.ResourceTypes.INDEX_ENTRY; import static org.neo4j.kernel.impl.locking.ResourceTypes.indexEntryResourceId; import static org.neo4j.values.storable.ValueGroup.GEOMETRY; @@ -577,6 +579,18 @@ public void acquireSharedLabelLock( long... ids ) ktx.assertOpen(); } + @Override + public void acquireSharedTokenLock() + { + acquireSharedLock( ResourceTypes.TOKEN_CREATE, 1 ); + } + + @Override + public void acquireExclusiveTokenLock() + { + acquireSharedLock( ResourceTypes.TOKEN_CREATE, 1 ); + } + @Override public void releaseSharedNodeLock( long... ids ) { @@ -605,6 +619,12 @@ public void releaseSharedLabelLock( long... ids ) ktx.assertOpen(); } + void acquireSharedSchemaLock( SchemaDescriptor schema ) + { + long[] lockingIds = schemaTokenLockingIds( schema ); + ktx.statementLocks().optimistic().acquireShared( ktx.lockTracer(), schema.keyType(), lockingIds ); + } + void sharedOptimisticLock( ResourceType resource, long resourceId ) { ktx.statementLocks().optimistic().acquireShared( ktx.lockTracer(), resource, resourceId );