Mongoose: Não reconecte após a desconexão

Criado em 26 out. 2016  ·  47Comentários  ·  Fonte: Automattic/mongoose

Depois de atualizar de 4.4.11 para> = 4.6.1, obtemos desconexões aleatórias e o mangusto nunca se reconecta.

versão do mangusto: 4.6.5
versão mongodb: 3.2.9

É assim que crio as conexões:

    var uri = 'mongodb://USENAME:PASSWORD<strong i="9">@host1</strong>:port1,host2:port2/database?authSource=admin';

    var options = {};
    options.server = {
      auto_reconnect: true,
      poolSize: 5,
      socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 },
      reconnectTries: 3000
    };

    options.replset = {
      auto_reconnect: true,
      poolSize: 5,
      socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 },
      reconnectTries: 3000
    };

    var db = mongoose.createConnection(uri, options);

    mongoose.connection.on('error', function(err) {
      console.log('MONGODB ERROR MONGOOSE LEVEL ' + server, err);
    });

    db.on('connecting', function() {
      console.info('MONGODB ' + server + ' connecting.');
    });

    db.on('error', function(err) {
      console.log('MONGODB ERROR ' + server, err);
    });

    db.on('close', function(err) {
      console.log('MONGODB CLOSE ' + server, err);
    });

    db.on('connected', function() {
      console.info('MONGODB ' + server + ' connected successfully.');
    });

    db.once('open', function callback() {
      console.info('MONGODB ' + server + ' opened successfully.');
    });

    db.on('reconnected', function() {
      console.info('MONGODB ' + server + ' reconnected.');
    });

    db.on('timeout', function() {
      console.info('MONGODB ' + server + ' timeout.');
    });

    db.on('disconnected', function() {
      console.info('MONGODB ' + server + ' disconnected');
    });

Esta é a sequência de eventos que recebo:

pid:3429 MONGODB geo_uri connected successfully.
pid:3429 MONGODB geo_uri opened successfully.
pid:3429 MONGODB dashboards_db connected successfully.
pid:3429 MONGODB dashboards_db opened successfully.
pid:3429 MONGODB tweet_analytics_db connected successfully.
pid:3429 MONGODB tweet_analytics_db opened successfully.
pid:3429 MONGODB fullcontact_enrichment_db disconnected
pid:3429 MONGODB ERROR fullcontact_enrichment_db { [MongoError: no valid replicaset members found]
  name: 'MongoError',
  message: 'no valid replicaset members found' }
pid:3429 MONGODB uri connected successfully.
pid:3429 MONGODB uri opened successfully.
pid:3429 MONGODB sync_reports_db connected successfully.
pid:3429 MONGODB sync_reports_db opened successfully.
pid:3429 MONGODB uri disconnected
pid:3429 MONGODB CLOSE uri

Em seguida, nenhum dos bancos de dados desconectados se reconecta. Devido à natureza aleatória do problema, não poderei fornecer um código para reproduzi-lo. Eu suspeito de algo saturado no nível de node.js ou algo assim. Independentemente da causa da desconexão, há algo que eu possa fazer para tentar me reconectar?

Comentários muito úteis

@Koslun o fork deve ser desnecessário agora. A correção é basicamente definir socketTimeout como 0 para o banco de dados socketOptions agora.

Aqui está como estão minhas opções de soquete agora.

    var opts = {
      server: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
      },
      replSet: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
    }

Todos 47 comentários

+1, houve 2 interrupções nos últimos 5 dias relacionadas a este problema. Minha solução alternativa atual é fazer meu processo travar explicitamente ( process.exit(0) ) quando o evento disconnected for emitido. Em seguida, o processo é reiniciado e a conexão corretamente aberta novamente.

