6

SfondoCome specificare che HTTP Status Code 304 (NotModified) non è una condizione di errore all'interno dell'API GetObject di Amazon S3?

che sto cercando di fare uso di S3 più grande livello di cache di un 'infinito' da un po 'in modo equo' documenti XML statici. Voglio assicurarmi che l'applicazione client (che sarà in esecuzione su migliaia di macchine contemporaneamente e richieda i documenti XML più volte all'ora) scarichi questi documenti XML solo se il loro contenuto è cambiato dall'ultima volta che l'applicazione client li ha scaricati.

approccio

Su Amazon S3, possiamo usare ETAG HTTP per questo. Di default gli oggetti Amazon S3 hanno il loro ETAG impostato sull'hash MD5 dell'oggetto.

Possiamo quindi specificare l'hash MD5 del documento XML all'interno della proprietà GetObjectRequest.ETagToNotMatch. Ciò garantisce che quando effettuiamo la chiamata (o nel mio caso la versione asincrona AmazonS3.BeginGetObject e AmazonS3.EndGetObject), che se il documento richiesto ha lo stesso hash MD5 contenuto nello GetObjectRequest.ETagToNotMatch, S3 restituisce automaticamente il codice di stato HTTP 304 (NotModified) e il contenuto effettivo del documento XML è non scaricato.

Problema

Il problema però è che quando si chiama AmazonS3.GetObject (o è asincrono equivalente) l'API di Amazon .Net vede in realtà il codice di stato HTTP 304 (NotModified) come un errore e riprova la richiesta GET a tre volte e infine lancia un Amazon.S3.AmazonS3Exception: Maximum number of retry attempts reached : 3.

Ovviamente potrei cambiare questa implementazione per utilizzare AmazonS3.GetObjectMetaData e quindi confrontare l'ETAG e utilizzare AmazonS3.GetObject se non corrispondono, ma poi ci sono due richieste a S3 invece di una quando il file è obsoleto. Preferirei avere una richiesta indipendentemente dal fatto che il documento XML abbia bisogno di essere scaricato o meno.

Qualche idea? È un bug o mi sto perdendo qualcosa? C'è anche un modo per ridurre il numero di tentativi a uno e 'elaborare' l'eccezione (anche se mi sento 'pessimo' su questa rotta).

Attuazione

sto usando l'AWS SDK per .NET (versione 1.3.14).

Qui è la mia realizzazione (ridotta leggermente per tenerlo più breve):

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; 
} 

Ho poi chiamo questo tipo:

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..."); 

Ciò allora produce questo output file di 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)<--- 

risposta

2

Ho anche postato questa domanda sul forum degli sviluppatori di Amazon e ho ricevuto una risposta da un dipendente AWS ufficiale:

Dopo aver esaminato questo, capiamo il problema ma stiamo cercando per un feedback su come gestirlo al meglio.

primo approccio è quello di avere questo ritorno operazione con una proprietà sul GetObjectResponse indica che l'oggetto non è stato restituito o impostato il flusso di output a null. Questo sarebbe più pulito da codificare ma crea un leggero comportamento di interruzione per chiunque si basi su un'eccezione lanciata, anche se dopo i 3 tentativi. Sarebbe anche incoerente con l'operazione CopyObject che genera un'eccezione senza tutti i pazzi tentativi.

L'altra opzione è di generare un'eccezione simile a CopyObject che ci mantiene coerenti e senza modifiche di interruzione ma è più difficile codificare .

Se qualcuno ha opinioni su come gestirlo, risponda allo questa discussione.

Norm

ho già aggiunto i miei pensieri al thread, se qualcun altro è interessato a partecipare ecco il link:

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


NOTA: Quando questo è stato risolto da Amazon, aggiornerò la mia risposta per riflettere il risultato.


UPDATE: (2012-01-24) Ancora in attesa di ulteriori informazioni da Amazon.

+0

Di recente ci siamo imbattuti in questo, e ho deciso che una patch rapida per l'SDK AWS sarebbe la migliore risposta dal momento che Amazon ovviamente non ha intenzione di risolverlo in qualunque momento presto. https://github.com/skilitix/aws-sdk-net è il nostro fork che corregge il tentativo su 304 e aggiunge un booleano 'NotModified' a' GetObjectResponse' - se passo il tempo a capire un'API più compatibile con le versioni precedenti Presenterò una patch ad Amazon (nel frattempo accettiamo PRs: P). –

+0

@SimonBuchan Fantastico! Controllerò. – InvertedAcceleration

+0

Nota che non siamo al passo con le versioni precedenti, siamo già in ritardo di circa 3 punti, ma sembrano essere solo nuove API e dovrebbero auto-fondersi. –