Résumé

  • Dans ONC RPC, le client choisit un XID sur 32 bits et le serveur le recopie dans la réponse. Cette égalité relie deux messages ; elle n’ordonne pas les appels et ne certifie pas combien de fois la procédure a modifié l’état.
  • NFS a transformé cette limite abstraite en problème de fichiers. Un cache de doublons volatil réduisait le risque pendant un temps ; les sessions NFSv4.1 ont borné les appels en vol avec des slots, des séquences locales et des réponses conservées.

L’effet avait eu lieu, mais son témoin avait disparu

Un client demande la suppression d’un nom. Le serveur l’efface, prépare une réponse positive, puis le réseau perd cette réponse. Pour le client, le silence est identique à celui d’une requête qui n’aurait jamais atteint le serveur. Son temporisateur expire et il recommence.

La deuxième copie ne porte pas en elle l’histoire de la première. Le serveur peut l’exécuter et obtenir une erreur, parce que le nom n’existe plus. Il peut aussi décider qu’elle est ancienne et renvoyer le résultat conservé. La différence dépend d’un fait absent du paquet : le serveur se souvient-il encore de l’issue initiale ?

Les premières spécifications ne masquaient pas ce doute. RFC 1050, en avril 1988, séparait le format RPC de la fiabilité. RFC 1057, qui le remplaça en juin, précisait que sous UDP un appel retransmis sans réponse pouvait avoir été exécuté zéro, une ou plusieurs fois. Une réponse permettait seulement de conclure qu’une exécution au moins avait eu lieu.

Ce n’était pas une faiblesse propre à UDP. UDP rendait la fenêtre évidente. Une connexion peut également se rompre après l’effet et avant que l’application ne reçoive son résultat. Le canal et l’opération n’ont pas la même frontière de fin.

Le XID classait les réponses, pas le temps

Chaque message RPC version 2 commence par un entier non signé de 32 bits, le XID. Le message REPLY reprend celui du CALL correspondant. Si un client a plusieurs appels en attente, ce champ lui permet de remettre chaque réponse dans la bonne attente locale.

RFC 5531 borne expressément l’usage côté service : le serveur peut comparer deux XID pour détecter une retransmission, mais ne peut pas lire ce champ comme un numéro de séquence. Deux valeurs voisines ne disent pas que leurs opérations se suivent. Une valeur plus grande ne signifie ni plus récente, ni mieux autorisée, ni plus durable.

Le texte laisse également les deux choix décisifs aux implémentations. Lors d’une retransmission, le client peut réutiliser son ancien XID. Après une exécution, le serveur peut mémoriser cet XID et refuser de recommencer un appel égal. Cette coopération donne un certain degré d’exécution au plus une fois ; elle n’est pas imposée par les quatre octets.

Un identifiant n’a de portée que celle que lui construit le système. Il faut au minimum le demandeur, le programme, sa version, la procédure et une durée de conservation. Sinon, un même entier réutilisé plus tard pourrait ressembler à tort à l’ancien appel, ou un vrai doublon pourrait devenir neuf après une perte de mémoire.

Le transport fiable ne conservait pas l’histoire du service

Les RFC 1831 et 5531 reprennent une formulation subtile : recevoir une réponse sur un transport fiable autorise, dans leur modèle, une conclusion d’exécution exactement une fois. Mais ne rien recevoir reste ambigu. Même avec TCP, l’application a besoin de délais d’attente et de reconnexion pour les pannes du serveur.

La portée est donc celle de l’échange qui a abouti. TCP peut livrer un flux ordonné sur une connexion ; il ne garantit pas que l’état d’une procédure survivra au processus qui l’a exécutée. Après une coupure, le client ignore encore si le serveur a validé l’effet juste avant de tomber.

Le XID n’est pas davantage une preuve d’authentification. RPC possède des champs distincts pour les credentials et les verifiers. Associer une réponse au bon appel ne prouve ni l’identité de son auteur par le seul XID, ni la fraîcheur universelle, ni la persistance d’un résultat.

Le pari stateless de NFS révélait le prix du redémarrage

RFC 1094 cherchait des serveurs NFS aussi dépourvus d’état de protocole que possible. L’avantage était puissant : après une panne ou une interruption, le client pouvait simplement réessayer jusqu’au retour du service, sans reconstruire une session complexe.

Pour rendre ce choix acceptable, les opérations devaient être idempotentes autant que possible. Répéter la même lecture, ou une écriture identique au même emplacement, devait tendre vers le même effet. Pourtant, la spécification marquait déjà plusieurs procédures comme potentiellement non idempotentes : REMOVE, RENAME, LINK ou MKDIR ne traversent pas toutes une deuxième application sans changer de sens.

La répétition d’un appel n’est donc pas une répétition du monde. Entre les deux, un nom peut avoir disparu, une autre écriture peut s’être intercalée, un serveur peut avoir redémarré. La simplicité du serveur déplace le travail vers la définition précise des opérations et la récupération du client.