Meu palpite é que o mangusto tenta se reconectar, mas falha dentro do intervalo reconnectTries * reconnectInterval , e o evento error não é emitido devido a esta regressão (https://github.com/Automattic/mongoose/pull / 4653). O que eu não sei é por que mongoose / mongo são desconectados aleatoriamente, eu não tinha esse comportamento antes.
Qual hospedagem mongo você está executando? mLab?

Nós mesmos o hospedamos na AWS. Infelizmente process.exit(0) não é uma opção para nós.

Eu apliquei as alterações em # 4653 e obtive o mesmo comportamento. Desconexão após algumas horas:

2016-10-27T11:26:42 pid:5276 MONGODB sync_reports_db connected successfully.
2016-10-27T11:26:42 pid:5276 MONGODB sync_reports_db opened successfully.
.... 2 hours later
2016-10-27T13:45:45 pid:5276 MONGODB sync_reports_db disconnected
2016-10-27T13:45:45 pid:5276 MONGODB CLOSE sync_reports_db

Nenhum evento error foi emitido? (deve ser, 30 segundos após disconnected um)

Não, dê uma olhada no código na descrição do problema. A menos que eu esteja fazendo algo errado, existe um manipulador de eventos para o evento error . Na verdade, o processo ainda está em execução e o mangusto não acionou nenhum outro evento.

Temos tido os mesmos problemas nos últimos dias desde 4.6.5 - desconexões aleatórias que fazem com que o processo do nó fique preso. Mas nenhum evento error . Reverter para 4.5.3 funciona.

@loris isso está relacionado a https://github.com/Automattic/mongoose/commit/f7ebee0c992c45cdb27ba7f0675556b980cddaad em 4.6.6 ?

@mck Sim, https://github.com/Automattic/mongoose/commit/f7ebee0c992c45cdb27ba7f0675556b980cddaad corrige o evento error que não está sendo emitido quando o mecanismo de nova tentativa de conexão do mongodb falha. No entanto, não tenho ideia de por que desconexões aleatórias estão acontecendo em primeiro lugar, alguma ideia @ vkarpov15?

fwiw, isso acontecia para nós 40-50% do tempo se tentássemos fazer uma operação salvar / atualizar (escrevendo cerca de 650kb)

Sim, eu realmente não tenho muitas boas ideias. Você pode tentar chamar close() na conexão e, em seguida, chamar connect() você mesmo novamente. @loris , você tem uma experiência semelhante em que um salvamento / atualização intenso parece causar isso?

Nós também estamos enfrentando isso. Em um de nossos serviços, estamos obtendo um evento de erro (a conexão N com ds0XXXXX-a0.mongolab.com:XXXXX expirou), seguido por um evento desconectado. E a conexão nunca se restabelece. Em outro serviço, obtemos um evento desconectado após uma solicitação pesada no banco de dados, que está removendo 2M de registros. E então ele não consegue se reconectar (mongoose 4.6.6, DB versão 3.0.12).

Aconteceu conosco novamente, alguns minutos atrás, executando o mongodb no mLab também (não tenho certeza se está relacionado a isso). Executamos uma consulta pesada, que atingiu o tempo limite ( unhandledRejection { MongoError: connection 0 to ds****-a0.mongolab.com:**** timed out } , o evento disconnected foi emitido corretamente, mas nada depois, nenhum error , nenhum reconnected etc, portanto, esse servidor da web continuou executando e recebendo solicitações HTTP, mas todos eles expiraram até que o reiniciemos porque ele continuou executando consultas do mongoose que eram armazenadas em buffer e nunca retornavam.

Nossa configuração de mangusto (4.6.5 no nó 7.0.0):

const mongoConnectOpts = { reconnectTries: 10, reconnectInterval: 500, socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } };
mongoose.connect(process.env.MONGODB_URI, { server: mongoConnectOpts, replset: mongoConnectOpts });
mongoose.connection.on('error', err => {
  console.log({ event: 'mongoose:error', ...err });
  process.exit(0);
});
mongoose.connection.on('connected', () => console.log({ event: 'mongoose:connected' }));
mongoose.connection.on('disconnected', () => console.log({ event: 'mongoose:disconnected' }));
mongoose.connection.on('reconnected', () => console.log({ event: 'mongoose:reconnected' }));

A solução temporária seria process.exit(0) também no evento disconnected para forçar o servidor web a reiniciar e configurar uma nova conexão mongodb. Alguma ideia?

Sim, eu já ouvi esse tipo de coisa acontecendo com o mlab antes. TBH, neste caso, eu apenas travaria o servidor e reiniciei, redes irregulares realmente lentas tendem a causar problemas para o driver mongodb e imagino que depurar isso envolveria a coordenação com o mlab.

Não tenho certeza do que está errado agora (mangusto, driver mongo, mlab ou heroku), mas nos últimos dias, executar uma solicitação da web que executa uma consulta mangusto pesada (levando mais de 30 segundos para responder) irá acionar uma solicitação heroku timeout (este é um mecanismo no heroku que expira qualquer requisição web que leva mais de 30s). Uma vez que a solicitação da web tenha expirado, qualquer solicitação da web a seguir nesse servidor que requeira uma consulta de mangusto também expirará. O que é um problema real é que o evento ZERO é acionado pelo mangusto (sem error , close , disconnected , etc ...), então não temos como detectar o desconecte e reinicie o servidor. Aqui está como configuramos o mangusto:

mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.NODE_ENV === 'development');
const mongoConnectOpts = { reconnectTries: 10, reconnectInterval: 500, socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } };
mongoose.connect(process.env.MONGODB_URI, { server: mongoConnectOpts, replset: mongoConnectOpts });
mongoose.connection.on('error', err => {
  logfmt.log({ event: 'mongoose:error', ...err });
  process.exit(0);
});
mongoose.connection.on('connected', () => logfmt.log({ event: 'mongoose:connected', uri: _.truncate(process.env.MONGODB_URI) }));
mongoose.connection.on('disconnected', () => {
  logfmt.log({ event: 'mongoose:disconnected' });
  process.exit(0);
});
mongoose.connection.on('close', () => logfmt.log({ event: 'mongoose:close' }));
mongoose.connection.on('reconnected', () => logfmt.log({ event: 'mongoose:reconnected' }));

