Richiedere precondizioni

Questa pagina illustra le precondizioni delle richieste, che utilizzi per impedire che le richieste vengano applicate a una risorsa quando questa si trova in uno stato imprevisto.

Introduzione

Quando le precondizioni vengono utilizzate in una richiesta a Cloud Storage, la richiesta procede solo se la risorsa di destinazione soddisfa i criteri definiti nelle precondizioni. I controlli delle precondizioni assicurano che un bucket o un oggetto si trovi nello stato previsto, consentendoti di eseguire aggiornamenti di lettura-modifica-scrittura sicuri e operazioni condizionali.

Le precondizioni vengono spesso utilizzate per evitare race condition nelle richieste di modifica, come caricamenti, eliminazioni o aggiornamenti dei metadati. Le race condition possono verificarsi quando la stessa richiesta viene inviata ripetutamente o quando processi indipendenti tentano di modificare la stessa risorsa. Per saperne di più, consulta Esempi di race condition e danneggiamento dei dati. Le precondizioni vengono spesso utilizzate anche per recuperare i metadati e i dati degli oggetti in richieste successive, per garantire che l'oggetto non sia cambiato nel tempo tra le due richieste.

Criteri di precondizione

Cloud Storage supporta l'utilizzo di diverse proprietà delle risorse immutabili nelle precondizioni:

La tabella seguente elenca le precondizioni supportate dall'API JSON e dall'API XML:

API JSON API XML Descrizione
Parametro di query ifGenerationMatch Intestazione x-goog-if-generation-match La richiesta procede se il generation della risorsa di destinazione corrisponde al valore utilizzato nella precondizione. Se i valori non corrispondono, la richiesta non va a buon fine e viene restituita una risposta 412 Precondition Failed.
Parametro di query ifMetagenerationMatch Intestazione x-goog-if-metageneration-match La richiesta procede se il metageneration della risorsa di destinazione corrisponde al valore utilizzato nella precondizione. Se i valori non corrispondono, la richiesta non va a buon fine e viene restituita una risposta 412 Precondition Failed.
Parametro di query ifGenerationNotMatch N/D La richiesta procede se il valore generation della risorsa di destinazione non corrisponde al valore utilizzato nella precondizione. Se i valori corrispondono, la richiesta non va a buon fine e viene restituita una risposta 304 Not Modified.
Parametro di query ifMetagenerationNotMatch N/D La richiesta procede se il valore metageneration della risorsa di destinazione non corrisponde al valore utilizzato nella precondizione. Se i valori corrispondono, la richiesta non va a buon fine e viene restituita una risposta 304 Not Modified.
Intestazione If-Match Intestazione If-Match Applicabile alle richieste che recuperano dati. La richiesta procede se il ETag della risorsa di destinazione corrisponde al valore utilizzato nella precondizione. Se i valori non corrispondono, la richiesta non va a buon fine e viene restituita una risposta 412 Precondition Failed.
Intestazione If-None-Match Intestazione If-None-Match Applicabile alle richieste che recuperano dati. La richiesta procede se il valore ETag della risorsa di destinazione non corrisponde al valore utilizzato nella precondizione. Se i valori corrispondono, la richiesta non va a buon fine e viene restituita una risposta 304 Not Modified.
N/D Intestazione If-Modified-Since La richiesta procede se la risorsa di destinazione ha una data Last-Modified successiva al valore utilizzato nella precondizione. Se la risorsa di destinazione non soddisfa questa precondizione, la richiesta non va a buon fine e viene restituita una risposta 304 Not Modified.
N/D Intestazione If-Unmodified-Since La richiesta procede se la risorsa di destinazione ha una data Last-Modified precedente o uguale al valore utilizzato nella precondizione. Se la risorsa di destinazione non soddisfa questa precondizione, la richiesta non va a buon fine e viene restituita una risposta 412 Precondition Failed.

Precondizioni per la composizione degli oggetti

Quando esegui la composizione degli oggetti, sia l'API JSON sia l'API XML supportano quanto segue:

  • Le precondizioni generation-match e metageneration-match per l'oggetto di destinazione.

  • La precondizione generation-match per ogni oggetto di origine. L'utilizzo di questa precondizione impedisce l'utilizzo di componenti errati nel caso in cui un processo indipendente sovrascriva uno dei componenti previsti della composizione. Se utilizzi le precondizioni e si verifica una sovrascrittura di questo tipo, le operazioni compose non vanno a buon fine e viene restituita una risposta 412 Precondition Failed.

