Conditions préalables aux requêtes

Cette page traite des conditions préalables de requête, que vous utilisez pour empêcher les requêtes de s'appliquer à une ressource lorsque celle-ci est dans un état inattendu.

Présentation

Lorsque des conditions préalables sont utilisées dans une requête adressée à Cloud Storage, la requête n'est exécutée que si la ressource ciblée répond aux critères définis dans les conditions préalables. Les vérifications de conditions préalables vous permettent de vous assurer qu'un bucket ou un objet se trouve dans l'état attendu, ce qui vous permet d'effectuer des mises à jour de lecture-modification-écriture et des opérations conditionnelles de façon sécurisée.

Les conditions préalables sont souvent utilisées pour éviter les conditions de concurrence dans les requêtes de mutation, par exemple pour les importations, les suppressions ou les mises à jour de métadonnées. Des conditions de concurrence peuvent survenir lorsque la même requête est envoyée à plusieurs reprises ou lorsque des processus indépendants tentent de modifier la même ressource. Pour en savoir plus, consultez la section Exemples de conditions de concurrence et de corruption de données. Les conditions préalables sont également utilisées lors de la récupération des métadonnées et des données d'objet dans les requêtes successives, pour garantir que l'objet n'a pas changé entre les deux requêtes.

Critères de condition préalable

Cloud Storage accepte l'utilisation de plusieurs propriétés de ressource immuable différentes dans des conditions préalables :

Le tableau suivant répertorie les conditions préalables acceptées par l'API JSON et l'API XML :

API JSON API XML Description
Paramètre de requête ifGenerationMatch En-tête x-goog-if-generation-match La requête est exécutée si le generation de la ressource cible correspond à la valeur utilisée dans la condition préalable. Si les valeurs ne correspondent pas, la requête échoue avec une réponse 412 Precondition Failed.
Paramètre de requête ifMetagenerationMatch En-tête x-goog-if-metageneration-match La requête est exécutée si le metageneration de la ressource cible correspond à la valeur utilisée dans la condition préalable. Si les valeurs ne correspondent pas, la requête échoue avec une réponse 412 Precondition Failed.
Paramètre de requête ifGenerationNotMatch Non disponible La requête est exécutée si le generation de la ressource cible ne correspond pas à la valeur utilisée dans la condition préalable. Si les valeurs correspondent, la requête échoue avec une réponse 304 Not Modified.
Paramètre de requête ifMetagenerationNotMatch Non disponible La requête est exécutée si le metageneration de la ressource cible ne correspond pas à la valeur utilisée dans la condition préalable. Si les valeurs correspondent, la requête échoue avec une réponse 304 Not Modified.
En-tête If-Match En-tête If-Match Applicable aux requêtes qui récupèrent des données. La requête est exécutée si le ETag de la ressource cible correspond à la valeur utilisée dans la condition préalable. Si les valeurs ne correspondent pas, la requête échoue avec une réponse 412 Precondition Failed.
En-tête If-None-Match En-tête If-None-Match Applicable aux requêtes qui récupèrent des données. La requête est exécutée si le ETag de la ressource cible ne correspond pas à la valeur utilisée dans la condition préalable. Si les valeurs correspondent, la requête échoue avec une réponse 304 Not Modified.
Non disponible En-tête If-Modified-Since La requête est exécutée si la ressource cible a une date Last-Modified ultérieure à la valeur utilisée dans la condition préalable. Si la ressource cible ne remplit pas cette condition préalable, la requête échoue avec une réponse 304 Not Modified.
Non disponible En-tête If-Unmodified-Since La requête continue si la ressource cible a une date Last-Modified antérieure ou égale à la valeur utilisée dans la condition préalable. Si la ressource cible ne remplit pas cette condition préalable, la requête échoue avec une réponse 412 Precondition Failed.

Conditions préalables de composition d'objets

Lors de la composition d'objets, l'API JSON et l'API XML sont compatibles avec les éléments suivants :

  • Les conditions préalables de correspondance de génération et de métagénération pour l'objet de destination.

  • La condition préalable de correspondance de génération pour chaque objet source. L'utilisation de cette condition préalable empêche l'utilisation de composants incorrects dans le cas où un processus indépendant écraserait l'un des composants prévus de la composition. Si vous utilisez des conditions préalables et que ce type d'écrasement se produit, les opérations compose échouent avec une réponse 412 Precondition Failed.

