package de.axone.cache.ng;
import static org.testng.Assert.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.Test;
import de.axone.cache.ng.CacheNG.AutomaticClient;
import de.axone.cache.ng.CacheNG.UniversalAccessor;
import de.axone.cache.ng.CacheNGTestHelpers.RN;
import de.axone.tools.E;
import de.axone.tools.Mapper;
@SuppressWarnings( "unused" )
@Test( groups="cacheng.multithreaded" )
public class CacheNGTest_AutomaticClient_Multithreaded {
private static final int NUM_THREADS = 1000,
NUM_ENTRIES = 100, // Must be multiple of 2
THREAD_DELAY_MS = 10;
public void testAutomaticClientParallelSingleOneByOneAccessor() throws InterruptedException {
//final AtomicInteger index = new AtomicInteger( 0 );
final DelayedIndexProvider index = new DelayedIndexProvider();
TestAccessor_Single accessor =
new TestAccessor_Single( index::getAndIncrement );
CacheNG.AutomaticClient<String,String> auto =
new AutomaticClientImpl<>( new CacheHashMap<>( RN.S_S ) );
List<Thread> ts = new LinkedList<>();
for( int i=0; i<NUM_THREADS; i++ ){
Thread t = new Thread( new FetchRunnerOneByOne( accessor, auto ), "T-" + i );
ts.add( t );
}
for( Thread t : ts ) t.start();
for( Thread t : ts ) t.join();
assertEquals( index.getAndIncrement(), NUM_ENTRIES, "Amount of fetches" );
}
public void testAutomaticClientParallelSingleManyAtOnceAccessor() throws InterruptedException {
//final AtomicInteger index = new AtomicInteger( 0 );
final DelayedIndexProvider index = new DelayedIndexProvider();
TestAccessor_Single accessor =
//new TestAccessor_Single( new PrintingIndexProvider( index ) );
new TestAccessor_Single( index::getAndIncrement );
CacheNG.AutomaticClient<String,String> auto =
new AutomaticClientImpl<>( new CacheHashMap<>( RN.S_S ) );
List<Thread> ts = new LinkedList<>();
for( int i=0; i<NUM_THREADS; i++ ){
Thread t = new Thread( new FetchRunnerManyAtOnce( accessor, auto ), "T-" + i );
ts.add( t );
}
for( Thread t : ts ) t.start();
for( Thread t : ts ) t.join();
assertEquals( index.getAndIncrement(), NUM_ENTRIES, "Amount of fetches" );
}
public void testAutomaticClientParallelMultiOneByOneAccessor() throws InterruptedException {
//final AtomicInteger index = new AtomicInteger( 0 );
final DelayedIndexProvider index = new DelayedIndexProvider();
TestAccessor_Multi accessor =
new TestAccessor_Multi( index::getAndIncrement );
CacheNG.AutomaticClient<String,String> auto =
new AutomaticClientImpl<>( new CacheHashMap<>( RN.S_S ) );
List<Thread> ts = new LinkedList<>();
for( int i=0; i<NUM_THREADS; i++ ){
Thread t = new Thread( new FetchRunnerOneByOne( accessor, auto ), "T-" + i );
ts.add( t );
}
for( Thread t : ts ) t.start();
for( Thread t : ts ) t.join();
assertEquals( index.getAndIncrement(), NUM_ENTRIES, "Amount of fetches" );
}
public void testAutomaticClientParallelMultiManyAtOnceAccessor() throws InterruptedException {
//final AtomicInteger index = new AtomicInteger( 0 );
final DelayedIndexProvider index = new DelayedIndexProvider();
TestAccessor_Multi accessor =
new TestAccessor_Multi( index::getAndIncrement );
CacheNG.AutomaticClient<String,String> auto =
new AutomaticClientImpl<>( new CacheHashMap<>( RN.S_S ) );
List<Thread> ts = new LinkedList<>();
for( int i=0; i<NUM_THREADS; i++ ){
Thread t = new Thread( new FetchRunnerManyAtOnce( accessor, auto ), "T-" + i );
ts.add( t );
}
for( Thread t : ts ) t.start();
for( Thread t : ts ) t.join();
assertEquals( index.getAndIncrement(), NUM_ENTRIES, "Amount of fetches" );
}
private class FetchRunnerOneByOne implements Runnable {
final CacheNG.UniversalAccessor<String, String> accessor;
final CacheNG.AutomaticClient<String, String> autoClient;
public FetchRunnerOneByOne( UniversalAccessor<String, String> accessor,
AutomaticClient<String, String> autoClient ) {
this.accessor = accessor;
this.autoClient = autoClient;
}
@Override
public void run() {
for( int i=0; i<NUM_ENTRIES; i++ ) {
char c = (char)('A' + i);
String expected = ""+c+i;
String result = autoClient.fetch( ""+c, accessor );
assertEquals( result, expected );
}
}
}
private class FetchRunnerManyAtOnce implements Runnable {
final CacheNG.UniversalAccessor<String, String> accessor;
final CacheNG.AutomaticClient<String, String> autoClient;
public FetchRunnerManyAtOnce( UniversalAccessor<String, String> accessor,
AutomaticClient<String, String> autoClient ) {
this.accessor = accessor;
this.autoClient = autoClient;
}
@Override
public void run() {
for( int i=0; i<NUM_ENTRIES; i+=2 ) {
int j = i+1;
char c = (char)('A'+i),
d = (char)('A'+j);
List<String> request = Mapper.asLinkedList( ""+c, ""+d );
Map<String,String> expected = Mapper.hashMap( ""+c, ""+c+i, ""+d, ""+d+j );
Map<String,String> result = autoClient.fetch( request, accessor );
//print( c + "/" + d + "->" + expected + "///" + result );
assertEquals( result, expected );
}
}
}
private static synchronized void print( String x ){
E._rrt( x );
}
@FunctionalInterface
interface IndexProvider {
public int getAndIncrement();
}
private static final class PrintingIndexProvider implements IndexProvider {
private final AtomicInteger index;
public PrintingIndexProvider( AtomicInteger index ) {
this.index = index;
}
@Override
public int getAndIncrement() {
int result = index.getAndIncrement();
print( "-->" + result );
return result;
}
}
private static final class DelayedIndexProvider implements IndexProvider {
private final AtomicInteger index = new AtomicInteger();
@Override
public int getAndIncrement() {
synchronized( this ){
try {
this.wait( THREAD_DELAY_MS );
} catch( InterruptedException e ) {}
}
return index.getAndIncrement();
}
}
class TestAccessor_Single
implements CacheNG.UniversalAccessor<String, String> {
final IndexProvider index;
public TestAccessor_Single( IndexProvider index ) {
this.index = index;
}
@Override
public String fetch( String key ) {
return key + index.getAndIncrement();
}
}
class TestAccessor_Multi
implements CacheNG.UniversalAccessor<String, String> {
final IndexProvider index;
public TestAccessor_Multi( IndexProvider index ) {
this.index = index;
}
@Override
public Map<String,String> fetch( Collection<String> keys ) {
Map<String,String> result = new HashMap<>();
for( String key : keys ){
result.put( key, key + index.getAndIncrement() );
}
return result;
}
}
}