/** * Starts the race and returns without waiting for contestants to complete. * Any exception thrown by contestant will be lost. */ public void goAsync() throws Throwable { asyncExecution = true; go( 0, TimeUnit.MILLISECONDS ); }
/** * Starts the race and waits indefinitely for all contestants to either fail or succeed. * * @throws Throwable on any exception thrown from any contestant. */ public void go() throws Throwable { go( 0, TimeUnit.MILLISECONDS ); }
@Test void shouldWaitForAllContestantsToComplete() throws Throwable { // GIVEN Race race = new Race(); final AtomicInteger completed = new AtomicInteger(); int count = 5; race.addContestants( count, throwing( () -> { sleep( current().nextInt( 100 ) ); completed.incrementAndGet(); } ) ); // WHEN race.go(); // THEN assertEquals( count, completed.get() ); }
private void raceEncode( String[] INPUT, Function<Value, String> encodeFunction ) throws Throwable { Race race = new Race(); for ( String input : INPUT ) { final Value inputValue = Values.of( new String[]{input} ); race.addContestant( () -> { String first = encodeFunction.apply( inputValue ); for ( int i = 0; i < 1000; i++ ) { String encoded = encodeFunction.apply( inputValue ); assertEquals( "Each attempt at encoding should yield the same result. Turns out that first one was '" + first + "', yet another one was '" + encoded + "'", first, encoded ); } } ); } race.go(); }
private void tryOnce( final GraphDatabaseAPI db, final Node node ) throws Throwable { Race race = new Race().withRandomStartDelays(); race.addContestants( Runtime.getRuntime().availableProcessors(), () -> { try ( Transaction ignored = db.beginTx() ) { assertEquals( relCount, count( node.getRelationships() ) ); } } ); race.go(); }
@Test void shouldConsultEndCondition() throws Throwable { // GIVEN CallCountBooleanSupplier endCondition = new CallCountBooleanSupplier( 100 ); Race race = new Race().withEndCondition( endCondition ); race.addContestants( 20, throwing( () -> sleep( 10 ) ) ); // WHEN race.go(); // THEN assertTrue( endCondition.callCount.get() >= 100 ); }
@Test public void shouldKeepHighestDuringConcurrentOfferings() throws Throwable { // GIVEN final HighestTransactionId highest = new HighestTransactionId( -1, -1, -1 ); Race race = new Race(); int updaters = max( 2, getRuntime().availableProcessors() ); final AtomicInteger accepted = new AtomicInteger(); for ( int i = 0; i < updaters; i++ ) { final long id = i + 1; race.addContestant( () -> { if ( highest.offer( id, id, id ) ) { accepted.incrementAndGet(); } } ); } // WHEN race.go(); // THEN assertTrue( accepted.get() > 0 ); assertEquals( updaters, highest.get().transactionId() ); }
@Test public void incrementAndGetVersionMustBeAtomic() throws Throwable { try ( MetaDataStore store = newMetaDataStore() ) { long initialVersion = store.incrementAndGetVersion(); int threads = Runtime.getRuntime().availableProcessors(); int iterations = 500; Race race = new Race(); race.addContestants( threads, () -> { for ( int i = 0; i < iterations; i++ ) { store.incrementAndGetVersion(); } } ); race.go(); assertThat( store.incrementAndGetVersion(), is( initialVersion + (threads * iterations) + 1 ) ); } }
@Test void shouldBreakOnError() throws Throwable { // GIVEN String error = "Noooo"; Race race = new Race(); race.withEndCondition( () -> false ); // <-- never end race.addContestant( () -> { throw new RuntimeException( error ); } ); race.addContestants( 3, () -> { } ); // WHEN Exception exception = assertThrows( Exception.class, () -> race.go() ); assertEquals( error, exception.getMessage() ); }
@Test void shouldHaveMultipleEndConditions() throws Throwable { // GIVEN ControlledBooleanSupplier endCondition1 = spy( new ControlledBooleanSupplier( false ) ); ControlledBooleanSupplier endCondition2 = spy( new ControlledBooleanSupplier( false ) ); ControlledBooleanSupplier endCondition3 = spy( new ControlledBooleanSupplier( false ) ); Race race = new Race().withEndCondition( endCondition1, endCondition2, endCondition3 ); race.addContestant( () -> endCondition2.set( true ) ); race.addContestants( 3, Runnables.EMPTY_RUNNABLE ); // WHEN race.go(); // THEN verify( endCondition1, atLeast( 4 ) ).getAsBoolean(); verify( endCondition2, atLeast( 4 ) ).getAsBoolean(); }
private void doRace( BiFunction<NumberArray,Integer,Runnable> contestantCreator ) throws Throwable { PageCache pageCache = pageCacheRule.getPageCache( fs ); PagedFile file = pageCache.map( dir.file( "file" ), pageCache.pageSize(), CREATE, DELETE_ON_CLOSE ); Race race = new Race(); try ( NumberArray array = getNumberArray( file ) ) { for ( int i = 0; i < CONTESTANTS; i++ ) { race.addContestant( contestantCreator.apply( array, i ) ); } race.go(); } }
@Test public void shouldHandleConcurrentGetOrCreate() throws Throwable { // GIVEN Groups groups = new Groups(); Race race = new Race(); String name = "MyGroup"; for ( int i = 0; i < Runtime.getRuntime().availableProcessors(); i++ ) { race.addContestant( () -> { Group group = groups.getOrCreate( name ); assertEquals( LOWEST_NONGLOBAL_ID, group.id() ); } ); } // WHEN race.go(); // THEN Group otherGroup = groups.getOrCreate( "MyOtherGroup" ); assertEquals( LOWEST_NONGLOBAL_ID + 1, otherGroup.id() ); }
@Test public void shouldSupportConcurrentGet() throws Throwable { // GIVEN int highLabelId = 10; int numberOfNodes = 100; int[][] expectedLabels = new int[numberOfNodes][]; NodeLabelsCache cache = new NodeLabelsCache( NumberArrayFactory.AUTO_WITHOUT_PAGECACHE, highLabelId ); for ( int i = 0; i < numberOfNodes; i++ ) { cache.put( i, asLongArray( expectedLabels[i] = randomLabels( random.nextInt( 5 ), highLabelId ) ) ); } // WHEN Race getRace = new Race(); for ( int i = 0; i < 10; i++ ) { getRace.addContestant( new LabelGetter( cache, expectedLabels, numberOfNodes ) ); } // THEN expected labels should be had (asserted in LabelGetter), and no exceptions (propagated by go()) getRace.go(); }
@Test public void shouldHandleMultipleConcurrentStoreCopyRequests() throws Throwable { // GIVEN Race race = new Race(); CountingAction action = new CountingAction(); int threads = Runtime.getRuntime().availableProcessors() * 10; race.addContestants( threads, throwing( () -> { parkARandomWhile(); try ( Resource lock = mutex.storeCopy( action ) ) { parkARandomWhile(); } } ) ); race.go(); // THEN // It's hard to make predictions about what should have been seen. Most importantly is that // The lock doesn't hang any requests and that number of calls to the action less than number of threads assertThat( action.count(), lessThan( threads ) ); }
@Test public void concurrentCreatingOfIndexesShouldNotInterfere() throws Throwable { // WHEN concurrently creating indexes for different labels Race race = new Race(); for ( int i = 0; i < threads; i++ ) { race.addContestant( indexCreate( i ), 1 ); } race.go(); // THEN they should all be observed as existing in the end try ( Transaction tx = db.beginTx() ) { List<IndexDefinition> indexes = asList( db.schema().getIndexes() ); assertEquals( threads, indexes.size() ); Set<String> labels = new HashSet<>(); for ( IndexDefinition index : indexes ) { assertTrue( labels.add( single( index.getLabels() ).name() ) ); } tx.success(); } }
@Repeat( times = 10 ) @Test public void shouldCopeWithConcurrentIncrementOfProcessorsAndShutdown() throws Throwable { // GIVEN TaskExecutor<Void> executor = new DynamicTaskExecutor<>( 1, 2, 2, PARK, "test" ); Race race = new Race().withRandomStartDelays(); race.addContestant( executor::close ); race.addContestant( () -> executor.processors( 1 ) ); // WHEN race.go( 10, SECONDS ); // THEN we should be able to do so, there was a recent fix here and before that fix // shutdown() would hang, that's why we wait for 10 seconds here to cap it if there's an issue. }
@Test public void concurrentDroppingOfIndexesShouldNotInterfere() throws Throwable { // GIVEN created indexes List<IndexDefinition> indexes = new ArrayList<>(); try ( Transaction tx = db.beginTx() ) { for ( int i = 0; i < threads; i++ ) { indexes.add( db.schema().indexFor( label( i ) ).on( KEY ).create() ); } tx.success(); } // WHEN dropping them Race race = new Race(); for ( IndexDefinition index : indexes ) { race.addContestant( indexDrop( index ), 1 ); } race.go(); // THEN they should all be observed as dropped in the end try ( Transaction tx = db.beginTx() ) { assertEquals( 0, asList( db.schema().getIndexes() ).size() ); tx.success(); } }
/** * The test case is basically loads of concurrent CREATE/DELETE NODE or sometimes just CREATE, keeping the created node in an array * for dedicated deleter threads to pick up and delete as fast as they can see them. This concurrently with large creation transactions. */ @Test public void shouldStressIt() throws Throwable { // given Race race = new Race().withMaxDuration( 5, TimeUnit.SECONDS ); AtomicReferenceArray<Node> nodeHeads = new AtomicReferenceArray<>( NUMBER_OF_CREATORS ); for ( int i = 0; i < NUMBER_OF_CREATORS; i++ ) { race.addContestant( creator( nodeHeads, i ) ); } race.addContestants( NUMBER_OF_DELETORS, deleter( nodeHeads ) ); // when race.go(); // then DatabaseLayout dbLayout = db.databaseLayout(); db.shutdownAndKeepStore(); assertTrue( new ConsistencyCheckService().runFullConsistencyCheck( dbLayout, defaults(), NONE, toOutputStream( System.out ), false, new ConsistencyFlags( true, true, true, false ) ).isSuccessful() ); }
private void assertBothSucceeds( Runnable lockAction1, Runnable lockAction2 ) throws Throwable { assertUU(); Race race = new Race(); LockContestant c1 = new LockContestant( lockAction1 ); LockContestant c2 = new LockContestant( lockAction2 ); // when race.addContestant( c1 ); race.addContestant( c2 ); race.go(); // then Pair<Boolean,Boolean> c1State = c1.state(); Pair<Boolean,Boolean> c2State = c2.state(); assertTrue( withState( "Expected both to acquire lock.", c1State, c2State ), c1State.first() && c2State.first() ); assertTrue( withState( "Expected both to be started.", c1State, c2State ), c1State.other() && c2State.other() ); }
@Test public void concurrentSchemaRuleAdd() throws Throwable { SchemaCache cache = newSchemaCache(); Race race = new Race(); int indexNumber = 10; for ( int i = 0; i < indexNumber; i++ ) { int id = i; race.addContestant( () -> cache.addSchemaRule( newIndexRule( id, id, id ) ) ); } race.go(); assertEquals( indexNumber, Iterables.count( cache.indexDescriptors() ) ); for ( int labelId = 0; labelId < indexNumber; labelId++ ) { assertEquals( 1, Iterators.count( cache.indexDescriptorsForLabel( labelId ) ) ); } for ( int propertyId = 0; propertyId < indexNumber; propertyId++ ) { assertEquals( 1, Iterators.count( cache.indexesByProperty( propertyId ) ) ); } }