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)<---
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). –
@SimonBuchan Fantastico! Controllerò. – InvertedAcceleration
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. –