@Test void mustNotProcessEventInSameThreadWhenNotShutDown() throws Exception { EventConsumer consumer = new EventConsumer(); AsyncEvents<Event> asyncEvents = new AsyncEvents<>( consumer, AsyncEvents.Monitor.NONE ); executor.submit( asyncEvents ); asyncEvents.send( new Event() ); Thread processingThread = consumer.poll( 10, TimeUnit.SECONDS ).processedBy; asyncEvents.shutdown(); assertThat( processingThread, is( not( Thread.currentThread() ) ) ); }
@Override protected synchronized void doStop() throws IOException { asyncEventProcessor.shutdown(); asyncEventProcessor.awaitTermination(); outputSupplier.close(); }
@Override protected synchronized void doStart() { asyncLogProcessingExecutor.submit( asyncEventProcessor ); asyncEventProcessor.awaitStartup(); }
final CountDownLatch awaitStartLatch = new CountDownLatch( 1 ); final EventConsumer consumer = new EventConsumer(); final AsyncEvents<Event> asyncEvents = new AsyncEvents<>( consumer, AsyncEvents.Monitor.NONE ); executor.submit( asyncEvents ); asyncEvents.send( new Event() ); asyncEvents.awaitTermination(); consumer.eventsProcessed.offer( specialShutdownObservedEvent ); } ); asyncEvents.send( new Event() ); asyncEvents.send( new Event() ); asyncEvents.send( new Event() ); asyncEvents.send( new Event() ); asyncEvents.send( new Event() ); asyncEvents.shutdown(); awaitShutdownFuture.get();
BadCollector( OutputStream out, long tolerance, int collect, int backPressureThreshold, boolean skipBadEntriesLogging, Monitor monitor ) { this.out = new PrintStream( out ); this.tolerance = tolerance; this.collect = collect; this.backPressureThreshold = backPressureThreshold; this.logBadEntries = !skipBadEntriesLogging; this.monitor = monitor; this.logger = new AsyncEvents<>( this::processEvent, AsyncEvents.Monitor.NONE ); this.eventProcessor = new Thread( logger ); this.eventProcessor.start(); }
private void collect( ProblemReporter report ) { boolean collect = collects( report.type() ); if ( collect ) { // This type of problem is collected and we're within the max threshold, so it's OK long count = badEntries.incrementAndGet(); if ( tolerance == UNLIMITED_TOLERANCE || count <= tolerance ) { // We're within the threshold if ( logBadEntries ) { // Send this to the logger... but first apply some back pressure if queue is growing big while ( queueSize.sum() >= backPressureThreshold ) { LockSupport.parkNanos( TimeUnit.MILLISECONDS.toNanos( 10 ) ); } logger.send( report ); queueSize.add( 1 ); } return; // i.e. don't treat this as an exception } } InputException exception = report.exception(); throw collect ? withMessage( exception, format( "Too many bad entries %d, where last one was: %s", badEntries.longValue(), exception.getMessage() ) ) : exception; }
@Override public void send( T event ) { AsyncEvent prev = STACK_UPDATER.getAndSet( this, event ); assert prev != null; event.next = prev; if ( prev == END_SENTINEL ) { LockSupport.unpark( backgroundThread ); } else if ( prev == SHUTDOWN_SENTINEL ) { AsyncEvent events = STACK_UPDATER.getAndSet( this, SHUTDOWN_SENTINEL ); process( events ); } }
private void process( AsyncEvent events ) { events = reverseAndStripEndMark( events ); while ( events != null ) { @SuppressWarnings( "unchecked" ) T event = (T) events; eventConsumer.accept( event ); events = events.next; } }
public AsyncRequestLog( FileSystemAbstraction fs, ZoneId logTimeZone, String logFile, long rotationSize, int rotationKeepNumber ) throws IOException { NamedThreadFactory threadFactory = new NamedThreadFactory( "HTTP-Log-Rotator", true ); ExecutorService rotationExecutor = Executors.newCachedThreadPool( threadFactory ); outputSupplier = new RotatingFileOutputStreamSupplier( fs, new File( logFile ), rotationSize, 0, rotationKeepNumber, rotationExecutor ); FormattedLogProvider logProvider = FormattedLogProvider.withZoneId( logTimeZone ) .toOutputStream( outputSupplier ); asyncLogProcessingExecutor = Executors.newSingleThreadExecutor( new NamedThreadFactory( "HTTP-Log-Writer" ) ); asyncEventProcessor = new AsyncEvents<>( this, this ); AsyncLogProvider asyncLogProvider = new AsyncLogProvider( asyncEventProcessor, logProvider ); log = asyncLogProvider.getLog( "REQUEST" ); }
private void collect( ProblemReporter report ) { boolean collect = collects( report.type() ); if ( collect ) { // This type of problem is collected and we're within the max threshold, so it's OK long count = badEntries.incrementAndGet(); if ( tolerance == UNLIMITED_TOLERANCE || count <= tolerance ) { // We're within the threshold if ( logBadEntries ) { // Send this to the logger logger.send( report ); } return; // i.e. don't treat this as an exception } } InputException exception = report.exception(); throw collect ? withMessage( exception, format( "Too many bad entries %d, where last one was: %s", badEntries.longValue(), exception.getMessage() ) ) : exception; }
@Override public void run() { assert backgroundThread == null : "A thread is already running " + backgroundThread; backgroundThread = Thread.currentThread(); startupLatch.release(); try { do { AsyncEvent events = STACK_UPDATER.getAndSet( this, END_SENTINEL ); process( events ); if ( stack == END_SENTINEL && !shutdown ) { LockSupport.park( this ); } } while ( !shutdown ); AsyncEvent events = STACK_UPDATER.getAndSet( this, SHUTDOWN_SENTINEL ); process( events ); } finally { backgroundThread = null; shutdownLatch.release(); } }
private void process( AsyncEvent events ) { events = reverseAndStripEndMark( events ); while ( events != null ) { @SuppressWarnings( "unchecked" ) T event = (T) events; eventConsumer.accept( event ); events = events.next; } }
@Test void eventsMustBeProcessedByBackgroundThread() throws Exception { EventConsumer consumer = new EventConsumer(); AsyncEvents<Event> asyncEvents = new AsyncEvents<>( consumer, AsyncEvents.Monitor.NONE ); executor.submit( asyncEvents ); Event firstSentEvent = new Event(); asyncEvents.send( firstSentEvent ); Event firstProcessedEvent = consumer.poll( 10, TimeUnit.SECONDS ); Event secondSentEvent = new Event(); asyncEvents.send( secondSentEvent ); Event secondProcessedEvent = consumer.poll( 10, TimeUnit.SECONDS ); asyncEvents.shutdown(); assertThat( firstProcessedEvent, is( firstSentEvent ) ); assertThat( secondProcessedEvent, is( secondSentEvent ) ); }
@Override public void close() { logger.shutdown(); try { logger.awaitTermination(); eventProcessor.join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { out.flush(); out.close(); } }
public BadCollector( OutputStream out, long tolerance, int collect, boolean skipBadEntriesLogging ) { this.out = new PrintStream( out ); this.tolerance = tolerance; this.collect = collect; this.logBadEntries = !skipBadEntriesLogging; this.logger = new AsyncEvents<>( this::processEvent, AsyncEvents.Monitor.NONE ); this.eventProcessor = new Thread( logger ); this.eventProcessor.start(); }
@Override public void send( T event ) { AsyncEvent prev = STACK_UPDATER.getAndSet( this, event ); assert prev != null; event.next = prev; if ( prev == END_SENTINEL ) { LockSupport.unpark( backgroundThread ); } else if ( prev == SHUTDOWN_SENTINEL ) { AsyncEvent events = STACK_UPDATER.getAndSet( this, SHUTDOWN_SENTINEL ); process( events ); } }
@Override protected synchronized void doStart() { asyncLogProcessingExecutor.submit( asyncEventProcessor ); asyncEventProcessor.awaitStartup(); }
@Test void mustProcessEventsDirectlyWhenShutDown() { assertTimeout( ofSeconds( 10 ), () -> { EventConsumer consumer = new EventConsumer(); AsyncEvents<Event> asyncEvents = new AsyncEvents<>( consumer, AsyncEvents.Monitor.NONE ); executor.submit( asyncEvents ); asyncEvents.send( new Event() ); Thread threadForFirstEvent = consumer.poll( 10, TimeUnit.SECONDS ).processedBy; asyncEvents.shutdown(); assertThat( threadForFirstEvent, is( not( Thread.currentThread() ) ) ); Thread threadForSubsequentEvents; do { asyncEvents.send( new Event() ); threadForSubsequentEvents = consumer.poll( 10, TimeUnit.SECONDS ).processedBy; } while ( threadForSubsequentEvents != Thread.currentThread() ); } ); }
@Override public void close() { logger.shutdown(); try { logger.awaitTermination(); } finally { out.flush(); out.close(); } }
public AsyncRequestLog( FileSystemAbstraction fs, ZoneId logTimeZone, String logFile, long rotationSize, int rotationKeepNumber ) throws IOException { NamedThreadFactory threadFactory = new NamedThreadFactory( "HTTP-Log-Rotator", true ); ExecutorService rotationExecutor = Executors.newCachedThreadPool( threadFactory ); outputSupplier = new RotatingFileOutputStreamSupplier( fs, new File( logFile ), rotationSize, 0, rotationKeepNumber, rotationExecutor ); FormattedLogProvider logProvider = FormattedLogProvider.withZoneId( logTimeZone ) .toOutputStream( outputSupplier ); asyncLogProcessingExecutor = Executors.newSingleThreadExecutor( new NamedThreadFactory( "HTTP-Log-Writer" ) ); asyncEventProcessor = new AsyncEvents<>( this, this ); AsyncLogProvider asyncLogProvider = new AsyncLogProvider( asyncEventProcessor, logProvider ); log = asyncLogProvider.getLog( "REQUEST" ); }