Lembre-se de uma viagem de solução de problemas de vazamento de memória Rust |

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 jeprofpara analisar as diferenças de memória ( --baseparâ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 -po 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 Dropa 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 Defaultestrutura 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 defaultserá 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.
  • DefaultSó 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
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/u/6839317/blog/11044292
Recomendado
Clasificación