// Setup Redis cache (Default cache TTL: 60 seconds)
cachegoose(mongoose, { engine: 'redis', client: redisCache }, process.env.NODE_ENV === 'development');

@Ioris você pode ver se isso ainda acontece se você desabilitar o cachegoose?

@ vkarpov15 Tentaremos verificar isso, mas não é fácil, pois o bug só acontece na produção e não podemos nos dar ao luxo de rodar a produção com o cache desabilitado.
@aartiles @ mck- @lushchick vocês usam o plugin de cache mongoose?

É possível que isso esteja relacionado a https://github.com/christkv/mongodb-core/issues/148. Tenho enfrentado um problema semelhante quando um membro de um replicaset fica indisponível.

Não usamos o plugin de cache do mongoose.

Ainda estou investigando, o que posso dizer até agora:

  • Consegui reproduzir o problema em minha máquina local: inicio um loop executando uma consulta rápida a cada segundos e registrando os resultados, algum atraso depois, executo uma consulta pesada (como uma consulta não indexada em uma coleção de um milhão de linhas). Todas as consultas (as rápidas também) serão bloqueadas / expiradas, nenhum evento mongoose será disparado.
  • O problema apareceu em 4.6.5 , o downgrade para 4.6.4 corrigiu o problema (as consultas rápidas ainda estão sendo executadas enquanto a consulta pesada está em execução)
  • Parece que não tem nada a ver com:

    • o plugin de cache mongoose

    • executando um conjunto de réplicas ou não

Fiz mais pesquisas e o problema veio com a atualização para [email protected] , e o commit de bugs lá é https://github.com/mongodb/node-mongodb-native/pull/1418
parece que eles corrigiram um erro de digitação, mas esse eventName (com o erro de digitação) está sendo usado por alguns departamentos

@loris Você sabe onde estão as referências restantes com o nome do evento errado? Posso fazer um PR para consertar, mas não consigo encontrar nenhum.

@jakesjews Também não consigo encontrar nenhuma referência, então posso estar errado sobre a causa de ter verificado versões incompatíveis em minha máquina,

