6

AntecedentesCómo especificar que el código de estado HTTP 304 (no modificado) no es una condición de error dentro de la API GetObject de Amazon S3?

que estoy tratando de hacer uso de S3 tan grande capa de almacenamiento en caché de un 'infinito' para algunos 'bastante' documentos XML estáticos. Quiero asegurarme de que la aplicación cliente (que se ejecutará en miles de máquinas al mismo tiempo y solicitando los documentos XML muchas veces por hora) solo descargue estos documentos XML si su contenido ha cambiado desde la última vez que la aplicación cliente los descargó.

Enfoque

en Amazon S3, podemos utilizar ETAG HTTP para esto. Por defecto, los objetos de Amazon S3 tienen su ETAG configurado en el hash MD5 del objeto.

A continuación, podemos especificar el hash MD5 del documento XML dentro de la propiedad GetObjectRequest.ETagToNotMatch. Esto garantiza que cuando realizamos la llamada AmazonS3.GetObject (o en mi caso la versión asincrónica AmazonS3.BeginGetObject y AmazonS3.EndGetObject), si el documento solicitado tiene el mismo hash MD5 que el GetObjectRequest.ETagToNotMatch, S3 devuelve automáticamente el código de estado HTTP 304 (NotModified)) y el contenido real del documento XML es no descargado.

Problema

El problema sin embargo es que al llamar AmazonS3.GetObject (o es asíncrono equivalente) de la API de Amazon .Net realmente ve el código de estado HTTP 304 (NotModified) como un error y se vuelve a intentar la petición de obtención de tres veces y luego arroja un Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3.

Obviamente podría cambiar esta implementación para usar AmazonS3.GetObjectMetaData y luego comparar el ETAG y usar AmazonS3.GetObject si no coinciden, pero luego hay dos solicitudes a S3 en vez de una cuando el archivo está desactualizado. Preferiría tener una solicitud independientemente de si el documento XML debe descargarse o no.

¿Alguna idea? ¿Es esto un error o me estoy perdiendo algo? ¿Hay alguna forma en que pueda reducir el número de intentos a uno y 'procesar' la excepción (aunque me siento 'asco' por esta ruta).

Implementación

estoy usando el AWS SDK para .NET (versión 1.3.14).

Aquí es mi aplicación (reducido ligeramente para mantenerlo corto):

public Task<GetObjectResponse> DownloadString(string key, string etag = null) { 

    var request = new GetObjectRequest { Key = key, BucketName = Bucket }; 

    if (etag != null) { 
     request.ETagToNotMatch = etag; 
    } 

    var task = Task<GetObjectResponse>.Factory.FromAsync(_s3Client.BeginGetObject, _s3Client.EndGetObject, request, null); 

    return task; 
} 

entonces yo llamo a esto como:

var dlTask   = s3Manager.DownloadString("new one", "d7db7bc318d6eb9222d728747879b52e"); 
var responseTasks = new[] 
    { 
     dlTask.ContinueWith(x => _log.Error("Error downloading string.", x.Exception), TaskContinuationOptions.OnlyOnFaulted), 
     dlTask.ContinueWith(x => _log.Warn("Downloading string was cancelled."), TaskContinuationOptions.OnlyOnCanceled), 
     dlTask.ContinueWith(x => _log.Info(string.Format("Done with download: {0}", x.Result.ETag)), TaskContinuationOptions.OnlyOnRanToCompletion) 
    }; 

try { 
    Task.WaitAny(responseTasks); 
} catch (AggregateException aex) { 
    _log.Error("Error while processing download string.", aex); 
} 

_log.Info("Exiting..."); 

Esto produce entonces esta salida del archivo de registro:

