J'ai ce scénario suivant:
/projects/1
et reçoit un ETag ./projects/1
avec l'ETag de l'étape # 1./projects/1
avec l'ETag de l'étape # 1.Normalement, la deuxième demande PUT recevrait une réponse 412, car l'ETag est désormais périmé - la première demande PUT a modifié la ressource, de sorte que l'ETag ne correspond plus.
Mais que se passe-t-il si les deux demandes PUT sont envoyées en même temps (ou exactement l'une après l'autre)? La première demande PUT n'a pas le temps de traiter et de mettre à jour la ressource avant l'arrivée de PUT # 2, ce qui fait que PUT # 2 écrase PUT # 1. L'intérêt de verrouillage optimiste est que cela ne se produise pas ...
Le mécanisme ETag spécifie uniquement le protocole de communication pour un verrouillage optimiste. Il incombe au service d'application de mettre en œuvre le mécanisme de détection des mises à jour simultanées pour appliquer le verrouillage optimiste.
Dans une application typique qui utilise une base de données, vous le feriez généralement en ouvrant une transaction lors du traitement d'une demande PUT. Vous devriez normalement lire l'état existant de la base de données à l'intérieur de cette transaction (pour obtenir un verrou de lecture), vérifier votre validité Etag et écraser les données (d'une manière qui provoquera un conflit d'écriture en cas de transaction simultanée incompatible), puis engagez-vous. Si vous configurez la transaction correctement, l'une des validations devrait échouer car elles essaieront toutes les deux de mettre à jour les mêmes données simultanément. Vous pourrez ensuite utiliser cet échec de transaction pour renvoyer 412 ou réessayer la demande, si cela est logique pour l'application.
Vous devez exécuter la paire suivante de manière atomique:
D'autres appellent cela une transaction - mais fondamentalement, l'exécution atomique de ces deux opérations est ce qui empêche l'une d'écraser l'autre par accident de timing; sans cela, vous avez une condition de course, comme vous le constatez.
Ceci est toujours considéré comme un verrouillage optimiste, si vous regardez la situation dans son ensemble: que la ressource elle-même n'est pas verrouillée par la lecture initiale (GET) par un utilisateur ou des utilisateurs qui consultent les données, que ce soit avec l'intention de les mettre à jour ou non.
Un certain comportement atomique est nécessaire, mais cela se produit dans une seule demande (le PUT) plutôt que d'essayer de maintenir un verrou sur plusieurs interactions réseau; c'est un verrouillage optimiste: l'objet n'est pas verrouillé par le GET mais peut toujours être mis à jour en toute sécurité par PUT.
Il existe également de nombreuses façons de réaliser l'exécution atomique de ces deux opérations - le verrouillage de la ressource n'est pas la seule option; par exemple, un thread léger ou un verrou d'objet peut suffire et dépend de l'architecture et du contexte d'exécution de votre application.
C'est au développeur de l'application de vérifier le E-Tag et de fournir cette logique. Ce n'est pas magique que le serveur Web fasse pour vous car il ne sait que calculer E-Tag
en-têtes pour le contenu statique. Prenons donc votre scénario ci-dessus et décomposons comment l'interaction doit se produire.
GET /projects/1
Le serveur reçoit la demande, détermine le E-Tag pour cette version de l'enregistrement, le renvoyant avec le contenu réel.
200 - OK
E-Tag: "412"
Content-Type: application/json
{modified: false}
Puisque le client a maintenant la valeur E-Tag, il peut l'inclure avec la requête PUT
:
PUT /projects/1
If-Match: "412"
Content-Type: application/json
{modified: true}
À ce stade, votre application doit effectuer les opérations suivantes:
Envoyez la réponse de réussite.
204 No Content
E-Tag: "543"
Si une autre demande vient et tente d'effectuer une PUT
similaire à la demande ci-dessus, la deuxième fois que votre code serveur l'évalue, vous êtes responsable de fournir le message d'erreur.
En cas d'échec, envoyez la réponse d'échec.
412 Precondition Failed
C'est le code que vous devez réellement écrire. Le E-Tag peut en fait être n'importe quel texte (dans les limites définies dans la spécification HTTP). Il n'est pas nécessaire que ce soit un nombre. Il peut également s'agir d'une valeur de hachage.