Numéros de génération et conditions préalables

Les numéros de génération d'objets permettent aux utilisateurs d'identifier de manière unique les ressources de données et d'appliquer des conditions préalables afin de garantir l'atomicité de leurs transactions en plusieurs étapes.

Générations

Même si la gestion des versions d'objet n'est pas activée, tous les objets de Cloud Storage comportent des numéros de génération et de méta-génération. Le numéro de génération change à chaque fois que l'objet est écrasé, et le numéro de méta-génération à chaque mise à jour des métadonnées de l'objet.

Étant donné que les numéros de méta-génération d'objets sont réinitialisés à un pour chaque nouvelle génération d'objets, ils n'ont de signification que lorsqu'ils sont associés à un numéro de génération.

Les buckets conservent également un numéro de méta-génération permettant aux utilisateurs d'identifier de manière unique un état de métadonnées de bucket. Les buckets ne contenant pas de données de charge utile, et donc pas de numéros de génération, leurs numéros de méta-génération ont une signification en eux-mêmes.

Exemple : importation en parallèle

Lors d'importations parallèles, vous divisez un objet en plusieurs éléments que vous transférez simultanément vers un emplacement temporaire, puis vous composez (compose) l'objet d'origine à partir de ces éléments temporaires. Si un processus indépendant utilise par inadvertance le même nom qu'un ou plusieurs éléments temporaires importés, des composants incorrects sont utilisés lors de la tentative de composition (compose) de l'objet, et l'objet est corrompu.

Le recours aux numéros de génération évite la corruption de l'objet. Si vous incluez le numéro de génération de chaque élément importé lorsque vous exécutez la requête compose, la composition (compose) a lieu avec les bons éléments ou la requête échoue avec une réponse 404 Not Found.

Conditions préalables

Les conditions préalables indiquent à Cloud Storage de n'exécuter la requête que si le numéro de génération ou de méta-génération de l'objet concerné répond à vos critères de conditions préalables. Les vérifications des numéros de génération et de méta-génération garantissent que l'objet se trouve dans l'état attendu, ce qui vous permet d'effectuer des mises à jour sécurisées en lecture-modification-écriture et des opérations conditionnelles sur les objets.

Lorsqu'une condition préalable match (correspondance) utilise un numéro de génération ou de méta-génération spécifique, l'objet Cloud Storage auquel s'applique la requête doit disposer du même numéro de génération ou de méta-génération. Si c'est le cas, la requête aboutit. Dans le cas contraire, la requête échoue et une réponse 412 Precondition Failed est renvoyée.

Lorsqu'une condition préalable match (correspondance) utilise la valeur 0 au lieu d'un numéro de génération, la requête n'aboutit que si le bucket Cloud Storage ne contient pas d'objets actifs portant le nom spécifié dans la requête. Si un tel objet existe, la requête échoue et une réponse 412 Precondition Failed est renvoyée.

Les conditions préalables sont souvent utilisées dans les requêtes de mutation (importations, suppressions, copies ou mises à jour de métadonnées) pour éviter les conditions de concurrence. Des conditions de concurrence peuvent survenir dans les cas où la même requête est envoyée à plusieurs reprises ou lorsque des processus indépendants interfèrent les uns avec les autres. Les conditions de concurrence peuvent, par exemple, être créées par plusieurs tentatives d'exécution d'une requête après une interruption du réseau ou par des utilisateurs effectuant une opération de lecture-modification-écriture sur le même objet.

Outre les conditions préalables qui utilisent les numéros de génération et de méta-génération, il existe également des conditions préalables utilisant des ETags. Les ETags de l'API XML pour les objets non composites ne changent que lorsque le contenu est modifié, tandis que les ETags pour les objets composites et les ressources de l'API JSON changent à chaque fois que le contenu ou les métadonnées sont modifiés. Pour en savoir plus sur les ETags, consultez la section relative aux bonnes pratiques concernant les hashtags et les ETags.

Coût des conditions préalables

Les conditions préalables entraînent des frais facturés et ont un impact sur les performances. En effet, pour chaque opération de mutation, vous émettez également une requête de métadonnées GET facturable permettant de déterminer le numéro de génération ou de méta-génération de l'objet. En termes de performances, les conditions préalables risquent de doubler la partie de réseau correspondant à la latence d'opération générale en y ajoutant un aller-retour, ce qui peut avoir un impact important sur les opérations sensibles à la latence. En termes de coût, la requête de métadonnées GET qui permet d'utiliser des conditions préalables est facturée au taux de 0,004 USD pour 10 000 opérations.

En fonction de votre application, il existe des moyens d'éviter les coûts liés à 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 ;
  • le recours à un schéma de nommage évitant plusieurs mutations du même nom d'objet, pour ne pas avoir à utiliser de conditions préalables ;
  • la connaissance pratique des objets qui viennent d'être créés, afin que vous sachiez déjà quand utiliser la condition préalable if-generation-match:0 ;
  • la mémorisation des résultats des appels GET effectués avant les mutations.

