Object Storage - Écritures conditionnelles

Voir en Markdown

Découvrez comment utiliser les en-têtes conditionnels If-Match et If-None-Match pour prévenir les écrasements et éviter les conditions de course sur vos buckets OVHcloud Object Storage

Objectif

Ce guide explique comment utiliser les en-têtes HTTP conditionnels If-Match et If-None-Match sur les requêtes PutObject, DeleteObject et CompleteMultipartUpload de vos buckets OVHcloud Object Storage.

Prérequis

En pratique

Pourquoi utiliser les écritures conditionnelles ?

L'Object Storage est par nature concurrent - plusieurs instances d'application, microservices ou traitements en arrière-plan peuvent lire et écrire le même objet simultanément. Sans coordination, ces écritures concurrentes peuvent s'écraser silencieusement, entraînant des pertes de données difficiles à détecter et à reproduire.

Les écritures conditionnelles résolvent ce problème au niveau de la couche de stockage, sans nécessiter de service de coordination externe comme une base de données ou un gestionnaire de verrous distribués. En associant une précondition à un appel API S31, vous garantissez que l'opération ne se déroule que si l'objet est dans l'état attendu au moment où la requête est évaluée.

Bénéfices clés :

  • Intégrité des données en environnement concurrent : Évitez qu'une écriture n'écrase silencieusement les modifications d'un autre processus survenues entre votre lecture et votre écriture (la classique race condition de type vérifier-puis-agir).
  • Primitive de verrouillage distribué : Implémentez un verrou léger natif au stockage avec If-None-Match: * sur PutObject - seul le premier qui écrit réussit ; tous les autres reçoivent 412 Precondition Failed.
  • Comparer-et-échanger atomique : Lisez l'ETag d'un objet, modifiez-le, puis réécrivez-le avec If-Match: <etag_original>. L'écriture est rejetée si l'objet a été modifié entre-temps - un modèle de concurrence optimiste sûr.
  • Aucune infrastructure supplémentaire : La coordination s'effectue directement au sein du service Object Storage - pas besoin de bases de données, files de messages ou gestionnaires de verrous supplémentaires.
  • Tout client compatible S3 : Les en-têtes conditionnels font partie du standard HTTP (RFC 9110) et de l'API S3 - ils fonctionnent avec AWS CLI, n'importe quel SDK S3 et les appels HTTP directs.

Vue d'ensemble

Les requêtes conditionnelles vous permettent d'associer une précondition à un appel API S3, basée sur l'état actuel de l'objet cible. L'Object Storage OVHcloud évalue la condition de manière atomique avant d'exécuter l'opération. Si la condition n'est pas satisfaite, la requête est rejetée sans modifier aucune donnée.

Deux en-têtes HTTP sont supportés, conformément à la RFC 9110 :

En-têteValeur acceptéeSignification
If-MatchValeur ETag brute ou *Exécute uniquement si l'ETag courant de l'objet correspond
If-None-Match* uniquementExécute uniquement si l'objet n'existe pas

Opérations supportées :

OpérationIf-MatchIf-None-Match
PutObject
DeleteObject
CompleteMultipartUpload
Info

Format de l'ETag : Lors de l'utilisation de If-Match, fournissez l'ETag sous forme de chaîne brute sans guillemets - par exemple d41d8cd98f00b204e9800998ecf8427e, et non "d41d8cd98f00b204e9800998ecf8427e".

Pour récupérer l'ETag courant d'un objet, utilisez aws s3api head-object --bucket <nom_du_bucket> --key <clé_de_l_objet> et lisez le champ ETag dans la réponse en supprimant les guillemets entourants.

Warning

Il n'est pas possible d'envoyer à la fois If-Match et If-None-Match dans la même requête. Cela retourne une erreur HTTP 400 Bad Request.

Codes de réponse

Code HTTPSignification
200 OK / 204 No ContentLa condition est satisfaite - opération exécutée
404 Not FoundCondition If-Match : l'objet cible n'existe pas (aucune version courante, ou la version courante est un marqueur de suppression)
412 Precondition FailedCondition If-Match : l'objet existe mais son ETag ne correspond pas ; ou If-None-Match: * et l'objet existe déjà
409 ConditionalRequestConflictUne opération concurrente est en conflit - nouvelle tentative requise

