Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 :

  1. 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_buffers rend tous les buffers d’un sendmsg_zc.)

  2. 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).

  3. 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 que Vec<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.