MongoDB · Wiki de Troubleshooting

Troubleshooting
MongoDB

Base de consulta de incidentes reais de MongoDB. Cada artigo traz o resumo do caso, o checklist de diagnóstico e resolução, os comandos usados com exemplos e os links da documentação oficial. Novos casos entram como novos artigos ao final.

1 artigo
mongosh + docs oficiais
Atualizado em outubro/2026

Artigos

ArtigoSintomaCausaResolução
Index build travado (createIndexes pendurado)createIndexes pendurado no currentOp por mais de 90 minutosNó hidden com votes: 1 fora do ar + commit quorum padrão (votingMembers)killOp e recriação dos índices com commitQuorum "majority"
Antes de copiar um comandoBanco, coleção, índices e hosts dos exemplos são fictícios: troque pelos do seu ambiente. Os links apontam para mongodb.com/docs/manual na versão mais recente; use o seletor de versão da página para a versão do seu servidor.

Index build travado (createIndexes pendurado)

Dois createIndexes ficaram pendurados por mais de 90 minutos em uma coleção pequena, porque um nó hidden votante estava fora do ar e o commit quorum padrão exige todos os votantes.

index buildcommit quorumreplica sethidden

Resumo do incidente e lições

Incidente em uma coleção pequena de um replica set de 5 membros votantes, 2 deles hidden. Dois createIndexes iniciados pelo MongoDB Compass ficaram pendurados no currentOp por mais de 90 minutos, apesar de a coleção ter só centenas de documentos.

ItemDetalhe
SintomaDois createIndexes (via MongoDB Compass) apareciam no currentOp por mais de 90 minutos.
Coleçãonome-base-dados.nome-collection (341 documentos, ~135 KB). Coleção pequena: lentidão não era a causa.
Replica setrsExemplo, 5 membros votantes, 2 deles hidden (nós 4 e 5, com votes: 1).
CausaO nó mongodb-app-4 (hidden, votes: 1) estava fora do ar. O commit quorum padrão (votingMembers) exige todos os votantes com dados, então o build não finalizava.
ResoluçãoOs builds foram abortados com killOp e os índices recriados com commitQuorum: "majority".
PendênciasRecuperar o nó 4 e decidir sobre votes: 0 nos hidden.

Lições do incidente

Hidden não é "fora do voto"Membro hidden com votes: 1 conta no commit quorum padrão do index build. A queda dele trava o createIndex com quorum padrão.
getIndexes() pode listar índices em construçãoVer o índice na lista não prova que o build terminou. Verifique também o currentOp.
killOp descarta o índiceEm um createIndexes pendente, o killOp aborta o build e o índice é removido. Não assuma que ele sobrevive.
setIndexCommitQuorum atua em um build por vezCada createIndexes com um índice é um build separado. Passar nomes de builds diferentes na mesma chamada retorna IndexNotFound ("Cannot find an index build... with the provided index names"), mesmo com os builds ativos. Chame o comando uma vez por índice (veja o passo "Criar, ajustar e remover índices").
Collection: { W: 1 } em lockStats é históricoÉ uma contagem acumulada, não um lock ativo. Para saber se há bloqueio, olhe locks e waitingForLock.
Colagem no mongoshBlocos multilinha colados com o prompt de continuação (|) geram SyntaxError. Cole em uma linha só.

Conceitos: commit quorum, hidden e maioria

Os conceitos que explicam por que um index build pode ficar parado esperando um nó que nem recebe leituras.

ConceitoResumoDocumentação
Commit quorumQuantos membros devem concluir o build antes do commit. O padrão é votingMembers (todos os votantes com dados).Index builds
Membro hiddenNão recebe leituras e tem priority: 0, mas continua votante se votes: 1.Hidden members
Write concern majorityConfirmação de escrita pela maioria dos membros com voto.Write concern
Maioria e tolerância a falhasCom N votantes, a maioria é floor(N/2)+1. Número ímpar de votantes é preferível.Replica set elections
Valores aceitos para commitQuorum"votingMembers" (padrão), "majority", um número inteiro, o nome de um tag set ou 0.

Checklist para um createIndex que não termina

