Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.util.io.HttpRequests
import software.amazon.awssdk.core.exception.SdkException
import software.amazon.awssdk.services.codewhispererruntime.CodeWhispererRuntimeClient
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeResponse
import software.amazon.awssdk.services.codewhispererruntime.model.ContentChecksumType
Expand All @@ -32,15 +33,18 @@ import software.amazon.awssdk.services.codewhispererruntime.model.UploadContext
import software.amazon.awssdk.services.codewhispererruntime.model.UploadIntent
import software.amazon.awssdk.services.codewhispererstreaming.model.ExportContext
import software.amazon.awssdk.services.codewhispererstreaming.model.ExportIntent
import software.amazon.awssdk.services.codewhispererstreaming.model.ThrottlingException
import software.amazon.awssdk.services.codewhispererstreaming.model.TransformationDownloadArtifactType
import software.amazon.awssdk.services.codewhispererstreaming.model.TransformationExportContext
import software.amazon.awssdk.services.codewhispererstreaming.model.ValidationException
import software.aws.toolkits.core.utils.error
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.info
import software.aws.toolkits.jetbrains.core.AwsClientManager
import software.aws.toolkits.jetbrains.services.amazonq.APPLICATION_ZIP
import software.aws.toolkits.jetbrains.services.amazonq.AWS_KMS
import software.aws.toolkits.jetbrains.services.amazonq.CONTENT_SHA256
import software.aws.toolkits.jetbrains.services.amazonq.RetryableOperation
import software.aws.toolkits.jetbrains.services.amazonq.SERVER_SIDE_ENCRYPTION
import software.aws.toolkits.jetbrains.services.amazonq.SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
import software.aws.toolkits.jetbrains.services.amazonq.clients.AmazonQStreamingClient
Expand All @@ -52,7 +56,9 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.calculateTo
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getTelemetryOptOutPreference
import java.io.File
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.time.Instant
import java.util.concurrent.TimeoutException

@Service(Service.Level.PROJECT)
class GumbyClient(private val project: Project) {
Expand Down Expand Up @@ -152,15 +158,34 @@ class GumbyClient(private val project: Project) {
apiCall: () -> T,
apiName: String,
): T {
var result: CodeWhispererRuntimeResponse? = null
var result: T? = null
try {
result = apiCall()
LOG.info { "$apiName request ID: ${result.responseMetadata()?.requestId()}" }
return result
RetryableOperation<Unit>().execute(
operation = {
result = apiCall()
},
isRetryable = { e ->
when (e) {
is ValidationException,
is ThrottlingException,
is SdkException,
is TimeoutException,
is SocketTimeoutException,
-> true
else -> false
Copy link
Contributor Author

@dhasani23 dhasani23 May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the same retry strategy from:

isRetryable = { e ->
when (e) {
is ValidationException,
is ThrottlingException,
is SdkException,
is TimeoutException,
-> true
else -> false

in the ExportResultArchive (download) API

and added SocketTimeoutException (which is not a type of TimeoutException, so it needs to be explicitly listed here)

}
},
errorHandler = { e, attempts ->
LOG.error(e) { "After $attempts attempts, $apiName failed: ${e.message}" }
throw e
}
)
} catch (e: Exception) {
LOG.error(e) { "$apiName failed: ${e.message}" }
throw e // pass along error to callee
LOG.error(e) { "$apiName failed: ${e.message}; may have been retried up to 3 times" }
throw e
}
LOG.info { "$apiName request ID: ${result?.responseMetadata()?.requestId()}" }
return result ?: error("$apiName failed")
}

suspend fun downloadExportResultArchive(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ class RetryableOperation<T> {
isRetryable: (Exception) -> Boolean = { it is RetryableException },
errorHandler: (suspend (Exception, Int) -> Nothing),
): T {
while (attempts < MAX_RETRY_ATTEMPTS) {
while (attempts < MAX_ATTEMPTS) {
try {
return operation()
} catch (e: Exception) {
attempts++
if (attempts >= MAX_RETRY_ATTEMPTS || !isRetryable(e)) {
if (attempts >= MAX_ATTEMPTS || !isRetryable(e)) {
errorHandler.invoke(e, attempts)
}
delay(getJitteredDelay())
Expand All @@ -48,6 +48,6 @@ class RetryableOperation<T> {
companion object {
private const val INITIAL_DELAY = 100L
private const val MAX_BACKOFF = 10000L
private const val MAX_RETRY_ATTEMPTS = 3
private const val MAX_ATTEMPTS = 4
Copy link
Contributor Author

@dhasani23 dhasani23 May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing the intention was to do 3 retries (after the initial attempt), meaning this should technically be set to 4, and naming it MAX_ATTEMPTS is more accurate.

}
}
Loading