Au cours d'un certain test de résistance continu, nous avons constaté que la mémoire du nœud frontal de GreptimeDB continuait d'augmenter même lorsque le volume des requêtes était stable, jusqu'à ce qu'elle soit tuée par le MOO. Nous avons estimé que Frontend devrait avoir une fuite de mémoire, nous avons donc commencé un voyage pour résoudre les fuites de mémoire.
Profilage de tas
Sur les grands projets, il est presque impossible de détecter des fuites de mémoire simplement en regardant le code. Nous devons donc d’abord faire une analyse statistique de l’utilisation de la mémoire du programme. Heureusement, le jemalloc utilisé par GreptimeDB est livré avec le profilage de tas et nous prenons également en charge l'exportation du fichier de vidage du profil jemalloc . Ainsi, lorsque la mémoire du nœud Frontend de GreptimeDB a atteint 300 Mo et 800 Mo, nous avons respectivement vidé ses fichiers de profil de mémoire, puis utilisé l'outil intégré de jemalloc jeprof
pour analyser les différences de mémoire ( --base
paramètres) entre les deux, et enfin les avons affichés avec un graphique de flamme :
De toute évidence, le long bloc au milieu de l’image est occupé par les 500 Mo de mémoire en constante augmentation. En observant attentivement, il existe des traces de pile liées aux threads. Se pourrait-il que trop de discussions aient été créées ? J'ai simplement utilisé ps -T -p
la commande pour vérifier le processus du nœud Frontend plusieurs fois. Le nombre de threads était stable à 84, et c'étaient tous des threads dont la création était prévue. Ainsi, la raison « trop de threads » peut être éliminée.
En regardant plus bas, nous avons trouvé de nombreuses traces de pile liées au runtime Tokio, et les fuites de tâches Tokio sont également une fuite de mémoire courante. A cette époque, nous utiliserons un autre artefact : Tokio-console .
Console de Tokyo
Tokio Console est l'outil de diagnostic officiel de Tokio. Les résultats de sortie sont les suivants :
Nous constatons qu’il y a en réalité 5 559 tâches en cours d’exécution, et la plupart d’entre elles sont à l’état inactif ! Nous pouvons donc confirmer que la fuite de mémoire se produit dans la tâche de Tokio. La question se pose désormais : où dans le code GreptimeDB apparaissent tant de tâches Tokio qui ne peuvent pas être terminées ?
À partir de la colonne "Emplacement" de la figure ci-dessus, nous pouvons voir où la tâche est générée :
impl Runtime {
/// Spawn a future and execute it in this thread pool
///
/// Similar to Tokio::runtime::Runtime::spawn()
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
self.handle.spawn(future)
}
}
La tâche suivante consiste à trouver tout le code dans GreptimeDB qui appelle cette méthode.
..Default::default()
!
Après une inspection minutieuse du code, nous avons finalement localisé l'endroit où la tâche Tokio avait fui et corrigé la fuite dans le PR #1512 . En termes simples, dans le constructeur d'une structure qui sera créée fréquemment, nous avons généré une tâche Tokio qui peut continuer à s'exécuter en arrière-plan, mais nous n'avons pas réussi à la recycler à temps. Pour la gestion des ressources, créer la tâche dans le constructeur lui-même ne pose pas de problème, tant que Drop
la tâche peut être terminée avec succès dans . Le problème avec notre fuite de mémoire est que nous avons ignoré cette convention.
Ce constructeur Default::default()
est également appelé dans la méthode de la structure, ce qui rend plus difficile la recherche de la cause première.
Rust dispose d'une méthode très pratique pour construire votre propre structure en utilisant une autre structure, à savoir " Struct Update Syntax ". Si struct est implémenté Default
, nous pouvons simplement l'utiliser dans le constructeur de champ de struct ..Default::default()
. S'il Default::default()
y a un "effet secondaire" à l'intérieur (par exemple, la raison de notre fuite de mémoire cette fois-ci - la création d'une tâche Tokio exécutée en arrière-plan), une attention particulière doit être portée à : une fois la construction de la structure terminée, la Default
structure temporaire créée sera jeté. Faites du bon travail dans le recyclage des ressources.
Par exemple, le petit exemple suivant : (Rust Playground )
struct A {
i: i32,
}
impl Default for A {
fn default() -> Self {
println!("called A::default()");
A { i: 42 }
}
}
#[derive(Default)]
struct B {
a: A,
i: i32,
}
impl B {
fn new(a: A) -> Self {
B {
a,
// A::default() is called in B::default(), even though "a" is provided here.
..Default::default()
}
}
}
fn main() {
let a = A { i: 1 };
let b = B::new(a);
println!("{}", b.a.i);
}
La méthode de struct A default
sera appelée et imprimée called A::default()
.
Résumer
- Pour résoudre les fuites de mémoire dans les programmes Rust, nous pouvons utiliser le profilage de tas de jemalloc pour exporter des fichiers de vidage, puis générer un graphique de flamme pour afficher visuellement l'utilisation de la mémoire.
- Tokio-console peut facilement afficher l'état d'exécution des tâches du runtime Tokio ; accordez une attention particulière aux tâches inactives croissantes.
- Essayez de ne pas laisser de code avec des effets secondaires dans le constructeur des structures couramment utilisées.
Default
Ne doit être utilisé que pour les structures de type valeur.
À propos de Greptime
Greptime Greptime Technology a été fondée en 2022 et améliore et développe actuellement deux produits, la base de données de séries chronologiques GreptimeDB et GreptimeCloud.
GreptimeDB est une base de données de séries chronologiques écrite en langage Rust. Elle est distribuée, open source, native du cloud et hautement compatible. Elle aide les entreprises à lire, écrire, traiter et analyser les données de séries chronologiques en temps réel tout en réduisant les coûts à long terme. stockage.
Basé sur GreptimeDB open source, GreptimeCloud fournit aux utilisateurs une base de données DBaaS entièrement gérée, ainsi que des produits d'application combinés à l'observabilité, à l'Internet des objets et à d'autres domaines. L'utilisation du cloud pour fournir des logiciels et des services peut permettre un approvisionnement et une livraison rapides en libre-service, un support d'exploitation et de maintenance standardisé et une meilleure flexibilité des ressources. GreptimeCloud est officiellement ouvert aux tests internes. Bienvenue pour suivre le compte officiel ou le site officiel pour les derniers développements !
Site officiel : https://greptime.com/
Compte public : GreptimeDB
GitHub : https://github.com/GreptimeTeam/greptimedb
Documentation : https://docs.greptime.com/
Twitter : https://twitter.com/Greptime
Slack : https://greptime.com/slack
LinkedIn : https://www.linkedin.com/company/greptime/
Un programmeur né dans les années 1990 a développé un logiciel de portage vidéo et en a réalisé plus de 7 millions en moins d'un an. La fin a été très éprouvante ! Des lycéens créent leur propre langage de programmation open source en guise de cérémonie de passage à l'âge adulte - commentaires acerbes des internautes : s'appuyant sur RustDesk en raison d'une fraude généralisée, le service domestique Taobao (taobao.com) a suspendu ses services domestiques et repris le travail d'optimisation de la version Web Java 17 est la version Java LTS la plus utilisée Part de marché de Windows 10 Atteignant 70 %, Windows 11 continue de décliner Open Source Daily | Google soutient Hongmeng pour prendre le relais des téléphones Android open source pris en charge par Docker ; Electric ferme la plate-forme ouverte Apple lance la puce M4 Google supprime le noyau universel Android (ACK) Prise en charge de l'architecture RISC-V Yunfeng a démissionné d'Alibaba et prévoit de produire des jeux indépendants pour les plates-formes Windows à l'avenir