Versioning et marqueurs de suppression

Toutes les conditions sont évaluées par rapport à la version courante de l'objet, que le versioning soit activé ou non sur le bucket.

Info

Un marqueur de suppression (delete marker) dans un bucket versionné n'est pas considéré comme un objet existant :

  • If-Match (avec un ETag ou *) → 404 Not Found lorsque la version courante est un marqueur de suppression.
  • If-None-Match: *réussit lorsque la version courante est un marqueur de suppression (traité comme inexistant).
Info

Support du chiffrement : Les écritures conditionnelles sont supportées pour les objets non chiffrés et les objets chiffrés avec SSE-S3. Le support des objets chiffrés avec SSE-C sera disponible dans une prochaine version.

PutObject - écriture conditionnelle

Ajoutez une précondition à l'upload d'un objet pour éviter les écrasements accidentels ou implémenter une logique de premier arrivé, premier servi.

Cas d'usage :

  • Premier arrivé, premier servi / verrou distribué : Plusieurs processus tentent de créer le même objet simultanément. Seul le premier appel avec If-None-Match: * réussit - tous les autres reçoivent 412.
  • Écrasement sécurisé : Lisez un objet, modifiez-le, puis réécrivez-le avec If-Match: <etag_original>. Si un autre client a modifié l'objet entre-temps, l'écriture est rejetée.
  • Initialisation idempotente : Créez un objet de configuration sans risque d'écraser une version existante.
Via AWS CLI
Via curl (API REST S3)
# Écrire uniquement si l'objet n'existe pas (premier arrivé, premier servi)
aws s3api put-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --body <chemin_fichier_local> \
  --if-none-match "*"

# Écrire uniquement si l'ETag courant correspond (écrasement sécurisé)
aws s3api put-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --body <chemin_fichier_local> \
  --if-match <etag_courant>

# Écrire uniquement si l'objet existe (n'importe quel ETag)
aws s3api put-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --body <chemin_fichier_local> \
  --if-match "*"

Téléversement réussi :

{
    "ETag": "d41d8cd98f00b204e9800998ecf8427e"
}

Téléversement refusé (condition non satisfaite) :

An error occurred (PreconditionFailed) when calling the PutObject operation:
At least one of the pre-conditions you specified did not hold
Comportement selon l'état du versioning pour PutObject
État du bucketIf-None-Match: *If-Match: <etag>If-Match: *
Non versionnéRéussit uniquement si aucun objet n'existeRéussit uniquement si l'ETag courant correspondRéussit uniquement si l'objet existe
Versionné - version courante existante412 Precondition FailedRéussit si l'ETag correspond → crée une nouvelle versionRéussit → crée une nouvelle version
Versionné - version courante est un marqueur de suppressionRéussit → crée une nouvelle version404 Not Found404 Not Found
Versionné - aucune versionRéussit → crée la première version404 Not Found404 Not Found

DeleteObject - suppression conditionnelle

Supprimez un objet uniquement si la condition ETag est satisfaite. Seul If-Match est supporté pour DeleteObject - If-None-Match n'est pas applicable à la suppression.

Cas d'usage :

  • Suppression sécurisée : Lisez l'ETag d'un objet, puis supprimez-le avec If-Match: <etag>. La suppression est rejetée si l'objet a été modifié entre votre lecture et votre suppression.
  • Supprimer-si-existe : Utilisez If-Match: * pour supprimer un objet uniquement s'il existe actuellement.
Via AWS CLI
# Supprimer uniquement si l'ETag courant correspond
aws s3api delete-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --if-match <etag_courant>

# Supprimer uniquement si l'objet existe (n'importe quel ETag)
aws s3api delete-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --if-match "*"

# Supprimer une version spécifique uniquement si son ETag correspond
aws s3api delete-object \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --version-id <id_de_version> \
  --if-match <etag_de_la_version>

Lorsque --version-id est spécifié, l'ETag est évalué par rapport à cette version spécifique, et non par rapport à la version courante.

Info

Dans un bucket versionné, une suppression réussie via DeleteObject crée un marqueur de suppression comme nouvelle version courante - elle ne supprime pas définitivement l'objet. Pour supprimer définitivement une version spécifique, incluez --version-id dans la requête.