Conditions préalables de copie d'objets

Lors de la copie ou de la réécriture d'un objet dans Cloud Storage, l'API JSON et l'API XML sont compatibles avec l'utilisation de conditions préalables standards pour l'objet de destination. Chaque API accepte des conditions préalables supplémentaires pour les objets sources :

  • L'API JSON accepte les conditions préalables de génération et de métagénération pour l'objet source, qui sont spécifiées à l'aide de paramètres de requête précédés de ifSource.

  • Toutes les conditions préalables acceptées par l'API XML peuvent être utilisées pour l'objet source. Ces conditions préalables sont spécifiées dans les en-têtes précédés du préfixe x-goog-copy-source-.

Valeur 0 dans une condition préalable de correspondance de génération

La condition préalable de correspondance de génération accepte la valeur 0 comme cas particulier. Lorsqu'une condition préalable de correspondance de génération avec la valeur 0 est incluse dans une requête, celle-ci ne se poursuit que si aucun objet portant le nom spécifié n'existe dans le bucket, ou s'il n'existe que des versions obsolètes de l'objet dans le bucket. S'il existe une version active portant le nom spécifié, la requête échoue avec le code d'état 412 Precondition Failed.

Bonnes pratiques et pistes de réflexion

  • Vous pouvez utiliser plusieurs conditions préalables dans une même requête. Si l'une des conditions préalables n'est pas remplie, la requête globale échoue.

  • Les buckets n'ont pas de numéro de génération, bien qu'ils comportent un numéro de métagénération. Vous ne devez pas utiliser de conditions préalables qui spécifient un numéro de génération dans une requête de bucket.

  • Si vous utilisez une condition préalable de métagénération dans une requête d'objet, vous devez également utiliser une condition préalable de génération. Cela empêche la requête d'aboutir sur un objet différent qui possède par coïncidence un numéro de métagénération répondant à la condition préalable.

  • Pour les buckets qui ont à la fois des versions d'objet actives et archivées, les requêtes d'objet ne s'appliquent pas aux versions archivées, sauf si un numéro de génération est explicitement inclus dans la requête. Cela signifie que pour une requête générale qui utilise des conditions préalables, la requête échoue si la version active ne satisfait pas la condition préalable, qu'une version archivée corresponde ou non aux conditions préalables.

  • Vous devez généralement utiliser des conditions préalables de génération et de métagénération au lieu de conditions préalables ETag. La paire que constituent les numéros de génération et de metagénération permet de garder une trace de toutes les mises à jour d'objets, y compris les modifications de métadonnées, fournissant ainsi une garantie plus solide que celle des ETag. De plus, les numéros de génération et de métagénération sont cohérents entre les API, contrairement aux ETags.

  • Les conditions préalables ne peuvent pas être utilisées dans les importations en plusieurs parties avec l'API XML. Toute tentative de désactivation entraîne une erreur 400 NotImplemented.

Coût des conditions préalables

De nombreuses architectures utilisant des conditions préalables nécessitent d'effectuer une requête de métadonnées d'objet avant la requête principale afin de déterminer le numéro de génération et/ou de métagénération actuel :

  • Une requête supplémentaire signifie que vous pouvez doubler la partie de réseau correspondant à la latence opérationnelle globale en ajoutant un aller-retour, ce qui peut avoir un impact important sur les opérations sensibles à la latence.

Selon votre application, il existe des moyens de réduire les impacts de l'utilisation de conditions préalables, par exemple :

  • le stockage en local des numéros de génération et de méta-génération de vos objets de sorte que vous connaissiez déjà les bons numéros à utiliser dans votre condition préalable ;
  • la connaissance, au niveau de l'application, des objets nouvellement créés, afin de savoir à quel moment utiliser la condition préalable if-generation-match:0 ;

Exemple : utiliser une condition préalable

L'exemple suivant utilise la condition préalable de correspondance de génération dans une requête pour importer un objet. Pour que la requête aboutisse, il doit y avoir un objet préexistant stocké dans le bucket avec le nom spécifié, et le numéro de génération de l'objet préexistant doit correspondre au numéro fourni dans la condition préalable :

Ligne de commande

Utilisez l'option --if-generation-match avec la commande normale :

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

