@@ -6,9 +6,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.clients
66import com.intellij.openapi.components.Service
77import com.intellij.openapi.components.service
88import com.intellij.openapi.project.Project
9- import kotlinx.coroutines.delay
109import kotlinx.coroutines.future.await
11- import software.amazon.awssdk.core.exception.RetryableException
1210import software.amazon.awssdk.core.exception.SdkException
1311import software.amazon.awssdk.services.codewhispererstreaming.CodeWhispererStreamingAsyncClient
1412import software.amazon.awssdk.services.codewhispererstreaming.model.ExportContext
@@ -21,11 +19,11 @@ import software.aws.toolkits.core.utils.warn
2119import software.aws.toolkits.jetbrains.core.awsClient
2220import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
2321import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
22+ import software.aws.toolkits.jetbrains.services.amazonq.RetryableOperation
2423import java.time.Instant
2524import java.util.concurrent.TimeoutException
2625import java.util.concurrent.atomic.AtomicReference
2726import javax.naming.ServiceUnavailableException
28- import kotlin.random.Random
2927
3028@Service(Service .Level .PROJECT )
3129class AmazonQStreamingClient (private val project : Project ) {
@@ -115,60 +113,3 @@ class AmazonQStreamingClient(private val project: Project) {
115113 fun getInstance (project : Project ) = project.service<AmazonQStreamingClient >()
116114 }
117115}
118-
119- class RetryableOperation <T > {
120- private var attempts = 0
121- private var currentDelay = INITIAL_DELAY
122-
123- private fun getJitteredDelay (): Long {
124- currentDelay = (currentDelay * 2 ).coerceAtMost(MAX_BACKOFF )
125- return (currentDelay * (0.5 + Random .nextDouble(0.5 ))).toLong()
126- }
127-
128- fun execute (
129- operation : () -> T ,
130- isRetryable : (Exception ) -> Boolean = { it is RetryableException },
131- errorHandler : ((Exception , Int ) -> Nothing )? = null,
132- ): T {
133- while (attempts < MAX_RETRY_ATTEMPTS ) {
134- try {
135- return operation()
136- } catch (e: Exception ) {
137- attempts++
138- if (attempts >= MAX_RETRY_ATTEMPTS || ! isRetryable(e)) {
139- errorHandler?.invoke(e, attempts) ? : throw e
140- }
141-
142- Thread .sleep(getJitteredDelay())
143- }
144- }
145-
146- throw RuntimeException (" Unexpected state after $attempts attempts" )
147- }
148-
149- suspend fun executeSuspend (
150- operation : suspend () -> T ,
151- isRetryable : (Exception ) -> Boolean = { it is RetryableException },
152- errorHandler : (suspend (Exception , Int ) -> Nothing )? = null,
153- ): T {
154- while (attempts < MAX_RETRY_ATTEMPTS ) {
155- try {
156- return operation()
157- } catch (e: Exception ) {
158- attempts++
159- if (attempts >= MAX_RETRY_ATTEMPTS || ! isRetryable(e)) {
160- errorHandler?.invoke(e, attempts) ? : throw e
161- }
162- delay(getJitteredDelay())
163- }
164- }
165-
166- throw RuntimeException (" Unexpected state after $attempts attempts" )
167- }
168-
169- companion object {
170- private const val INITIAL_DELAY = 100L
171- private const val MAX_BACKOFF = 10000L
172- private const val MAX_RETRY_ATTEMPTS = 3
173- }
174- }
0 commit comments