Skip to content

Commit

Permalink
Cache the PropertyExistenceEnforcer
Browse files Browse the repository at this point in the history
Only create new instances of PropertyExistenceEnforcer when needed after the schema has changed.

In addition to that we now only apply the PropertyExistenceEnforcer if there are data changes.
  • Loading branch information
thobe committed Mar 24, 2017
1 parent 4be5579 commit 892a248
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 159 deletions.
Expand Up @@ -25,12 +25,13 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import org.neo4j.helpers.collection.Iterators;
import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates;
import org.neo4j.kernel.api.schema_new.constaints.ConstraintBoundary;
import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor;
import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor;
import org.neo4j.kernel.impl.constraints.ConstraintSemantics;
Expand All @@ -56,6 +57,8 @@ public class SchemaCache
private final Map<SchemaDescriptor, NewIndexDescriptor> indexDescriptors = new HashMap<>();
private final ConstraintSemantics constraintSemantics;

private final Map<Class<?>,Object> dependantState = new ConcurrentHashMap<>();

public SchemaCache( ConstraintSemantics constraintSemantics, Iterable<SchemaRule> initialRules )
{
this.constraintSemantics = constraintSemantics;
Expand Down Expand Up @@ -128,8 +131,14 @@ public Iterator<ConstraintDescriptor> constraintsForSchema( SchemaDescriptor des
return Iterators.filter( SchemaDescriptor.equalTo( descriptor ), constraints.iterator() );
}

public <P, T> T getOrCreateDependantState( Class<T> type, Function<P,T> factory, P parameter )
{
return type.cast( dependantState.computeIfAbsent( type, key -> factory.apply( parameter ) ) );
}

public void addSchemaRule( SchemaRule rule )
{
dependantState.clear();
if ( rule instanceof ConstraintRule )
{
ConstraintRule constraintRule = (ConstraintRule) rule;
Expand Down Expand Up @@ -163,6 +172,7 @@ public void load( List<SchemaRule> schemaRuleIterator )

public void removeSchemaRule( long id )
{
dependantState.clear();
if ( constraintRuleById.containsKey( id ) )
{
ConstraintRule rule = constraintRuleById.remove( id );
Expand Down
Expand Up @@ -20,6 +20,7 @@
package org.neo4j.kernel.impl.api.store;

import java.util.Iterator;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
Expand Down Expand Up @@ -610,6 +611,12 @@ public int degreeRelationshipsInGroup( StorageStatement storeStatement, long nod
relationshipGroupRecord, storeStatement.recordCursors() );
}

@Override
public <T> T getOrCreateSchemaDependantState( Class<T> type, Function<StoreReadLayer,T> factory )
{
return schemaCache.getOrCreateDependantState( type, factory, this );
}

private void visitNode( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor )
{
try ( Cursor<RelationshipItem> relationships = nodeGetRelationships( statement, nodeItem, Direction.BOTH ) )
Expand Down
Expand Up @@ -322,7 +322,7 @@ public void createCommands(
txStateVisitor, storeLayer, storageStatement, txState, countsRecordState );
try ( TxStateVisitor visitor = txStateVisitor )
{
txState.accept( txStateVisitor );
txState.accept( visitor );
}

// Convert record state into commands
Expand Down
Expand Up @@ -20,6 +20,7 @@
package org.neo4j.storageengine.api;

import java.util.Iterator;
import java.util.function.Function;
import java.util.function.IntPredicate;

import org.neo4j.collection.primitive.PrimitiveIntIterator;
Expand Down Expand Up @@ -384,4 +385,6 @@ DoubleLongRegister indexSample( LabelSchemaDescriptor descriptor, DoubleLongRegi

int degreeRelationshipsInGroup( StorageStatement storeStatement, long id, long groupId, Direction direction,
Integer relType );

<T> T getOrCreateSchemaDependantState( Class<T> type, Function<StoreReadLayer, T> factory );
}
Expand Up @@ -19,9 +19,7 @@
*/
package org.neo4j.kernel.impl.enterprise;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiPredicate;

import org.neo4j.cursor.Cursor;
Expand All @@ -31,7 +29,6 @@
import org.neo4j.kernel.api.exceptions.schema.RelationshipPropertyExistenceException;
import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaProcessor;
import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor;
import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor;
import org.neo4j.kernel.impl.constraints.StandardConstraintSemantics;
Expand All @@ -43,6 +40,7 @@
import org.neo4j.storageengine.api.txstate.TxStateVisitor;

import static org.neo4j.kernel.api.exceptions.schema.ConstraintValidationException.Phase.VERIFICATION;
import static org.neo4j.kernel.impl.enterprise.PropertyExistenceEnforcer.getOrCreatePropertyExistenceEnforcerFrom;

public class EnterpriseConstraintSemantics extends StandardConstraintSemantics
{
Expand Down Expand Up @@ -135,48 +133,16 @@ private CreateConstraintFailureException createConstraintFailure( ConstraintVali
public TxStateVisitor decorateTxStateVisitor( StoreReadLayer storeLayer, ReadableTransactionState txState,
TxStateVisitor visitor )
{
ExistenceConstraintCollector collector = new ExistenceConstraintCollector();
Iterator<ConstraintDescriptor> constraints = storeLayer.constraintsGetAll();
while ( constraints.hasNext() )
if ( !txState.hasDataChanges() )
{
collector.addIfRelevant( constraints.next() );
}

if ( collector.hasExistenceConstraints() )
{
return new PropertyExistenceEnforcer( visitor, txState, storeLayer, collector.labelExists, collector.relTypeExists );
}
return visitor;
}

private static class ExistenceConstraintCollector implements SchemaProcessor
{
final List<LabelSchemaDescriptor> labelExists = new ArrayList<>();
final List<RelationTypeSchemaDescriptor> relTypeExists = new ArrayList<>();

void addIfRelevant( ConstraintDescriptor constraint )
{
if ( constraint.enforcesPropertyExistence() )
{
constraint.schema().processWith( this );
}
}

@Override
public void processSpecific( LabelSchemaDescriptor schema )
{
labelExists.add( schema );
}

@Override
public void processSpecific( RelationTypeSchemaDescriptor schema )
{
relTypeExists.add( schema );
}

boolean hasExistenceConstraints()
{
return labelExists.size() > 0 || relTypeExists.size() > 0;
// If there are no data changes, there is no need to enforce constraints. Since there is no need to
// enforce constraints, there is no need to build up the state required to be able to enforce constraints.
// In fact, it might even be counter productive to build up that state, since if there are no data changes
// there would be schema changes instead, and in that case we would throw away the schema-dependant state
// we just built when the schema changing transaction commits.
return visitor;
}
return getOrCreatePropertyExistenceEnforcerFrom( storeLayer )
.decorate( visitor, txState, storeLayer );
}
}

0 comments on commit 892a248

Please sign in to comment.