Préconditions de requête

Cette page traite des préconditions 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.

Introduction

Lorsque des préconditions 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 préconditions. Les vérifications de préconditions 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 préconditions sont souvent utilisées pour éviter les conditions de concurrence dans les requêtes de mutation telles que 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 Exemples de conditions de concurrence et de corruption de données. Les préconditions sont également utilisées lors de la récupération des métadonnées et des données d'objet dans des requêtes successives, pour garantir que l'objet n'a pas changé entre les deux requêtes.

Critères de précondition

Cloud Storage accepte l'utilisation de plusieurs propriétés de ressource immuables différentes dans les préconditions :

Le tableau suivant recense les préconditions 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 paramètre generation de la ressource cible correspond à la valeur utilisée dans la précondition. 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 paramètre metageneration de la ressource cible correspond à la valeur utilisée dans la précondition. Si les valeurs ne correspondent pas, la requête échoue avec une réponse 412 Precondition Failed.
Paramètre de requête ifGenerationNotMatch N/A La requête est exécutée si le paramètre generation de la ressource cible ne correspond pas à la valeur utilisée dans la précondition. Si les valeurs correspondent, la requête échoue avec une réponse 304 Not Modified.
Paramètre de requête ifMetagenerationNotMatch N/A La requête est exécutée si le paramètre metageneration de la ressource cible ne correspond pas à la valeur utilisée dans la précondition. 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 paramètre ETag de la ressource cible correspond à la valeur utilisée dans la précondition. 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 paramètre ETag de la ressource cible ne correspond pas à la valeur utilisée dans la précondition. Si les valeurs correspondent, la requête échoue avec une réponse 304 Not Modified.
N/A 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 précondition. Si la ressource cible ne remplit pas cette précondition, la requête échoue avec une réponse 304 Not Modified.
N/A En-tête If-Unmodified-Since La requête est exécutée si la ressource cible a une date Last-Modified antérieure ou égale à la valeur utilisée dans la précondition. Si la ressource cible ne remplit pas cette précondition, la requête échoue avec une réponse 412 Precondition Failed.

Préconditions de composition d'objets

Lors de la composition d'objets, l'API JSON et l'API XML acceptent les préconditions suivantes :

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

  • La précondition de correspondance de génération pour chaque objet source. L'utilisation de cette précondition 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 préconditions et qu'un écrasement de ce type se produit, les opérations compose échouent avec une réponse 412 Precondition Failed.

Préconditions 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 permettent l'utilisation de préconditions standards pour l'objet de destination. Chaque API accepte des préconditions supplémentaires pour les objets sources :

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

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

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

La précondition de correspondance de génération accepte la valeur 0 comme cas particulier. Lorsqu'une précondition 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 préconditions dans une même requête. Si l'une des préconditions 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 préconditions qui spécifient un numéro de génération dans une requête de bucket.

  • Si vous utilisez une précondition de métagénération dans une requête d'objet, vous devez également utiliser une précondition 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 précondition.

  • Pour les buckets qui comportent à 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 préconditions, la requête échoue si la version active ne satisfait pas la précondition, qu'une version archivée corresponde ou non aux préconditions.

  • Vous devez généralement utiliser des préconditions de génération et de métagénération au lieu de préconditions ETag. La paire que constituent les numéros de génération et de métagénération permet de garder une trace de toutes les mises à jour d'objets, y compris les modifications de métadonnées. Cela offre une garantie plus solide que celle fournie par les 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 préconditions ne peuvent pas être utilisées dans les importations en plusieurs parties avec l'API XML. Tenter de les utiliser entraîne une erreur 400 NotImplemented.

Coût des préconditions

De nombreuses architectures utilisant des préconditions 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 implique 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 l'impact de l'utilisation de préconditions, par exemple :

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

Exemple : utiliser une précondition

L'exemple suivant utilise la précondition 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 précondition :

Ligne de commande

Utilisez le flag --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 pour l'objet que vous remplacez. Par 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. Par exemple, my-bucket.

API JSON

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

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

    Où :

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

API XML

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

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

    Où :

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

Scénarios d'utilisation de préconditions

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

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 entraîner 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 envoyez une requête de suppression de l'objet. Cependant, en raison d'une condition de réseau, telle qu'une perte temporaire de connectivité sur un routeur intermédiaire, 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 nouvel objet 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, qui était apparemment perdue. Lorsque la demande arrive au niveau de Cloud Storage, elle aboutit car un nouvel objet 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 en plus, vous n'êtes pas 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 décrite ci-dessus, vous devez d'abord obtenir les métadonnées de l'objet file.txt afin de déterminer sa génération actuelle. Vous utilisez ensuite la génération dans une précondition de correspondance de génération que vous incluez dans la requête de suppression. L'utilisation de la précondition garantit que seul l'objet portant ce numéro de génération sera supprimé, quels que soient 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 précondition est envoyée. Toute tentative non souhaitée 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 éviter un grand nombre de ces conditions de concurrence en utilisant la valeur 0 dans une précondition de correspondance de génération incluse dans la requête d'importation. Utiliser cette précondition 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ération actuelle de l'objet.

Ces préconditions 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, ensemble, 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 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 par le biais de deux requêtes distinctes. Vous demandez d'abord les métadonnées de l'objet, mais avant que vous ne puissiez demander les données de l'objet, un processus indépendant ou un autre utilisateur 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 que vous ne puissiez envoyer la requête de mise à jour des métadonnées avec les modifications souhaitées, un processus indépendant ou un autre utilisateur 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 précondition 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 alors 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 précondition 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 précondition 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 précondition. 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 précondition aboutit. Cela signifie que la requête globale se déroule normalement et que la version mise à jour du contenu est téléchargée.

Étapes suivantes