Condizioni preliminari per le richieste

In questa pagina vengono descritte le condizioni preliminari delle richieste, che puoi utilizzare per impedire l'applicazione delle richieste a una risorsa quando questa si trova in uno stato imprevisto.

Introduzione

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

Le precondizioni vengono spesso utilizzate per evitare le condizioni di gara in richieste mutate, come caricamenti, eliminazioni o aggiornamenti dei metadati. Le condizioni di gara possono verificarsi quando la stessa richiesta viene inviata ripetutamente o quando processi indipendenti tentano di modificare la stessa risorsa. Per ulteriori informazioni, vedi Esempi di condizioni di gara e danneggiamento dei dati. Le precondizioni vengono spesso utilizzate quando si recuperano metadati e dati degli oggetti nelle richieste successive, per garantire che l'oggetto non sia cambiato nell'intervallo di tempo tra le due richieste.

Criteri di precondizione

Cloud Storage supporta l'uso di diverse proprietà immutabili delle risorse in condizioni preliminari:

Nella tabella seguente sono elencate le condizioni preliminari supportate dall'API JSON e dall'API XML:

API JSON API XML Descrizione
ifGenerationMatch parametro di query Intestazione x-goog-if-generation-match La richiesta procede se il valore generation della risorsa di destinazione corrisponde al valore utilizzato nella condizione preliminare. Se i valori non corrispondono, la richiesta non riesce e restituisce una risposta 412 Precondition Failed.
ifMetagenerationMatch parametro di query Intestazione x-goog-if-metageneration-match La richiesta procede se il valore metageneration della risorsa di destinazione corrisponde al valore utilizzato nella condizione preliminare. Se i valori non corrispondono, la richiesta non riesce e restituisce una risposta 412 Precondition Failed.
ifGenerationNotMatch parametro di query N/A La richiesta procede se il valore generation della risorsa di destinazione non corrisponde al valore utilizzato nella condizione preliminare. Se i valori corrispondono, la richiesta non riesce e restituisce una risposta 304 Not Modified.
ifMetagenerationNotMatch parametro di query N/A La richiesta procede se il valore metageneration della risorsa di destinazione non corrisponde al valore utilizzato nella condizione preliminare. Se i valori corrispondono, la richiesta non riesce e restituisce una risposta 304 Not Modified.
Intestazione If-Match Intestazione If-Match Applicabile per le richieste che recuperano dati. La richiesta procede se il valore ETag della risorsa di destinazione corrisponde al valore utilizzato nella condizione preliminare. Se i valori non corrispondono, la richiesta non riesce e restituisce una risposta 412 Precondition Failed.
Intestazione If-None-Match Intestazione If-None-Match Applicabile per le richieste che recuperano dati. La richiesta procede se il valore ETag della risorsa di destinazione non corrisponde al valore utilizzato nella condizione preliminare. Se i valori corrispondono, la richiesta non riesce e restituisce una risposta 304 Not Modified.
N/A Intestazione If-Modified-Since La richiesta procede se la risorsa di destinazione ha una data Last-Modified successiva al valore utilizzato nella condizione preliminare. Se la risorsa di destinazione non soddisfa questa condizione preliminare, la richiesta non va a buon fine e viene inviata una risposta 304 Not Modified.
N/A 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 condizione preliminare, la richiesta non va a buon fine e viene inviata una risposta 412 Precondition Failed.

Condizioni preliminari per la composizione di oggetti

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

  • Le precondizioni di generazione e corrispondenza della metagenerazione per l'oggetto di destinazione.

  • La precondizione per la corrispondenza di generazione per ogni oggetto di origine. L'utilizzo di questa condizione preliminare 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, le operazioni compose non riescono e una risposta 412 Precondition Failed.

Condizioni preliminari per la copia degli oggetti

Durante la copia o la riscrittura di un oggetto all'interno di Cloud Storage, sia l'API JSON sia l'API XML supportano l'utilizzo di precondizioni standard per l'oggetto di destinazione. Ogni API offre un supporto aggiuntivo di precondizione 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 preceduti dal prefisso ifSource.

  • Tutte le condizioni preliminari 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 per la corrispondenza di generazione

