Skip to content

Commit

Permalink
Faster ConstraintHaIT
Browse files Browse the repository at this point in the history
  • Loading branch information
tinwelint committed Dec 16, 2015
1 parent 8b81436 commit 7f55f32
Showing 1 changed file with 105 additions and 28 deletions.
133 changes: 105 additions & 28 deletions enterprise/ha/src/test/java/org/neo4j/kernel/api/ConstraintHaIT.java
Expand Up @@ -19,7 +19,7 @@
*/ */
package org.neo4j.kernel.api; package org.neo4j.kernel.api;


import org.junit.Rule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
Expand All @@ -28,6 +28,8 @@
import java.io.File; import java.io.File;


import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.DependencyResolver;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.QueryExecutionException;
Expand All @@ -36,6 +38,7 @@
import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.ConstraintDefinition; import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.ConstraintType; import org.neo4j.graphdb.schema.ConstraintType;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.helpers.Exceptions; import org.neo4j.helpers.Exceptions;
import org.neo4j.kernel.TopLevelTransaction; import org.neo4j.kernel.TopLevelTransaction;
import org.neo4j.kernel.api.ConstraintHaIT.NodePropertyExistenceConstraintHaIT; import org.neo4j.kernel.api.ConstraintHaIT.NodePropertyExistenceConstraintHaIT;
Expand All @@ -55,6 +58,7 @@
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
Expand All @@ -64,6 +68,7 @@
import static org.neo4j.helpers.collection.Iterables.count; import static org.neo4j.helpers.collection.Iterables.count;
import static org.neo4j.helpers.collection.Iterables.single; import static org.neo4j.helpers.collection.Iterables.single;
import static org.neo4j.helpers.collection.Iterables.toList; import static org.neo4j.helpers.collection.Iterables.toList;
import static org.neo4j.helpers.collection.IteratorUtil.singleOrNull;
import static org.neo4j.io.fs.FileUtils.deleteRecursively; import static org.neo4j.io.fs.FileUtils.deleteRecursively;


@RunWith( Suite.class ) @RunWith( Suite.class )
Expand All @@ -74,6 +79,10 @@
} ) } )
public class ConstraintHaIT public class ConstraintHaIT
{ {
@ClassRule
public static ClusterRule clusterRule = new ClusterRule( AbstractConstraintHaIT.class )
.withSharedSetting( HaSettings.read_timeout, "4000s" );

public static class NodePropertyExistenceConstraintHaIT extends AbstractConstraintHaIT public static class NodePropertyExistenceConstraintHaIT extends AbstractConstraintHaIT
{ {
@Override @Override
Expand All @@ -82,6 +91,18 @@ protected void createConstraint( GraphDatabaseService db, String type, String va
db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT exists(n.`%s`)", type, value ) ); db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT exists(n.`%s`)", type, value ) );
} }


@Override
protected ConstraintDefinition getConstraint( GraphDatabaseService db, String type, String value )
{
return singleOrNull( db.schema().getConstraints( DynamicLabel.label( type ) ) );
}

@Override
protected IndexDefinition getIndex( GraphDatabaseService db, String type, String value )
{
return null;
}

@Override @Override
protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey,
String value ) String value )
Expand Down Expand Up @@ -130,6 +151,18 @@ protected void createConstraint( GraphDatabaseService db, String type, String va
db.execute( String.format( "CREATE CONSTRAINT ON ()-[r:`%s`]-() ASSERT exists(r.`%s`)", type, value ) ); db.execute( String.format( "CREATE CONSTRAINT ON ()-[r:`%s`]-() ASSERT exists(r.`%s`)", type, value ) );
} }


@Override
protected ConstraintDefinition getConstraint( GraphDatabaseService db, String type, String value )
{
return singleOrNull( db.schema().getConstraints( DynamicRelationshipType.withName( type ) ) );
}

@Override
protected IndexDefinition getIndex( GraphDatabaseService db, String type, String value )
{
return null;
}

