|
110 | 110 | import java.util.Set; |
111 | 111 | import java.util.concurrent.CountDownLatch; |
112 | 112 | import java.util.concurrent.ExecutionException; |
| 113 | +import java.util.concurrent.Semaphore; |
113 | 114 | import java.util.concurrent.TimeUnit; |
114 | 115 | import java.util.concurrent.atomic.AtomicBoolean; |
115 | 116 | import java.util.concurrent.locks.Condition; |
@@ -454,67 +455,32 @@ protected static final class IndexThrottle { |
454 | 455 | private final CounterMetric throttleTimeMillisMetric = new CounterMetric(); |
455 | 456 | private volatile long startOfThrottleNS; |
456 | 457 | private static final ReleasableLock NOOP_LOCK = new ReleasableLock(new NoOpLock()); |
457 | | - private final ReleasableLock lockReference = new ReleasableLock(new ReentrantLock()); |
458 | | - private final Lock pauseIndexingLock = new ReentrantLock(); |
459 | | - private final Condition pauseCondition = pauseIndexingLock.newCondition(); |
460 | | - private final ReleasableLock pauseLockReference = new ReleasableLock(pauseIndexingLock); |
461 | | - private volatile AtomicBoolean pauseIndexing = new AtomicBoolean(); |
| 458 | + private final PauseLock throttlingLock; |
| 459 | + private final ReleasableLock lockReference; |
462 | 460 | private volatile ReleasableLock lock = NOOP_LOCK; |
463 | 461 |
|
| 462 | + public IndexThrottle(boolean pause) { |
| 463 | + throttlingLock = new PauseLock(pause ? 0 : 1); |
| 464 | + lockReference = new ReleasableLock(throttlingLock); |
| 465 | + } |
| 466 | + |
464 | 467 | public Releasable acquireThrottle() { |
465 | | - if (lock == pauseLockReference) { |
466 | | - pauseLockReference.acquire(); |
467 | | - try { |
468 | | - while (pauseIndexing.getAcquire()) { |
469 | | - logger.trace("Waiting on pause indexing lock" + |
470 | | - ""); |
471 | | - pauseCondition.await(); |
472 | | - } |
473 | | - } catch (InterruptedException e) { |
474 | | - Thread.currentThread().interrupt(); |
475 | | - throw new RuntimeException(e); |
476 | | - } finally { |
477 | | - // System.out.println("Acquired pause indexing lock"); |
478 | | - logger.trace("Acquired pause indexing lock"); |
479 | | - } |
480 | | - return pauseLockReference; |
481 | | - } else { |
482 | | - return lock.acquire(); |
483 | | - } |
| 468 | + return lock.acquire(); |
484 | 469 | } |
485 | 470 |
|
486 | 471 | /** Activate throttling, which switches the lock to be a real lock */ |
487 | 472 | public void activate() { |
488 | 473 | assert lock == NOOP_LOCK : "throttling activated while already active"; |
489 | 474 | startOfThrottleNS = System.nanoTime(); |
| 475 | + throttlingLock.throttle(); |
490 | 476 | lock = lockReference; |
491 | 477 | } |
492 | 478 |
|
493 | | - public void activatePause() { |
494 | | - assert lock == NOOP_LOCK : "throttling activated while already active"; |
495 | | - startOfThrottleNS = System.nanoTime(); |
496 | | - // System.out.println("Activate pause"); |
497 | | - pauseIndexing.setRelease(true); |
498 | | - lock = pauseLockReference; |
499 | | - } |
500 | | - |
501 | 479 | /** Deactivate throttling, which switches the lock to be an always-acquirable NoOpLock */ |
502 | 480 | public void deactivate() { |
503 | 481 | assert lock != NOOP_LOCK : "throttling deactivated but not active"; |
504 | 482 |
|
505 | | - if (lock == pauseLockReference) { |
506 | | - logger.trace("Deactivate index throttling pause"); |
507 | | - |
508 | | - // Signal the threads that are waiting on pauseCondition |
509 | | - pauseLockReference.acquire(); |
510 | | - try { |
511 | | - // System.out.println("Deactivate pause"); |
512 | | - pauseIndexing.setRelease(false); |
513 | | - pauseCondition.signalAll(); |
514 | | - } finally { |
515 | | - pauseLockReference.close(); |
516 | | - } |
517 | | - } |
| 483 | + throttlingLock.unthrottle(); |
518 | 484 | lock = NOOP_LOCK; |
519 | 485 |
|
520 | 486 | assert startOfThrottleNS > 0 : "Bad state of startOfThrottleNS"; |
@@ -604,6 +570,58 @@ public Condition newCondition() { |
604 | 570 | } |
605 | 571 | } |
606 | 572 |
|
| 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 | + |
607 | 625 | /** |
608 | 626 | * Perform document index operation on the engine |
609 | 627 | * @param index operation to perform |
|
0 commit comments