Conditions préalables dans l'API XML

Dans l'API XML, les numéros de génération et de méta-génération sont exposés via les en-têtes de réponse x-goog-generation et x-goog-metageneration. Ces en-têtes sont renvoyés dans la réponse d'une requête HEAD pour un objet.

Consultez la documentation de référence sur les en-têtes HTTP pour obtenir une liste complète des en-têtes de requête de condition préalable qui permettent de subordonner la requête à l'état de l'objet de la requête. Par exemple, vous pouvez :

  • utiliser la condition préalable x-goog-if-generation-match pour exécuter une requête seulement si le numéro de génération de la condition préalable concorde avec le numéro de génération de l'objet de la requête. Si vous utilisez 0 à la place d'un numéro de génération, la requête n'aboutit que si votre bucket ne comporte pas d'objet actif concordant avec l'objet désigné dans la requête ;

  • utiliser les conditions préalables x-goog-if-metageneration-match pour exécuter une requête seulement si le numéro de méta-génération de l'en-tête concorde avec le numéro de méta-génération de l'objet de la requête ;

  • utiliser la condition préalable If-Modified-Since avec les requêtes GET ou HEAD. Ces demandes ne s'exécutent que si l'heure et la date de création de la génération la plus récente de l'objet (c'est-à-dire la dernière modification de l'objet) sont ultérieures à l'heure et la date spécifiées dans la condition préalable ;

  • utiliser des ETags et les conditions préalables If-Match ou If-None-Match avec les requêtes GET ou HEAD. Ces requêtes ne s'exécutent que si l'objet de la requête concorde ou ne concorde pas avec l'ETag spécifié dans la condition préalable ;

  • utiliser plusieurs conditions préalables dans la même requête. Par exemple, si vous utilisez x-goog-if-generation-match, vous pouvez également utiliser x-goog-if-metageneration-match.

Conditions préalables dans l'API JSON

Dans l'API JSON, les numéros de génération et de méta-génération sont accessibles via les propriétés generation et metageneration d'une réponse contenant une ressource d'objet ou de bucket. Une ressource d'objet ou de bucket est renvoyée dans le corps de réponse d'une requête GET pour l'objet ou pour le bucket.

Pour utiliser des conditions préalables sur les requêtes, ajoutez à la fin de l'URL les conditions préalables en tant que paramètres de requête. Pour chaque condition préalable, ajoutez un paramètre de requête portant le même nom que la condition préalable. Voici par exemple une requête qui utilise ifGenerationMatch : https://www.googleapis.com/storage/v1/b/testgrid-triage-testing/o/test?ifGenerationMatch=1122334455

Dans cet exemple, l'API n'exécute cette requête que si la génération de l'objet est 1122334455.

L'API JSON accepte aussi les ETags HTTP 1.1 et les en-têtes HTTP If-Match et If-None-Match correspondants pour toutes les ressources, y compris les buckets, les objets et les LCA. Un ETag est renvoyé dans l'en-tête de réponse à chaque fois qu'une ressource est renvoyée. Il est également inclus dans la ressource elle-même.

Voici quelques exemples de conditions préalables que vous pouvez utiliser pour subordonner la requête à l'état de l'objet de la requête :

  • Utilisez les conditions préalables ifGenerationMatch et ifGenerationNotMatch pour exécuter une requête seulement si le numéro de génération de la propriété concorde ou ne concorde pas avec le numéro de génération de l'objet de la requête. Si vous utilisez 0 à la place d'un numéro de génération dans la condition préalable ifGenerationMatch, la requête n'aboutit que si votre bucket ne comporte pas d'objet actif concordant avec l'objet désigné dans la requête.

  • Utilisez les conditions préalables ifMetagenerationMatch et ifMetagenerationNotMatch pour exécuter une requête sur un bucket ou un objet. Les requêtes n'aboutissent que si le numéro de méta-génération de la propriété concorde ou ne concorde pas avec le numéro de méta-génération du bucket ou de l'objet de la requête.

  • Utilisez des ETags et les conditions préalables If-Match ou If-None-Match comme en-têtes des requêtes. Ces requêtes ne s'exécutent que si l'objet de la requête concorde ou ne concorde pas avec l'ETag spécifié dans la condition préalable.

  • Utilisez plusieurs conditions préalables dans la même requête. Par exemple, si vous utilisez ifGenerationMatch dans la requête d'un objet, vous pouvez également utiliser ifMetagenerationMatch.

