110110import java .util .Set ;
111111import java .util .concurrent .CountDownLatch ;
112112import java .util .concurrent .ExecutionException ;
113- import java .util .concurrent .Semaphore ;
114113import java .util .concurrent .TimeUnit ;
115114import java .util .concurrent .atomic .AtomicBoolean ;
116115import java .util .concurrent .locks .Condition ;
@@ -455,33 +454,66 @@ protected static final class IndexThrottle {
455454 private final CounterMetric throttleTimeMillisMetric = new CounterMetric ();
456455 private volatile long startOfThrottleNS ;
457456 private static final ReleasableLock NOOP_LOCK = new ReleasableLock (new NoOpLock ());
458- private final PauseLock throttlingLock ;
459- private final ReleasableLock lockReference ;
457+ // This lock throttles indexing to 1 thread (per shard)
458+ private final ReleasableLock lockReference = new ReleasableLock (new ReentrantLock ());
459+ // This lock pauses indexing completely (on a per shard basis)
460+ private final Lock pauseIndexingLock = new ReentrantLock ();
461+ private final Condition pauseCondition = pauseIndexingLock .newCondition ();
462+ private final ReleasableLock pauseLockReference = new ReleasableLock (pauseIndexingLock );
463+ private volatile AtomicBoolean suspendThrottling = new AtomicBoolean ();
464+ private final boolean pauseWhenThrottled ; // Should throttling pause indexing ?
460465 private volatile ReleasableLock lock = NOOP_LOCK ;
461466
462467 public IndexThrottle (boolean pause ) {
463- throttlingLock = new PauseLock (pause ? 0 : 1 );
464- lockReference = new ReleasableLock (throttlingLock );
468+ pauseWhenThrottled = pause ;
465469 }
466470
467471 public Releasable acquireThrottle () {
468- return lock .acquire ();
472+ var lockCopy = this .lock ;
473+ if (lockCopy == pauseLockReference ) {
474+ try (var ignored = pauseLockReference .acquire ()) {
475+ // If pause throttling is activated and not temporarily suspended
476+ while ((lock == pauseLockReference ) && (suspendThrottling .getAcquire () == false )) {
477+ logger .trace ("Waiting on pause indexing lock" );
478+ pauseCondition .await ();
479+ }
480+ } catch (InterruptedException e ) {
481+ Thread .currentThread ().interrupt ();
482+ throw new RuntimeException (e );
483+ } finally {
484+ logger .trace ("Acquired pause indexing lock" );
485+ }
486+ return (() -> {});
487+ } else {
488+ return lockCopy .acquire ();
489+ }
469490 }
470491
471492 /** Activate throttling, which switches the lock to be a real lock */
472493 public void activate () {
473494 assert lock == NOOP_LOCK : "throttling activated while already active" ;
495+
474496 startOfThrottleNS = System .nanoTime ();
475- throttlingLock .throttle ();
476- lock = lockReference ;
497+ if (pauseWhenThrottled ) {
498+ lock = pauseLockReference ;
499+ logger .trace ("Activated index throttling pause" );
500+ } else {
501+ lock = lockReference ;
502+ }
477503 }
478504
479505 /** Deactivate throttling, which switches the lock to be an always-acquirable NoOpLock */
480506 public void deactivate () {
481507 assert lock != NOOP_LOCK : "throttling deactivated but not active" ;
482508
483- throttlingLock .unthrottle ();
484509 lock = NOOP_LOCK ;
510+ if (pauseWhenThrottled ) {
511+ // Signal the threads that are waiting on pauseCondition
512+ try (Releasable releasableLock = pauseLockReference .acquire ()) {
513+ pauseCondition .signalAll ();
514+ }
515+ logger .trace ("Deactivated index throttling pause" );
516+ }
485517
486518 assert startOfThrottleNS > 0 : "Bad state of startOfThrottleNS" ;
487519 long throttleTimeNS = System .nanoTime () - startOfThrottleNS ;
@@ -508,6 +540,26 @@ boolean isThrottled() {
508540 return lock != NOOP_LOCK ;
509541 }
510542
543+ /** Suspend throttling to allow another task such as relocation to acquire all indexing permits */
544+ public void suspendThrottle () {
545+ if (pauseWhenThrottled ) {
546+ try (Releasable releasableLock = pauseLockReference .acquire ()) {
547+ suspendThrottling .setRelease (true );
548+ pauseCondition .signalAll ();
549+ }
550+ }
551+ }
552+
553+ /** Reverse what was done in {@link #suspendThrottle()} */
554+ public void resumeThrottle () {
555+ if (pauseWhenThrottled ) {
556+ try (Releasable releasableLock = pauseLockReference .acquire ()) {
557+ suspendThrottling .setRelease (false );
558+ pauseCondition .signalAll ();
559+ }
560+ }
561+ }
562+
511563 boolean throttleLockIsHeldByCurrentThread () { // to be used in assertions and tests only
512564 if (isThrottled ()) {
513565 return lock .isHeldByCurrentThread ();
@@ -570,58 +622,6 @@ public Condition newCondition() {
570622 }
571623 }
572624
573- /* A lock implementation that allows us to control how many threads can take the lock
574- * In particular, this is used to set the number of allowed threads to 1 or 0
575- * when index throttling is activated.
576- */
577- protected static final class PauseLock implements Lock {
578- private final Semaphore semaphore = new Semaphore (Integer .MAX_VALUE );
579- private final int allowThreads ;
580-
581- public PauseLock (int allowThreads ) {
582- this .allowThreads = allowThreads ;
583- }
584-
585- public void lock () {
586- semaphore .acquireUninterruptibly ();
587- }
588-
589- @ Override
590- public void lockInterruptibly () throws InterruptedException {
591- semaphore .acquire ();
592- }
593-
594- @ Override
595- public void unlock () {
596- semaphore .release ();
597- }
598-
599- @ Override
600- public boolean tryLock () {
601- throw new UnsupportedOperationException ();
602- }
603-
604- @ Override
605- public boolean tryLock (long time , TimeUnit unit ) throws InterruptedException {
606- throw new UnsupportedOperationException ();
607- }
608-
609- @ Override
610- public Condition newCondition () {
611- throw new UnsupportedOperationException ();
612- }
613-
614- public void throttle () {
615- assert semaphore .availablePermits () == Integer .MAX_VALUE ;
616- semaphore .acquireUninterruptibly (Integer .MAX_VALUE - allowThreads );
617- }
618-
619- public void unthrottle () {
620- assert semaphore .availablePermits () <= allowThreads ;
621- semaphore .release (Integer .MAX_VALUE - allowThreads );
622- }
623- }
624-
625625 /**
626626 * Perform document index operation on the engine
627627 * @param index operation to perform
0 commit comments