@Builder private BatchedPermitsRequester(String resourceId, String requestorIdentifier, long targetMillisBetweenRequests, RequestSender requestSender, MetricContext metricContext) { Preconditions.checkArgument(!Strings.isNullOrEmpty(resourceId), "Must provide a resource id."); Preconditions.checkArgument(!Strings.isNullOrEmpty(requestorIdentifier), "Must provide a requestor identifier."); this.permitBatchContainer = new PermitBatchContainer(); this.lock = new ReentrantLock(); this.newPermitsAvailable = this.lock.newCondition(); /** Ensures there is only one in-flight request at a time. */ this.requestSemaphore = new Semaphore(1); /** Number of not-yet-satisfied permits. */ this.permitsOutstanding = new SynchronizedAverager(); this.targetMillisBetweenRequests = targetMillisBetweenRequests > 0 ? targetMillisBetweenRequests : DEFAULT_TARGET_MILLIS_BETWEEN_REQUESTS; this.requestSender = requestSender; this.retryStatus = new RetryStatus(); this.basePermitRequest = new PermitRequest(); this.basePermitRequest.setResource(resourceId); this.basePermitRequest.setRequestorIdentifier(requestorIdentifier); this.restRequestTimer = metricContext == null ? null : metricContext.timer(REST_REQUEST_TIMER); this.restRequestHistogram = metricContext == null ? null : metricContext.histogram(REST_REQUEST_PERMITS_HISTOGRAM); this.callbackCounter = new AtomicLong(); }
@Override public void onSuccess(Response<PermitAllocation> result) { BatchedPermitsRequester.this.retries = 0; BatchedPermitsRequester.this.callbackCounter.incrementAndGet(); BatchedPermitsRequester.this.lock.lock(); try { PermitAllocation allocation = result.getEntity(); log.debug("Received permit allocation " + allocation); Long retryDelay = allocation.getMinRetryDelayMillis(GetMode.NULL); if (retryDelay != null) { BatchedPermitsRequester.this.retryStatus.blockRetries(retryDelay, null); } if (allocation.getPermits() > 0) { BatchedPermitsRequester.this.permitBatchContainer.addPermitAllocation(allocation); } BatchedPermitsRequester.this.requestSemaphore.release(); if (allocation.getPermits() > 0) { BatchedPermitsRequester.this.newPermitsAvailable.signalAll(); } } finally { try { this.timerContext.close(); } catch (IOException ioe) { // Do nothing } BatchedPermitsRequester.this.lock.unlock(); } }
return; if (!this.retryStatus.canRetryNow()) { this.requestSemaphore.release(); return;
return true; if (this.retryStatus.canRetryWithinMillis(10000)) { long callbackCounterSnap = this.callbackCounter.get(); maybeSendNewPermitRequest();
public boolean canRetryNow() { return canRetryWithinMillis(0); }
private void nonRetriableFail(Throwable exc, String msg) { BatchedPermitsRequester.this.retryStatus.blockRetries(RETRY_DELAY_ON_NON_RETRIABLE_EXCEPTION, exc); BatchedPermitsRequester.this.callbackCounter.incrementAndGet(); BatchedPermitsRequester.this.requestSemaphore.release(); log.error(msg, exc); // Wake up all threads so they can return false BatchedPermitsRequester.this.newPermitsAvailable.signalAll(); } }