Porblemas de lentidão, desconexões automáticas ou interrupções de tarefas após dias de operação do robô aspirador com Valetudo estão frequentemente ligados a vazamentos de memória (Memory Leaks). Este artigo analisa as causas comuns e apresenta soluções práticas para aumentar a estabilidade do sistema.
O Impacto e Detecção de Vazamentos
Um vazamento de memória ocorre quando um programa não libera corretamente a memória que não é mais necessária, causando uma redução gradual da memória disponível. No Valetudo, isso compromete a precisão no agendamento de tarefas e a responsividade da rede.
Ao monitorar os logs via Logger.js, um padrão de memória em "dente de serra ascendente" é um forte indicativo. Os principais indicadores a observar são:
- Crescimento superior a 50% no uso de memória após 72 horas contínuas de execução.
- Falha na liberação efetiva de memória após a execução do Garbage Collector (GC).
- Atrasos superiores a 30 segundos na execução de tarefas agendadas.
Análise das Fontes Comuns de Vazamanto
1. Temporizadores e Listeners de Eventos Não Removidos
Em Scheduler.js, o uso de setInterval sem uma chamada correspondente a clearInterval impede que as funções de callback e seus escopos sejam recolhidos.
// Exemplo de código com risco de vazamento
this.taskReevaluationHandle = setInterval(() => {
this.reevaluateScheduledTasks();
}, MILLISECONDS_PER_MINUTE);
// Correção necessária durante a desativação
clearInterval(this.taskReevaluationHandle);
this.taskReevaluationHandle = null;
2. Cache com Crescimento Ilimitado
O módulo KeyValueDeduplicationCache.js utiliza um Map para armazenar hashes. Sem um mecanismo de expiração ou limpeza, o cache cresce indefinidamente.
// Implementação de cache sem controle de tamanho
this.entryMap = new Map();
// Solução proposta: limpeza periódica do cache
const cleanupInterval = setInterval(() => {
this.entryMap.clear(); // Limpa todas as entradas periodicamente
}, 24 * 60 * 60 * 1000); // A cada 24 horas
3. Estouro do Armazenamento de Eventos
A classe ValetudoEventStore.js define um limite de 50 registros, mas condições excepcionais podem causar seu excesso.
// Limite configurado no armazenamento
const MAX_EVENT_RECORDS = 50;
// Lógica de autocorreção quando o limite é excedido
if (this.storedEvents.size > MAX_EVENT_RECORDS) {
const entries = Array.from(this.storedEvents.keys());
entries.sort();
const keyToRemove = entries[0];
this.storedEvents.delete(keyToRemove);
}
Plano de Correção Sistemático
1. Aprimorando o Mecanismo de Garbage Collection (GC)
A lógica de GC do Valetudo pode ser otimizada para ser mais responsiva ao uso de memória.
// Lógica de GC original em Valetudo.js
this.gcTriggerInterval = setInterval(() => {
if (this.memoryMonitor.requiresGc()) {
global.gc();
}
}, 60_000);
// Versão otimizada: frequência de GC dinâmica
let currentGcDelay = 60_000;
const checkAndCollect = () => {
const heapUsedMB = process.memoryUsage().heapUsed / 1048576;
if (heapUsedMB > 150) { // Limiar alto: GC mais frequente
currentGcDelay = 30_000;
global.gc();
} else if (heapUsedMB < 80) { // Limiar baixo: GC menos frequente
currentGcDelay = 120_000;
}
this.gcTriggerInterval = setTimeout(checkAndCollect, currentGcDelay);
};
checkAndCollect();
2. Implementando Gestão Inteligente de Cache
Para o KeyValueDeduplicationCache.js, uma política de evicção LRU (Least Recently Used) é mais eficiente que a limpeza total.
// Refatoração do construtor
constructor() {
this.hashMethod = KeyValueDeduplicationCache.selectHashAlgorithm();
this.dataStore = new Map();
this.usageTracker = new Map(); // Rastreia a última vez que a chave foi usada
this.maximumItems = 1000; // Limite configurável
}
// Atualização da lógica de inserção/atualização
put(key, value) {
this.dataStore.set(key, value);
this.usageTracker.set(key, Date.now());
this.enforceSizeLimit();
}
enforceSizeLimit() {
while (this.dataStore.size > this.maximumItems) {
// Encontra a chave com o menor timestamp (mais antigo)
let oldestKey = null;
let oldestTime = Infinity;
for (const [key, time] of this.usageTracker) {
if (time < oldestTime) {
oldestTime = time;
oldestKey = key;
}
}
if (oldestKey) {
this.dataStore.delete(oldestKey);
this.usageTracker.delete(oldestKey);
}
}
}
3. Garantindo a Liberação Correta de Recursos
No WebServer.js, é crucial limpar conexões e listeners de eventos ao encerrar.
// Correção para conexões WebSocket em WebServer.js
this.webSocketServer.on('connection', (clientSocket) => {
const pingPongInterval = setInterval(() => {
if (clientSocket.readyState === clientSocket.OPEN) {
clientSocket.ping();
}
}, 25_000);
const cleanup = () => {
clearInterval(pingPongInterval);
clientSocket.removeAllListeners();
};
clientSocket.on('close', cleanup);
clientSocket.on('error', cleanup);
});
Verificação e Monitoramento Pós-Correção
Para validar as correções, implemente monitoramento detalhado e realize testes de estresse.
Monitoramento em tempo real: Adicione uma função de log de memória ao seu sistema.
// Função utilitária para logar uso de memória
const logMemoryStatus = (loggerInstance) => {
const memoryStats = process.memoryUsage();
const heapUsedMB = (memoryStats.heapUsed / 1048576).toFixed(2);
loggerInstance.debug(`Status atual da memória: ${heapUsedMB} MB no heap.`);
};
Teste de estresse: Execute múltiplas iterações de tarefas completas e compare os picos de memória.
| Cenário do Teste | Pico de Memória (Antes) | Pico de Memória (Depois) | Melhoria na Estabilidade |
|---|---|---|---|
| Limpeza Padrão Completa | ~250 MB | ~135 MB | 46% |
| Limpeza por Zonas | ~200 MB | ~118 MB | 41% |
| Execução de Tarefas Agendadas | ~185 MB | ~100 MB | 46% |
Manutenção Preventiva Contínua
- Auditoria de Logs: Revise semanalmente os logs de memória, focando na métrica
heapUsed. - Atualizações: Acompanhe as notas de lançamento e o
README.mddo projeto principal para aplicar correções oficiasi. - Supervisão de Recursos: Implemente um script de supervisão que reinicie o serviço se o uso de memória persistir acima de um limite crítico (ex: 300 MB).
Após a aplicação destas correções, o sistema Valetudo consegue operar continuamente, mantendo o consumo de memória estável em faixas controladas. Estas melhorias estão disponíveis nas versões mais recentes do código-fonte.