Durante um certo teste de estresse contínuo, descobrimos que a memória do nó Frontend do GreptimeDB continuou a aumentar mesmo quando o volume de solicitação estava estável, até ser eliminado pelo OOM. Achamos que o Frontend deveria ter um vazamento de memória, então iniciamos uma jornada para solucionar problemas de vazamento de memória.
Perfil de pilha
Em projetos grandes é quase impossível encontrar vazamentos de memória apenas olhando o código. Portanto, primeiro precisamos fazer uma análise estatística do uso de memória do programa. Felizmente, o jemalloc usado pelo GreptimeDB vem com perfil de heap e também oferecemos suporte à exportação do arquivo de despejo de perfil jemalloc . Portanto, quando a memória do nó Frontend do GreptimeDB atingiu 300 MB e 800 MB, descartamos seus arquivos de perfil de memória respectivamente, depois usamos a ferramenta integrada do jemalloc jeprof
para analisar as diferenças de memória ( --base
parâmetros) entre os dois e, finalmente, exibi-los com um gráfico em chamas:
Obviamente, o longo bloco no meio da imagem é ocupado pelos cada vez maiores 500 MB de memória. Observando com atenção, existem rastreamentos de pilha relacionados ao thread. Será que muitos tópicos foram criados? Simplesmente usei ps -T -p
o comando para verificar o processo do nó Frontend várias vezes. O número de threads estava estável em 84, e todos eram threads que estavam previstos para serem criados. Portanto, o motivo de "muitos tópicos" pode ser eliminado.
Olhando mais abaixo, encontramos muitos rastreamentos de pilha relacionados ao tempo de execução do Tokio, e vazamentos de tarefas do Tokio também são um vazamento de memória comum. Neste momento usaremos outro artefato: Tokio-console .
Console de Tóquio
O Tokio Console é a ferramenta de diagnóstico oficial do Tokio. Os resultados são os seguintes:
Vemos que na verdade existem 5.559 tarefas em execução, e a maioria delas está no estado inativo! Assim podemos confirmar que o vazamento de memória ocorre na tarefa do Tokio. Agora a questão é: onde no código GreptimeDB são geradas tantas tarefas do Tokio que não podem ser finalizadas?
Na coluna "Localização" na figura acima podemos ver onde a tarefa é gerada :
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)
}
}
A próxima tarefa é encontrar todo o código no GreptimeDB que chama esse método.
..Default::default()
!
Após uma inspeção cuidadosa do código, finalmente localizamos onde a tarefa do Tokio vazou e corrigimos o vazamento no PR #1512 . Simplificando, no construtor de uma estrutura que será criada com frequência, geramos uma tarefa do Tokio que pode continuar a ser executada em segundo plano, mas não conseguimos reciclá-la a tempo. Para gerenciamento de recursos, criar a tarefa no próprio construtor não é um problema, desde que Drop
a tarefa possa ser finalizada com sucesso em . O lado ruim do nosso vazamento de memória é que ignoramos essa convenção.
Esse construtor Default::default()
também é chamado no método da estrutura, o que torna mais difícil encontrarmos a causa raiz.
Rust possui um método muito conveniente para construir sua própria estrutura usando outra estrutura, chamada " Struct Update Syntax ". Se struct for implementado Default
, podemos simplesmente usá-lo no campo construtor de struct ..Default::default()
. Se Default::default()
houver um "efeito colateral" interno (por exemplo, o motivo do nosso vazamento de memória desta vez - a criação de uma tarefa Tokio em execução em segundo plano), atenção especial deve ser dada a: após a conclusão da construção da estrutura, a Default
estrutura temporária criada serão descartados. Faça um bom trabalho na reciclagem de recursos.
Por exemplo, o seguinte pequeno exemplo: (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);
}
O método da estrutura A default
será chamado e impresso called A::default()
.
Resumir
- Para solucionar vazamentos de memória em programas Rust, podemos usar o perfil de heap do jemalloc para exportar arquivos de despejo e, em seguida, gerar um gráfico em degradê para exibir visualmente o uso de memória;
- O Tokio-console pode exibir facilmente o status de execução da tarefa do tempo de execução do Tokio, prestando atenção especial às crescentes tarefas ociosas;
- Tente não deixar código com efeitos colaterais no construtor de estruturas comumente usadas.
Default
Só deve ser usado para estruturas de tipo de valor.
Sobre o Greptime
Greptime Greptime Technology foi fundada em 2022 e atualmente está melhorando e construindo dois produtos, banco de dados de série temporal GreptimeDB e GreptimeCloud.
GreptimeDB é um banco de dados de série temporal escrito em linguagem Rust. É distribuído, de código aberto, nativo da nuvem e altamente compatível. Ele ajuda as empresas a ler, escrever, processar e analisar dados de série temporal em tempo real, reduzindo o custo de longo prazo. armazenar.
Baseado no GreptimeDB de código aberto, o GreptimeCloud fornece aos usuários DBaaS totalmente gerenciado, bem como produtos de aplicativos combinados com observabilidade, Internet das Coisas e outros campos. Usar a nuvem para fornecer software e serviços pode alcançar rápido provisionamento e entrega de autoatendimento, operação padronizada e suporte de manutenção e melhor flexibilidade de recursos. GreptimeCloud foi oficialmente aberto para testes internos. Bem-vindo a seguir a conta oficial ou o site oficial para os últimos desenvolvimentos!
Site oficial: https://greptime.com/
Conta pública: GreptimeDB
GitHub: https://github.com/GreptimeTeam/greptimedb
Documentação: https://docs.greptime.com/
Twitter: https://twitter.com/Greptime
Folga: https://greptime.com/slack
LinkedIn: https://www.linkedin.com/company/greptime/
Um programador nascido na década de 1990 desenvolveu um software de portabilidade de vídeo e faturou mais de 7 milhões em menos de um ano. O final foi muito punitivo! Alunos do ensino médio criam sua própria linguagem de programação de código aberto como uma cerimônia de maioridade - comentários contundentes de internautas: Contando com RustDesk devido a fraude desenfreada, serviço doméstico Taobao (taobao.com) suspendeu serviços domésticos e reiniciou o trabalho de otimização de versão web Java 17 é a versão Java LTS mais comumente usada no mercado do Windows 10 Atingindo 70%, o Windows 11 continua a diminuir Open Source Daily | Google apoia Hongmeng para assumir o controle de telefones Android de código aberto apoiados pela ansiedade e ambição da Microsoft; Electric desliga a plataforma aberta Apple lança chip M4 Google exclui kernel universal do Android (ACK) Suporte para arquitetura RISC-V Yunfeng renunciou ao Alibaba e planeja produzir jogos independentes para plataformas Windows no futuro