Où :

  • GENERATION correspond au numéro de génération prévu de l'objet que vous remplacez. Exemple :1122334455667788

  • OBJECT_LOCATION correspond au chemin d'accès local à votre objet. Par exemple, Desktop/dog.png.

  • DESTINATION_BUCKET_NAME correspond au nom du bucket dans lequel vous importez votre objet. Exemple :my-bucket

API JSON

  1. Vous devez installer et initialiser gcloud CLI afin de générer un jeton d'accès pour l'en-tête Authorization.

    Vous pouvez également créer un jeton d'accès à l'aide d'OAuth 2.0 Playground et l'inclure dans l'en-tête Authorization.

  2. Exécutez cURL pour appeler l'API JSON avec une requête d'objet 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"

    Où :

    • OBJECT_LOCATION correspond au chemin d'accès local à votre objet. Exemple :Desktop/dog.png
    • OBJECT_CONTENT_TYPE correspond au type de contenu de l'objet. Exemple :image/png
    • BUCKET_NAME correspond au nom du bucket dans lequel vous importez votre objet. Exemple :my-bucket
    • OBJECT_NAME correspond au nom que vous souhaitez attribuer à l'objet. Exemple :dog.png
    • GENERATION correspond au numéro de génération prévu de l'objet que vous remplacez. Exemple :1122334455667788

API XML

  1. Vous devez installer et initialiser gcloud CLI afin de générer un jeton d'accès pour l'en-tête Authorization.

    Vous pouvez également créer un jeton d'accès à l'aide d'OAuth 2.0 Playground et l'inclure dans l'en-tête Authorization.

  2. Utilisez cURL pour appeler l'API XML avec une requête d'objet 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"

    Où :

    • OBJECT_LOCATION correspond au chemin d'accès local à votre objet. Exemple :Desktop/dog.png
    • OBJECT_CONTENT_TYPE correspond au type de contenu de l'objet. Exemple :image/png
    • GENERATION correspond au numéro de génération prévu de l'objet que vous remplacez. Exemple :1122334455667788
    • BUCKET_NAME correspond au nom du bucket dans lequel vous importez votre objet. Exemple :my-bucket
    • OBJECT_NAME correspond au nom que vous souhaitez attribuer à l'objet. Exemple :dog.png

Scénarios d'utilisation de conditions préalables

Les scénarios suivants explorent les conditions de concurrence et les exemples de mise en cache qui bénéficient de l'utilisation de conditions préalables.

Tentatives multiples d'exécution de requête

Cloud Storage est un système distribué. Étant donné que les requêtes peuvent échouer en raison des conditions de réseau ou de service, la méthode recommandée pour effectuer de nouvelles tentatives après échec consiste à utiliser un intervalle exponentiel entre les tentatives. Cependant, en raison de la nature des systèmes distribués, ces tentatives peuvent parfois aboutir à un comportement surprenant.

Prenons le cas suivant : vous souhaitez supprimer un objet file.txt stocké dans l'un de vos buckets. Vous souhaitez ensuite ajouter un objet portant le même nom au bucket. Pour ce faire, vous émettez une demande de suppression pour supprimer l'objet. Cependant, en raison d'une condition de réseau, telle qu'un routeur intermédiaire qui perd temporairement sa connectivité, la requête ne parvient pas à atteindre Cloud Storage et vous ne recevez pas de réponse.

Comme vous n'avez pas reçu de réponse à la première requête de suppression de l'objet, vous en émettez une seconde, qui aboutit, et vous recevez une réponse confirmant la suppression. Une minute plus tard, vous importez un nouveau fichier file.txt, et l'importation aboutit.

Une condition de concurrence survient si le routeur qui a perdu la connectivité la récupère par la suite et envoie à Cloud Storage votre requête de suppression d'origine, apparemment perdue. Lorsque la demande arrive sur Cloud Storage, elle aboutit car un nouveau file.txt est présent. Cloud Storage envoie une réponse que vous ne recevez pas, car votre client a cessé de l'écouter. Non seulement le nouveau fichier est supprimé, contrairement à vos intentions, mais vous n'êtes pas non plus au courant de la seconde suppression.

Le schéma suivant illustre le déroulement des événements :

Éviter la condition de concurrence