Tendo um problema semelhante: uma falha ao reconectar após uma desconexão. Isso se manifesta no cliente pendurando em qualquer operação de banco de dados mongoose (tempo limite por meio do buffer de operação).

@jakesjews meu problema também é exclusivo para conjuntos de réplicas e não ocorre com uma única conexão de nó. Olhando mais para o replset.js principal.

Habilitou o registro de depuração no node-mongodb-native para ver se a reconexão HA estava funcionando conforme o esperado, parece que está.

attemptReconnect for replset with id successful resuming topologyMonitor 1

Apesar do driver node-mongodb-native afirmar que realizou com sucesso seu tryReconnect, mongoose nunca emitirá um evento conectado ou reconectado como faz quando um único nó não-replset se reconecta.

Como @loris mencionou, process.exit (0) -> reiniciar do serviço funcionaria (no meu caso), pois o problema está diretamente relacionado à reconexão a um conjunto de réplicas, mas novamente não é o ideal.

[email protected]
[email protected]

@ mck- Encontrado o mesmo que você, fazer o downgrade para 4.5.3 corrigiu o problema de reconexão para minha conexão de replicaset.

Posso atualizar totalmente para 4.5.10 antes de começar a falhar em 4.6.0, onde o problema de reconexão do replicaset ocorre pela primeira vez. A estimativa atual é que isso pode estar relacionado à atualização para o driver mongodb 2.2.9.

@loris, você poderia fornecer seu caso de teste para que possamos experimentá-lo?

Eu ainda estou olhando para isso, eu estava errado anteriormente sobre o commit de correção de digitação. Parece que o culpado é https://github.com/christkv/mongodb-core/pull/146/commits/09caa9d1e5423acd2f8f154f7b7430028e77e57f
Fornecer um caso de teste é um pouco complicado, pois só acontece desta maneira:

  • mongoose 4.6.8 , conectando ao meu localhost mongodb (3.2) com as configurações padrão
  • 2 rotas expressas, uma executando uma consulta de mangusto de longa execução (vários segundos), uma executando uma consulta de mangusto de execução rápida (o problema não ocorre ao executar as consultas de mangusto diretamente no nó, com caso de teste setInterval / setTimeout por exemplo, então, meu palpite é que está relacionado a como a conexão do pool é tratada)
  • Se eu executar a rota expressa de longa duração e tentar acertar a rota rápida, a última continuará rodando sem retornar
  • Definindo poolSize para 50 vez do padrão, corrija o problema
  • Verificar o commit anterior de mongodb-core corrige o problema também (a rota expressa de execução rápida retorna em alguns ms enquanto a rota de execução longa está sendo processada) (com o tamanho do pool padrão)
  • Então, acho que https://github.com/christkv/mongodb-core/pull/146/commits/09caa9d1e5423acd2f8f154f7b7430028e77e57f mudou algo em como um único mangusto de longa duração pode usar todas as conexões disponíveis em um pool

Uma correção acabou de pousar no mongo-core que pode corrigir esse problema.

@loris sim, aumentar o tamanho da piscina vai ajudar. Parece que você está enfrentando um problema de trem lento, em que o servidor mongodb não consegue lidar com mais de poolSize solicitações simultaneamente. Aumentar o tamanho do pool ajudará, mas não aumente muito ou você começará a ter problemas de desempenho com o WiredTiger.

Oi,
alguma atualização disso? Estou vendo o mesmo problema, o auto_reconnect não funciona ao usar um conjunto de réplicas. Acabei de experimentar o mongoose 4.7.0 com o mongodb 2.2.11 e ainda não consegui reconectar. Estou usando o mongod versão 3.2.10.

Estou executando tudo em um único host (laptop) com três instâncias do mongod em execução em portas diferentes com diretórios de banco de dados diferentes. Não parece que isso deva ser um problema, mas sou novo em mongo / mongoose / node / javascript. Meu aplicativo de nó com mongoose também está sendo executado no mesmo host.

