Une grande partie du travail d'ingénierie que nous avons accompli ces dernières années s'est articulée autour de l'aide apportée aux clients pour réduire leur utilisation du CPU et leur temps d'exécution. Il y a quelques mois, nous avons mis à jour notre modèle de tarification pour refléter cela, en supprimant la mesure de la durée pour nous concentrer strictement sur les requêtes et la consommation de vCPU. Ce nouveau modèle offre aux clients davantage de contrôle : lorsqu'ils écrivent un code plus efficace, ils réduisent directement leurs dépenses.
Cette recherche d'efficacité se reflète également dans certains de nos récents travaux d'ingénierie axés sur les clients, où nous avons été confrontés à la nécessité de prendre en charge des requêtes back-end de plus longue durée, notamment pour des flux de travail agentiques exigeants en calcul qui amènent les services à atteindre leur limite en temps réel.
Puisque le traitement par proxy des requêtes vers les grands modèles de langage (LLM) prend du temps, nous voulions offrir aux développeurs un moyen d'attendre ces réponses sans monopoliser leurs ressources disponibles en périphérie. Répondre à ce besoin s'est transformé en une amélioration plus globale de la plateforme que nous continuerons à étendre. L'amélioration s'est traduit par une nouvelle fonctionnalité de transfert des requêtes, et elle est disponible dès maintenant dans la version 0.13.0 du SDK Rust de Fastly.
Gestion des limites des invités Wasm et des requêtes de longue durée
Compute applique généralement un délai d'expiration en temps réel de 2 minutes pour les invités Wasm. Cette limite est intentionnelle afin de protéger les ressources et de garantir un bon renouvellement des requêtes. Cette conception fonctionne également parfaitement pour la grande majorité des microservices et de la logique de périphérie où nous maintenons une correspondance directe entre les invités Wasm en cours d'exécution et les requêtes entrantes.
Cependant, cette architecture peut parfois devenir problématique lors de l'attente de back-ends lents ou ne répondant pas. Cela peut être dû à une charge élevée sur le serveur d'origine qui ralentit toutes les requêtes en cours, ou à la nécessité de terminer un calcul intensif pour générer une réponse. Parfois, cela ne relève même pas du contrôle du serveur d'origine, comme une requête POST de longue durée qui prend du temps à écrire les octets du corps du message vers le serveur d'origine en raison d'un transfert volumineux ou d'un faible débit d'octets provenant d'un client mobile.
(Remarque : Les invités Wasm sont des modules Wasm compilés qui s'exécutent au sein d'un hôte Wasm. Les invités Wasm sont exécutés dans un bac à sable strict afin que l'hôte puisse exécuter en toute sécurité du code non approuvé sans lui permettre de compromettre le système sous-jacent.)
La solution : PendingRequest::send_to_client
Afin de mieux prendre en charge ces requêtes back-end de longue durée et d'augmenter le débit RPS, nous avons introduit une nouvelle fonctionnalité PendingRequest::send_to_client. Grâce à elle, nous pouvons envoyer une requête asynchrone à un serveur d'origine, et s'il s'agit d'une requête de type direct pass ou hit-for-pass, nous pouvons la transférer du client à l'hôte pendant qu'elle est encore en cours, avant même d'en connaître la réponse finale. Nous demandons ensuite à la plateforme de transmettre la réponse au client en aval dès qu'elle est finalement obtenue. Puisque la requête continue d'être exécutée en arrière-plan, le client Wasm peut se fermer de manière anticipée et libérer ses ressources pour de futures requêtes. Cette approche augmente considérablement le débit RPS et empêche les services Compute d'épuiser leurs instances Compute disponibles en attendant que les requêtes de longue durée se terminent.
Exemple Hello World :
use fastly::{Error, Request};
fn main() -> Result<(), Error> {
// 1. Receive the incoming downstream request from the client:
let req = Request::from_client();
// 2. Dispatch the direct pass request asynchronously to your slow backend:
let pending_req = req.with_pass(true).send_async("my_slow_origin_backend")?;
// 3. Hand off the unresolved, in-flight request directly to the host daemon:
pending_req.send_to_client()?;
// 4. Terminate the guest cleanly.
// The host daemon takes over, monitoring the request and piping the eventual
// response back downstream completely independently.
Ok(())
}Principaux cas d'utilisation
Passerelles d'IA à forte puissance de calcul et boucles LLM de longue durée : plus tôt cette année, nous avons exploré la façon dont les développeurs créent des boucles d'agents d'IA à faible latence sécurisées directement sur Fastly Compute. Dans un flux de travail agentique, un service en périphérie coordonne souvent plusieurs itérations de raisonnement, d'appels d'outils et de prompts de modèles externes. Même si notre durée d'exécution offre un environnement exceptionnellement sûr pour exécuter ces agents autonomes, les lourdes tâches de génération de texte ou les traitements complexes provenant de fournisseurs de LLM en amont peuvent considérablement augmenter les temps de traitement avant que les en-têtes ne soient générés. En réutilisant ces modèles de connexion avec la nouvelle API dans le SDK Rust, vous pouvez utiliser Fastly Compute comme une passerelle d'IA intelligente et à haut débit. Votre application en périphérie peut autoriser la requête via des back-ends dynamiques, évaluer les autorisations des clients ou injecter des couches de mise en cache sémantique, puis déclencher l'important appel LLM de manière asynchrone.
Optimisation de la protection : la plupart des architectures acheminent les requêtes initiales vers un POP en périphérie, qui les transmet ensuite via un proxy à un POP bouclier où d'importantes manipulations ont lieu. Souvent, le POP en périphérie ne modifie pas la réponse finale, mais transmet simplement directement la réponse du POP bouclier. Grâce au transfert de requête en attente, le POP en périphérie peut déclencher la requête bouclier de manière asynchrone, la transférer et mettre fin instantanément à son empreinte client locale, ce qui réduit considérablement le temps réel d'exécution total dans vos POP en périphérie les plus proches.

Figure 1 : Le graphique montre un service Compute qui fait office de bouclier vers NYC et reçoit un trafic régulier à Brisbane (BNE) transférant ses requêtes de bouclier. Le temps d'exécution à BNE disparaît presque après l'utilisation de la fonctionnalité de transfert.
Importations de longue durée : si votre service reçoit du contenu entrant en périphérie de la part de clients à faible bande passante, l'utilisation de cette fonctionnalité permet à votre service d'éviter d'atteindre les délais d'expiration en temps réel de l'invité en permettant à la plateforme de poursuivre l'importation indépendamment de l'exécution de l'invité Wasm.
Modification des en-têtes et gestion des erreurs
Cette nouvelle fonctionnalité inclut également la possibilité de modifier les en-têtes sur la réponse finale. Vous pouvez mettre en attente des modifications pour supprimer ou insérer des en-têtes spécifiques avant d'effectuer le transfert effectif. Cela vous permet de spécifier des modifications telles que « supprimer ce nom d'en-tête » ou « insérer cet en-tête avec la valeur X », ce qui sera fait une fois la réponse reçue :
use fastly::{Error, Request};
use fastly::http::request::PendingResponseKind;
fn main() -> Result<(), Error> {
// 1. Receive the incoming downstream request from the client:
let req = Request::from_client();
// 2. Dispatch the direct pass request asynchronously to your slow backend:
let mut pending = req.with_pass(true).send_async("my_slow_origin_backend")?;
// 3.1 Remove the `Server` header added by the origin's application server:
pending.remove_response_header("Server", PendingResponseKind::Response);
// 3.2 Insert the current service version for debugging:
pending.set_response_header(
"X-Service-Version",
fastly::compute_runtime::service_version().to_string(),
PendingResponseKind::Any
);
// 4. Hand off the unresolved, in-flight request directly to the host daemon:
pending.send_to_client()?;
// 5. Terminate the guest cleanly.
// The host daemon takes over, monitoring the request and piping the eventual
// response back downstream completely independently.
Ok(())
}Notez l'utilisation du type PendingResponseKind dans l'exemple ci-dessus. Cela permet au service de contrôler si les modifications d'en-tête sont appliquées aux réponses d'origine PendingResponseKind::Response, aux réponses d'erreur synthétiques générées lorsque la requête en cours échoue PendingResponseKind::Error, ou aux deux PendingResponseKind::Any. Dans les cas où la requête en cours a échoué, une réponse 5XX appropriée sera générée et renvoyée au client. Il s'agira généralement de l'un des éléments suivants :
504 Gateway Timeout : réponse renvoyée automatiquement si la connexion avec votre serveur d'origine expire ou si le délai d'expiration du premier octet (First Byte Timeout) pour le back-end est atteint.
502 Bad Gateway : réponse renvoyée si le serveur d'origine renvoie un corps de réponse incomplet ou erroné, si le back-end possède un certificat de sécurité de la couche de transport non valide ou s'il ne fonctionne pas et ne répond pas correctement.
Vous pouvez utiliser le graphique « Backend Requests & Errors » dans le tableau de bord d'observabilité du panneau de contrôle de Fastly pour voir quel type d'erreurs back-end sont responsables des réponses 502 et 504. Consultez la page Erreurs de requête back-end pour plus d'informations.
Commencez dès aujourd'hui
Vous n'avez pas besoin d'attendre d'avoir déployé votre service en production pour voir comment cette nouvelle fonctionnalité peut vous aider ! Elle est entièrement prise en charge dans les récentes versions de Viceroy, notre environnement de test local. Vous pouvez émuler les délais d'expiration du back-end de longue durée, restructurer votre code pour utiliser Request::send_async/PendingRequest::send_to_client et déboguer vos opérations d'en-tête en file d'attente directement depuis votre terminal de développement local. Bien que la fonctionnalité de transfert soit actuellement exclusive à notre SDK Rust (v0.13.0), nous l'étendrons au reste de nos SDK officiels. N'hésitez pas non plus à consulter notre documentation pour plus d'informations et dites-nous ce que vous développez sur notre forum.

