Search code examples
amazon-web-servicesamazon-s3aws-lambda

CopyObjectCommand transient failures on lambda


I'm writing a lambda to copy files from a source s3 bucket to a destination bucket in the same account, and then delete the file from the original bucket. The lambda functions normally for most files, but occasionally will repeatedly fail to copy a file over, even though the original file will always get deleted. I notice that when a file fails to copy over, uploading it and running the lambda again will cause it to consistently fail to copy, but other files with the same file type and extension will copy successfully, including files of a larger or smaller size. The lambda will not throw or log any error, but if I check the destination s3 bucket then I'll see that no object has been copied. The destination object key is a UUID generated within the lambda and I'm confident there's nothing else interacting with files in the destination bucket. The files I'm testing are also relatively small (<100 KB). The relevant code looks like this:

const s3Client = new S3Client({
  region: 'us-east-1'
});

var copyObjectParams = {
  Bucket: DESTINATION_BUCKET,
  CopySource: path.join('', sourceBucketName, sourceObjectKey),
  Key: destinationKey
};

await s3Client.send(new CopyObjectCommand(copyObjectParams), function (err,data){
  if (err) throw err;
  else console.log(data);
});

await s3Client.send(new DeleteObjectCommand({
  Bucket: sourceBucketName,
  Key: sourceObjectKey
}));

My first question is: the copyObjectResult is not logged consistently. When a file fails to copy over there is no error thrown, and no result logged in the console. However, occasionally even when an object is copied over successfully, there will be no log in the console. Other times, there will be a log containing the $metadata and CopyObjectResult. Am I logging the errors incorrectly? How can I get more useful error logs from the copyObjectCommand?

More importantly, my second question is: what is causing these transient issues? Why does the deleteObject command always run regardless of the outcome of the copyObject command? (of course if i screw up the copyObjectParams on purpose, then an error does get thrown as expected) For some reason, the issue seemed to resolve after I increased the lambda's RAM from 128 to 512, but I haven't tested the function rigorously after these changes, and I'm not convinced that increasing memory would resolve an issue with an API call when the lambda can handle other S3/dynamo client actions just fine. But I've been very confused by the transient failures so far and the lack of logs is not helping.


Solution

  • From this answer and documentation's Response and special errors section, it seems like the error happened somewhere during in the service and not in the SDK which works as the middle layer between client and S3 service performing the operation, hence the SDK didn't raise error when the operation failed. Looks like the recommended approach in the documentation would be to use waitUntilObjectExists to check if operation is successful

    try {
      var copyObjectParams = {
        Bucket: DESTINATION_BUCKET,
        CopySource: path.join('', sourceBucketName, sourceObjectKey),
        Key: destinationKey
      };
    
      const copyObjectResult = await s3Client.send(new CopyObjectCommand(copyObjectParams));
      console.log('Copy Object Result:', copyObjectResult); // Log the full result
      await waitUntilObjectExists(
        { s3Client },
        { Bucket: destinationBucket, Key: destinationKey },
      );
      console.log(
        `Successfully copied ${sourceBucketName}/${sourceObjectKey} to ${destinationBucket}/${destinationKey}`,
      );
      await s3Client.send(new DeleteObjectCommand({
        Bucket: sourceBucketName,
        Key: sourceObjectKey
      }));
    
    } catch (error) {
      console.error('Error copying or deleting object:', error); // Log the full error object
    }