@Override @Override
protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, String value ) protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, String value )
{ {
Expand Down Expand Up @@ -183,6 +216,18 @@ protected void createConstraint( GraphDatabaseService db, String type, String va
db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT n.`%s` IS UNIQUE", type, value ) ); db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT n.`%s` IS UNIQUE", type, value ) );
} }


@Override
protected ConstraintDefinition getConstraint( GraphDatabaseService db, String type, String value )
{
return singleOrNull( db.schema().getConstraints( DynamicLabel.label( type ) ) );
}

@Override
protected IndexDefinition getIndex( GraphDatabaseService db, String type, String value )
{
return singleOrNull( db.schema().getIndexes( DynamicLabel.label( type ) ) );
}

@Override @Override
protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey,
String value ) String value )
Expand Down Expand Up @@ -223,11 +268,30 @@ public abstract static class AbstractConstraintHaIT
private static final String TYPE = "Type"; private static final String TYPE = "Type";
private static final String PROPERTY_KEY = "name"; private static final String PROPERTY_KEY = "name";


@Rule // These type/key methods are due to the ClusterRule being a ClassRule so that one cluster
public ClusterRule clusterRule = new ClusterRule( getClass() ); // is used for all the tests, and so they need to have each their own constraint
protected String type( int id )
{
return TYPE + "_" + getClass().getSimpleName() + "_" + id;
}

protected String key( int id )
{
return PROPERTY_KEY + "_" + getClass().getSimpleName() + "_" + id;
}


protected abstract void createConstraint( GraphDatabaseService db, String type, String value ); protected abstract void createConstraint( GraphDatabaseService db, String type, String value );


/**
* @return {@code null} if it has been dropped.
*/
protected abstract ConstraintDefinition getConstraint( GraphDatabaseService db, String type, String value );

/**
* @return {@code null} if it has been dropped.
*/
protected abstract IndexDefinition getIndex( GraphDatabaseService db, String type, String value );

protected abstract void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, protected abstract void createEntityInTx( GraphDatabaseService db, String type, String propertyKey,
String value ); String value );


Expand All @@ -245,11 +309,13 @@ public void shouldCreateConstraintOnMaster() throws Exception
// given // given
ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); ClusterManager.ManagedCluster cluster = clusterRule.startCluster();
HighlyAvailableGraphDatabase master = cluster.getMaster(); HighlyAvailableGraphDatabase master = cluster.getMaster();
String type = type( 0 );
String key = key( 0 );


// when // when
try ( Transaction tx = master.beginTx() ) try ( Transaction tx = master.beginTx() )
{ {
createConstraint( master, TYPE, PROPERTY_KEY ); createConstraint( master, type, key );
tx.success(); tx.success();
} }


Expand All @@ -260,9 +326,9 @@ public void shouldCreateConstraintOnMaster() throws Exception
{ {
try ( Transaction tx = clusterMember.beginTx() ) try ( Transaction tx = clusterMember.beginTx() )
{ {
ConstraintDefinition constraint = single( clusterMember.schema().getConstraints() ); ConstraintDefinition constraint = getConstraint( clusterMember, type, key );
validateLabelOrRelationshipType( constraint ); validateLabelOrRelationshipType( constraint, type );
assertEquals( PROPERTY_KEY, single( constraint.getPropertyKeys() ) ); assertEquals( key, single( constraint.getPropertyKeys() ) );
tx.success(); tx.success();
} }
} }
Expand All @@ -274,11 +340,13 @@ public void shouldNotBePossibleToCreateConstraintsDirectlyOnSlaves() throws Exce
// given // given
ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); ClusterManager.ManagedCluster cluster = clusterRule.startCluster();
HighlyAvailableGraphDatabase slave = cluster.getAnySlave(); HighlyAvailableGraphDatabase slave = cluster.getAnySlave();
String type = type( 1 );
String key = key( 1 );