La precondizione per la corrispondenza di generazione accetta il valore 0 come caso speciale. Quando in una richiesta viene inclusa una precondizione per la corrispondenza di generazione con valore 0, la richiesta procede solo se nel bucket non esiste alcun oggetto con il nome specificato o se sono presenti solo versioni non correnti dell'oggetto nel bucket. Se esiste una versione live con il nome specificato, la richiesta non riesce e viene restituito un codice di stato 412 Precondition Failed.

Best practice e considerazioni

  • Puoi utilizzare più precondizioni in una singola richiesta. Se una qualsiasi delle condizioni preliminari non viene soddisfatta, la richiesta complessiva non va a buon fine.

  • I bucket non hanno un numero di generazione, anche se hanno un numero di metagenerazione. Non utilizzare condizioni preliminari che specificano un numero di generazione in una richiesta di bucket.

  • Se utilizzi una precondizione di metagenerazione in una richiesta di oggetto, devi utilizzare sempre anche una precondizione di generazione. Questo impedisce che la richiesta abbia esito positivo su un oggetto diverso che ha accidentalmente un numero di metagenerazione che supera la precondizione.

  • Per i bucket che hanno versioni dell'oggetto attive e non correnti, le richieste degli oggetti non si applicano alle versioni non correnti, a meno che un numero di generazione non sia incluso esplicitamente nella richiesta. Ciò significa che, per una richiesta generale che utilizza le precondizioni, la richiesta non va a buon fine se la versione live non soddisfa la condizione preliminare, indipendentemente dal fatto che una versione non corrente soddisfa o meno le 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, comprese le modifiche ai metadati, garantendo una maggiore garanzia rispetto agli ETag. Inoltre, i numeri di generazione e metagenerazione sono coerenti tra le API, mentre gli ETag no.

  • Le condizioni preliminari non possono essere utilizzate nei caricamenti multiparte dell'API XML. Se provi a farlo, verrà visualizzato un errore 400 NotImplemented.

Costo delle precondizioni

Molte architetture che utilizzano 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 attuale:

  • Una richiesta aggiuntiva significa che puoi raddoppiare la porzione di rete della latenza complessiva delle operazioni aggiungendo un round trip aggiuntivo, che potrebbe essere un fattore importante per le operazioni sensibili alla latenza.

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

  • Archiviazione dei numeri di generazione e metagenerazione degli oggetti localmente in modo da conoscere già i numeri corretti da utilizzare nella condizione preliminare.
  • Conoscere l'applicazione degli oggetti appena creati, in modo da sapere già quando utilizzare la condizione preliminare if-generation-match:0.

Esempio: utilizzo di una condizione preliminare

L'esempio seguente utilizza la precondizione per la corrispondenza di generazione in una richiesta di caricamento di un oggetto. Affinché la richiesta prosegua, deve essere presente un oggetto preesistente archiviato nel bucket con il nome specificato e il numero di generazione dell'oggetto preesistente deve corrispondere al numero fornito nella condizione preliminare:

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. Installa e inizializzatogcloud CLI per generare un token di accesso per l'intestazione Authorization.

    In alternativa, puoi creare un token di accesso utilizzando OAuth 2.0 Playground e includerlo nell'intestazione Authorization.

  2. Usa cURL per chiamare l'API JSON con una richiesta Oggetto POST:

    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 dell'oggetto. Ad esempio, Desktop/dog.png.
    • OBJECT_CONTENT_TYPE è il tipo di contenuto dell'oggetto. Ad esempio, image/png.
    • BUCKET_NAME è il nome del bucket in cui carichi l'oggetto. Ad esempio, my-bucket.
    • OBJECT_NAME è il nome che vuoi assegnare all'oggetto. Ad esempio, dog.png.
    • GENERATION è il numero di generazione previsto dell'oggetto che stai sostituendo. Ad esempio, 1122334455667788.

API XML

  1. Installa e inizializzatogcloud CLI per generare un token di accesso per l'intestazione Authorization.

    In alternativa, puoi creare un token di accesso utilizzando OAuth 2.0 Playground e includerlo nell'intestazione Authorization.

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

    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 dell'oggetto. Ad esempio, Desktop/dog.png.
    • OBJECT_CONTENT_TYPE è il tipo di contenuto 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 carichi l'oggetto. Ad esempio, my-bucket.
    • OBJECT_NAME è il nome che vuoi assegnare all'oggetto. Ad esempio, dog.png.