Roteiro para um createIndex que não termina. Siga em ordem e só parta para o passo destrutivo se aceitar recriar o índice.

  1. Listar os builds e as operações
    mongosh
    db.currentOp({ "command.createIndexes": { $exists: true } })
  2. Ver a saúde dos membros Existe algum membro votante fora do ar?
    mongosh
    rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health }))
  3. Conferir votos e hidden
    mongosh
    rs.conf().members.map(m => ({ host: m.host, hidden: m.hidden, priority: m.priority, votes: m.votes }))
  4. Ajustar o commit quorum de cada build pendente Uma chamada por índice, com "majority". Preserva o índice.
    mongosh
    db.getSiblingDB("nome-base-dados").runCommand({ setIndexCommitQuorum: "nome-collection", indexNames: ["campo_a_1"], commitQuorum: "majority" })
  5. Só se aceitar recriar: abortar e recriar Use killOp ou dropIndexes e recrie com commit quorum "majority".
    mongosh
    db.getSiblingDB("nome-base-dados").getCollection("nome-collection").createIndex({ campo_a: 1 }, { name: "campo_a_1" }, "majority")
  6. Confirmar currentOp vazio e getIndexes() no primário e nos secundários (veja o passo de secundários).
  7. Resolver a causa raiz Recuperar o nó que está fora ou avaliar votes: 0 nos hidden.
PrevençãoEm ferramentas gráficas como o Compass, o commit quorum é o padrão. Enquanto houver um votante fora, crie índices pelo shell com "majority".

Monitorar operações e index builds

db.currentOp()

Lista as operações em andamento no servidor e aceita um documento de filtro. Campos úteis: opid (usado no killOp), secs_running, ns, command, msg/progress (fase e progresso do index build), locks/waitingForLock e client/appName (origem, ex.: MongoDB Compass).

mongosh
db.currentOp({ "command.createIndexes": { $exists: true } })

// Filtrar pelo namespace
db.currentOp({ ns: "nome-base-dados.nome-collection" })

// Namespace ou fase de index build
db.currentOp({ $or: [ { ns: "nome-base-dados.nome-collection" }, { msg: /Index Build/ } ] })
CampoSignificado
opidIdentificador da operação (usado no killOp).
secs_runningHá quantos segundos a operação roda.
nsNamespace (banco.coleção).
commandComando em execução (ex.: createIndexes e os índices).
msg / progressFase e progresso (done/total) de operações como index build.
locks, waitingForLockLocks atualmente retidos ou aguardados.
client, appNameOrigem da conexão (ex.: MongoDB Compass).

Documentação oficialdb.currentOp()

$currentOp (estágio de aggregation)

Equivalente em aggregation do currentOp, mais flexível para filtrar e projetar. Deve ser executado no banco admin. Opções comuns: allUsers (operações de todos os usuários), idleConnections e idleSessions.

mongosh
db.getSiblingDB("admin").aggregate([
  { $currentOp: { allUsers: true, idleConnections: true } },
  { $match: { ns: "nome-base-dados.nome-collection" } }
])

Documentação oficial$currentOp

Procurar transações abertas

Retorna sessões com transação aberta. Uma transação longa pode bloquear o commit final de um index build. No incidente a consulta não retornou nada, o que descartou essa hipótese.

mongosh
db.getSiblingDB("admin").aggregate([
  { $currentOp: { allUsers: true, idleSessions: true } },
  { $match: { "transaction": { $exists: true } } }
])

Documentação oficial$currentOp

db.killOp()

Solicita o encerramento de uma operação pelo opid.

mongosh
db.killOp(-1597692886)
CuidadoEm um createIndexes com build ainda pendente, aborta o build e o índice é descartado. Use apenas se aceitar recriar o índice.

Documentação oficialdb.killOp()

Inspecionar coleções e índices

db.getSiblingDB()

Troca de banco sem usar use. Útil em scripts e quando se está em outro banco, como admin.

mongosh
db.getSiblingDB("nome-base-dados")

Documentação oficialdb.getSiblingDB()

db.collection.getIndexes()

Lista os índices da coleção. Pode incluir índices cujo build ainda não terminou, portanto confirme também com currentOp.

mongosh
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").getIndexes()

Documentação oficialdb.collection.getIndexes()

db.collection.stats()

Retorna estatísticas de armazenamento: count, size, storageSize, totalIndexSize, indexSizes e nindexes. Foi usado para constatar que a coleção era pequena (341 documentos), descartando lentidão como causa. Em versões recentes, a alternativa é o estágio $collStats.

mongosh
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").stats()

Documentação oficialdb.collection.stats() · $collStats

$indexStats

Mostra estatísticas de uso de cada índice (quantas vezes foi usado desde o último restart). Útil para identificar índices sem uso.

