@@ -78,6 +78,7 @@ public class QueryBatcherImpl extends BatcherImpl implements QueryBatcher {
7878 private final AtomicBoolean started = new AtomicBoolean (false );
7979 private final Object lock = new Object ();
8080 private final Map <Forest ,List <QueryTask >> blackListedTasks = new HashMap <>();
81+ private boolean isSingleThreaded = false ;
8182 private JobTicket jobTicket ;
8283 private Thread runJobCompletionListeners ;
8384
@@ -350,6 +351,12 @@ private synchronized void initialize() {
350351 // now we've set the threadCount
351352 threadCountSet = true ;
352353 }
354+ // If we are iterating and if we have the thread count to 1, we have a single thread acting as both
355+ // consumer and producer of the ThreadPoolExecutor queue. Hence, we produce till the maximum and start
356+ // consuming and produce again. Since the thread count is 1, there is no worry about thread utilization.
357+ if (getThreadCount () == 1 ) {
358+ isSingleThreaded = true ;
359+ }
353360 logger .info ("Starting job batchSize={}, threadCount={}, onUrisReady listeners={}, failure listeners={}" ,
354361 getBatchSize (), getThreadCount (), urisReadyListeners .size (), failureListeners .size ());
355362 threadPool = new QueryThreadPoolExecutor (getThreadCount (), this );
@@ -713,6 +720,91 @@ private void shutdownIfAllForestsAreDone() {
713720 threadPool .shutdown ();
714721 }
715722
723+ private class IteratorTask implements Runnable {
724+
725+ private QueryBatcher batcher ;
726+
727+ IteratorTask (QueryBatcher batcher ) {
728+ this .batcher = batcher ;
729+ }
730+
731+ @ Override
732+ public void run () {
733+ try {
734+ List <String > uriQueue = new ArrayList <>(getBatchSize ());
735+ while (iterator .hasNext ()) {
736+ uriQueue .add (iterator .next ());
737+ // if we've hit batchSize or the end of the iterator
738+ if (uriQueue .size () == getBatchSize () || !iterator .hasNext ()) {
739+ final List <String > uris = uriQueue ;
740+ uriQueue = new ArrayList <>(getBatchSize ());
741+ Runnable processBatch = new Runnable () {
742+ public void run () {
743+ QueryBatchImpl batch = new QueryBatchImpl ()
744+ .withBatcher (batcher )
745+ .withTimestamp (Calendar .getInstance ())
746+ .withJobTicket (getJobTicket ());
747+ try {
748+ long currentBatchNumber = batchNumber .incrementAndGet ();
749+ // round-robin from client 0 to (clientList.size() - 1);
750+ List <DatabaseClient > currentClientList = clientList .get ();
751+ int clientIndex = (int ) (currentBatchNumber % currentClientList .size ());
752+ DatabaseClient client = currentClientList .get (clientIndex );
753+ batch = batch .withJobBatchNumber (currentBatchNumber )
754+ .withClient (client )
755+ .withJobResultsSoFar (resultsSoFar .addAndGet (uris .size ()))
756+ .withItems (uris .toArray (new String [uris .size ()]));
757+ logger .trace ("batch size={}, jobBatchNumber={}, jobResultsSoFar={}" , uris .size (),
758+ batch .getJobBatchNumber (), batch .getJobResultsSoFar ());
759+ for (QueryBatchListener listener : urisReadyListeners ) {
760+ try {
761+ listener .processEvent (batch );
762+ } catch (Throwable e ) {
763+ logger .error ("Exception thrown by an onUrisReady listener" , e );
764+ }
765+ }
766+ } catch (Throwable t ) {
767+ batch = batch .withItems (uris .toArray (new String [uris .size ()]));
768+ for (QueryFailureListener listener : failureListeners ) {
769+ try {
770+ listener .processFailure (new QueryBatchException (batch , t ));
771+ } catch (Throwable e ) {
772+ logger .error ("Exception thrown by an onQueryFailure listener" , e );
773+ }
774+ }
775+ logger .warn ("Error iterating to queue uris: {}" , t .toString ());
776+ }
777+ }
778+ };
779+ threadPool .execute (processBatch );
780+ // If the queue is almost full, stop producing and add a task to continue later
781+ if (isSingleThreaded && threadPool .getQueue ().remainingCapacity () <= 2 && iterator .hasNext ()) {
782+ threadPool .execute (new IteratorTask (batcher ));
783+ return ;
784+ }
785+ }
786+ }
787+ } catch (Throwable t ) {
788+ for (QueryFailureListener listener : failureListeners ) {
789+ QueryBatchImpl batch = new QueryBatchImpl ()
790+ .withItems (new String [0 ])
791+ .withClient (clientList .get ().get (0 ))
792+ .withBatcher (batcher )
793+ .withTimestamp (Calendar .getInstance ())
794+ .withJobResultsSoFar (0 );
795+
796+ try {
797+ listener .processFailure (new QueryBatchException (batch , t ));
798+ } catch (Throwable e ) {
799+ logger .error ("Exception thrown by an onQueryFailure listener" , e );
800+ }
801+ }
802+ logger .warn ("Error iterating to queue uris: {}" , t .toString ());
803+ }
804+ runJobCompletionListeners .start ();
805+ threadPool .shutdown ();
806+ }
807+ }
716808
717809 /* startIterating launches in a separate thread (actually a task handled by
718810 * threadPool) and just loops through the Iterator<String>, batching uris of
@@ -729,86 +821,7 @@ private void shutdownIfAllForestsAreDone() {
729821 * their listeners handled, they should use try-catch and handle them.
730822 */
731823 private void startIterating () {
732- final QueryBatcher batcher = this ;
733- Runnable queueUris = new Runnable () {
734- public void run () {
735- try {
736- final AtomicLong batchNumber = new AtomicLong (0 );
737- final AtomicLong resultsSoFar = new AtomicLong (0 );
738- List <String > uriQueue = new ArrayList <>(getBatchSize ());
739- while ( iterator .hasNext () ) {
740- try {
741- uriQueue .add (iterator .next ());
742- // if we've hit batchSize or the end of the iterator
743- if ( uriQueue .size () == getBatchSize () || ! iterator .hasNext () ) {
744- final List <String > uris = uriQueue ;
745- uriQueue = new ArrayList <>(getBatchSize ());
746- Runnable processBatch = new Runnable () {
747- public void run () {
748- long currentBatchNumber = batchNumber .incrementAndGet ();
749- // round-robin from client 0 to (clientList.size() - 1);
750- List <DatabaseClient > currentClientList = clientList .get ();
751- int clientIndex = (int ) (currentBatchNumber % currentClientList .size ());
752- DatabaseClient client = currentClientList .get (clientIndex );
753- QueryBatchImpl batch = new QueryBatchImpl ()
754- .withClient (client )
755- .withBatcher (batcher )
756- .withTimestamp (Calendar .getInstance ())
757- .withJobTicket (getJobTicket ())
758- .withJobBatchNumber (currentBatchNumber )
759- .withJobResultsSoFar (resultsSoFar .addAndGet (uris .size ()));
760- batch = batch .withItems (uris .toArray (new String [uris .size ()]));
761- logger .trace ("batch size={}, jobBatchNumber={}, jobResultsSoFar={}" , uris .size (),
762- batch .getJobBatchNumber (), batch .getJobResultsSoFar ());
763- for (QueryBatchListener listener : urisReadyListeners ) {
764- try {
765- listener .processEvent (batch );
766- } catch (Throwable e ) {
767- logger .error ("Exception thrown by an onUrisReady listener" , e );
768- }
769- }
770- }
771- };
772- threadPool .execute (processBatch );
773- }
774- } catch (Throwable t ) {
775- QueryBatchImpl batch = new QueryBatchImpl ()
776- .withItems (new String [0 ])
777- .withClient (clientList .get ().get (0 ))
778- .withBatcher (batcher )
779- .withTimestamp (Calendar .getInstance ())
780- .withJobResultsSoFar (0 );
781- for ( QueryFailureListener listener : failureListeners ) {
782- try {
783- listener .processFailure (new QueryBatchException (batch , t ));
784- } catch (Throwable e ) {
785- logger .error ("Exception thrown by an onQueryFailure listener" , e );
786- }
787- }
788- logger .warn ("Error iterating to queue uris: {}" , t .toString ());
789- }
790- }
791- } catch (Throwable t ) {
792- for ( QueryFailureListener listener : failureListeners ) {
793- try {
794- QueryBatchImpl batch = new QueryBatchImpl ()
795- .withItems (new String [0 ])
796- .withClient (clientList .get ().get (0 ))
797- .withBatcher (batcher )
798- .withTimestamp (Calendar .getInstance ())
799- .withJobResultsSoFar (0 );
800- listener .processFailure (new QueryBatchException (batch , t ));
801- } catch (Throwable e ) {
802- logger .error ("Exception thrown by an onQueryFailure listener" , e );
803- }
804- }
805- logger .warn ("Error iterating to queue uris" , t .toString ());
806- }
807- runJobCompletionListeners .start ();
808- threadPool .shutdown ();
809- }
810- };
811- threadPool .execute (queueUris );
824+ threadPool .execute (new IteratorTask (this ));
812825 }
813826
814827 public void stop () {
0 commit comments