Profilage mémoire Go pour les pics de trafic : guide pprof
Le profilage mémoire Go vous aide à gérer les pics de trafic soudains. Un guide pratique pprof pour repérer les points chauds d'allocation dans le JSON, les scans DB et le middleware.

Ce que font les pics soudains de trafic à la mémoire d'un service Go
Un « pic mémoire » en production ne signifie rarement qu'un seul nombre a augmenté. Vous pouvez voir le RSS (mémoire du processus) monter rapidement alors que le heap Go bouge à peine, ou le heap croître et chuter en vagues nettes à chaque passage du GC. En même temps, la latence s'aggrave souvent parce que le runtime passe plus de temps à nettoyer.
Schémas courants dans les métriques :
- Le RSS augmente plus vite que prévu et parfois ne redescend pas complètement après le pic
- Le heap in-use augmente, puis chute en cycles nets à chaque exécution du GC
- Le taux d'allocation bondit (octets alloués par seconde)
- Le temps de pause du GC et le temps CPU du GC augmentent, même si chaque pause reste courte
- La latence des requêtes augmente et la latence des queues devient bruitée
Les pics de trafic amplifient les allocations par requête parce que le « petit » gaspillage se multiplie linéairement avec la charge. Si une requête alloue 50 Ko supplémentaires (buffers JSON temporaires, objets de scan par ligne, données de contexte middleware), alors à 2 000 requêtes/s vous alimentez l'allocateur à hauteur d'environ 100 Mo par seconde. Go peut en gérer beaucoup, mais le GC doit toujours tracer et libérer ces objets de courte durée. Quand l'allocation dépasse le nettoyage, la cible du heap grandit, le RSS suit, et vous pouvez atteindre les limites mémoire.
Les symptômes sont familiers : OOM tués par votre orchestrateur, pics de latence soudains, plus de temps passé dans le GC, et un service qui semble « occupé » même quand le CPU n'est pas saturé. Vous pouvez aussi obtenir du thrash du GC : le service reste en ligne mais alloue et collecte constamment, ce qui fait chuter le débit au pire moment.
pprof aide à répondre rapidement à une question : quels chemins de code allouent le plus, et ces allocations sont-elles nécessaires ? Un profil heap montre ce qui est retenu maintenant. Les vues axées sur les allocations (comme alloc_space) montrent ce qui est créé et jeté.
Ce que pprof ne fera pas : expliquer chaque octet du RSS. RSS comprend plus que le heap Go (stacks, métadonnées du runtime, mappings OS, allocations cgo, fragmentation). pprof est surtout utile pour pointer les points chauds d'allocation dans votre code Go, pas pour fournir un total exact au niveau du conteneur.
Mettre en place pprof en toute sécurité (pas à pas)
pprof est le plus simple à utiliser via des endpoints HTTP, mais ces endpoints peuvent révéler beaucoup d'informations sur votre service. Traitez-les comme une fonctionnalité d'admin, pas comme une API publique.
1) Ajouter les endpoints pprof
En Go, la configuration la plus simple est d'exécuter pprof sur un serveur admin séparé. Cela garde les routes de profilage à l'écart de votre routeur principal et de votre middleware.
package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
// Admin only: bind to localhost
log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
}()
// Your main server starts here...
// http.ListenAndServe(":8080", appHandler)
select {}
}
Si vous ne pouvez pas ouvrir un second port, vous pouvez monter les routes pprof dans votre serveur principal, mais il est plus facile de les exposer par accident. Un port admin séparé est le comportement par défaut le plus sûr.
2) Verrouillez l'accès avant déploiement
Commencez par des contrôles simples à ne pas rater. Lier à localhost signifie que les endpoints ne sont pas accessibles depuis Internet à moins que quelqu'un n'expose aussi ce port.
Une checklist rapide :
- Exécuter pprof sur un port admin, pas sur le port principal orienté utilisateur
- Lier à 127.0.0.1 (ou une interface privée) en production
- Ajouter une allowlist au niveau réseau (VPN, bastion ou sous-réseau interne)
- Exiger une authentification si votre edge peut l'appliquer (basic auth ou token)
- Vérifier que vous pouvez récupérer les profils dont vous avez besoin : heap, allocs, goroutine
3) Construire et déployer en toute sécurité
Gardez le changement petit : ajoutez pprof, shippez-le et confirmez qu'il n'est accessible que depuis les endroits attendus. Si vous avez un environnement staging, testez d'abord en simulant une charge et en capturant un profil heap et allocs.
En production, déployez progressivement (une instance ou une petite fraction du trafic). Si pprof est mal configuré, le rayon d'impact reste réduit pendant que vous corrigez.
Capturer les bons profils pendant un pic
Pendant un pic, un seul instantané suffit rarement. Capturez une petite timeline : quelques minutes avant le pic (baseline), pendant le pic (impact) et quelques minutes après (recovery). Cela facilite la séparation des changements d'allocation réels du comportement normal d'initialisation.
Si vous pouvez reproduire le pic avec une charge contrôlée, faites correspondre la production autant que possible : mélange d'endpoints, tailles des payloads et concurrence. Un pic de petites requêtes se comporte très différemment d'un pic de grosses réponses JSON.
Prenez à la fois un profil heap et un profil axé sur les allocations. Ils répondent à des questions différentes :
- Heap (inuse) montre ce qui est vivant et retient de la mémoire maintenant
- Allocations (alloc_space ou alloc_objects) montrent ce qui est fortement alloué, même si c'est libéré rapidement
Un pattern de capture pratique : prenez un profil heap, puis un profil d'allocations, puis répétez 30 à 60 secondes plus tard. Deux points pendant le pic vous aident à voir si un chemin suspect est stable ou en accélération.
# examples: adjust host/port and timing to your setup
curl -o heap_during.pprof "http://127.0.0.1:6060/debug/pprof/heap"
curl -o allocs_30s.pprof "http://127.0.0.1:6060/debug/pprof/allocs?seconds=30"
En parallèle des fichiers pprof, enregistrez quelques statistiques runtime pour pouvoir expliquer ce que faisait le GC au même moment. La taille du heap, le nombre de GC et le temps de pause sont généralement suffisants. Même une courte ligne de log à chaque instant de capture aide à corréler « les allocations ont augmenté » avec « le GC s'est mis à tourner en continu ».
Gardez des notes d'incident pendant l'opération : version du build (commit/tag), version de Go, flags importants, changements de config et quel trafic se passait (endpoints, tenants, tailles de payload). Ces détails comptent souvent plus tard quand vous comparez des profils et réalisez que le mix de requêtes a changé.
Comment lire les profils heap et d'allocations
Un profil heap répond à des questions différentes selon la vue.
Inuse space montre ce qui est encore en mémoire au moment de la capture. Utilisez-le pour détecter des fuites, des caches longue durée ou des requêtes qui laissent des objets derrière elles.
Alloc space (allocations totales) montre ce qui a été alloué sur une période, même si c'est libéré rapidement. Servez-vous-en quand les pics causent beaucoup de travail GC, des sauts de latence ou des OOM dus au churn.
Le sampling compte. Go n'enregistre pas chaque allocation. Il échantillonne les allocations (contrôlé par runtime.MemProfileRate), donc de petites allocations très fréquentes peuvent être sous-représentées et les chiffres restent des estimations. Les plus gros coupables ressortent toutefois souvent, surtout en conditions de pic. Cherchez des tendances et des contributeurs majeurs, pas une comptabilité parfaite.
Les vues pprof les plus utiles :
- top : lecture rapide de qui domine en inuse ou alloc (vérifiez flat et cumulative)
- list
: sources d'allocation au niveau des lignes dans une fonction chaude - graph : chemins d'appel qui expliquent comment on arrive là
Les diffs sont là où c'est vraiment pratique. Comparez un profil baseline (trafic normal) avec un profil de pic pour mettre en évidence ce qui a changé, au lieu de courir après le bruit de fond.
Validez vos conclusions par un petit changement avant un gros refactor :
- Réutilisez un buffer (ou ajoutez un petit
sync.Pool) dans le chemin chaud - Réduisez la création d'objets par requête (par exemple, évitez de construire des maps intermédiaires pour le JSON)
- Re-profilez sous la même charge et confirmez que le diff diminue là où vous attendiez
Si les chiffres évoluent dans le bon sens, vous avez trouvé une cause réelle, pas seulement un rapport effrayant.
Trouver les points chauds d'allocation dans l'encodage JSON
Pendant les pics, le travail JSON peut devenir une grosse facture mémoire car il tourne sur chaque requête. Les points chauds JSON apparaissent souvent comme beaucoup de petites allocations qui poussent le GC.
Signes alarmants dans pprof
Si le heap ou la vue d'allocations pointe vers encoding/json, regardez de près ce que vous lui fournissez. Ces schémas gonflent souvent les allocations :
- Utiliser
map[string]any(ou[]any) pour les réponses au lieu de structs typés - Marshaler le même objet plusieurs fois (par ex. le logger et la réponse)
- Pretty printing avec
json.MarshalIndenten production - Construire du JSON via des chaînes temporaires (
fmt.Sprintf, concaténation de chaînes) avant le marshal - Convertir de grands
[]byteenstring(ou inversement) juste pour correspondre à une API
json.Marshal alloue toujours un nouveau []byte pour la sortie complète. json.NewEncoder(w).Encode(v) évite généralement ce gros buffer car il écrit sur un io.Writer, mais il peut encore allouer en interne, surtout si v contient beaucoup d'any, de maps ou de structures à base de pointeurs.
Correctifs rapides et expériences simples
Commencez par des structs typés pour la forme de votre réponse. Elles réduisent le travail de réflexion et évitent le boxing en interface par champ.
Ensuite, supprimez les temporaires évitables par requête : réutilisez un bytes.Buffer via un sync.Pool (avec précaution), n'indent pas en production, et ne re-marshalez pas juste pour les logs.
Petites expériences permettant de confirmer que JSON est en cause :
- Remplacez
map[string]anypar un struct pour un endpoint chaud et comparez les profils - Passez de
MarshalàEncoderécrivant directement dans la réponse - Retirez
MarshalIndentou le formatage debug-only et re-profilez sous la même charge - Évitez l'encodage JSON pour des réponses mises en cache et mesurez la baisse
Trouver les points chauds d'allocation dans le scan de requêtes
Quand la mémoire bondit pendant un pic, les lectures de base de données surprennent souvent. On se focalise facilement sur le temps SQL, mais l'étape de scan peut allouer beaucoup par ligne, surtout si vous scannez dans des types flexibles.
Coupables fréquents :
- Scannage dans
interface{}(oumap[string]any) et laisser le driver décider des types - Conversion de
[]byteenstringpour chaque champ - Utilisation de wrappers nullable (
sql.NullString,sql.NullInt64) sur de gros jeux de résultats - Récupération de gros champs text/blob dont vous n'avez pas toujours besoin
Un pattern qui brûle de la mémoire silencieusement consiste à scanner des données de ligne dans des variables temporaires, puis à copier dans une struct réelle (ou construire une map par ligne). Si vous pouvez scanner directement dans une struct avec des champs concrets, vous évitez des allocations et des vérifications de type supplémentaires.
La taille des lots et la pagination changent votre forme mémoire. Récupérer 10 000 lignes dans un slice alloue pour la croissance du slice et pour chaque ligne, tout en une fois. Si le handler n'a besoin que d'une page, poussez cela dans la requête et gardez la taille de page stable. Si vous devez traiter beaucoup de lignes, streamez-les et agrégez de petits résumés au lieu de stocker chaque ligne.
Les gros champs textuels demandent de l'attention. Beaucoup de drivers retournent le texte comme []byte. La convertir en string copie les données, donc le faire pour chaque ligne peut faire exploser les allocations. Si vous n'avez besoin de la valeur que parfois, retarde la conversion ou scannez moins de colonnes pour cet endpoint.
Pour confirmer si c'est le driver ou votre code qui alloue le plus, regardez ce qui domine vos profils :
- Si les frames pointent vers votre code de mapping, concentrez-vous sur les cibles de scan et les conversions
- Si les frames pointent vers
database/sqlou le driver, réduisez d'abord les lignes et colonnes, puis considérez des options spécifiques au driver - Vérifiez à la fois alloc_space et alloc_objects ; beaucoup de petites allocations peuvent être pires que quelques grosses
Exemple : un endpoint « lister les commandes » fait SELECT * dans []map[string]any. Pendant un pic, chaque requête construit des milliers de petites maps et chaînes. Changer la requête pour sélectionner seulement les colonnes nécessaires et scanner dans []Order{ID int64, Status string, TotalCents int64} fait souvent chuter les allocations immédiatement. La même idée s'applique si vous profilez un backend Go généré par AppMaster : le point chaud est généralement la façon dont vous structurez et scannez les données, pas la DB elle-même.
Patterns middleware qui allouent silencieusement par requête
Le middleware semble peu coûteux parce que c'est « juste un wrapper », mais il s'exécute sur chaque requête. Pendant un pic, de petites allocations par requête s'additionnent rapidement et apparaissent comme une hausse du taux d'allocation.
Le middleware de logging est une source fréquente : formatage de chaînes, construction de maps de champs ou copie des headers pour un rendu plus joli. Les helpers d'ID de requête peuvent allouer quand ils génèrent un ID, le convertissent en string, puis l'attachent au contexte. Même context.WithValue peut allouer si vous stockez de nouveaux objets (ou de nouvelles chaînes) à chaque requête.
La compression et la gestion du corps sont un autre coupable fréquent. Si le middleware lit tout le body pour « peek » ou valider, vous pouvez vous retrouver avec un gros buffer par requête. Le middleware gzip peut allouer beaucoup s'il crée de nouveaux readers/writers à chaque fois au lieu de réutiliser des buffers.
Les couches d'auth et de session sont similaires. Si chaque requête parse des tokens, décode base64 des cookies ou charge des blobs de session dans de nouvelles structs, vous avez un churn constant même quand le handler est léger.
Le tracing et les métriques peuvent allouer plus que prévu quand les labels sont construits dynamiquement. Concaténer les noms de route, user agents ou IDs de tenant en de nouvelles chaînes par requête est un coût caché classique.
Patterns qui apparaissent souvent comme « mort par mille coupures » :
- Construire des lignes de log avec
fmt.Sprintfet de nouveauxmap[string]anypar requête - Copier les headers dans de nouvelles maps ou slices pour le logging ou la signature
- Allouer de nouveaux buffers gzip et readers/writers au lieu d'utiliser un pool
- Créer des labels de métriques à haute cardinalité (beaucoup de chaînes uniques)
- Stocker de nouvelles structs dans le contexte à chaque requête
Pour isoler le coût du middleware, comparez deux profils : un avec la chaîne complète activée et un autre avec le middleware temporairement désactivé ou remplacé par un no-op. Un test simple est un endpoint de santé qui devrait être presque sans allocation. Si /health alloue fortement pendant un pic, le handler n'est probablement pas le problème.
Si vous construisez des backends Go générés par AppMaster, la même règle s'applique : gardez les fonctions transversales (logging, auth, tracing) mesurables, et traitez les allocations par requête comme un budget à auditer.
Correctifs qui paient généralement vite
Une fois que vous avez des vues heap et allocs depuis pprof, priorisez les changements qui réduisent les allocations par requête. L'objectif n'est pas d'utiliser des astuces intelligentes, mais de faire en sorte que le chemin chaud crée moins d'objets de courte durée, surtout sous charge.
Commencez par les gains sûrs et ennuyeux
Si les tailles sont prévisibles, préallouez. Si un endpoint retourne habituellement ~200 éléments, créez votre slice avec une capacité de 200 pour éviter des copies multiples.
Évitez de construire des chaînes dans les chemins chauds. fmt.Sprintf est pratique, mais il alloue souvent. Pour le logging, préférez des champs structurés et réutilisez un petit buffer quand ça a du sens.
Si vous générez de grosses réponses JSON, pensez à les streamer plutôt que de construire un énorme []byte ou string en mémoire. Un pattern courant de pic : la requête arrive, vous lisez un gros body, vous construisez une grosse réponse, la mémoire monte jusqu'à ce que le GC rattrape.
Changements rapides qui apparaissent typiquement clairement dans des profils avant/après :
- Préallouer slices et maps quand vous connaissez l'ordre de grandeur
- Remplacer le formatage via fmt dans les handlers par des alternatives moins coûteuses
- Streamer de grosses réponses JSON (encoder directement sur le response writer)
- Utiliser
sync.Poolpour des objets réutilisables et à forme constante (buffers, encoders) et les réinitialiser correctement - Définir des limites de requête (taille du body, taille du payload, taille de page) pour plafonner les cas extrêmes
Utiliser sync.Pool avec précaution
sync.Pool aide quand vous allouez toujours la même chose, comme un bytes.Buffer par requête. Il peut aussi nuire si vous pooler des objets aux tailles imprévisibles ou si vous oubliez de les réinitialiser, ce qui maintient des grands tableaux en vie.
Mesurez avant/après en utilisant la même charge :
- Capturez un profil allocs pendant la fenêtre de pic
- Appliquez un changement à la fois
- Relancez le même mix de requêtes et comparez les allocs/op totals
- Surveillez la latence aux queues, pas seulement la mémoire
Si vous construisez des backends Go générés par AppMaster, ces correctifs s'appliquent toujours au code personnalisé autour des handlers, intégrations et middleware. C'est là que se cachent souvent les allocations déclenchées par les pics.
Erreurs courantes avec pprof et faux positifs
La façon la plus rapide de perdre une journée est d'optimiser la mauvaise chose. Si le service est lent, commencez par le CPU. S'il est tué par OOM, commencez par le heap. S'il survit mais que le GC tourne sans arrêt, regardez le taux d'allocation et le comportement du GC.
Un autre piège est de se contenter de regarder « top ». « Top » cache le contexte. Inspectez toujours les piles d'appel (ou un flame graph) pour voir qui a appelé l'allocateur. La correction se trouve souvent une ou deux frames au-dessus de la fonction chaude.
Faites aussi attention à la confusion inuse vs churn. Une requête peut allouer 5 Mo d'objets de courte durée, déclencher du GC additionnel et finir avec seulement 200 Ko en inuse. Si vous ne regardez que l'inuse, vous manquez le churn. Si vous regardez uniquement les allocations totales, vous pouvez optimiser quelque chose qui ne reste jamais en mémoire et qui n'est pas un risque d'OOM.
Vérifications rapides avant de changer le code :
- Confirmez la bonne vue : heap inuse pour la rétention, alloc_space/alloc_objects pour le churn
- Comparez les stacks, pas seulement les noms de fonctions (
encoding/jsonest souvent un symptôme) - Reproduisez le trafic de façon réaliste : mêmes endpoints, tailles de payload, headers, concurrence
- Capturez un baseline et un profil de pic, puis differez-les
Des tests de charge irréalistes donnent des faux positifs. Si votre test envoie de minuscules corps JSON mais que la production envoie 200 Ko, vous optimiserez le mauvais chemin. Si votre test ne retourne qu'une ligne, vous ne verrez jamais le comportement de scan qui apparaît avec 500 lignes.
Ne courez pas après le bruit. Si une fonction apparaît seulement dans le profil de pic (pas dans le baseline), c'est un bon indice. Si elle apparaît dans les deux au même niveau, c'est peut-être du travail normal de fond.
Exemple d'incident réaliste
Un lundi matin, une promo est envoyée et votre API Go reçoit 8× le trafic habituel. Le premier symptôme n'est pas un crash. Le RSS monte, le GC s'active, et la latence p95 augmente. L'endpoint le plus chaud est GET /api/orders parce que l'app mobile le rafraîchit à chaque ouverture d'écran.
Vous prenez deux snapshots : un en période calme (baseline) et un pendant le pic. Capturez le même type de profil heap pour que la comparaison reste équitable.
Le flow qui marche dans l'instant :
- Prenez un profil heap baseline et notez le RPS, le RSS et la p95
- Pendant le pic, prenez un autre profil heap plus un profil d'allocations dans la même fenêtre d'1 à 2 minutes
- Comparez les plus gros allocateurs entre les deux et concentrez-vous sur ce qui a le plus augmenté
- Remontez de la fonction la plus grosse vers ses callers jusqu'à atteindre votre handler
- Faites un petit changement, déployez sur une seule instance et re-profilez
Dans ce cas, le profil de pic montrait que la majorité des nouvelles allocations venait de l'encodage JSON. Le handler construisait des map[string]any pour les lignes, puis faisait json.Marshal sur une slice de maps. Chaque requête créait beaucoup de strings temporaires et de valeurs interface.
La correction minimale sûre a été d'arrêter de construire des maps. Scannez les lignes de la base directement dans une struct typée et encodez cette slice. Rien d'autre n'a changé : mêmes champs, même forme de réponse, mêmes codes. Après le déploiement sur une instance, les allocations sur le chemin JSON ont chuté, le temps du GC a baissé et la latence s'est stabilisée.
Ce n'est qu'ensuite que vous déployez progressivement en surveillant la mémoire, le GC et les taux d'erreur. Si vous construisez des services sur une plateforme no-code comme AppMaster, c'est aussi un rappel d'utiliser des modèles de réponse typés et cohérents pour éviter des coûts d'allocation cachés.
Prochaines étapes pour éviter le prochain pic mémoire
Une fois le pic stabilisé, rendez le prochain ennuyeux. Traitez le profilage comme un exercice répétable.
Rédigez un petit runbook que votre équipe pourra suivre quand elle est fatiguée. Il doit indiquer quoi capturer, quand le capturer et comment le comparer à un baseline connu. Restez concret : commandes exactes, où stocker les profils et à quoi ressemblent les allocateurs « normaux » pour vos endpoints majeurs.
Ajoutez une surveillance légère de la pression d'allocation avant d'atteindre l'OOM : taille du heap, cycles GC par seconde et octets alloués par requête. Attraper « allocations par requête en hausse de 30% semaine sur semaine » est souvent plus utile que d'attendre une alarme mémoire dure.
Poussez les contrôles en amont avec un petit test de charge en CI sur un endpoint représentatif. Un petit changement de réponse peut doubler les allocations s'il déclenche des copies supplémentaires, et mieux vaut le détecter avant que la production ne le fasse.
Si vous exécutez un backend Go généré, exportez la source et profilez-la de la même manière. Le code généré reste du code Go, et pprof pointera vers de vraies fonctions et lignes.
Si vos exigences changent souvent, AppMaster (appmaster.io) peut être un moyen pratique de reconstruire et régénérer des backends Go propres au fur et à mesure que l'app évolue, puis de profiler le code exporté sous une charge réaliste avant de le déployer.
FAQ
Une montée soudaine de trafic augmente généralement le taux d'allocation plus que vous ne l'imaginez. Même de petits objets temporaires par requête s'additionnent linéairement avec le RPS, ce qui force le GC à tourner plus souvent et peut faire grimper le RSS même si le tas vivant n'est pas énorme.
Les métriques de heap suivent la mémoire gérée par Go, mais RSS inclut plus : les stacks des goroutines, les métadonnées du runtime, les mappings OS, la fragmentation et des allocations non-heap (y compris certains usages de cgo). Il est normal que RSS et heap évoluent différemment lors d'un pic ; utilisez pprof pour pointer les points chauds d'allocation plutôt que d'essayer d'« aligner » le RSS exactement.
Commencez par un profil heap quand vous suspectez une rétention (quelque chose reste vivant), et par un profil axé sur les allocations (comme allocs/alloc_space) quand vous suspectez du churn (beaucoup d'objets de courte durée). Pendant les pics de trafic, le churn est souvent le vrai problème car il augmente le temps CPU du GC et la latence aux queues.
La configuration la plus simple et la plus sûre est d'exposer pprof sur un serveur d'administration séparé lié à 127.0.0.1, accessible uniquement en interne. Traitez pprof comme une interface d'administration car il peut révéler des détails internes du service.
Capturez une courte chronologie : un profil quelques minutes avant le pic (baseline), un pendant le pic (impact) et un après (recovery). Cela facilite l'identification des changements plutôt que de courir après des allocations de fond normales.
Utilisez inuse pour trouver ce qui est réellement retenu au moment de la capture, et alloc_space (ou alloc_objects) pour identifier ce qui est fortement alloué. L'erreur fréquente est d'utiliser uniquement inuse et de manquer le churn qui cause le thrash du GC et les pics de latence.
Si encoding/json domine les allocations, le problème est souvent la forme des données plutôt que le package lui-même. Remplacer map[string]any par des structs typés, éviter json.MarshalIndent et ne pas construire du JSON via des chaînes temporaires réduit souvent les allocations immédiatement.
Scanner des lignes dans des cibles flexibles comme interface{} ou map[string]any, convertir des []byte en string pour de nombreux champs, et récupérer trop de lignes ou de colonnes peuvent allouer beaucoup par requête. Sélectionnez uniquement les colonnes nécessaires, paginez les résultats et scannez directement dans des champs de struct concrets pour des gains importants.
Le middleware s'exécute à chaque requête, donc de petites allocations deviennent énormes sous charge. Construire des chaînes pour les logs, créer des labels à haute cardinalité pour le tracing, générer des IDs de requête, créer des readers/writers gzip à chaque requête et stocker des objets nouveaux dans le contexte sont des causes courantes d'un churn d'allocation continu.
Oui. La même démarche basée sur le profil s'applique à n'importe quel code Go, généré ou écrit à la main. Si vous exportez le backend généré, vous pouvez exécuter pprof, identifier les chemins d'allocation et ajuster modèles, handlers et logique transversale pour réduire les allocations par requête avant le prochain pic.