mongosh
db.getCollection("nome-collection").aggregate([{ $indexStats: {} }])

Documentação oficial$indexStats

Conectar em secundários e verificar a replicação

Depois de resolver, confirme que os índices replicaram em cada membro. Com directConnection=true, o mongosh conecta apenas ao host indicado.

bash
mongosh "mongodb://mongodb-app-2.exemplo.com.br:27017/?directConnection=true"
mongosh
db.getMongo().setReadPref("secondaryPreferred")
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").getIndexes()

Em secundários é preciso permitir leitura, com setReadPref("secondaryPreferred") ou rs.secondaryOk() (conforme a versão do mongosh). Repita em cada membro.

Documentação oficialRead preference · rs.secondaryOk() · Connection strings (directConnection)

Criar, ajustar e remover índices

setIndexCommitQuorum

Altera o commit quorum de um index build em andamento, sem abortá-lo. O indexNames deve corresponder exatamente a um único build. Se os índices foram criados em comandos createIndexes separados, execute uma chamada por índice. Era a alternativa que preservaria os índices, em vez do killOp. Retorna IndexNotFound quando os nomes não correspondem a um build ativo.

mongosh
db.getSiblingDB("nome-base-dados").runCommand({ setIndexCommitQuorum: "nome-collection", indexNames: ["campo_a_1"], commitQuorum: "majority" })
db.getSiblingDB("nome-base-dados").runCommand({ setIndexCommitQuorum: "nome-collection", indexNames: ["campo_b_1"], commitQuorum: "majority" })

Documentação oficialsetIndexCommitQuorum

createIndex() com commit quorum

O terceiro argumento é o commitQuorum. Com "majority", o build finaliza quando a maioria dos votantes com dados confirma, e a queda de um nó não trava a operação.

mongosh
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").createIndex({ campo_a: 1 }, { name: "campo_a_1" }, "majority")
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").createIndex({ campo_b: 1 }, { name: "campo_b_1" }, "majority")

Documentação oficialdb.collection.createIndex() · createIndexes (comando) · Index builds em coleções populadas

dropIndexes (remover índice ou cancelar build)

Remove um índice. Em MongoDB 4.4+, também é a forma recomendada de abortar um index build em andamento, em vez de killOp.

mongosh
db.getSiblingDB("nome-base-dados").getCollection("nome-collection").dropIndexes("campo_a_1")

Documentação oficialdb.collection.dropIndex() · dropIndexes (comando)

Replica set: estado e configuração

rs.status()

Estado em tempo real do replica set: papel de cada membro (PRIMARY, SECONDARY), health, optime, lastHeartbeatMessage (mostrou "Connection refused" no nó 4) e writeMajorityCount.

mongosh
rs.status()

// Versão resumida usada no incidente
rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health, lag: m.optimeDate }))

Documentação oficialrs.status()

rs.conf()

Mostra a configuração (não o estado) do replica set: hidden, priority, votes, tags. Foi o que revelou que os nós hidden 4 e 5 tinham votes: 1.

mongosh
rs.conf().members.map(m => ({ host: m.host, hidden: m.hidden, priority: m.priority, votes: m.votes }))

Documentação oficialrs.conf() · Replica set configuration

rs.reconfig() (ex.: votes: 0)

Aplica uma nova configuração. Zerar votes exige priority: 0 (já é o caso dos hidden). Os nós continuam replicando, mas deixam de contar para eleições, maioria de escrita e commit quorum de index build.

mongosh
cfg = rs.conf()
cfg.members.forEach(m => {
  if (m.host.startsWith("mongodb-app-4") || m.host.startsWith("mongodb-app-5")) m.votes = 0
})
rs.reconfig(cfg)
Trade-offCom votes: 0 nos dois hidden, restam 3 votantes (nós 1, 2 e 3), maioria = 2, tolerância a 1 falha. Antes, com 5 votantes, eram 2.

Documentação oficialrs.reconfig()

rs.remove() e rs.add()

rs.remove() retira o membro da configuração. Para voltar, é preciso rs.add() e, em geral, uma initial sync. Só faça se o nó estiver perdido de vez.

mongosh
rs.remove("mongodb-app-4.exemplo.com.br:27017")
rs.add({ host: "mongodb-app-4.exemplo.com.br:27017", hidden: true, priority: 0, votes: 1 })
Número par de votantesRemover um dos cinco votantes deixa 4, número par, que não é o ideal.

Documentação oficialrs.remove() · rs.add()