ADR-032 — Préservation des données confiées : zéro discard silencieux
Statut : Accepté (2026-06-11). Décline les Principes d’ingénierie 3 (zéro présomption sur buffers/encodages) et 7 (verbosité au service de la clarté) en une règle transverse de conception d’API.
Catégorie : Méthode (transverse, tout le framework).
Contexte
Une grande partie des API d’Air prend possession de données fournies par
l’appelant : buffers (Vec<u8>, Vec<Vec<u8>>), descripteurs de fichiers
(OwnedFd), structures (chemins, messages, requêtes). C’est notamment le modèle
d’ownership de la couche 0 io_uring (S1) : la donnée est déplacée dans un slot à
la soumission, puis restituée à la complétion.
Le risque, récurrent et silencieux, est la perte de donnée : une API qui prend
plusieurs buffers mais n’en rend qu’un, un FD reçu non matérialisé (fuite), une
troncature kernel (MSG_CTRUNC) avalée sans signal, un buffer non restitué sur un
chemin d’erreur. Ces pertes ne lèvent pas d’erreur — elles disparaissent —, ce
qui les rend coûteuses à diagnostiquer et dangereuses (fuites de ressources,
corruption logique en aval).
Le cas qui a fait émerger la règle : au Temps 2b d’io_uring, une première
implémentation de la restitution zero-copy d’un sendmsg_zc multi-buffers ne
rendait que le premier buffer, droppant les autres. Comportement « pratique »
en apparence, perte de donnée réelle.
Décision
Une API Air ne discarde jamais une donnée que l’appelant lui a confiée : elle restitue tout, proprement et sûrement.
Concrètement, lorsqu’une opération prend l’ownership de données de l’appelant :
-
Restitution intégrale. Elle rend l’intégralité de ce qu’elle a pris. Une opération vectorielle/multi-buffers restitue toute la collection (
Vec<Vec<u8>>), jamais un sous-ensemble. (Exemple :into_zero_copy_buffersrend tous les buffers d’unsendmsg_zc.) -
Ressources matérialisées, jamais fuitées. Les ressources reçues du kernel (FD via
SCM_RIGHTS…) sont matérialisées en types possédés (OwnedFd), jamais laissées en entiers bruts ni perdues. Une troncature (MSG_CTRUNC,MSG_TRUNC,ERANGE…) est signalée explicitement à l’appelant ; les éléments partiels sont fermés/libérés proprement (pas de fuite). -
Chemins d’erreur compris. La restitution vaut aussi en cas d’erreur : si une soumission échoue après avoir pris l’ownership, la donnée est rendue (ou l’erreur la transporte), jamais silencieusement abandonnée.
Distinction : consommation explicite ≠ discard
La règle interdit la perte silencieuse, pas la consommation documentée par
contrat. Une API peut légitimement consommer une donnée si c’est sa sémantique
explicite et typée. Exemple : submit_close(fd: OwnedFd) consomme le FD (cédé
au kernel) — ce n’est pas un discard, c’est un transfert d’ownership voulu, gravé
dans la signature. Le test de décision :
« Cette API a-t-elle pris une donnée de l’appelant ? Si oui : la lui rend-elle intégralement, ou la consomme-t-elle explicitement par contrat ? » Tout ce qui n’est ni l’un ni l’autre — une donnée prise puis perdue sans le dire — est proscrit.
Conséquences
Bénéfices
- Aucune fuite de ressource ni perte de donnée silencieuse : cycle de vie des ressources prévisible, auditable, sûr par construction.
- Diagnostic : une troncature/perte devient un signal typé, pas un bug fantôme.
- Cohérence d’API transverse : l’appelant sait qu’il récupère toujours ce qu’il a confié.
Coûts (assumés)
- Types de retour parfois plus verbeux (
Vec<Vec<u8>>plutôt queVec<u8>, métadonnées de troncature explicites). C’est l’esprit du Principe 7 (verbosité au service de la clarté) : on préfère un retour complet et honnête à un retour « simple » qui ment par omission.
Portée et application
- S’applique à toute la surface d’Air, toutes couches (couche 0 io_uring/IPC, primitives couche 1, runtime, services).
- Le reviewer vérifie, pour toute API prenant de l’ownership : restitution intégrale ? ressources reçues possédées et troncatures signalées ? chemin d’erreur couvert ? consommation, si elle existe, explicite dans la signature ?
Articulation avec les autres ADRs / principes
- Principe 3 (zéro présomption sur buffers/encodages) : la restitution rend les octets bruts tels quels, sans perte ni réinterprétation.
- Principe 7 (verbosité au service de la clarté) : justifie le surcoût de verbosité des retours complets.
- Principe 4 (validation en amont) : les conditions de troncature sont détectées et signalées, pas avalées.
- ADR-021 (conventions transverses couche 0) et ADR-028 (soundness io_uring) : même esprit de sûreté par construction ; cet ADR généralise « ne rien perdre » à toute la surface.
- ADR-019 (modèle d’erreurs) : sur les chemins d’erreur, la donnée confiée est restituée et/ou l’erreur porte le contexte ; rien ne disparaît.
Statut futur
ADR immuable dans sa règle (restitution intégrale + matérialisation sûre + chemins d’erreur + distinction consommation explicite) sauf RFC (ADR-015).
Licence du document : MPL 2.0 Statut : Document de méthode, transverse au framework.