Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
@@ -0,0 +1,4 @@
{
"type" : "bugfix",
"description" : "Amazon Q Code Transformation: retry initial project upload"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runInEdt
import com.intellij.serviceContainer.AlreadyDisposedException
import com.intellij.util.io.HttpRequests
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.apache.commons.codec.digest.DigestUtils
import software.amazon.awssdk.core.exception.SdkClientException
import software.amazon.awssdk.services.codewhispererruntime.model.ResumeTransformationResponse
Expand All @@ -20,6 +22,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.Transformation
import software.amazon.awssdk.services.codewhispererruntime.model.TransformationUserActionStatus
import software.amazon.awssdk.services.codewhispererstreaming.model.TransformationDownloadArtifactType
import software.amazon.awssdk.services.ssooidc.model.SsoOidcException
import software.aws.toolkits.core.utils.Waiters.waitUntil
import software.aws.toolkits.core.utils.error
import software.aws.toolkits.core.utils.exists
import software.aws.toolkits.core.utils.getLogger
Expand Down Expand Up @@ -55,6 +58,8 @@ import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.nio.file.Path
import java.time.Instant
import java.util.Base64
Expand Down Expand Up @@ -142,7 +147,7 @@ class CodeModernizerSession(
*
* Based on [CodeWhispererCodeScanSession]
*/
fun createModernizationJob(copyResult: MavenCopyCommandsResult?): CodeModernizerStartJobResult {
suspend fun createModernizationJob(copyResult: MavenCopyCommandsResult?): CodeModernizerStartJobResult {
LOG.info { "Compressing local project" }
val payload: File?
var payloadSize = 0
Expand Down Expand Up @@ -377,8 +382,10 @@ class CodeModernizerSession(
/**
* Adapted from [CodeWhispererCodeScanSession]
*/
fun uploadPayload(payload: File): String {
val sha256checksum: String = Base64.getEncoder().encodeToString(DigestUtils.sha256(FileInputStream(payload)))
suspend fun uploadPayload(payload: File): String {
val sha256checksum: String = Base64.getEncoder().encodeToString(withContext(Dispatchers.IO) {
DigestUtils.sha256(FileInputStream(payload))
})
if (isDisposed.get()) {
throw AlreadyDisposedException("Disposed when about to create upload URL")
}
Expand All @@ -394,17 +401,23 @@ class CodeModernizerSession(
throw AlreadyDisposedException("Disposed when about to upload project artifact to s3")
}
val uploadStartTime = Instant.now()
try {
waitUntil (
exceptionsToIgnore = setOf(
UnknownHostException::class,
SocketTimeoutException::class,
HttpRequests.HttpStatusException::class,
ConnectException::class
Copy link
Contributor Author

Choose a reason for hiding this comment

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

UnknownHostException and SocketTimeoutException can be reproduced by turning off your WiFi, HttpStatusException could be a 500 or something, and I've occasionally seen ConnectException too, so I think all of these are worth retrying on.

)
)
{
clientAdaptor.uploadArtifactToS3(
createUploadUrlResponse.uploadUrl(),
payload,
sha256checksum,
createUploadUrlResponse.kmsKeyArn().orEmpty(),
) { shouldStop.get() }
} catch (e: Exception) {
LOG.error { "Unexpected error when uploading project artifact to S3: $e" }
throw e // pass along error to callee
}
LOG.info { "Upload to S3 succeeded" }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not a big fan of implementing our own retry logic rather than doing it at the SDK layer, but the existing S3 client's upload functionality requires a bucket name as a part of the request, and 1) I don't think we should have the internal S3 bucket names we use floating around this repo, 2) we use multiple different buckets, and 3) we don't have access to the bucket name in the response of our CreateUploadUrl — this API would have to be changed and then we can use the existing S3 client.

Copy link
Contributor Author

@dhasani23 dhasani23 Dec 17, 2024

Choose a reason for hiding this comment

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

Update: use waitUntil instead of a for loop; makes this retry logic even simpler

if (!shouldStop.get()) {
LOG.info { "Uploaded artifact. Latency: ${calculateTotalLatency(uploadStartTime, Instant.now())}ms" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class GumbyClient(private val project: Project) {
var result: CodeWhispererRuntimeResponse? = null
try {
result = apiCall()
LOG.info { "$apiName request ID: ${result.responseMetadata()?.requestId()}" }
return result
} catch (e: Exception) {
LOG.error(e) { "$apiName failed: ${e.message}" }
Expand Down
Loading