Posso reproduzir isso simplesmente encerrando todos os processos mongod
(launchctl stop mongod01; launchctl stop mongod02; launchctl stop mongod03)
aguarde a mensagem de conexão fechada e reinicie (substitua "stop" por "start" nos comandos launchctl). Meu aplicativo nunca se reconecta ao mongo.

Se eu fizer o mesmo teste com uma única instância do mongod não configurada como um conjunto de réplicas, o mongoose reconectará sem problemas.

fico feliz em fornecer logs ou tentar patches, se isso for útil.

Depois de fazer algumas pesquisas, acho que posso ter encontrado uma fonte do problema. Parece que quando a reconexão automática é verdadeira, o buffer de conexão não deve se tornar ativo https://github.com/Automattic/mongoose/blob/master/lib/drivers/node-mongodb-native/connection.js#L153 na conexão fechar eventos. No entanto, não há mais propriedade autoReconnect sendo definida dentro da classe replset em mongodb-native https://github.com/mongodb/node-mongodb-native/blob/2.2/lib/replset.js, portanto, qualquer evento de fechamento de um dos nós faz com que o buffer fique permanentemente habilitado. Tive alguma sorte com o commit https://github.com/eflexsystems/mongoose/commit/5ac12727f34b41791f94643b66c8cc88aff4d66a, mas quero dar a ele mais algum tempo para ver se causou outros problemas antes de fazer uma solicitação de pull.

@joeldodson, você está descrevendo o mesmo problema que eu experimentei. Apenas um aviso, lançamentos> = 4.6.0 parecem conter o problema. Eu tentaria 4.5.10 entretanto, ele tem se reconectado a um replset e uma conexão única muito bem para mim.

Obrigado @jakesjews e @ kog13

Tentei 4.5.10 e o mongoose reconecta depois de reiniciar o conjunto de réplicas. No entanto, o db.readyState não parece ser limpo depois que todas as instâncias no conjunto de réplicas foram interrompidas. Estamos verificando isso para determinar se devemos rejeitar quaisquer solicitações (para que não fiquem na fila esperando que o aplicativo se reconecte ao banco de dados). Além disso, não recebi as notificações de desconexão ou conexão fechada com 4.5.10.

Já tenho uma lógica que fica em um loop com um temporizador de 5 segundos para tentar conectar se o banco de dados não estiver disponível quando o aplicativo for iniciado. Tentamos chamar isso no manipulador db.on ('fechado', function () {...}) e parece funcionar bem. Minha preocupação, porém, é se tentar conectar explicitamente terá algum conflito com qualquer lógica de reconexão subjacente. Já que a reconexão não parece estar acontecendo para conjuntos de réplicas, estou pensando que está tudo bem. Além disso, definimos auto_reconnect como false nas opções de conexão para o servidor e o replset.

@jakesjews - Tentei o patch que você mencionou acima, mas ainda não consegui reconectar. Talvez eu tenha perdido algo, mas parecia que o patch era mais para garantir que o evento close seja gerado e o readyState seja atualizado.

Estou feliz em tentar mais patches para auto_reconnect, se alguém tiver algum. Eu vou continuar cavando também.

obrigado.

@joeldodson , além do patch acima, você também precisará depender da versão mais recente do mongo-core, que tem correções para garantir que a conexão do monitor do conjunto de réplicas permaneça ativa. Se você experimentar o meu garfo, ele já deve estar com ele.

Acho que estou em um bom lugar com relação aos tempos limite e failover agora com o mangusto. Se alguém quiser experimentar, use meu fork of mongoose , além de definir socketTimeout como 0 em socketOptions.

O motivo para definir socketTimeout como 0 é que há um bug no mongo-core para o qual ainda preciso registrar um problema. O problema é causado pela redução / expansão dinâmica do pool de conexões. A maneira como o pool funciona é que sempre que ele adiciona uma nova conexão, essa conexão será encerrada após 30 segundos de inatividade. Os eventos de tempo limite acionam a remoção do pool e também executam uma verificação comparando o número de tempos limite com um limite de 30 tentativas de conexão. Há uma pulsação que é executada a cada 2 segundos e limpa o número de tempos limite, mas se 30 ou mais solicitações forem enfileiradas em paralelo, isso fará com que todas elas atinjam o tempo limite entre as pulsações e destruirá o pool de conexão. Por enquanto, definir o tempo limite nas conexões para 0 evita que as conexões sejam removidas no pool se estiverem inativas e evita o problema. Se você quiser replicar o problema, tente definir um tamanho de pool de conexão para algo em torno de 50 e executar 50 consultas paralelas. Depois disso, a piscina será destruída em cerca de 30 segundos. Observe que o tempo limite não afeta a verificação de pulsação do replicaset, pois ele tem seu próprio tempo limite.