Precondizioni per la copia degli oggetti

Quando copi o riscrivi un oggetto all'interno di Cloud Storage, sia l'API JSON che l'API XML supportano l'utilizzo di precondizioni standard per l'oggetto di destinazione. Ogni API ha un supporto aggiuntivo per le precondizioni per gli oggetti di origine:

  • L'API JSON supporta le precondizioni di generazione e metagenerazione per l'oggetto di origine, che vengono specificate utilizzando parametri di ricerca con prefisso ifSource.

  • Tutte le precondizioni supportate dall'API XML possono essere utilizzate per l'oggetto di origine. Queste precondizioni sono specificate nelle intestazioni con prefisso x-goog-copy-source-.

Il valore 0 in una precondizione di corrispondenza della generazione

La precondizione generation-match accetta il valore 0 come caso speciale. Quando una precondizione generation-match con un valore 0 è inclusa in una richiesta, la richiesta procede solo se nel bucket non esiste alcun oggetto con il nome specificato o se nel bucket sono presenti solo versioni non attuali dell'oggetto. Se esiste una versione pubblicata con il nome specificato, la richiesta non va a buon fine e viene restituito il codice di stato 412 Precondition Failed.

Best practice e considerazioni

  • Puoi utilizzare più precondizioni in una singola richiesta. Se una qualsiasi delle precondizioni non viene soddisfatta, l'intera richiesta non va a buon fine.

  • I bucket non hanno un numero di generazione, ma hanno un numero di metagenerazione. Non devi utilizzare precondizioni che specificano un numero di generazione in una richiesta di bucket.

  • Se utilizzi una precondizione di metagenerazione in una richiesta di oggetto, devi sempre utilizzare anche una precondizione di generazione. In questo modo, la richiesta non viene eseguita correttamente su un oggetto diverso che, per coincidenza, ha un numero di metagenerazione che supera la precondizione.

  • Per i bucket che hanno versioni degli oggetti sia attive che non attuali, le richieste di oggetti non si applicano alle versioni non attuali, a meno che un numero di generazione non sia esplicitamente incluso nella richiesta. Ciò significa che per una richiesta generale che utilizza precondizioni, la richiesta non va a buon fine se la versione live non corrisponde alla precondizione, indipendentemente dal fatto che una versione non corrente corrisponda o meno alle precondizioni.

  • In genere, è consigliabile utilizzare le precondizioni di generazione e metagenerazione anziché le precondizioni ETag. Insieme, i numeri di generazione e metagenerazione tengono traccia di tutti gli aggiornamenti degli oggetti, incluse le modifiche ai metadati, fornendo una garanzia più solida rispetto agli ETag. Inoltre, i numeri di generazione e metagenerazione sono coerenti tra le API, mentre gli ETag non lo sono.

  • Le precondizioni non possono essere utilizzate nei caricamenti multiparte dell'API XML. Se provi a farlo, viene generato un errore 400 NotImplemented.

Costo delle precondizioni

Molte architetture che utilizzano le precondizioni richiedono di effettuare una richiesta di metadati dell'oggetto prima della richiesta principale, al fine di determinare il numero di generazione e/o metagenerazione corrente:

  • Una richiesta aggiuntiva significa che puoi raddoppiare la parte di rete della latenza complessiva dell'operazione aggiungendo un'andata e ritorno aggiuntiva, che potrebbe essere un fattore importante nelle operazioni sensibili alla latenza.

A seconda dell'applicazione, esistono modi per ridurre gli impatti dell'utilizzo delle precondizioni, ad esempio:

  • Archiviare localmente i numeri di generazione e metagenerazione degli oggetti, in modo da conoscere già i numeri corretti da utilizzare nella precondizione.
  • Conoscenza a livello di applicazione di quali oggetti sono stati appena creati, in modo da sapere già quando utilizzare la precondizione if-generation-match:0.

Esempio: utilizzo di una precondizione

L'esempio seguente utilizza la precondizione generation-match in una richiesta di caricamento di un oggetto. Affinché la richiesta possa procedere, nel bucket deve essere archiviato un oggetto preesistente con il nome specificato e il numero di generazione dell'oggetto preesistente deve corrispondere al numero fornito nella precondizione:

Riga di comando

Utilizza il flag --if-generation-match insieme al comando normale:

gcloud storage cp OBJECT_LOCATION gs://DESTINATION_BUCKET_NAME --if-generation-match=GENERATION

Dove:

  • GENERATION è il numero di generazione previsto dell'oggetto che stai sostituendo. Ad esempio, 1122334455667788.

  • OBJECT_LOCATION è il percorso locale dell'oggetto. Ad esempio, Desktop/dog.png.

  • DESTINATION_BUCKET_NAME è il nome del bucket in cui stai caricando l'oggetto. Ad esempio, my-bucket.

API JSON

  1. Avere gcloud CLI installato e inizializzato, che ti consente di generare un token di accesso per l'intestazione Authorization.

  2. Utilizza cURL per chiamare l'API JSON con una richiesta POST Object:

    curl -X POST --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      "https://storage.googleapis.com/upload/storage/v1/b/BUCKET_NAME/o?uploadType=media&name=OBJECT_NAME"&ifGenerationMatch=GENERATION"

    Dove:

    • OBJECT_LOCATION è il percorso locale del tuo oggetto. Ad esempio, Desktop/dog.png.
    • OBJECT_CONTENT_TYPE è il tipo di contenuti dell'oggetto. Ad esempio, image/png.
    • BUCKET_NAME è il nome del bucket in cui stai caricando l'oggetto. Ad esempio, my-bucket.
    • OBJECT_NAME è il nome che vuoi assegnare al tuo oggetto. Ad esempio, dog.png.
    • GENERATION è il numero di generazione previsto dell'oggetto che stai sostituendo. Ad esempio, 1122334455667788.

API XML

  1. Avere gcloud CLI installato e inizializzato, che ti consente di generare un token di accesso per l'intestazione Authorization.

  2. Utilizza cURL per chiamare l'API XML con una richiesta PUT Object:

    curl -X PUT --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      -H "x-goog-if-generation-match: GENERATION" \
      "https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME"

    Dove:

    • OBJECT_LOCATION è il percorso locale del tuo oggetto. Ad esempio, Desktop/dog.png.
    • OBJECT_CONTENT_TYPE è il tipo di contenuti dell'oggetto. Ad esempio, image/png.
    • GENERATION è il numero di generazione previsto dell'oggetto che stai sostituendo. Ad esempio, 1122334455667788.
    • BUCKET_NAME è il nome del bucket in cui stai caricando l'oggetto. Ad esempio, my-bucket.
    • OBJECT_NAME è il nome che vuoi assegnare al tuo oggetto. Ad esempio, dog.png.

Scenari per l'utilizzo delle precondizioni

I seguenti scenari esplorano esempi di race condition e memorizzazione nella cache che traggono vantaggio dall'utilizzo delle precondizioni.

Più tentativi di richiesta

Cloud Storage è un sistema distribuito. Poiché le richieste possono non riuscire a causa di condizioni di rete o di servizio, il modo consigliato per riprovare in caso di errori è il backoff esponenziale. Tuttavia, a causa della natura dei sistemi distribuiti, a volte questi nuovi tentativi possono causare comportamenti imprevisti.

Considera il seguente caso: vuoi eliminare un oggetto, file.txt, archiviato in uno dei tuoi bucket. In seguito, vuoi aggiungere un nuovo oggetto con lo stesso nome al bucket. Per farlo, invii una richiesta di eliminazione per eliminare l'oggetto. Tuttavia, una condizione di rete, ad esempio un router intermedio che perde temporaneamente la connettività, impedisce alla richiesta di raggiungere Cloud Storage e non ricevi una risposta.

Poiché non hai ricevuto una risposta alla prima richiesta, invii una seconda richiesta di eliminazione per l'oggetto, che va a buon fine, e ricevi una risposta che conferma l'eliminazione. Un minuto dopo, carichi un nuovo file.txt e il caricamento va a buon fine.

Si verifica una race condition se il router che ha perso la connettività la recupera successivamente e invia la richiesta di eliminazione originale, apparentemente persa, a Cloud Storage. Quando la richiesta arriva a Cloud Storage, l'operazione riesce perché è presente un nuovo file.txt. Cloud Storage invia una risposta che non ricevi perché il tuo client ha smesso di ascoltarla. Non solo il nuovo file viene eliminato, contrariamente alle tue intenzioni, ma non sei nemmeno a conoscenza della seconda eliminazione.

Il seguente diagramma mostra cosa è successo:

Prevenzione della race condition

