mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-22 08:21:25 +00:00
170 lines
10 KiB
HTML
170 lines
10 KiB
HTML
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>GitHubRateLimitChecker.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">GitHub API for Java</a> > <a href="index.source.html" class="el_package">org.kohsuke.github</a> > <span class="el_source">GitHubRateLimitChecker.java</span></div><h1>GitHubRateLimitChecker.java</h1><pre class="source lang-java linenums">package org.kohsuke.github;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InterruptedIOException;
|
|
import java.util.Objects;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
/**
|
|
* A GitHub API Rate Limit Checker called before each request.
|
|
*
|
|
* <p>
|
|
* GitHub allots a certain number of requests to each user or application per period of time. The number of requests
|
|
* remaining and the time when the number will be reset is returned in the response header and can also be requested
|
|
* using {@link GitHub#getRateLimit()}. The "requests per interval" is referred to as the "rate limit".
|
|
* </p>
|
|
* <p>
|
|
* Different parts of the GitHub API have separate rate limits, but most of REST API uses {@link RateLimitTarget#CORE}.
|
|
* Checking your rate limit using {@link GitHub#getRateLimit()} does not effect your rate limit. GitHub prefers that
|
|
* clients stop before exceeding their rate limit rather than stopping after they exceed it.
|
|
* </p>
|
|
* <p>
|
|
* This class provides the infrastructure for calling the appropriate {@link RateLimitChecker} before each request and
|
|
* retrying than call many times as needed. Each {@link RateLimitChecker} decides whether to wait and for how long. This
|
|
* allows for a wide range of {@link RateLimitChecker} implementations, including complex throttling strategies and
|
|
* polling.
|
|
* </p>
|
|
*/
|
|
class GitHubRateLimitChecker {
|
|
|
|
@Nonnull
|
|
private final RateLimitChecker core;
|
|
|
|
@Nonnull
|
|
private final RateLimitChecker search;
|
|
|
|
@Nonnull
|
|
private final RateLimitChecker graphql;
|
|
|
|
@Nonnull
|
|
private final RateLimitChecker integrationManifest;
|
|
|
|
<span class="fc" id="L44"> private static final Logger LOGGER = Logger.getLogger(GitHubRateLimitChecker.class.getName());</span>
|
|
|
|
GitHubRateLimitChecker() {
|
|
<span class="fc" id="L47"> this(RateLimitChecker.NONE, RateLimitChecker.NONE, RateLimitChecker.NONE, RateLimitChecker.NONE);</span>
|
|
<span class="fc" id="L48"> }</span>
|
|
|
|
GitHubRateLimitChecker(@Nonnull RateLimitChecker core,
|
|
@Nonnull RateLimitChecker search,
|
|
@Nonnull RateLimitChecker graphql,
|
|
<span class="fc" id="L53"> @Nonnull RateLimitChecker integrationManifest) {</span>
|
|
<span class="fc" id="L54"> this.core = Objects.requireNonNull(core);</span>
|
|
<span class="fc" id="L55"> this.search = Objects.requireNonNull(search);</span>
|
|
<span class="fc" id="L56"> this.graphql = Objects.requireNonNull(graphql);</span>
|
|
<span class="fc" id="L57"> this.integrationManifest = Objects.requireNonNull(integrationManifest);</span>
|
|
<span class="fc" id="L58"> }</span>
|
|
|
|
/**
|
|
* Constructs a new {@link GitHubRateLimitChecker} with a new checker for a particular target.
|
|
*
|
|
* Only one {@link RateLimitChecker} is allowed per target.
|
|
*
|
|
* @param checker
|
|
* the {@link RateLimitChecker} to apply.
|
|
* @param rateLimitTarget
|
|
* the {@link RateLimitTarget} for this checker. If {@link RateLimitTarget#NONE}, checker will be ignored
|
|
* and no change will be made.
|
|
* @return a new {@link GitHubRateLimitChecker}
|
|
*/
|
|
GitHubRateLimitChecker with(@Nonnull RateLimitChecker checker, @Nonnull RateLimitTarget rateLimitTarget) {
|
|
<span class="pc bpc" id="L73" title="1 of 2 branches missed."> return new GitHubRateLimitChecker(rateLimitTarget == RateLimitTarget.CORE ? checker : core,</span>
|
|
<span class="pc bpc" id="L74" title="1 of 2 branches missed."> rateLimitTarget == RateLimitTarget.SEARCH ? checker : search,</span>
|
|
<span class="pc bpc" id="L75" title="1 of 2 branches missed."> rateLimitTarget == RateLimitTarget.GRAPHQL ? checker : graphql,</span>
|
|
<span class="pc bpc" id="L76" title="1 of 2 branches missed."> rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST ? checker : integrationManifest);</span>
|
|
}
|
|
|
|
/**
|
|
* Checks whether there is sufficient requests remaining within this client's rate limit quota to make the current
|
|
* request.
|
|
* <p>
|
|
* This method does not do the actual check. Instead it selects the appropriate {@link RateLimitChecker} and
|
|
* {@link GHRateLimit.Record} for the current request's {@link RateLimitTarget}. It then calls
|
|
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)}.
|
|
* </p>
|
|
* <p>
|
|
* It is up to {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} to which decide if the rate limit
|
|
* has been exceeded. If it has, {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} will sleep for as
|
|
* long is it chooses and then return {@code true}. If not, that method will return {@code false}.
|
|
* </p>
|
|
* <p>
|
|
* As long as {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} returns {@code true}, this method
|
|
* will request updated rate limit information and call
|
|
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} again. This looping allows different
|
|
* {@link RateLimitChecker} implementations to apply any number of strategies to controlling the speed at which
|
|
* requests are made.
|
|
* </p>
|
|
* <p>
|
|
* When the {@link RateLimitChecker} returns {@code false} this method will return and the request processing will
|
|
* continue.
|
|
* </p>
|
|
* <p>
|
|
* If the {@link RateLimitChecker} for this the current request's urlPath is {@link RateLimitChecker#NONE} the rate
|
|
* limit is not checked.
|
|
* </p>
|
|
*
|
|
* @param client
|
|
* the {@link GitHubClient} to check
|
|
* @param request
|
|
* the {@link GitHubRequest} to check against
|
|
* @throws IOException
|
|
* if there is an I/O error
|
|
*/
|
|
void checkRateLimit(GitHubClient client, GitHubRequest request) throws IOException {
|
|
<span class="fc" id="L116"> RateLimitChecker guard = selectChecker(request.rateLimitTarget());</span>
|
|
<span class="fc bfc" id="L117" title="All 2 branches covered."> if (guard == RateLimitChecker.NONE) {</span>
|
|
<span class="fc" id="L118"> return;</span>
|
|
}
|
|
|
|
// For the first rate limit, accept the current limit if a valid one is already present.
|
|
<span class="fc" id="L122"> GHRateLimit rateLimit = client.rateLimit(request.rateLimitTarget());</span>
|
|
<span class="fc" id="L123"> GHRateLimit.Record rateLimitRecord = rateLimit.getRecord(request.rateLimitTarget());</span>
|
|
<span class="fc" id="L124"> long waitCount = 0;</span>
|
|
try {
|
|
<span class="fc bfc" id="L126" title="All 2 branches covered."> while (guard.checkRateLimit(rateLimitRecord, waitCount)) {</span>
|
|
<span class="fc" id="L127"> waitCount++;</span>
|
|
|
|
// When rate limit is exceeded, sleep for one additional second beyond when the
|
|
// called {@link RateLimitChecker} sleeps.
|
|
// Reset time is only accurate to the second, so adding a one second buffer for safety is a good idea.
|
|
// This also keeps polling clients from querying too often.
|
|
<span class="fc" id="L133"> Thread.sleep(1000);</span>
|
|
|
|
// After the first wait, always request a new rate limit from the server.
|
|
<span class="fc" id="L136"> rateLimit = client.getRateLimit(request.rateLimitTarget());</span>
|
|
<span class="fc" id="L137"> rateLimitRecord = rateLimit.getRecord(request.rateLimitTarget());</span>
|
|
}
|
|
<span class="nc" id="L139"> } catch (InterruptedException e) {</span>
|
|
<span class="nc" id="L140"> throw (IOException) new InterruptedIOException(e.getMessage()).initCause(e);</span>
|
|
<span class="fc" id="L141"> }</span>
|
|
<span class="fc" id="L142"> }</span>
|
|
|
|
/**
|
|
* Gets the appropriate {@link RateLimitChecker} for a particular target.
|
|
*
|
|
* Analogous with {@link GHRateLimit#getRecord(RateLimitTarget)}.
|
|
*
|
|
* @param rateLimitTarget
|
|
* the rate limit to check
|
|
* @return the {@link RateLimitChecker} for a particular target
|
|
*/
|
|
@Nonnull
|
|
private RateLimitChecker selectChecker(@Nonnull RateLimitTarget rateLimitTarget) {
|
|
<span class="fc bfc" id="L155" title="All 2 branches covered."> if (rateLimitTarget == RateLimitTarget.NONE) {</span>
|
|
<span class="fc" id="L156"> return RateLimitChecker.NONE;</span>
|
|
<span class="fc bfc" id="L157" title="All 2 branches covered."> } else if (rateLimitTarget == RateLimitTarget.CORE) {</span>
|
|
<span class="fc" id="L158"> return core;</span>
|
|
<span class="pc bpc" id="L159" title="1 of 2 branches missed."> } else if (rateLimitTarget == RateLimitTarget.SEARCH) {</span>
|
|
<span class="fc" id="L160"> return search;</span>
|
|
<span class="nc bnc" id="L161" title="All 2 branches missed."> } else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {</span>
|
|
<span class="nc" id="L162"> return graphql;</span>
|
|
<span class="nc bnc" id="L163" title="All 2 branches missed."> } else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {</span>
|
|
<span class="nc" id="L164"> return integrationManifest;</span>
|
|
} else {
|
|
<span class="nc" id="L166"> throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());</span>
|
|
}
|
|
}
|
|
}
|
|
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html> |