Skip to content

Commit 7bd230d

Browse files
Merge pull request #92 from leanix/feature/CID-3408/Handle-secondary-rate-limit
CID-3408: Handle secondary rate limit
2 parents 2b909dc + 9f3bf55 commit 7bd230d

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

src/main/kotlin/net/leanix/githubagent/client/GitHubClient.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.leanix.githubagent.client
22

33
import feign.Response
4+
import io.github.resilience4j.retry.annotation.Retry
45
import net.leanix.githubagent.config.FeignClientConfig
56
import net.leanix.githubagent.dto.ArtifactsListResponse
67
import net.leanix.githubagent.dto.GitHubAppResponse
@@ -30,45 +31,52 @@ interface GitHubClient {
3031
@RequestHeader("Accept") accept: String = "application/vnd.github.v3+json"
3132
): GitHubAppResponse
3233

34+
@Retry(name = "secondary_rate_limit")
3335
@GetMapping("/api/v3/app/installations")
3436
fun getInstallations(
3537
@RequestHeader("Authorization") jwt: String,
3638
@RequestParam("per_page", defaultValue = "30") perPage: Int,
3739
@RequestParam("page", defaultValue = "1") page: Int
3840
): List<Installation>
3941

42+
@Retry(name = "secondary_rate_limit")
4043
@GetMapping("/api/v3/app/installations/{installationId}")
4144
fun getInstallation(
4245
@PathVariable("installationId") installationId: Long,
4346
@RequestHeader("Authorization") jwt: String
4447
): Installation
4548

49+
@Retry(name = "secondary_rate_limit")
4650
@PostMapping("/api/v3/app/installations/{installationId}/access_tokens")
4751
fun createInstallationToken(
4852
@PathVariable("installationId") installationId: Long,
4953
@RequestHeader("Authorization") jwt: String,
5054
@RequestBody emptyBody: String = ""
5155
): InstallationTokenResponse
5256

57+
@Retry(name = "secondary_rate_limit")
5358
@GetMapping("/api/v3/organizations")
5459
fun getOrganizations(
5560
@RequestHeader("Authorization") jwt: String,
5661
@RequestParam("per_page", defaultValue = "30") perPage: Int,
5762
@RequestParam("since", defaultValue = "1") since: Int
5863
): List<Organization>
5964

65+
@Retry(name = "secondary_rate_limit")
6066
@GetMapping("/api/v3/orgs/{org}/repos")
6167
fun getRepositories(
6268
@PathVariable("org") org: String,
6369
@RequestHeader("Authorization") token: String
6470
): List<Repository>
6571

72+
@Retry(name = "secondary_rate_limit")
6673
@GetMapping("/api/v3/search/code")
6774
fun searchManifestFiles(
6875
@RequestHeader("Authorization") token: String,
6976
@RequestParam("q") query: String,
7077
): GitHubSearchResponse
7178

79+
@Retry(name = "secondary_rate_limit")
7280
@GetMapping("/api/v3/repos/{owner}/{repo}/actions/runs/{runId}/artifacts")
7381
fun getRunArtifacts(
7482
@PathVariable("owner") owner: String,
@@ -77,6 +85,7 @@ interface GitHubClient {
7785
@RequestHeader("Authorization") token: String
7886
): ArtifactsListResponse
7987

88+
@Retry(name = "secondary_rate_limit")
8089
@GetMapping("/api/v3/repos/{owner}/{repo}/actions/artifacts/{artifactId}/zip")
8190
fun downloadArtifact(
8291
@PathVariable("owner") owner: String,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package net.leanix.githubagent.config
2+
3+
import io.github.resilience4j.core.IntervalFunction
4+
import io.github.resilience4j.retry.RetryConfig
5+
import io.github.resilience4j.retry.RetryRegistry
6+
import org.slf4j.LoggerFactory
7+
import org.springframework.context.annotation.Bean
8+
import org.springframework.context.annotation.Configuration
9+
import java.time.Duration
10+
import java.time.temporal.ChronoUnit
11+
import java.util.*
12+
13+
@Configuration
14+
class Resilience4jConfig {
15+
16+
private val logger = LoggerFactory.getLogger(Resilience4jConfig::class.java)
17+
18+
@Bean
19+
fun retryRegistry(): RetryRegistry {
20+
val intervalWithExponentialBackoff = IntervalFunction
21+
.ofExponentialBackoff(Duration.ofSeconds(20), 2.0)
22+
val retryConfig = RetryConfig.custom<Any>()
23+
.maxAttempts(5)
24+
.intervalFunction(intervalWithExponentialBackoff)
25+
.retryOnException { throwable ->
26+
logger.debug("Retrying due to exception: ${throwable.message}")
27+
true
28+
}
29+
.retryOnResult { false }
30+
.build()
31+
32+
val registry = RetryRegistry.of(retryConfig)
33+
34+
registry.retry("secondary_rate_limit").eventPublisher
35+
.onRetry { event ->
36+
val readableWaitTime = String.format(
37+
Locale.getDefault(),
38+
"%d minutes, %d seconds",
39+
event.waitInterval.toMinutes(),
40+
event.waitInterval.minus(event.waitInterval.toMinutes(), ChronoUnit.MINUTES).seconds
41+
)
42+
logger.info(
43+
"Retrying call due to ${event.name}, attempt: ${event.numberOfRetryAttempts}, " +
44+
"wait time: $readableWaitTime"
45+
)
46+
}
47+
.onError { event ->
48+
logger.error(
49+
"Call failed due to ${event.name}, after attempts: ${event.numberOfRetryAttempts}, " +
50+
"last exception: ${event.lastThrowable.message}"
51+
)
52+
}
53+
54+
return registry
55+
}
56+
}

src/main/kotlin/net/leanix/githubagent/services/GitHubGraphQLService.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Component
1414
import org.springframework.web.reactive.function.client.WebClient
1515
import reactor.util.retry.Retry
1616
import java.time.Duration
17+
import io.github.resilience4j.retry.annotation.Retry as ResilienceRetry
1718

1819
@Component
1920
class GitHubGraphQLService(
@@ -24,6 +25,7 @@ class GitHubGraphQLService(
2425
private const val PAGE_COUNT = 20
2526
}
2627

28+
@ResilienceRetry(name = "secondary_rate_limit")
2729
fun getRepositories(
2830
token: String,
2931
cursor: String? = null
@@ -67,6 +69,7 @@ class GitHubGraphQLService(
6769
}
6870
}
6971

72+
@ResilienceRetry(name = "secondary_rate_limit")
7073
fun getManifestFileContent(
7174
owner: String,
7275
repositoryName: String,
@@ -95,6 +98,7 @@ class GitHubGraphQLService(
9598
return (result.data?.repository?.manifestFile as? RepositoryManifestBlob)?.text
9699
}
97100

101+
@ResilienceRetry(name = "secondary_rate_limit")
98102
fun getRepository(
99103
owner: String,
100104
repositoryName: String,

0 commit comments

Comments
 (0)