Comportement selon l'état du versioning pour DeleteObject
État du bucketIf-Match: <etag>If-Match: *
Non versionnéSuppression définitive si l'ETag correspondSuppression définitive si l'objet existe
Versionné - version courante existanteCrée un marqueur de suppression si l'ETag de la version courante correspondCrée un marqueur de suppression si une version courante existe
Versionné - version courante est un marqueur de suppression404 Not Found404 Not Found

CompleteMultipartUpload - finalisation conditionnelle

Associez une précondition à l'étape finale d'un upload multipart. La condition est évaluée au moment de l'appel à CompleteMultipartUpload, et non lors de l'initiation avec CreateMultipartUpload.

Cas d'usage :

  • Remplacement atomique de fichier volumineux : Démarrez un upload multipart d'un fichier de remplacement. À la finalisation, utilisez If-Match: <etag> pour vous assurer que l'objet cible n'a pas été modifié pendant l'upload.
  • Premier arrivé, premier servi pour les fichiers volumineux : Utilisez If-None-Match: * à la finalisation pour qu'un seul upload multipart concurrent crée définitivement l'objet.
Via AWS CLI
# Finaliser uniquement si l'objet n'existe pas encore
aws s3api complete-multipart-upload \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --upload-id <id_upload> \
  --multipart-upload file://parts.json \
  --if-none-match "*"

# Finaliser uniquement si l'ETag courant correspond
aws s3api complete-multipart-upload \
  --bucket <nom_du_bucket> \
  --key <clé_de_l_objet> \
  --upload-id <id_upload> \
  --multipart-upload file://parts.json \
  --if-match <etag_courant>
Warning

En cas d'erreur 409 ConditionalRequestConflict sur CompleteMultipartUpload, vous devez redémarrer entièrement la séquence d'upload multipart :

  1. Appelez AbortMultipartUpload pour nettoyer l'upload en cours.
  2. Récupérez l'ETag courant avec HeadObject.
  3. Appelez CreateMultipartUpload pour démarrer un nouvel upload.
  4. Re-téléversez toutes les parties avec UploadPart.
  5. Relancez CompleteMultipartUpload avec la condition mise à jour.
Comportement selon l'état du versioning pour CompleteMultipartUpload
État du bucketIf-None-Match: *If-Match: <etag>If-Match: *
Non versionnéRéussit uniquement si aucun objet n'existe au moment de la finalisationRéussit uniquement si l'ETag courant correspond au moment de la finalisationRéussit uniquement si l'objet existe au moment de la finalisation
Versionné - version courante existante412 Precondition FailedRéussit si l'ETag correspond → crée une nouvelle versionRéussit → crée une nouvelle version
Versionné - version courante est un marqueur de suppressionRéussit → crée une nouvelle version404 Not Found404 Not Found
Versionné - aucune versionRéussit → crée la première version404 Not Found404 Not Found

Considérations importantes

  • Atomicité : La vérification de la condition et l'opération s'exécutent en une seule unité atomique. Aucune opération concurrente ne peut modifier l'objet entre la vérification et l'écriture.
  • Un seul en-tête par requête : Vous ne pouvez pas combiner If-Match et If-None-Match dans la même requête - cela retourne une erreur 400 Bad Request.
  • Nouvelle tentative sur 409 pour PutObject / DeleteObject : En cas de 409 ConditionalRequestConflict, récupérez l'ETag courant de l'objet avec HeadObject et relancez votre requête avec la valeur mise à jour.
  • Aucune modification IAM requise : Aucune permission supplémentaire n'est nécessaire. Les permissions existantes s3:PutObject et s3:DeleteObject sont suffisantes.
  • Compatibilité avec Object Lock : Les en-têtes conditionnels sont évalués indépendamment des règles Object Lock (WORM). Les deux contraintes s'appliquent.

Aller plus loin

Échangez avec notre communauté d'utilisateurs.

1 : S3 est une marque déposée appartenant à Amazon Technologies, Inc. Les services de OVHcloud ne sont pas sponsorisés, approuvés, ou affiliés de quelque manière que ce soit.

Cette page vous a-t-elle aidé ?