RFC 1813 décrivit le danger plus directement pour NFSv3. Une nouvelle application d’une opération non idempotente pouvait être destructrice. Même une reconnexion sur transport orienté connexion pouvait déclencher la répétition d’un appel dont la première issue restait inconnue.

Un cache de doublons était une mémoire de responsabilité

De nombreux serveurs NFSv3 conservaient les demandes récentes et leur statut final. Lorsqu’une copie reconnue revenait, ils restituaient l’ancienne réponse au lieu de refaire l’opération. Le cache économisait du calcul, mais sa fonction la plus importante était la correction : il gardait à la frontière d’exécution la preuve nécessaire pour ne pas faire payer deux fois une seule intention.

Cette preuve avait une date d’expiration. RFC 1813 indique que le cache vivait généralement en mémoire vive. Un redémarrage l’effaçait. Sa taille finie imposait des remplacements ; une partition assez longue pouvait chasser l’entrée avant que le client ne reçoive la réponse. Le doublon ultérieur redevenait alors indistinguable d’un appel neuf.

La limite ne venait pas d’un mauvais XID. Elle venait de l’écart entre une référence de 32 bits et la durée réelle de l’incertitude. Pour l’option EXCLUSIVE de CREATE, NFSv3 associait d’ailleurs un verifier à l’objet créé, précisément parce qu’un cache volatil ne suffisait pas. La force supplémentaire appartenait à cette procédure et à son stockage, pas au XID en général.

Les slots ont transformé une histoire infinie en obligation bornée

NFSv4.1 a choisi une autre géométrie. RFC 5661 introduisit les sessions. RFC 8881 décrit aujourd’hui une table finie de slots, chacun muni d’un identifiant de séquence et de la réponse gardée pour l’appel courant.

Le demandeur choisit un slot libre. Un nouvel appel avance la séquence de ce slot ; une retransmission reprend la séquence actuelle. Le répondant peut alors distinguer localement un appel nouveau, une copie de l’appel en cours et un message désordonné. Si le premier s’est terminé, la copie reçoit la réponse du cache.

Le point décisif n’est pas seulement la séquence. La table limite le nombre d’appels simultanés, donc le nombre de résultats dont le serveur doit assumer la garde. Quand le client avance la séquence, il montre qu'il est passé au résultat suivant et que l’ancienne entrée peut être remplacée.

Avec les XID seuls, les appels peuvent être nombreux et se terminer dans n’importe quel ordre. Garder toutes les réponses possibles d’un espace de 32 bits serait impraticable. Le slot donne un budget de mémoire communément négocié et une règle de libération vérifiable.

« Exactement une fois » conservait une frontière de persistance

Une table bien ordonnée n’est pas immortelle. RFC 8881 rappelle qu’une sémantique complète à travers les redémarrages exige de persister le cache de réponses et l’état de récupération pertinent. Si tout disparaît avec le processus, le serveur revenu ne peut pas témoigner d’un résultat qu’il ne possède plus.

NFSv4.1 n’a pas supprimé le XID. RPC continue à s’en servir pour faire correspondre appels et réponses, y compris dans des échanges hors du chemin SEQUENCE ordinaire. La session ajoute une mémoire spécialisée là où NFS demande une affirmation plus forte ; elle ne remplace pas toutes les identités par un registre universel.

La progression historique est donc faite de responsabilités ajoutées. Le XID fournit la comparaison. Le cache lui donne un effet temporaire. Le slot et sa séquence rendent la conservation finie et administrable. La persistance définit jusqu’à quelle panne l’affirmation demeure valable.

Ce que le même nombre ne démontre jamais seul

Deux appels portant le même XID ne prouvent pas qu’ils ont les mêmes arguments, le même principal ou le même serveur après bascule. Une absence dans le cache ne prouve pas la nouveauté. Un hit de cache ne certifie pas que les données sont déjà durables. Une opération dite idempotente peut encore produire un mauvais ordre : RFC 8881 montre qu’une ancienne écriture retardée peut recouvrir une écriture plus récente.

La bonne conclusion reste locale. Le numéro rend deux messages comparables. L’exécuteur doit encore garder l’association entre appel, effet, réponse et époque de récupération. Sans cette garde, « une fois » est un slogan attaché à un entier.

Sources et limites de preuve

La lignée RPC est documentée par les RFC 1050, 1057, 1831 et 5531. Le choix stateless de NFS et ses opérations répétables viennent de RFC 1094 ; le cache de doublons et ses fenêtres de panne de RFC 1813. Les sessions NFSv4.1 apparaissent dans RFC 5661 et leur spécification actuelle est RFC 8881. Ces textes ne mesurent ni les politiques actuelles de cache, ni leur persistance réelle, ni la conformité de tous les produits.