2011-10-11 13:21:20,376 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.6140812. 
2011-10-11 13:21:20,385 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one. 
2011-10-11 13:21:20,789 [11] INFO Amazon.S3.AmazonS3Client - Retry number 1 for request GetObject. 
2011-10-11 13:21:22,329 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.1400356. 
2011-10-11 13:21:22,329 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one. 
2011-10-11 13:21:23,929 [11] INFO Amazon.S3.AmazonS3Client - Retry number 2 for request GetObject. 
2011-10-11 13:21:26,508 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:00.9790314. 
2011-10-11 13:21:26,508 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one. 
2011-10-11 13:21:32,908 [11] INFO Amazon.S3.AmazonS3Client - Retry number 3 for request GetObject. 
2011-10-11 13:21:40,604 [11] INFO Amazon.S3.AmazonS3Client - Received response for GetObject (id 2ee99002-d148-4572-b19b-29259534f48f) with status code NotModified in 00:00:01.2950718. 
2011-10-11 13:21:40,605 [11] INFO Amazon.S3.AmazonS3Client - Request for GetObject is being redirect to https://s3.amazonaws.com/x/new%20one. 
2011-10-11 13:21:40,621 [11] ERROR Amazon.S3.AmazonS3Client - Error for GetResponse 
Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3 
    at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause) 
    at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode) 
    at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result) 
2011-10-11 13:21:40,635 [10] INFO Example.Program - Exiting... 
2011-10-11 13:21:40,638 [19] ERROR Example.Program - Error downloading string. 
System.AggregateException: One or more errors occurred. ---> Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3 
    at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause) 
    at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode) 
    at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result) 
    at Amazon.S3.AmazonS3Client.endOperation[T](IAsyncResult result) 
    at Amazon.S3.AmazonS3Client.EndGetObject(IAsyncResult asyncResult) 
    at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endMethod, TaskCompletionSource`1 tcs) 
    --- End of inner exception stack trace --- 
---> (Inner Exception #0) Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3 
    at Amazon.S3.AmazonS3Client.pauseOnRetry(Int32 retries, Int32 maxRetries, HttpStatusCode status, String requestAddr, WebHeaderCollection headers, Exception cause) 
    at Amazon.S3.AmazonS3Client.handleHttpResponse[T](S3Request userRequest, HttpWebRequest request, HttpWebResponse httpResponse, Int32 retries, TimeSpan lengthOfRequest, T& response, Exception& cause, HttpStatusCode& statusCode) 
    at Amazon.S3.AmazonS3Client.getResponseCallback[T](IAsyncResult result) 
    at Amazon.S3.AmazonS3Client.endOperation[T](IAsyncResult result) 
    at Amazon.S3.AmazonS3Client.EndGetObject(IAsyncResult asyncResult) 
    at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endMethod, TaskCompletionSource`1 tcs)<--- 

Respuesta

2

También publiqué esta pregunta en el foro de desarrolladores de Amazon y obtuve una respuesta de un empleado oficial de AWS:

Después de investigar esto, entendemos el problema, pero estamos buscando para obtener comentarios sobre la mejor manera de manejar esto.

El primer enfoque es hacer que esta operación retorne con una propiedad en el GetObjectResponse que indica que el objeto no se devolvió o estableció como nulo. Esto sería más claro para codificar en contra, pero crea un comportamiento de interrupción leve para cualquiera que confíe en una excepción lanzada, aunque después de los 3 intentos. También sería incompatible con la operación CopyObject que arroja una excepción sin todos los enloquecidos reintentos.

La otra opción es que lanzamos una excepción similar a CopyObject que nos mantiene consistentes y sin cambios de última hora, pero es más difícil para codificar.

Si alguien tiene opiniones sobre la forma de manejar esto, responda este hilo.

Norma

ya he añadido mis pensamientos para el hilo, si alguien más está interesado en participar aquí está el enlace:

AmazonS3.GetObject sees HTTP 304 (NotModified) as an error. Way to allow it?


NOTA: Cuando esto ha sido resuelto por Amazon. Actualizaré mi respuesta para reflejar el resultado.


ACTUALIZACIÓN: (2012-01-24) A la espera de más información de Amazon.

+0

Nos encontramos con esto recientemente, y decidí que un parche rápido para AWS SDK sería la mejor respuesta ya que Amazon obviamente no solucionará esto pronto. https://github.com/skilitix/aws-sdk-net es nuestro fork que arregla el reintento en 304 y agrega un booleano 'NotModified' a' GetObjectResponse' - si dedico tiempo a descubrir una API más compatible con la parte posterior Le enviaré un parche a Amazon (mientras tanto, aceptamos los PRs: P). –

+0

@SimonBuchan ¡Impresionante! Lo revisaré. – InvertedAcceleration

+0

Ten en cuenta que no mantenemos el ritmo de subida, ya tenemos ~ 3 puntos de retraso, pero parecen ser nuevas API y deberían fusionarse automáticamente. –

Cuestiones relacionadas