Aller au contenu principal

4. Contrôle de flux

4. Contrôle de flux

Les récepteurs doivent limiter la quantité de données qu'ils sont tenus de mettre en mémoire tampon, afin d'empêcher un expéditeur rapide de les submerger ou un expéditeur malveillant de consommer une grande quantité de mémoire. Pour permettre à un récepteur de limiter les engagements de mémoire pour une connexion, les flux sont contrôlés individuellement et sur l'ensemble de la connexion. Un récepteur QUIC contrôle la quantité maximale de données que l'expéditeur peut envoyer sur un flux ainsi que sur tous les flux à tout moment, comme décrit dans les Sections 4.1 et 4.2.

De même, pour limiter la concurrence au sein d'une connexion, un point de terminaison QUIC contrôle le nombre cumulatif maximal de flux que son pair peut initier, comme décrit dans la Section 4.6.

Les données envoyées dans les trames CRYPTO ne sont pas contrôlées de la même manière que les données de flux. QUIC s'appuie sur l'implémentation du protocole cryptographique pour éviter la mise en mémoire tampon excessive de données; voir [QUIC-TLS]. Pour éviter la mise en mémoire tampon excessive à plusieurs niveaux, les implémentations QUIC DEVRAIENT fournir une interface pour que l'implémentation du protocole cryptographique communique ses limites de mise en mémoire tampon.

4.1 Contrôle de flux de données

QUIC utilise un schéma de contrôle de flux basé sur des limites où un récepteur annonce la limite du total d'octets qu'il est prêt à recevoir sur un flux donné ou pour toute la connexion. Cela conduit à deux niveaux de contrôle de flux de données dans QUIC:

  • Contrôle de flux de flux, qui empêche un seul flux de consommer la totalité de la mémoire tampon de réception pour une connexion en limitant la quantité de données pouvant être envoyées sur chaque flux.

  • Contrôle de flux de connexion, qui empêche les expéditeurs de dépasser la capacité de la mémoire tampon d'un récepteur pour la connexion en limitant le total d'octets de données de flux envoyées dans les trames STREAM sur tous les flux.

Les expéditeurs NE DOIVENT PAS envoyer de données dépassant l'une ou l'autre limite.

Un récepteur établit des limites initiales pour tous les flux via des paramètres de transport pendant la négociation (Section 7.4). Par la suite, un récepteur envoie des trames MAX_STREAM_DATA (Section 19.10) ou MAX_DATA (Section 19.9) à l'expéditeur pour annoncer des limites plus importantes.

Un récepteur peut annoncer une limite plus grande pour un flux en envoyant une trame MAX_STREAM_DATA avec l'ID de flux correspondant. Une trame MAX_STREAM_DATA indique le décalage d'octets absolu maximal d'un flux. Un récepteur pourrait déterminer le décalage de contrôle de flux à annoncer en fonction du décalage actuel des données consommées sur ce flux.

Un récepteur peut annoncer une limite plus grande pour une connexion en envoyant une trame MAX_DATA, qui indique le maximum de la somme des décalages d'octets absolus de tous les flux. Un récepteur maintient une somme cumulative des octets reçus sur tous les flux, qui est utilisée pour vérifier les violations des limites de données de connexion ou de flux annoncées. Un récepteur pourrait déterminer la limite de données maximale à annoncer en fonction de la somme des octets consommés sur tous les flux.

Une fois qu'un récepteur a annoncé une limite pour la connexion ou un flux, ce n'est pas une erreur d'annoncer une limite plus petite, mais la limite plus petite n'a aucun effet.

Un récepteur DOIT fermer la connexion avec une erreur de type FLOW_CONTROL_ERROR si l'expéditeur viole les limites de données de connexion ou de flux annoncées; voir Section 11 pour les détails sur la gestion des erreurs.

Un expéditeur DOIT ignorer toutes les trames MAX_STREAM_DATA ou MAX_DATA qui n'augmentent pas les limites de contrôle de flux.

Si un expéditeur a envoyé des données jusqu'à la limite, il ne pourra pas envoyer de nouvelles données et est considéré comme bloqué. Un expéditeur DEVRAIT envoyer une trame STREAM_DATA_BLOCKED ou DATA_BLOCKED pour indiquer au récepteur qu'il a des données à écrire mais est bloqué par les limites de contrôle de flux. Si un expéditeur est bloqué pendant une période plus longue que le délai d'inactivité (Section 10.1), le récepteur pourrait fermer la connexion même lorsque l'expéditeur a des données disponibles pour la transmission. Pour empêcher la connexion de se fermer, un expéditeur limité par le contrôle de flux DEVRAIT envoyer périodiquement une trame STREAM_DATA_BLOCKED ou DATA_BLOCKED lorsqu'il n'a pas de paquets élicitant des ACK en vol.

