/* * Copyright (c) 2002-2017 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.neo4j.driver.v1.stress; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Session; import org.neo4j.driver.v1.StatementResult; import org.neo4j.driver.v1.util.TestNeo4j; import static java.util.Arrays.asList; import static org.junit.Assert.assertTrue; import static org.neo4j.driver.v1.GraphDatabase.driver; public class SessionPoolingStressIT { @Rule public final TestNeo4j neo4j = new TestNeo4j(); @Rule public final TestWatcher testWatcher = new TestWatcher() { @Override protected void failed( Throwable e, Description description ) { super.failed( e, description ); StringBuilder sb = new StringBuilder(); Map<Thread,StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); for ( Map.Entry<Thread,StackTraceElement[]> entry : allStackTraces.entrySet() ) { Thread thread = entry.getKey(); sb.append( thread ).append( " -- " ).append( thread.getState() ).append( System.lineSeparator() ); for ( StackTraceElement element : entry.getValue() ) { sb.append( " " ).append( element ).append( System.lineSeparator() ); } } System.out.println( sb.toString() ); } }; private static final int N_THREADS = 50; private static final int TEST_TIME = 10000; private static final List<String> QUERIES = asList( "RETURN 1295 + 42", "UNWIND range(1,10000) AS x CREATE (n {prop:x}) DELETE n " ); private Driver driver; private ExecutorService executor; @Before public void setUp() throws Exception { executor = Executors.newFixedThreadPool( N_THREADS ); } @After public void tearDown() throws Exception { if ( executor != null ) { executor.shutdownNow(); } if ( driver != null ) { driver.close(); } } @Test public void shouldWorkFine() throws Throwable { Config config = Config.build() .withoutEncryption() .toConfig(); driver = driver( neo4j.uri(), neo4j.authToken(), config ); AtomicBoolean stop = new AtomicBoolean(); AtomicReference<Throwable> failureReference = new AtomicReference<>(); doWork( stop, failureReference ); Thread.sleep( TEST_TIME ); stop.set( true ); executor.shutdown(); assertTrue( executor.awaitTermination( 20, TimeUnit.SECONDS ) ); Throwable failure = failureReference.get(); if ( failure != null ) { throw new AssertionError( "Some workers have failed", failure ); } } private void doWork( AtomicBoolean stop, AtomicReference<Throwable> failure ) { for ( int i = 0; i < N_THREADS; i++ ) { executor.execute( new Worker( driver, stop, failure ) ); } } private class Worker implements Runnable { private final Random random = ThreadLocalRandom.current(); private final Driver driver; private final AtomicBoolean stop; private final AtomicReference<Throwable> failureReference; Worker( Driver driver, AtomicBoolean stop, AtomicReference<Throwable> failureReference ) { this.driver = driver; this.stop = stop; this.failureReference = failureReference; } @Override public void run() { try { while ( !stop.get() ) { for ( String query : QUERIES ) { runQuery( query ); } } } catch ( Throwable failure ) { if ( !failureReference.compareAndSet( null, failure ) ) { Throwable firstFailure = failureReference.get(); synchronized ( firstFailure ) { firstFailure.addSuppressed( failure ); } } } } private void runQuery( String query ) throws InterruptedException { try ( Session session = driver.session() ) { StatementResult run = session.run( query ); Thread.sleep( random.nextInt( 100 ) ); run.consume(); Thread.sleep( random.nextInt( 100 ) ); } } } }