Notez que les conditions préalables de génération et de méta-génération ne sont pas acceptées pour les opérations de LCA ; utilisez plutôt l'ETag de la ressource d'entrée de contrôle d'accès. Il se trouve à l'intérieur de chaque ressource d'entrée de contrôle d'accès, qui sont également accessibles à partir de la ressource d'objet ou de bucket.

Exemples de conditions de concurrence

Lecture-modification-écriture en simultané

Un modèle courant de mise à jour des métadonnées de bucket ou d'objet consiste à lire l'état actuel, à appliquer des modifications en local et à renvoyer des métadonnées modifiées à Cloud Storage pour écriture. Ce modèle peut s'avérer risqué si deux processus indépendants ou plus tentent de réaliser la séquence simultanément.

Prenons le cas suivant : vous souhaitez ajouter une entrée à la liste de contrôle d'accès afin de permettre à un collaborateur d'accéder à votre bucket. Au même moment, un de vos collègues souhaite supprimer un autre collaborateur qui n'a plus besoin d'accéder au bucket.

Pour ce faire, vous et votre collègue lisez le même état initial concernant les métadonnées du bucket, et vous apportez chacun la modification souhaitée aux entrées de la liste de contrôle d'accès du bucket. Lorsque vous écrivez vos modifications dans Cloud Storage, les métadonnées sont bien mises à jour. Malheureusement, vos modifications sont perdues dès que votre collègue transfère ses propres modifications, car il ne pouvait pas savoir que vous aviez effectué des mises à jour. En conséquence, l'entrée de la liste de contrôle d'accès de votre collaborateur est perdue, il ne pourra pas accéder à votre bucket et personne ne sera au courant de ce qui s'est passé (sans revenir en arrière et consulter les entrées de la liste de contrôle d'accès).

Éviter la condition de concurrence

Vous et votre collègue pouvez éviter cette condition de concurrence en ajoutant une condition préalable if-metageneration-match à chacune de vos opérations d'écriture. Dans la condition préalable, vous utilisez tous les deux le numéro de méta-génération du bucket, qui fait partie des métadonnées que vous avez reçues lors de la première opération de lecture.

Lorsque vos modifications ajoutent l'entrée de la liste de contrôle d'accès, le numéro de méta-génération du bucket change. Maintenant que les conditions préalables sont utilisées, lorsque votre collègue tente d'écrire sa version des entrées de la liste de contrôle d'accès, le numéro de méta-génération du bucket ne concorde pas avec le numéro indiqué dans la condition préalable et il est informé de l'échec de la mise à jour avec le code de réponse 412 Precondition Failed. Après avoir reçu ce code de réponse, votre collègue peut réagir en conséquence, par exemple en procédant à un nouveau cycle de lecture-modification-écriture à l'aide des métadonnées mises à jour.

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, Google vous recommande d'effectuer de nouvelles tentatives après échec à 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 fichier file.txt stocké dans Cloud Storage. Vous souhaitez ensuite ajouter à Cloud Storage un nouveau fichier portant le même nom.

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 décidez d'importer 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 fichier 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. Envoyez ensuite la requête de suppression avec une condition préalable if-generation-match qui utilise le numéro de génération. 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. Avec la condition préalable if-generation-match, toute tentative involontaire de passer à une autre génération de fichier 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 faisant suite à votre requête de suppression, vous pouvez en éviter beaucoup en appliquant à la requête d'importation une condition préalable if-generation-match:0, qui 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 :

Limites de la condition préalable if-generation-match:0

La condition préalable if-generation-match:0 ne peut empêcher la création d'objet deux fois si le premier objet est supprimé, car l'absence de l'objet n'est pas identifiable de manière unique. Considérons le cas suivant : aucune donnée n'est perdue, mais vous vous retrouvez avec un fichier auquel vous ne vous attendiez pas.

  1. Vous exécutez d'abord une requête GET pour les métadonnées du fichier file.txt afin d'obtenir son numéro de génération. La réponse vous révèle que le fichier file.txt n'existe pas.

  2. Sachant cela, vous exécutez une requête pour importer le fichier file.txt avec la condition préalable if-generation-match:0, mais la requête expire tandis qu'un routeur intermédiaire perd temporairement la connectivité.

  3. Après avoir échoué la première fois, vous exécutez de nouveau votre requête d'importation, toujours avec la condition préalable if-generation-match:0. Cette fois, la requête aboutit.

  4. Peu de temps après, vous envoyez une requête de suppression du fichier file.txt, qui aboutit à son tour.

  5. Si le routeur qui a perdu la connectivité la récupère et envoie votre première requête d'importation à Cloud Storage, la condition préalable qui accompagnait la requête reste la même. Le fichier file.txt est donc recréé. Avec ou sans condition préalable, le fichier file.txt procède à une deuxième importation inattendue.

Cette page vous a-t-elle été utile ? Évaluez-la :

Envoyer des commentaires concernant…

Besoin d'aide ? Consultez notre page d'assistance.