4.2 Augmentation des limites de contrôle de flux

Les implémentations décident quand et combien de crédit annoncer dans les trames MAX_STREAM_DATA et MAX_DATA, mais cette section offre quelques considérations.

Pour éviter de bloquer un expéditeur, un récepteur PEUT envoyer une trame MAX_STREAM_DATA ou MAX_DATA plusieurs fois dans un aller-retour ou l'envoyer suffisamment tôt pour permettre le temps de perte de la trame et la récupération ultérieure.

Les trames de contrôle contribuent à la surcharge de connexion. Par conséquent, envoyer fréquemment des trames MAX_STREAM_DATA et MAX_DATA avec de petits changements est indésirable. D'autre part, si les mises à jour sont moins fréquentes, des augmentations plus importantes des limites sont nécessaires pour éviter de bloquer un expéditeur, nécessitant des engagements de ressources plus importants chez le récepteur. Il y a un compromis entre l'engagement de ressources et la surcharge lors de la détermination de la taille d'une limite annoncée.

Un récepteur peut utiliser un mécanisme d'ajustement automatique pour ajuster la fréquence et la quantité de crédit supplémentaire annoncé en fonction d'une estimation du temps d'aller-retour et du taux auquel l'application réceptrice consomme les données, similaire aux implémentations TCP courantes. Comme optimisation, un point de terminaison pourrait envoyer des trames liées au contrôle de flux uniquement lorsqu'il y a d'autres trames à envoyer, garantissant que le contrôle de flux ne provoque pas l'envoi de paquets supplémentaires.

Un expéditeur bloqué n'est pas tenu d'envoyer des trames STREAM_DATA_BLOCKED ou DATA_BLOCKED. Par conséquent, un récepteur NE DOIT PAS attendre une trame STREAM_DATA_BLOCKED ou DATA_BLOCKED avant d'envoyer une trame MAX_STREAM_DATA ou MAX_DATA; cela pourrait entraîner le blocage de l'expéditeur pour le reste de la connexion. Même si l'expéditeur envoie ces trames, les attendre entraînera le blocage de l'expéditeur pendant au moins un aller-retour complet.

Lorsqu'un expéditeur reçoit du crédit après avoir été bloqué, il pourrait être capable d'envoyer une grande quantité de données en réponse, entraînant une congestion à court terme; voir Section 7.7 de [QUIC-RECOVERY] pour une discussion sur la façon dont un expéditeur peut éviter cette congestion.

4.3 Performance du contrôle de flux

Si un point de terminaison ne peut pas garantir que son pair dispose toujours d'un crédit de contrôle de flux disponible supérieur au produit bande passante-délai du pair sur cette connexion, son débit de réception sera limité par le contrôle de flux.

La perte de paquets peut provoquer des lacunes dans la mémoire tampon de réception, empêchant l'application de consommer des données et de libérer de l'espace de mémoire tampon de réception.

L'envoi de mises à jour opportunes des limites de contrôle de flux peut améliorer les performances. L'envoi de paquets uniquement pour fournir des mises à jour de contrôle de flux peut augmenter la charge réseau et affecter négativement les performances. L'envoi de mises à jour de contrôle de flux avec d'autres trames, telles que les trames ACK, réduit le coût de ces mises à jour.

4.4 Gestion de l'annulation de flux

Les points de terminaison doivent finalement se mettre d'accord sur la quantité de crédit de contrôle de flux qui a été consommée sur chaque flux, afin de pouvoir comptabiliser tous les octets pour le contrôle de flux au niveau de la connexion.

À la réception d'une trame RESET_STREAM, un point de terminaison démontera l'état du flux correspondant et ignorera les données ultérieures arrivant sur ce flux.

RESET_STREAM termine une direction d'un flux de manière abrupte. Pour un flux bidirectionnel, RESET_STREAM n'a aucun effet sur le flux de données dans la direction opposée. Les deux points de terminaison DOIVENT maintenir l'état de contrôle de flux pour le flux dans la direction non terminée jusqu'à ce que cette direction entre dans un état terminal.

4.5 Taille finale du flux