Tenho estado muito sobrecarregado de trabalho recentemente, então não tive a chance de reunir todas essas mudanças, mas espero chegar a isso em breve.

Obrigado novamente @jakesjews. Peguei seus repositórios mongoose e mongodb-core. A reconexão funcionou. Eu não obtive os eventos 'conectado' e 'reconectado' que recebo ao usar uma única instância do Mongo. Além disso, o readyState não parece ser redefinido, ainda é 0, mesmo após a reconexão.

Fico feliz em ajudar com testes ou coleta de logs.

Ainda tendo problemas com [email protected]

Alguma novidade em relação a este assunto?

Experimentando os problemas também aqui. Tive que reverter a atualização da versão do mangusto por causa de problemas de conexão. Uma consulta que leva alguns segundos atualmente atinge o tempo limite de nossa conexão, onde antes não era o caso.

Este problema deve ser resolvido, caso contrário o pacote não é utilizável.
Provavelmente tentarei a solução @jakesjews quando tiver tempo. Até então usando 4.4.X

Se o problema for um bug com o mongo-core, isso não é realmente um problema com o mongoose. Se você está tendo o problema com a versão mais recente do mongoose, pode registrar um problema no repo mongodb-core ?

Na verdade, é um problema com mongoose pois foi atualizado para depender da versão de mongodb-core com problemas. Talvez mongoose deva ser restaurado para uma versão anterior de mongodb-core .

@jakesjews Vendo que [email protected] depende de [email protected] , que por sua vez depende de [email protected] , aplicar as correções de seu fork seria tudo o que é necessário para corrigir esses problemas com aparentemente sem efeitos colaterais?

E olhando para sua bifurcação, este commit é agora a única mudança necessária para um 4.7.xe / ou 4.8.x PR?

@Koslun o fork deve ser desnecessário agora. A correção é basicamente definir socketTimeout como 0 para o banco de dados socketOptions agora.

Aqui está como estão minhas opções de soquete agora.

    var opts = {
      server: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
      },
      replSet: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
    }

@jakesjews Ok, obrigado pelo rápido esclarecimento :).

Você sabe em qual versão do mongodb-core veremos uma correção para esse bug? Ou, alternativamente, tem um problema, RP ou compromisso que podemos rastrear?

Alguma atualização sobre o comentário de @Koslun ?

Alguma atualização para este problema?

O problema ainda está aberto desde 2016: open_mouth:

Eu me pergunto se esse problema ainda pode ocorrer com o mongoose 5.x com o driver mongodb 3.3.4 e o servidor MongoDB na versão 4.x? :pensamento:

Alguém sabe se o reconnectTries é redefinido quando a reconexão é bem-sucedida.

Instância do Fox, se reconnectTries for definido como 30 e, uma vez desconectado, o mangusto tenta 5 vezes e a conexão é bem-sucedida.
Da próxima vez que a conexão for perdida, qual será o contador de novas tentativas?
Ele tentará se reconectar 30 vezes?
Ou 25 vezes?

@ szabolcs-szilagyi sim, mas somente se você não definir useUnifiedTopology para true .

@ bhaveshvyas007 sim, faz. Aqui está o código relevante

Para a posteridade:

Se você estiver executando o Mongoose 5.x sem useUnifiedTopology , leia este guia para gerenciar conexões do MongoDB .

Se você estiver executando o Mongoose 5.x com useUnifiedTopology , esse problema não afetará você.

Esta página foi útil?
0 / 5 - 0 avaliações