@@ -244,6 +244,12 @@ Runnable getGlobalCheckpointSyncer() {
244244 */
245245 private final RefreshListeners refreshListeners ;
246246
247+ /**
248+ * Prevents new refresh listeners from being registered. Used to prevent becoming blocked on operations waiting for refresh
249+ * during relocation.
250+ */
251+ private final AtomicBoolean preventNewRefreshListeners = new AtomicBoolean (false );
252+
247253 private final AtomicLong lastSearcherAccess = new AtomicLong ();
248254 private final AtomicReference <Translog .Location > pendingRefreshLocation = new AtomicReference <>();
249255
@@ -608,42 +614,44 @@ public IndexShardState markAsRecovering(String reason, RecoveryState recoverySta
608614 public void relocated (final Consumer <ReplicationTracker .PrimaryContext > consumer )
609615 throws IllegalIndexShardStateException , InterruptedException {
610616 assert shardRouting .primary () : "only primaries can be marked as relocated: " + shardRouting ;
617+ preventNewRefreshListeners .set (true );
611618 try {
612- indexShardOperationPermits .blockOperations (30 , TimeUnit .MINUTES ,
613- () -> refresh ("relocation requested" ),
614- () -> {
615- // no shard operation permits are being held here, move state from started to relocated
616- assert indexShardOperationPermits .getActiveOperationsCount () == 0 :
619+ if (refreshListeners .refreshNeeded ()) {
620+ refresh ("relocated" );
621+ }
622+ indexShardOperationPermits .blockOperations (30 , TimeUnit .MINUTES , () -> {
623+ // no shard operation permits are being held here, move state from started to relocated
624+ assert indexShardOperationPermits .getActiveOperationsCount () == 0 :
617625 "in-flight operations in progress while moving shard state to relocated" ;
618- /*
619- * We should not invoke the runnable under the mutex as the expected implementation is to handoff the primary context
620- * via a network operation. Doing this under the mutex can implicitly block the cluster state update thread
621- * on network operations.
622- */
623- verifyRelocatingState ();
624- final ReplicationTracker .PrimaryContext primaryContext = replicationTracker .startRelocationHandoff ();
626+ /*
627+ * We should not invoke the runnable under the mutex as the expected implementation is to handoff the primary context via a
628+ * network operation. Doing this under the mutex can implicitly block the cluster state update thread on network operations.
629+ */
630+ verifyRelocatingState ();
631+ final ReplicationTracker .PrimaryContext primaryContext = replicationTracker .startRelocationHandoff ();
632+ try {
633+ consumer .accept (primaryContext );
634+ synchronized (mutex ) {
635+ verifyRelocatingState ();
636+ replicationTracker .completeRelocationHandoff (); // make changes to primaryMode and relocated flag only under mutex
637+ }
638+ } catch (final Exception e ) {
625639 try {
626- consumer .accept (primaryContext );
627- synchronized (mutex ) {
628- verifyRelocatingState ();
629- // make changes to primaryMode and relocated flag only under mutex
630- replicationTracker .completeRelocationHandoff ();
631- }
632- } catch (final Exception e ) {
633- try {
634- replicationTracker .abortRelocationHandoff ();
635- } catch (final Exception inner ) {
636- e .addSuppressed (inner );
637- }
638- throw e ;
640+ replicationTracker .abortRelocationHandoff ();
641+ } catch (final Exception inner ) {
642+ e .addSuppressed (inner );
639643 }
640- });
644+ throw e ;
645+ }
646+ });
641647 } catch (TimeoutException e ) {
642648 logger .warn ("timed out waiting for relocation hand-off to complete" );
643649 // This is really bad as ongoing replication operations are preventing this shard from completing relocation hand-off.
644650 // Fail primary relocation source and target shards.
645651 failShard ("timed out waiting for relocation hand-off to complete" , null );
646652 throw new IndexShardClosedException (shardId (), "timed out waiting for relocation hand-off to complete" );
653+ } finally {
654+ preventNewRefreshListeners .set (false );
647655 }
648656 }
649657
@@ -2667,7 +2675,7 @@ public void onAfter() {
26672675 */
26682676 private RefreshListeners buildRefreshListeners () {
26692677 return new RefreshListeners (
2670- indexSettings :: getMaxRefreshListeners ,
2678+ () -> preventNewRefreshListeners . get () ? 0 : indexSettings . getMaxRefreshListeners () ,
26712679 () -> refresh ("too_many_listeners" ),
26722680 threadPool .executor (ThreadPool .Names .LISTENER )::execute ,
26732681 logger , threadPool .getThreadContext ());
0 commit comments