La taille finale est la quantité de crédit de contrôle de flux consommée par un flux. En supposant que chaque octet contigu sur le flux a été envoyé une fois, la taille finale est le nombre d'octets envoyés. Plus généralement, il s'agit d'un de plus que le décalage de l'octet avec le plus grand décalage envoyé sur le flux, ou zéro si aucun octet n'a été envoyé.

Un expéditeur communique toujours de manière fiable la taille finale d'un flux au récepteur, quelle que soit la manière dont le flux est terminé. La taille finale est la somme des champs Offset et Length d'une trame STREAM avec un drapeau FIN, notant que ces champs peuvent être implicites. Alternativement, le champ Final Size d'une trame RESET_STREAM porte cette valeur. Cela garantit que les deux points de terminaison sont d'accord sur la quantité de crédit de contrôle de flux consommée par l'expéditeur sur ce flux.

Un point de terminaison connaîtra la taille finale d'un flux lorsque la partie réceptrice du flux entre dans l'état "Size Known" ou "Reset Recvd" (Section 3). Le récepteur DOIT utiliser la taille finale du flux pour comptabiliser tous les octets envoyés sur le flux dans son contrôleur de flux au niveau de la connexion.

Un point de terminaison NE DOIT PAS envoyer de données sur un flux à ou au-delà de la taille finale.

Une fois qu'une taille finale pour un flux est connue, elle ne peut pas changer. Si une trame RESET_STREAM ou STREAM est reçue indiquant un changement dans la taille finale du flux, un point de terminaison DEVRAIT répondre avec une erreur de type FINAL_SIZE_ERROR; voir Section 11 pour les détails sur la gestion des erreurs. Un récepteur DEVRAIT traiter la réception de données à ou au-delà de la taille finale comme une erreur de type FINAL_SIZE_ERROR, même après la fermeture d'un flux. La génération de ces erreurs n'est pas obligatoire, car exiger qu'un point de terminaison génère ces erreurs signifie également que le point de terminaison doit maintenir l'état de la taille finale pour les flux fermés, ce qui pourrait signifier un engagement d'état significatif.

4.6 Contrôle de la concurrence

Un point de terminaison limite le nombre cumulatif de flux entrants qu'un pair peut ouvrir. Seuls les flux avec un ID de flux inférieur à (max_streams * 4 + first_stream_id_of_type) peuvent être ouverts; voir Tableau 1. Les limites initiales sont définies dans les paramètres de transport; voir Section 18.2. Les limites ultérieures sont annoncées à l'aide des trames MAX_STREAMS; voir Section 19.11. Des limites distinctes s'appliquent aux flux unidirectionnels et bidirectionnels.

Si un paramètre de transport max_streams ou une trame MAX_STREAMS est reçu avec une valeur supérieure à 2^60, cela permettrait un ID de flux maximal qui ne peut pas être exprimé comme un entier de longueur variable; voir Section 16. Si l'un ou l'autre est reçu, la connexion DOIT être fermée immédiatement avec une erreur de connexion de type TRANSPORT_PARAMETER_ERROR si la valeur offensante a été reçue dans un paramètre de transport ou de type FRAME_ENCODING_ERROR si elle a été reçue dans une trame; voir Section 10.2.

Les points de terminaison NE DOIVENT PAS dépasser la limite fixée par leur pair. Un point de terminaison qui reçoit une trame avec un ID de flux dépassant la limite qu'il a envoyée DOIT traiter cela comme une erreur de connexion de type STREAM_LIMIT_ERROR; voir Section 11 pour les détails sur la gestion des erreurs.

Une fois qu'un récepteur a annoncé une limite de flux en utilisant la trame MAX_STREAMS, annoncer une limite plus petite n'a aucun effet. Les trames MAX_STREAMS qui n'augmentent pas la limite de flux DOIVENT être ignorées.

Comme avec le contrôle de flux des flux et de la connexion, ce document laisse les implémentations décider quand et combien de flux doivent être annoncés à un pair via MAX_STREAMS. Les implémentations peuvent choisir d'augmenter les limites lorsque les flux sont fermés, pour maintenir le nombre de flux disponibles pour les pairs approximativement constant.

Un point de terminaison qui est incapable d'ouvrir un nouveau flux en raison des limites du pair DEVRAIT envoyer une trame STREAMS_BLOCKED (Section 19.14). Ce signal est considéré comme utile pour le débogage. Un point de terminaison NE DOIT PAS attendre de recevoir ce signal avant d'annoncer un crédit supplémentaire, car cela signifierait que le pair sera bloqué pendant au moins un aller-retour complet, et potentiellement indéfiniment si le pair choisit de ne pas envoyer de trames STREAMS_BLOCKED.