@@ -83,7 +83,6 @@ public class ConcurrentUpdateSolrClient extends SolrClient {
8383 final int threadCount ;
8484 boolean shutdownExecutor = false ;
8585 int pollQueueTimeMillis = 250 ;
86- int stallTimeMillis ;
8786 private final boolean streamDeletes ;
8887 private boolean internalHttpClient ;
8988 private final int connectionTimeout ;
@@ -95,6 +94,8 @@ public class ConcurrentUpdateSolrClient extends SolrClient {
9594 AtomicInteger blockLoops ;
9695 AtomicInteger emptyQueueLoops ;
9796
97+ protected StallDetection stallDetection ;
98+
9899 /**
99100 * Use builder to construct this class. Uses the supplied HttpClient to send documents to the Solr
100101 * server.
@@ -121,16 +122,20 @@ protected ConcurrentUpdateSolrClient(Builder builder) {
121122 this .connectionTimeout = builder .connectionTimeoutMillis ;
122123 this .soTimeout = builder .socketTimeoutMillis ;
123124 this .pollQueueTimeMillis = builder .pollQueueTime ;
124- this .stallTimeMillis = Integer .getInteger ("solr.cloud.client.stallTime" , 15000 );
125125 this .defaultCollection = builder .defaultCollection ;
126126
127+ // Initialize stall detection
128+ int stallTimeMillis = Integer .getInteger ("solr.cloud.client.stallTime" , 15000 );
129+
127130 // make sure the stall time is larger than the polling time
128131 // to give a chance for the queue to change
129132 int minimalStallTime = pollQueueTimeMillis * 2 ;
130- if (minimalStallTime > this . stallTimeMillis ) {
131- this . stallTimeMillis = minimalStallTime ;
133+ if (minimalStallTime > stallTimeMillis ) {
134+ stallTimeMillis = minimalStallTime ;
132135 }
133136
137+ this .stallDetection = new StallDetection (stallTimeMillis , () -> queue .size ());
138+
134139 if (builder .executorService != null ) {
135140 this .scheduler = builder .executorService ;
136141 this .shutdownExecutor = false ;
@@ -147,6 +152,8 @@ protected ConcurrentUpdateSolrClient(Builder builder) {
147152 this .blockLoops = new AtomicInteger ();
148153 this .emptyQueueLoops = new AtomicInteger ();
149154 }
155+
156+ // processedCount is now managed by StallDetection
150157 }
151158
152159 public Set <String > getUrlParamNames () {
@@ -397,6 +404,7 @@ public void writeTo(OutputStream out) throws IOException {
397404 } else {
398405 onSuccess (response );
399406 }
407+ stallDetection .incrementProcessedCount ();
400408
401409 } finally {
402410 try {
@@ -527,8 +535,6 @@ public NamedList<Object> request(final SolrRequest<?> request, String collection
527535 Update update = new Update (req , collection );
528536 boolean success = queue .offer (update );
529537
530- long lastStallTime = -1 ;
531- int lastQueueSize = -1 ;
532538 for (; ; ) {
533539 synchronized (runners ) {
534540 // see if queue is half full, and we can add more runners
@@ -562,29 +568,7 @@ public NamedList<Object> request(final SolrRequest<?> request, String collection
562568 success = queue .offer (update , 100 , TimeUnit .MILLISECONDS );
563569 }
564570 if (!success ) {
565- // stall prevention
566- int currentQueueSize = queue .size ();
567- if (currentQueueSize != lastQueueSize ) {
568- // there's still some progress in processing the queue - not stalled
569- lastQueueSize = currentQueueSize ;
570- lastStallTime = -1 ;
571- } else {
572- if (lastStallTime == -1 ) {
573- // mark a stall but keep trying
574- lastStallTime = System .nanoTime ();
575- } else {
576- long currentStallTime =
577- TimeUnit .NANOSECONDS .toMillis (System .nanoTime () - lastStallTime );
578- if (currentStallTime > stallTimeMillis ) {
579- throw new IOException (
580- "Request processing has stalled for "
581- + currentStallTime
582- + "ms with "
583- + queue .size ()
584- + " remaining elements in the queue." );
585- }
586- }
587- }
571+ stallCheck ();
588572 }
589573 }
590574 } catch (InterruptedException e ) {
@@ -598,16 +582,17 @@ public NamedList<Object> request(final SolrRequest<?> request, String collection
598582 return dummy ;
599583 }
600584
585+ void stallCheck () throws IOException {
586+ stallDetection .stallCheck ();
587+ }
588+
601589 public synchronized void blockUntilFinished () throws IOException {
602590 lock = new CountDownLatch (1 );
603591 try {
604592
605593 waitForEmptyQueue ();
606594 interruptRunnerThreadsPolling ();
607595
608- long lastStallTime = -1 ;
609- int lastQueueSize = -1 ;
610-
611596 synchronized (runners ) {
612597
613598 // NOTE: if the executor is shut down, runners may never become empty. A scheduled task may
@@ -625,29 +610,11 @@ public synchronized void blockUntilFinished() throws IOException {
625610 // Need to check if the queue is empty before really considering this is finished
626611 // (SOLR-4260)
627612 int queueSize = queue .size ();
628- // stall prevention
629- if (lastQueueSize != queueSize ) {
630- // init, or no stall
631- lastQueueSize = queueSize ;
632- lastStallTime = -1 ;
633- } else {
634- if (lastStallTime == -1 ) {
635- lastStallTime = System .nanoTime ();
636- } else {
637- long currentStallTime =
638- TimeUnit .NANOSECONDS .toMillis (System .nanoTime () - lastStallTime );
639- if (currentStallTime > stallTimeMillis ) {
640- throw new IOException (
641- "Task queue processing has stalled for "
642- + currentStallTime
643- + " ms with "
644- + queueSize
645- + " remaining elements to process." );
646- // Thread.currentThread().interrupt();
647- // break;
648- }
649- }
613+ // stall prevention - only if queue is not empty
614+ if (queueSize > 0 ) {
615+ stallCheck ();
650616 }
617+
651618 if (queueSize > 0 && runners .isEmpty ()) {
652619 // TODO: can this still happen?
653620 log .warn (
@@ -685,8 +652,6 @@ public synchronized void blockUntilFinished() throws IOException {
685652 private void waitForEmptyQueue () throws IOException {
686653 boolean threadInterrupted = Thread .currentThread ().isInterrupted ();
687654
688- long lastStallTime = -1 ;
689- int lastQueueSize = -1 ;
690655 while (!queue .isEmpty ()) {
691656 if (log .isDebugEnabled ()) emptyQueueLoops .incrementAndGet ();
692657 if (ExecutorUtil .isTerminated (scheduler )) {
@@ -720,28 +685,10 @@ private void waitForEmptyQueue() throws IOException {
720685 queue .size ());
721686 }
722687 }
723- int currentQueueSize = queue .size ();
724- // stall prevention
725- if (currentQueueSize != lastQueueSize ) {
726- lastQueueSize = currentQueueSize ;
727- lastStallTime = -1 ;
728- } else {
729- lastQueueSize = currentQueueSize ;
730- if (lastStallTime == -1 ) {
731- lastStallTime = System .nanoTime ();
732- } else {
733- long currentStallTime = TimeUnit .NANOSECONDS .toMillis (System .nanoTime () - lastStallTime );
734- if (currentStallTime > stallTimeMillis ) {
735- throw new IOException (
736- "Task queue processing has stalled for "
737- + currentStallTime
738- + " ms with "
739- + currentQueueSize
740- + " remaining elements to process." );
741- // threadInterrupted = true;
742- // break;
743- }
744- }
688+
689+ // Only check for stalls if the queue is not empty
690+ if (!queue .isEmpty ()) {
691+ stallCheck ();
745692 }
746693 }
747694 if (threadInterrupted ) {
0 commit comments