Scenari di utilizzo delle condizioni preliminari

Gli scenari seguenti esplorano le condizioni di gara ed esempi di memorizzazione nella cache che sfruttano l'uso di precondizioni.

Più tentativi di richiesta

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

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

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

Si verifica una race condition se il router che ha perso la connettività successivamente lo recupera e invia la richiesta di eliminazione originale, apparentemente persa, in avanti a Cloud Storage. Quando la richiesta arriva a Cloud Storage, riuscita perché è presente un nuovo file.txt. Cloud Storage invia una risposta che non ricevi perché il client ha smesso di ascoltarla. Il nuovo file non viene solo eliminato, contrariamente alle tue intenzioni, ma non sai nemmeno che si è verificata la seconda eliminazione.

Il seguente diagramma mostra cosa è successo:

Prevenzione della race condition

Per evitare che si verifichi la situazione precedente, devi iniziare a recuperare i metadati per file.txt per determinarne la generazione attuale. Potrai quindi utilizzare la generazione in una precondizione di corrispondenza di generazione che includi nella richiesta di eliminazione. La precondizione garantisce che venga eliminato solo l'oggetto con lo specifico numero di generazione, indipendentemente da quando la richiesta di eliminazione raggiunge Cloud Storage o dal numero di volte in cui viene inviata la richiesta di eliminazione con la precondizione. Eventuali tentativi involontari di eliminare una generazione diversa di file.txt non vanno a buon fine con il codice di risposta 412 Precondition Failed.

Poiché interruzioni di rete simili potrebbero causare condizioni di gara per la richiesta di caricamento successiva alla richiesta di eliminazione, puoi evitarne molte utilizzando il valore 0 in una precondizione per la corrispondenza di generazione inclusa nella richiesta di caricamento. L'utilizzo di questa condizione preliminare garantisce che i nuovi tentativi di caricamento non scrivano accidentalmente l'oggetto due volte, perché la condizione preliminare consente alla richiesta di procedere solo se non esistono generazioni attuali dell'oggetto.

Con queste condizioni preliminari, proteggi i tuoi dati dalla perdita accidentale durante l'esecuzione delle richieste di eliminazione e caricamento. Questo è possibile vedere nel seguente diagramma:

Associazione di metadati degli oggetti

I dati e i metadati di un oggetto sono entità separate che definiscono l'oggetto in Cloud Storage. Poiché esistono separatamente, i dati dell'oggetto possono cambiare 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. Devi prima richiedere i metadati dell'oggetto, ma prima di poter richiedere i dati dell'oggetto, un processo o un utente indipendente sostituisce l'oggetto. La tua richiesta dei dati dell'oggetto è ancora riuscita, ma ora hai i metadati dell'oggetto precedente e i dati del nuovo oggetto.

  • Vuoi aggiornare i metadati per un oggetto, in modo da recuperare i metadati attuali dell'oggetto per determinarne lo stato attuale. Prima di poter inviare la richiesta di aggiornamento dei metadati con le modifiche desiderate, l'oggetto viene sostituito da un processo o un utente indipendente. La tua richiesta di modifica dei metadati per il nuovo oggetto è ancora riuscita, 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 per i metadati degli oggetti, quindi utilizzare questo valore in una precondizione per la corrispondenza di generazione nella seconda richiesta. In questo modo si garantisce che i metadati corrispondano correttamente ai dati o che la seconda richiesta non abbia esito positivo con un codice di risposta 412 Precondition Failed, consentendo 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 di corrispondenza della metagenerazione nella seconda richiesta.

Aggiornamento della copia locale

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

Per evitare download inutili di contenuti ancora attuali, puoi utilizzare il numero di generazione della copia locale come valore in una precondizione di generazione non corrispondente, che includi nella richiesta di download:

  • Se i dati nel bucket continuano a corrispondere alla copia locale, i numeri di generazione corrispondono, causando il mancato superamento della condizione preliminare. Di conseguenza, la richiesta complessiva non va a buon fine e restituisce 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 condizione preliminare viene soddisfatta. Ciò significa che la richiesta complessiva procede normalmente e scarica la versione aggiornata dei contenuti.

Passaggi successivi