Per evitare che si verifichi la situazione descritta sopra, devi iniziare recuperando i metadati di file.txt per determinare la generazione attuale. Poi, utilizzi la generazione in una precondizione di corrispondenza della generazione che includi nella richiesta di eliminazione. La precondizione garantisce che venga eliminato solo l'oggetto con quel numero di generazione specifico, indipendentemente da quando la richiesta di eliminazione raggiunge Cloud Storage o da quante volte viene inviata la richiesta di eliminazione con la precondizione. Qualsiasi tentativo involontario di eliminare una generazione diversa di file.txt non va a buon fine con il codice di risposta 412 Precondition Failed.

Poiché interruzioni di rete simili potrebbero causare race condition per la richiesta di caricamento che ha seguito la richiesta di eliminazione, puoi evitare molte di queste race condition utilizzando il valore 0 in una precondizione di corrispondenza della generazione inclusa nella richiesta di caricamento. L'utilizzo di questa precondizione garantisce che i nuovi tentativi di caricamento non scrivano accidentalmente l'oggetto due volte, perché la precondizione consente alla richiesta di procedere solo se non esistono generazioni correnti dell'oggetto.

Con queste precondizioni, proteggi i tuoi dati da perdite accidentali durante l'esecuzione delle richieste di eliminazione e caricamento. Questo è illustrato nel seguente diagramma:

Associazione dei metadati degli oggetti

I dati e i metadati di un oggetto sono entità separate che insieme definiscono l'oggetto in Cloud Storage. Poiché esistono separatamente, è possibile che i dati dell'oggetto cambino mentre lavori con i metadati dell'oggetto.

Considera i seguenti casi:

  • Vuoi scaricare i metadati e i dati di un oggetto, che devono essere recuperati da Cloud Storage in due richieste separate. Richiedi prima i metadati dell'oggetto, ma prima che tu possa richiedere i dati dell'oggetto, un processo o un utente indipendente sostituisce l'oggetto. La richiesta dei dati dell'oggetto ha comunque esito positivo, ma ora hai i metadati dell'oggetto precedente e i dati del nuovo oggetto.

  • Vuoi aggiornare i metadati di un oggetto, quindi recuperi i metadati correnti dell'oggetto per determinarne lo stato attuale. Prima che tu possa inviare la richiesta di aggiornamento dei metadati con le modifiche desiderate, un processo o un utente indipendente sostituisce l'oggetto. La richiesta di modifica dei metadati per il nuovo oggetto viene comunque eseguita correttamente, ma ora è associata a dati dell'oggetto diversi da quelli previsti.

Prevenzione della race condition

Per evitare che si verifichino queste situazioni, devi utilizzare il numero di generazione restituito nella richiesta iniziale dei metadati dell'oggetto, quindi utilizzare questo valore in una precondizione di corrispondenza della generazione nella seconda richiesta. In questo modo, i metadati corrispondono correttamente ai dati oppure la seconda richiesta non va a buon fine con un codice di risposta 412 Precondition Failed, consentendoti di richiedere i metadati corretti per il nuovo oggetto.

Se temi che i metadati dell'oggetto possano cambiare tra la prima e la seconda richiesta, puoi anche copiare il numero di metagenerazione trovato nella richiesta iniziale e utilizzarlo in una precondizione metageneration-match nella seconda richiesta.

Aggiornamento della copia locale

Se hai una copia locale di un oggetto archiviato in Cloud Storage, spesso vuoi che la copia locale rimanga aggiornata con la copia archiviata nel bucket. Tuttavia, se l'oggetto archiviato nel bucket non cambia, non vuoi sprecare tempo e risorse per scaricarlo di nuovo, soprattutto se l'oggetto è di grandi dimensioni.

Per evitare download non necessari di contenuti ancora aggiornati, puoi utilizzare il numero di generazione della tua copia locale come valore in una precondizione generation-not-match, che includi nella richiesta di download:

  • Se i dati nel bucket continuano a corrispondere alla copia locale, i numeri di generazione corrispondono, causando l'esito negativo della precondizione. Di conseguenza, la richiesta complessiva non va a buon fine con una risposta 304 Not Modified e i dati non vengono scaricati inutilmente.

  • Se i dati nel bucket sono cambiati, i numeri di generazione non corrispondono e la precondizione ha esito positivo. Ciò significa che la richiesta complessiva procede normalmente e scarica la versione aggiornata dei contenuti.

Passaggi successivi