// when // when
try ( Transaction ignored = slave.beginTx() ) try ( Transaction ignored = slave.beginTx() )
{ {
createConstraint( slave, TYPE, PROPERTY_KEY ); createConstraint( slave, type, key );
fail( "We expected to not be able to create a constraint on a slave in a cluster." ); fail( "We expected to not be able to create a constraint on a slave in a cluster." );
} }
catch ( QueryExecutionException e ) catch ( QueryExecutionException e )
Expand All @@ -293,21 +361,26 @@ public void shouldRemoveConstraints() throws Exception
// given // given
ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); ClusterManager.ManagedCluster cluster = clusterRule.startCluster();
HighlyAvailableGraphDatabase master = cluster.getMaster(); HighlyAvailableGraphDatabase master = cluster.getMaster();
String type = type( 2 );
String key = key( 2 );


long constraintCountBefore, indexCountBefore;
try ( Transaction tx = master.beginTx() ) try ( Transaction tx = master.beginTx() )
{ {
createConstraint( master, TYPE, PROPERTY_KEY ); constraintCountBefore = count( master.schema().getConstraints() );
indexCountBefore = count( master.schema().getIndexes() );
createConstraint( master, type, key );
tx.success(); tx.success();
} }
cluster.sync(); cluster.sync();


// and given I have some data for the constraint // and given I have some data for the constraint
createEntityInTx( cluster.getAnySlave(), TYPE, PROPERTY_KEY, "Foo" ); createEntityInTx( cluster.getAnySlave(), type, key, "Foo" );


// when // when
try ( Transaction tx = master.beginTx() ) try ( Transaction tx = master.beginTx() )
{ {
single( master.schema().getConstraints() ).drop(); getConstraint( master, type, key ).drop();
tx.success(); tx.success();
} }
cluster.sync(); cluster.sync();
Expand All @@ -317,9 +390,9 @@ public void shouldRemoveConstraints() throws Exception
{ {
try ( Transaction tx = clusterMember.beginTx() ) try ( Transaction tx = clusterMember.beginTx() )
{ {
assertEquals( count( clusterMember.schema().getConstraints() ), 0 ); assertNull( getConstraint( clusterMember, type, key ) );
assertEquals( count( clusterMember.schema().getIndexes() ), 0 ); assertNull( getIndex( clusterMember, type, key ) );
createConstraintViolation( clusterMember, TYPE, PROPERTY_KEY, "Foo" ); createConstraintViolation( clusterMember, type, key, "Foo" );
tx.success(); tx.success();
} }
} }
Expand All @@ -333,22 +406,24 @@ public void shouldNotAllowOldUncommittedTransactionsToResumeAndViolateConstraint
clusterRule.withSharedSetting( HaSettings.read_timeout, "4000s" ).startCluster(); clusterRule.withSharedSetting( HaSettings.read_timeout, "4000s" ).startCluster();
HighlyAvailableGraphDatabase slave = cluster.getAnySlave(); HighlyAvailableGraphDatabase slave = cluster.getAnySlave();
HighlyAvailableGraphDatabase master = cluster.getMaster(); HighlyAvailableGraphDatabase master = cluster.getMaster();
String type = type( 3 );
String key = key( 3 );


ThreadToStatementContextBridge txBridge = threadToStatementContextBridge( slave ); ThreadToStatementContextBridge txBridge = threadToStatementContextBridge( slave );


// And given there is an entity with property // And given there is an entity with property
createEntityInTx( master, TYPE, PROPERTY_KEY, "Foo" ); createEntityInTx( master, type, key, "Foo" );


// And given that I begin a transaction that will create a constraint violation // And given that I begin a transaction that will create a constraint violation
slave.beginTx(); slave.beginTx();
createConstraintViolation( slave, TYPE, PROPERTY_KEY, "Foo" ); createConstraintViolation( slave, type, key, "Foo" );
TopLevelTransaction slaveTx = txBridge.getTopLevelTransactionBoundToThisThread( true ); TopLevelTransaction slaveTx = txBridge.getTopLevelTransactionBoundToThisThread( true );
txBridge.unbindTransactionFromCurrentThread(); txBridge.unbindTransactionFromCurrentThread();