Pour éviter la situation ci-dessus, vous devez d'abord obtenir les métadonnées du fichier file.txt afin de déterminer sa génération actuelle. Vous utilisez ensuite la génération dans une condition préalable de correspondance de génération que vous incluez dans la requête de suppression. L'utilisation de la condition préalable garantit que seul l'objet portant ce numéro de génération est supprimé, quel que soit le moment où la requête de suppression atteint Cloud Storage ou le nombre de fois où la requête de suppression associée à la condition préalable est envoyée. Toute tentative involontaire de suppression d'une autre génération de file.txt échoue avec le code de réponse 412 Precondition Failed.

Étant donné que des interruptions de réseau similaires peuvent entraîner des conditions de concurrence pour la requête d'importation qui suit votre requête de suppression, vous pouvez en éviter beaucoup en utilisant la valeur 0 dans une condition préalable de correspondance de génération incluse dans la requête d'importation. Ceci garantit que les tentatives d'importation n'écrivent pas accidentellement l'objet deux fois, car elle permet à la requête d'aboutir seulement s'il n'y a pas de générations actuelles de l'objet.

Ces conditions préalables vous permettent de protéger vos données contre toute perte accidentelle lors de l'exécution des requêtes de suppression et d'importation. Le schéma suivant illustre ce cas de figure :

Association de métadonnées d'objets

Les données et les métadonnées d'un objet sont des entités distinctes qui définissent l'objet dans Cloud Storage. Comme elles existent séparément, il est possible que les données d'un objet soient modifiées lorsque vous utilisez les métadonnées de l'objet.

Considérons, par exemple, les cas suivants :

  • Vous souhaitez télécharger les métadonnées et les données d'un objet, qui doivent être extraites de Cloud Storage en deux requêtes distinctes. Vous demandez d'abord les métadonnées de l'objet, mais avant que vous puissiez demander les données de l'objet, un processus ou un utilisateur indépendant remplace l'objet. Votre requête pour les données d'objet aboutit, mais vous disposez désormais des métadonnées de l'ancien objet et des données du nouvel objet.

  • Vous souhaitez mettre à jour les métadonnées d'un objet. Vous devez donc récupérer les métadonnées actuelles de l'objet pour déterminer son état actuel. Avant de pouvoir envoyer la requête de mise à jour des métadonnées avec les modifications souhaitées, un processus ou un utilisateur indépendant remplace l'objet. Votre requête de modification des métadonnées du nouvel objet aboutit quand même, mais elle est maintenant associée à des données d'objet différentes de celles que vous souhaitiez.

Éviter la condition de concurrence

Pour éviter ces situations, vous devez utiliser le numéro de génération renvoyé dans la requête initiale pour les métadonnées d'objet, puis utiliser cette valeur dans une condition préalable de correspondance de génération dans la deuxième requête. Cela garantit que les métadonnées correspondent bien aux données, ou que la deuxième requête échoue avec un code de réponse 412 Precondition Failed, ce qui vous permet de demander les métadonnées appropriées pour le nouvel objet.

Si vous craignez que les métadonnées de l'objet puissent changer entre la première et la deuxième requête, vous pouvez également copier le numéro de métagénération trouvé dans la requête initiale et l'utiliser dans une condition préalable de correspondance de métagénération dans la deuxième requête.

Actualisation des copies locales

Si vous disposez d'une copie locale d'un objet stocké dans Cloud Storage, vous devez souvent veiller à maintenir votre copie locale à jour par rapport à la copie stockée dans votre bucket. Toutefois, si l'objet stocké dans votre bucket ne change pas, vous ne voulez pas perdre du temps et des ressources à le télécharger à nouveau, en particulier si l'objet est volumineux.

Pour éviter les téléchargements inutiles de contenus qui sont toujours à jour, vous pouvez utiliser le numéro de génération de votre copie locale comme valeur dans une condition préalable de non-correspondance de génération, que vous incluez dans votre requête de téléchargement :

  • Si les données de votre bucket continuent de correspondre à votre copie locale, les numéros de génération correspondent, ce qui entraîne l'échec de la condition préalable. Par conséquent, la requête globale échoue avec une réponse 304 Not Modified et les données ne sont pas inutilement téléchargées.

  • Si les données de votre bucket ont changé, les numéros de génération ne correspondent pas et la condition préalable aboutit. Cela signifie que la requête globale se déroule normalement et télécharge la version mise à jour du contenu.

Étape suivante