diff --git a/enterprise/ha/src/test/java/org/neo4j/kernel/api/ConstraintHaIT.java b/enterprise/ha/src/test/java/org/neo4j/kernel/api/ConstraintHaIT.java index a016727ae3991..24c3715d82fdf 100644 --- a/enterprise/ha/src/test/java/org/neo4j/kernel/api/ConstraintHaIT.java +++ b/enterprise/ha/src/test/java/org/neo4j/kernel/api/ConstraintHaIT.java @@ -19,7 +19,7 @@ */ package org.neo4j.kernel.api; -import org.junit.Rule; +import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -28,6 +28,8 @@ import java.io.File; import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.DynamicLabel; +import org.neo4j.graphdb.DynamicRelationshipType; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.QueryExecutionException; @@ -36,6 +38,7 @@ import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.schema.ConstraintDefinition; import org.neo4j.graphdb.schema.ConstraintType; +import org.neo4j.graphdb.schema.IndexDefinition; import org.neo4j.helpers.Exceptions; import org.neo4j.kernel.TopLevelTransaction; import org.neo4j.kernel.api.ConstraintHaIT.NodePropertyExistenceConstraintHaIT; @@ -55,6 +58,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -64,6 +68,7 @@ import static org.neo4j.helpers.collection.Iterables.count; import static org.neo4j.helpers.collection.Iterables.single; import static org.neo4j.helpers.collection.Iterables.toList; +import static org.neo4j.helpers.collection.IteratorUtil.singleOrNull; import static org.neo4j.io.fs.FileUtils.deleteRecursively; @RunWith( Suite.class ) @@ -74,6 +79,10 @@ } ) public class ConstraintHaIT { + @ClassRule + public static ClusterRule clusterRule = new ClusterRule( AbstractConstraintHaIT.class ) + .withSharedSetting( HaSettings.read_timeout, "4000s" ); + public static class NodePropertyExistenceConstraintHaIT extends AbstractConstraintHaIT { @Override @@ -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 ) ); } + @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 protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, String value ) @@ -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 ) ); } + @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 protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, String value ) { @@ -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 ) ); } + @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 protected void createEntityInTx( GraphDatabaseService db, String type, String propertyKey, String value ) @@ -223,11 +268,30 @@ public abstract static class AbstractConstraintHaIT private static final String TYPE = "Type"; private static final String PROPERTY_KEY = "name"; - @Rule - public ClusterRule clusterRule = new ClusterRule( getClass() ); + // These type/key methods are due to the ClusterRule being a ClassRule so that one cluster + // 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 ); + /** + * @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, String value ); @@ -245,11 +309,13 @@ public void shouldCreateConstraintOnMaster() throws Exception // given ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); HighlyAvailableGraphDatabase master = cluster.getMaster(); + String type = type( 0 ); + String key = key( 0 ); // when try ( Transaction tx = master.beginTx() ) { - createConstraint( master, TYPE, PROPERTY_KEY ); + createConstraint( master, type, key ); tx.success(); } @@ -260,9 +326,9 @@ public void shouldCreateConstraintOnMaster() throws Exception { try ( Transaction tx = clusterMember.beginTx() ) { - ConstraintDefinition constraint = single( clusterMember.schema().getConstraints() ); - validateLabelOrRelationshipType( constraint ); - assertEquals( PROPERTY_KEY, single( constraint.getPropertyKeys() ) ); + ConstraintDefinition constraint = getConstraint( clusterMember, type, key ); + validateLabelOrRelationshipType( constraint, type ); + assertEquals( key, single( constraint.getPropertyKeys() ) ); tx.success(); } } @@ -274,11 +340,13 @@ public void shouldNotBePossibleToCreateConstraintsDirectlyOnSlaves() throws Exce // given ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); HighlyAvailableGraphDatabase slave = cluster.getAnySlave(); + String type = type( 1 ); + String key = key( 1 ); // when 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." ); } catch ( QueryExecutionException e ) @@ -293,21 +361,26 @@ public void shouldRemoveConstraints() throws Exception // given ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); HighlyAvailableGraphDatabase master = cluster.getMaster(); + String type = type( 2 ); + String key = key( 2 ); + long constraintCountBefore, indexCountBefore; 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(); } cluster.sync(); // and given I have some data for the constraint - createEntityInTx( cluster.getAnySlave(), TYPE, PROPERTY_KEY, "Foo" ); + createEntityInTx( cluster.getAnySlave(), type, key, "Foo" ); // when try ( Transaction tx = master.beginTx() ) { - single( master.schema().getConstraints() ).drop(); + getConstraint( master, type, key ).drop(); tx.success(); } cluster.sync(); @@ -317,9 +390,9 @@ public void shouldRemoveConstraints() throws Exception { try ( Transaction tx = clusterMember.beginTx() ) { - assertEquals( count( clusterMember.schema().getConstraints() ), 0 ); - assertEquals( count( clusterMember.schema().getIndexes() ), 0 ); - createConstraintViolation( clusterMember, TYPE, PROPERTY_KEY, "Foo" ); + assertNull( getConstraint( clusterMember, type, key ) ); + assertNull( getIndex( clusterMember, type, key ) ); + createConstraintViolation( clusterMember, type, key, "Foo" ); tx.success(); } } @@ -333,22 +406,24 @@ public void shouldNotAllowOldUncommittedTransactionsToResumeAndViolateConstraint clusterRule.withSharedSetting( HaSettings.read_timeout, "4000s" ).startCluster(); HighlyAvailableGraphDatabase slave = cluster.getAnySlave(); HighlyAvailableGraphDatabase master = cluster.getMaster(); + String type = type( 3 ); + String key = key( 3 ); ThreadToStatementContextBridge txBridge = threadToStatementContextBridge( slave ); // 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 slave.beginTx(); - createConstraintViolation( slave, TYPE, PROPERTY_KEY, "Foo" ); + createConstraintViolation( slave, type, key, "Foo" ); TopLevelTransaction slaveTx = txBridge.getTopLevelTransactionBoundToThisThread( true ); txBridge.unbindTransactionFromCurrentThread(); // When I create a constraint try ( Transaction tx = master.beginTx() ) { - createConstraint( master, TYPE, PROPERTY_KEY ); + createConstraint( master, type, key ); tx.success(); } @@ -369,13 +444,13 @@ public void shouldNotAllowOldUncommittedTransactionsToResumeAndViolateConstraint } // And then both master and slave should keep working, accepting reads - assertConstraintHolds( master, TYPE, PROPERTY_KEY, "Foo" ); + assertConstraintHolds( master, type, key, "Foo" ); 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 - createEntityInTx( slave, TYPE, PROPERTY_KEY, "Bar" ); - createEntityInTx( master, TYPE, PROPERTY_KEY, "Baz" ); + createEntityInTx( slave, type, key, "Bar" ); + createEntityInTx( master, type, key, "Baz" ); } @Test @@ -383,6 +458,8 @@ public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnli { // Given ClusterManager.ManagedCluster cluster = clusterRule.startCluster(); + String type = type( 4 ); + String key = key( 4 ); HighlyAvailableGraphDatabase master = cluster.getMaster(); @@ -395,7 +472,7 @@ public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnli try ( Transaction tx = master.beginTx() ) { - createConstraint( master, TYPE, PROPERTY_KEY ); + createConstraint( master, type, key ); tx.success(); } @@ -405,10 +482,10 @@ public void newSlaveJoiningClusterShouldNotAcceptOperationsUntilConstraintIsOnli // Then try ( Transaction ignored = slave.beginTx() ) { - ConstraintDefinition definition = single( slave.schema().getConstraints() ); + ConstraintDefinition definition = getConstraint( slave, type, key ); assertThat( definition, instanceOf( constraintDefinitionClass() ) ); - assertThat( single( definition.getPropertyKeys() ), equalTo( PROPERTY_KEY ) ); - validateLabelOrRelationshipType( definition ); + assertThat( single( definition.getPropertyKeys() ), equalTo( key ) ); + validateLabelOrRelationshipType( definition, type ); } } @@ -418,15 +495,15 @@ private static ThreadToStatementContextBridge threadToStatementContextBridge( Hi 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 ) ) { - assertEquals( TYPE, constraint.getRelationshipType().name() ); + assertEquals( type, constraint.getRelationshipType().name() ); } else { - assertEquals( TYPE, constraint.getLabel().name() ); + assertEquals( type, constraint.getLabel().name() ); } } }