// When I create a constraint // When I create a constraint
try ( Transaction tx = master.beginTx() ) try ( Transaction tx = master.beginTx() )
{ {
createConstraint( master, TYPE, PROPERTY_KEY ); createConstraint( master, type, key );
tx.success(); tx.success();
} }


Expand All @@ -369,20 +444,22 @@ public void shouldNotAllowOldUncommittedTransactionsToResumeAndViolateConstraint
} }


// And then both master and slave should keep working, accepting reads // And then both master and slave should keep working, accepting reads
assertConstraintHolds( master, TYPE, PROPERTY_KEY, "Foo" ); assertConstraintHolds( master, type, key, "Foo" );
cluster.sync(); cluster.sync();
assertConstraintHolds( slave, TYPE, PROPERTY_KEY, "Foo" ); assertConstraintHolds( slave, type, key, "Foo" );


// And then I should be able to perform new write transactions, on both master and slave // And then I should be able to perform new write transactions, on both master and slave
createEntityInTx( slave, TYPE, PROPERTY_KEY, "Bar" ); createEntityInTx( slave, type, key, "Bar" );
createEntityInTx( master, TYPE, PROPERTY_KEY, "Baz" ); createEntityInTx( master, type, key, "Baz" );
} }


@Test @Test
public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnline() throws Throwable public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnline() throws Throwable
{ {
// Given // Given
ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); ClusterManager.ManagedCluster cluster = clusterRule.startCluster();
String type = type( 4 );
String key = key( 4 );


HighlyAvailableGraphDatabase master = cluster.getMaster(); HighlyAvailableGraphDatabase master = cluster.getMaster();


Expand All @@ -395,7 +472,7 @@ public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnli


try ( Transaction tx = master.beginTx() ) try ( Transaction tx = master.beginTx() )
{ {
createConstraint( master, TYPE, PROPERTY_KEY ); createConstraint( master, type, key );
tx.success(); tx.success();
} }


Expand All @@ -405,10 +482,10 @@ public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnli
// Then // Then
try ( Transaction ignored = slave.beginTx() ) try ( Transaction ignored = slave.beginTx() )
{ {
ConstraintDefinition definition = single( slave.schema().getConstraints() ); ConstraintDefinition definition = getConstraint( slave, type, key );
assertThat( definition, instanceOf( constraintDefinitionClass() ) ); assertThat( definition, instanceOf( constraintDefinitionClass() ) );
assertThat( single( definition.getPropertyKeys() ), equalTo( PROPERTY_KEY ) ); assertThat( single( definition.getPropertyKeys() ), equalTo( key ) );
validateLabelOrRelationshipType( definition ); validateLabelOrRelationshipType( definition, type );
} }
} }


Expand All @@ -418,15 +495,15 @@ private static ThreadToStatementContextBridge threadToStatementContextBridge( Hi
return dependencyResolver.resolveDependency( ThreadToStatementContextBridge.class ); return dependencyResolver.resolveDependency( ThreadToStatementContextBridge.class );
} }


private static void validateLabelOrRelationshipType( ConstraintDefinition constraint ) private static void validateLabelOrRelationshipType( ConstraintDefinition constraint, String type )
{ {
if ( constraint.isConstraintType( ConstraintType.RELATIONSHIP_PROPERTY_EXISTENCE ) ) if ( constraint.isConstraintType( ConstraintType.RELATIONSHIP_PROPERTY_EXISTENCE ) )
{ {
assertEquals( TYPE, constraint.getRelationshipType().name() ); assertEquals( type, constraint.getRelationshipType().name() );
} }
else else
{ {
assertEquals( TYPE, constraint.getLabel().name() ); assertEquals( type, constraint.getLabel().name() );
} }
} }
} }
Expand Down

0 comments on commit 7f55f32

Please sign in to comment.