Durante una determinada prueba de estrés continuo, descubrimos que la memoria del nodo Frontend de GreptimeDB continuó aumentando incluso cuando el volumen de solicitudes era estable, hasta que OOM lo eliminó. Consideramos que Frontend debería tener una pérdida de memoria, por lo que comenzamos un viaje para solucionar problemas de pérdidas de memoria.
Perfiles de montón
En proyectos grandes es casi imposible encontrar pérdidas de memoria con sólo mirar el código. Entonces, primero debemos hacer un análisis estadístico del uso de memoria del programa. Afortunadamente, el jemalloc utilizado por GreptimeDB viene con perfiles de montón y también admitimos la exportación del archivo de volcado del perfil de jemalloc . Entonces, cuando la memoria del nodo Frontend de GreptimeDB alcanzó los 300 MB y 800 MB, desechamos sus archivos de perfil de memoria respectivamente, luego usamos la herramienta incorporada de jemalloc jeprof
para analizar las diferencias de memoria ( --base
parámetros) entre los dos y finalmente los mostramos con un gráfico de llama:
Obviamente, el bloque largo en el medio de la imagen está ocupado por los 500 MB de memoria, cada vez mayores. Si se observa con atención, hay rastros de pila relacionados con subprocesos. ¿Será que se han creado demasiados hilos? Simplemente utilicé ps -T -p
el comando para verificar el proceso del nodo Frontend varias veces. La cantidad de subprocesos se mantuvo estable en 84, y todos eran subprocesos que se predijo que se crearían. Por lo tanto, se puede eliminar la razón por la que "demasiados subprocesos".
Mirando más abajo, encontramos muchos rastros de pila relacionados con el tiempo de ejecución de Tokio, y las fugas de tareas de Tokio también son una pérdida de memoria común. En este momento usaremos otro artefacto: Tokio-console .
Consola Tokio
Tokio Console es la herramienta de diagnóstico oficial de Tokio. Los resultados obtenidos son los siguientes:
Vemos que en realidad hay 5559 tareas en ejecución, ¡y la mayoría de ellas están en estado inactivo! Entonces podemos confirmar que la pérdida de memoria ocurre en la tarea de Tokio. Ahora la pregunta es: ¿En qué parte del código GreptimeDB se generan tantas tareas de Tokio que no se pueden finalizar?
En la columna "Ubicación" de la figura anterior podemos ver dónde se genera la tarea :
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 siguiente tarea es encontrar todo el código en GreptimeDB que llama a este método.
..Default::default()
!
Después de una inspección cuidadosa del código, finalmente localizamos dónde se filtró la tarea de Tokio y solucionamos la fuga en PR #1512 . En pocas palabras, en el constructor de una estructura que se creará con frecuencia, generamos una tarea de Tokio que puede continuar ejecutándose en segundo plano, pero no pudimos reciclarla a tiempo. Para la gestión de recursos, crear la tarea en el constructor en sí no es un problema, siempre que Drop
la tarea se pueda finalizar con éxito en . Lo malo de nuestra pérdida de memoria es que ignoramos esta convención.
Este constructor Default::default()
también se llama en el método de la estructura, lo que nos dificulta encontrar la causa raíz.
Rust tiene un método muy conveniente para construir su propia estructura usando otra estructura, a saber, " Struct Update Syntax ". Si se implementa struct Default
, simplemente podemos usarlo en el constructor de campos de struct ..Default::default()
. Si Default::default()
hay un "efecto secundario" interno (por ejemplo, el motivo de nuestra pérdida de memoria esta vez: crear una tarea de Tokio ejecutándose en segundo plano), se debe prestar especial atención a: una vez completada la construcción de la estructura, la Default
estructura temporal creada será descartado. Haga un buen trabajo en el reciclaje de recursos.
Por ejemplo, el siguiente pequeño ejemplo: (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);
}
El método de la estructura A default
será llamado e impreso called A::default()
.
Resumir
- Para solucionar problemas de pérdidas de memoria en programas Rust, podemos usar el perfil de montón de jemalloc para exportar archivos de volcado y luego generar un gráfico de llama para mostrar visualmente el uso de la memoria.
- Tokio-console puede mostrar fácilmente el estado de ejecución de las tareas en tiempo de ejecución de Tokio; preste especial atención a las crecientes tareas inactivas.
- Intente no dejar código con efectos secundarios en el constructor de estructuras de uso común.
Default
Solo debe usarse para estructuras de tipo valor.
Acerca de Greptime
Greptime Greptime Technology se fundó en 2022 y actualmente está mejorando y creando dos productos, la base de datos de series temporales GreptimeDB y GreptimeCloud.
GreptimeDB es una base de datos de series temporales escrita en lenguaje Rust. Es distribuida, de código abierto, nativa de la nube y altamente compatible. Ayuda a las empresas a leer, escribir, procesar y analizar datos de series temporales en tiempo real al tiempo que reduce el costo a largo plazo. almacenamiento.
Basado en GreptimeDB de código abierto, GreptimeCloud proporciona a los usuarios DBaaS totalmente administrado, así como productos de aplicaciones combinados con observabilidad, Internet de las cosas y otros campos. El uso de la nube para proporcionar software y servicios puede lograr un rápido aprovisionamiento y entrega de autoservicio, soporte de operación y mantenimiento estandarizado y una mayor flexibilidad de recursos. GreptimeCloud se ha abierto oficialmente para pruebas internas. ¡Bienvenido a seguir la cuenta oficial o el sitio web oficial para conocer los últimos desarrollos!
Sitio web oficial: https://greptime.com/
Cuenta pública: GreptimeDB
GitHub: https://github.com/GreptimeTeam/greptimedb
Documentación: https://docs.greptime.com/
Gorjeo: https://twitter.com/Greptime
Holgura: https://greptime.com/slack
LinkedIn: https://www.linkedin.com/company/greptime/
Un programador nacido en los años 90 desarrolló un software de portabilidad de vídeo y ganó más de 7 millones en menos de un año. ¡El final fue muy duro! Los estudiantes de secundaria crean su propio lenguaje de programación de código abierto como una ceremonia de mayoría de edad: comentarios agudos de los internautas: debido al fraude desenfrenado, confiando en RustDesk, el servicio doméstico Taobao (taobao.com) suspendió los servicios domésticos y reinició el trabajo de optimización de la versión web Java 17 es la versión Java LTS más utilizada. Cuota de mercado de Windows 10. Alcanzando el 70%, Windows 11 continúa disminuyendo. Open Source Daily | Google apoya a Hongmeng para hacerse cargo de los teléfonos Android de código abierto respaldados por Docker; Electric cierra la plataforma abierta Apple lanza el chip M4 Google elimina el kernel universal de Android (ACK) Soporte para la arquitectura RISC-V Yunfeng renunció a Alibaba y planea producir juegos independientes para plataformas Windows en el futuro