Menu Docs
Página inicial do Docs
/ / /
PyMongo

Solução de problemas

Nesta página

  • Conexão
  • Operações de leitura e escrita
  • Cursors
  • Projeções
  • Indexes
  • Data Formats
  • TLS
  • Tempo limite de operação do lado do cliente
  • Processos de bifurcação

Nesta página, você pode encontrar soluções para problemas comuns encontrados ao usar o PyMongo com o MongoDB.

Se você tentar se conectar ao MongoDB Server v3.4 ou anterior, o PyMongo poderá gerar o seguinte erro:

pymongo.errors.ConfigurationError: Server at localhost:27017 reports wire version 5, but this version of PyMongo requires at least 6 (MongoDB 3.6).

Isso ocorre quando a versão do driver é muito nova para o servidor ao qual está se conectando. Para resolver esse problema, atualize seu sistema do MongoDB para v3.6 ou posterior, ou faça downgrade para o PyMongo v3.x, que oferece suporte ao MongoDB Server v2.6 e posterior.

Uma exceção AutoReconnect indica que ocorreu umfailover . Isso significa que o PyMongo perdeu sua conexão com o membro primary original do conjunto de réplicas, e sua última operação pode ter falhado.

Quando esse erro ocorre, o PyMongo tenta encontrar automaticamente o novo membro principal para as operações subsequentes. Para gerenciar o erro, seu aplicativo deve realizar uma das seguintes ações:

  • Tente novamente a operação que pode ter falhado

  • Continue executando, entendendo que a operação pode ter falhado

Importante

O PyMongo gera um erro AutoReconnect em todas as operações até que o conjunto de réplicas eleja um novo membro primário.

Se você tentar se conectar a um conjunto de réplicas do MongoDB em um túnel SSH, receberá o seguinte erro:

File "/Library/Python/2.7/site-packages/pymongo/collection.py", line 1560, in count
return self._count(cmd, collation, session)
File "/Library/Python/2.7/site-packages/pymongo/collection.py", line 1504, in _count
with self._socket_for_reads() as (connection, slave_ok):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Library/Python/2.7/site-packages/pymongo/mongo_client.py", line 982, in _socket_for_reads
server = topology.select_server(read_preference)
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 224, in select_server
address))
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 183, in select_servers
selector, server_timeout, address)
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 199, in _select_servers_loop
self._error_message(selector))
pymongo.errors.ServerSelectionTimeoutError: localhost:27017: timed out

Isso ocorre porque o PyMongo descobre membros do conjunto de réplicas usando a resposta do comando isMaster , que contém os endereços e portas dos outros membros do conjunto de réplicas. No entanto, você não pode acessar esses endereços e portas através do túnel SSH.

Em vez disso, você pode se conectar diretamente a um único nó do MongoDB usando a opção directConnection=True com o tunelamento SSH.

Você recebe este erro se especificar tag-sets em sua preferência de leitura e o MongoDB não conseguir encontrar membros do conjunto de réplicas com as tags especificadas. Para evitar esse erro, inclua um dicionário vazio ({}) no final da lista de conjuntos de tags. Isso instrui o PyMongo a ler de qualquer membro que corresponda ao modo de referência de leitura quando não conseguir encontrar tags correspondentes.

O PyMongo não suporta mais o método count() . Em vez disso, utilize o count_documents() método da Collection classe .

Importante

O método count_documents() pertence à classe Collection . Se você tentar ligar para Cursor.count_documents(), o PyMongo gerará o seguinte erro:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Cursor' object has no attribute 'count'

Fornecer nomes de argumentos de palavras-chave inválidos faz com que o driver gere esse erro.

Verifique se os argumentos de palavra-chave especificados existem e estão escritos corretamente.

É comum em aplicativos da web codificar os ObjectIds dos documentos em URLs, como mostrado no exemplo de código a seguir:

"/posts/50b3bda58a02fb9a84d8991e"

Sua estrutura da Web passa a parte ObjectId da URL para o manipulador de solicitações como uma string. Você deve converter a string para uma instância do ObjectId antes de passá-la para o método do find_one() .

O exemplo de código a seguir mostra como realizar essa conversão em um aplicação MongoDB. O processo é semelhante para outras estruturas da web.

from pymongo import MongoClient
from bson.objectid import ObjectId
from flask import Flask, render_template
client = MongoClient()
app = Flask(__name__)
@app.route("/posts/<_id>")
def show_post(_id):
# NOTE!: converting _id from string to ObjectId before passing to find_one
post = client.db.posts.find_one({'_id': ObjectId(_id)})
return render_template('post.html', post=post)
if __name__ == "__main__":
app.run()

Após o campo _id , que é sempre o primeiro, os pares de valores-chave em um documento BSON podem estar em qualquer ordem. O shell mongo preserva a ordem das chaves ao ler e gravar dados, conforme mostrado pelos campos "b" e "a" no exemplo de código a seguir:

// mongo shell
db.collection.insertOne( { "_id" : 1, "subdocument" : { "b" : 1, "a" : 1 } } )
// Returns: WriteResult({ "nInserted" : 1 })
db.collection.findOne()
// Returns: { "_id" : 1, "subdocument" : { "b" : 1, "a" : 1 } }

O PyMongo representa documentos BSON como dicionários Python por padrão, e a ordem das chaves nos dicionários não está definida. Em Python, um dicionário declarado com a chave "a" primeiro é o mesmo que um com a chave "b" primeiro. No exemplo a seguir, as chaves são exibidas na mesma ordem, independentemente de sua ordem na declaração print :

print({'a': 1.0, 'b': 1.0})
# Returns: {'a': 1.0, 'b': 1.0}
print({'b': 1.0, 'a': 1.0})
# Returns: {'a': 1.0, 'b': 1.0}

Da mesma forma, os dicionários do Python podem não mostrar as chaves na ordem em que são armazenadas no BSON. O exemplo a seguir mostra o resultado da impressão do documento inserido no exemplo anterior:

print(collection.find_one())
# Returns: {'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}

Para preservar a ordem das chaves ao ler BSON, use a classe SON , que é um dicionário que lembra a ordem das chaves.

O seguinte exemplo de código mostra como criar uma coleção configurada para utilizar a classe SON :

from bson import CodecOptions, SON
opts = CodecOptions(document_class=SON)
CodecOptions(document_class=...SON..., tz_aware=False, uuid_representation=UuidRepresentation.UNSPECIFIED, unicode_decode_error_handler='strict', tzinfo=None, type_registry=TypeRegistry(type_codecs=[], fallback_encoder=None), datetime_conversion=DatetimeConversion.DATETIME)
collection_son = collection.with_options(codec_options=opts)

Quando você encontra o subdocumento anterior, o driver representa os resultados da query com SON objetos e preserva a ordem das chaves:

print(collection_son.find_one())
SON([('_id', 1.0), ('subdocument', SON([('b', 1.0), ('a', 1.0)]))])

O layout de armazenamento real do subdocumento agora está visível: "b" está antes de "a".

Como a ordem das chaves de um dicionário Python não está definida, você não pode prever como ele será serializado para o BSON. No entanto, o MongoDB considera subdocumentos iguais somente se suas chaves tiverem a mesma ordem. Se você usar um dicionário Python para fazer query de um subdocumento, ele pode não corresponder:

collection.find_one({'subdocument': {'b': 1.0, 'a': 1.0}}) is None
True

Como o Python considera os dois dicionários iguais, trocar a ordem das chaves em sua query não faz diferença:

collection.find_one({'subdocument': {'b': 1.0, 'a': 1.0}}) is None
True

Você pode resolver isso de duas maneiras. Primeiro, você pode corresponder o subdocumento campo a campo:

collection.find_one({'subdocument.a': 1.0,
'subdocument.b': 1.0})
{'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}

A query corresponde a qualquer subdocumento com um "a" de 1.0 e um "b" de 1.0, independentemente da ordem em que você os especifica no Python ou da ordem em que são armazenados no BSON. Essa query também agora corresponde a subdocumentos com chaves adicionais além de "a" e "b", enquanto a query anterior exigia uma correspondência exata.

A segunda solução é usar um objeto ~bson.son.SON para especificar a ordem das chaves:

query = {'subdocument': SON([('b', 1.0), ('a', 1.0)])}
collection.find_one(query)
{'_id': 1.0, 'subdocument': {'a': 1.0, 'b': 1.0}}

O driver preserva a ordem das chaves que você usa ao criar um ~bson.son.SON ao serializá-lo para BSON e usá-lo como uma query. Assim, você pode criar um subdocumento que corresponda exatamente ao subdocumento na coleção.

Observação

Para obter mais informações sobre a correspondência de subdocumentos, consulte o guia Query sobre documentos incorporados/aninhados na documentação do MongoDB Server .

O PyMongo v3.8 ou anterior gera um TypeError e um AttributeError se você fornecer argumentos inválidos para o construtor Cursor . O AttributeError é irrelevante, mas o TypeError contém informações de depuração, conforme mostrado no exemplo a seguir:

Exception ignored in: <function Cursor.__del__ at 0x1048129d8>
...
AttributeError: 'Cursor' object has no attribute '_Cursor__killed'
...
TypeError: __init__() got an unexpected keyword argument '<argument>'

Para corrigir isso, certifique-se de fornecer os argumentos de palavra-chave corretos. Você também pode atualizar para o PyMongo v3.9 ou posterior, o que remove o erro irrelevante.

Os cursores no MongoDB podem atingir o tempo limite no servidor se estiverem abertos há muito tempo sem que nenhuma operação seja executada neles. Isso pode levar a uma exceção CursorNotFound quando você tenta iterar pelo cursor.

O driver retorna um OperationFailure com esta mensagem se você tentar incluir e excluir campos em uma única projeção. Certifique-se de que sua projeção especifique apenas campos a serem incluídos ou campos a serem excluídos.

Se você executar uma operação de gravação que armazena um valor duplicado que viola umíndice único , o driver gera um DuplicateKeyException e o MongoDB lança um erro semelhante ao seguinte:

E11000 duplicate key error index

Esse erro resulta da tentativa de codificar um objeto UUID nativo para um objeto Binary quando a representação do UUID é UNSPECIFIED, conforme mostrado no exemplo de código a seguir:

unspecified_collection.insert_one({'_id': 'bar', 'uuid': uuid4()})
Traceback (most recent call last):
...
ValueError: cannot encode native uuid.UUID with UuidRepresentation.UNSPECIFIED.
UUIDs can be manually converted to bson.Binary instances using bson.Binary.from_uuid()
or a different UuidRepresentation can be configured. See the documentation for
UuidRepresentation for more information.

Em vez disso, você deve converter explicitamente um UUID nativo em um objeto Binary usando o método Binary.from_uuid() , conforme mostrado no exemplo a seguir:

explicit_binary = Binary.from_uuid(uuid4(), UuidRepresentation.STANDARD)
unspec_collection.insert_one({'_id': 'bar', 'uuid': explicit_binary})

O PyMongo decodifica valores BSON datetime para instâncias da classe datetime.datetime do Python. As instâncias de datetime.datetime estão limitadas a anos entre datetime.MINYEAR (1) e datetime.MAXYEAR (9999). Alguns drivers do MongoDB podem armazenar datas BSON com valores de ano muito fora dos suportados por datetime.datetime.

Existem algumas maneiras de contornar esse problema. Começando com o PyMongo 4.3, bson.decode pode decodificar valores BSON datetime de quatro maneiras. Você pode especificar o método de conversão usando o parâmetro datetime_conversion de ~bson.codec_options.CodecOptions.

A opção de conversão padrão é ~bson.codec_options.DatetimeConversion.DATETIME, que tentará decodificar o valor como datetime.datetime, permitindo que ~builtin.OverflowError ocorra em datas fora do intervalo. ~bson.codec_options.DatetimeConversion.DATETIME_AUTO altera esse comportamento para retornar ~bson.datetime_ms.DatetimeMS quando as representações estão fora do intervalo, enquanto retorna objetos ~datetime.datetime como antes:

from datetime import datetime
from bson.datetime_ms import DatetimeMS
from bson.codec_options import DatetimeConversion
from pymongo import MongoClient
client = MongoClient(datetime_conversion=DatetimeConversion.DATETIME_AUTO)
client.db.collection.insert_one({"x": datetime(1970, 1, 1)})
client.db.collection.insert_one({"x": DatetimeMS(2**62)})
for x in client.db.collection.find():
print(x)
{'_id': ObjectId('...'), 'x': datetime.datetime(1970, 1, 1, 0, 0)}
{'_id': ObjectId('...'), 'x': DatetimeMS(4611686018427387904)}

Para outras opções, consulte a documentação da API para o DatetimeConversion aula.

Outra opção que não envolve a configuração datetime_conversion é filtrar valores de documento fora do intervalo suportado por ~datetime.datetime:

from datetime import datetime
coll = client.test.dates
cur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})

Se você não precisar do valor de datetime, poderá filtrar apenas esse campo:

cur = coll.find({}, projection={'dt': False})

Uma mensagem de erro semelhante à seguinte significa que o OpenSSL não conseguiu verificar o certificado do servidor:

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

Isso geralmente acontece porque o OpenSSL não consegue acessar os certificados raiz do sistema ou porque os certificados estão desatualizados.

Se você usar Linux, certifique-se de ter as atualizações de certificado raiz mais recentes instaladas do seu fornecedor de Linux.

Se você usa macOS e se está executando o Python v3.7 ou posterior baixado de python.org, execute o seguinte comando para instalar certificados raiz:

open "/Applications/Python <YOUR PYTHON VERSION>/Install Certificates.command"

Dica

Para obter mais informações sobre este problema, consulte Problema do Python 29065.

Se você usar o embedded-pypy, talvez seja necessário definir uma variável de ambiente para informar ao OpenSSL onde encontrar certificados raiz. O seguinte exemplo de código mostra como instalar o módulo de certificação do PyPi e exporte a SSL_CERT_FILE variável de ambiente :

$ pypy -m pip install certifi
$ export SSL_CERT_FILE=$(pypy -c "import certifi; print(certifi.where())")

Dica

Para obter mais informações sobre esse problema, consulte problema portátil-pypy 15.

Uma mensagem de erro semelhante à seguinte significa que a versão OpenSSL usada pelo Python não suporta um protocolo TLS novo o suficiente para se conectar ao servidor:

[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version

As melhores práticas do setor recomendam, e algumas regulamentações exigem, que os protocolos TLS mais antigos sejam desabilitados em algumas implantações do MongoDB. Algumas implantações podem desativar o TLS 1.0, enquanto outras podem desativar o TLS 1.0 e o TLS 1.1.

Nenhuma alteração no aplicativo é necessária para que o PyMongo use as versões mais recentes do TLS, mas algumas versões do sistema operacional podem não fornecer uma versão do OpenSSL nova o suficiente para habilitá-las.

Se você usa macOS v10.12 (High Sierra) ou anterior, instale o Python a partir de python.org, homebrew, macports ou uma fonte semelhante.

Se você usa Linux ou outro Unix não macOS, use o seguinte comando para verificar sua versão do OpenSSL:

$ openssl version

Se o comando anterior mostrar um número de versão inferior a 1.0.1, suporte para TLS 1.1 ou mais recente não está disponível. Atualize para uma versão mais recente ou entre em contato com o fornecedor do sistema operacional para obter uma solução.

Para verificar a versão TLS do seu interpretador Python, instale o módulo requests e execute o seguinte código:

python -c "import requests; print(requests.get('https://www.howsmyssl.com/a/check', verify=False).json()['tls_version'])"

Você deve ver TLS 1.1 ou posterior.

Uma mensagem de erro semelhante à seguinte significa que a verificação de revogação do certificado falhou:

[('SSL routines', 'tls_process_initial_server_flight', 'invalid status response')]

Para obter mais detalhes, consulte a seção OCSP deste guia.

Ao usar o Python v3.10 ou posterior com versões do MongoDB anteriores à v4.0, você pode ver erros semelhantes às seguintes mensagens:

SSL handshake failed: localhost:27017: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)
SSL handshake failed: localhost:27017: EOF occurred in violation of protocol (_ssl.c:997)

Os registros do MongoDB Server também podem mostrar o seguinte erro:

2021-06-30T21:22:44.917+0100 E NETWORK [conn16] SSL: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher

Alterações feitas no módulo ssl no Python v3.10 pode causar incompatibilidades com versões do MongoDB anteriores à v4.0. Para resolver esse problema, tente uma ou mais das seguintes etapas:

  • Faça downgrade do Python para a3.9 ou anterior

  • Atualize o MongoDB Server para v4.2 ou posterior

  • Instalar o PyMongo com a opção OCSP , que depende do PyOpenSSL

Ao usar o OpenSSL v3 ou posterior, você poderá ver um erro semelhante à seguinte mensagem:

[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled

Esses tipos de erros ocorrem devido a proxies SSL desatualizados ou com erros que impõem erroneamente a renegociação do TLS legado .

Para resolver esse problema, execute as seguintes etapas:

1

Execute o seguinte comando para garantir que você tenha o OpenSSL vv3.0.4 ou posterior instalado:

openssl version
2

Crie um arquivo de configuração que inclua a opção UnsafeLegacyServerConnect. O exemplo seguinte mostra como configurar a opção UnsafeLegacyServerConnect:

openssl_conf = openssl_init
[openssl_init]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
Options = UnsafeLegacyServerConnect
3

Execute o Python enquanto configura a variável de ambiente do OPENSSL_CONF para utilizar o arquivo de configuração OpenSSL que você acabou de criar:

OPENSSL_CONF=/path/to/the/config/file/above.cnf python ...

Importante

Como a UnsafeLegacyServerConnect configuração da opção tem implicações de segurança, use esta solução alternativa como último recurso para solucionar unsafe legacy renegotiation disabled erros.

Este erro indica que o cliente não conseguiu encontrar um servidor disponível para executar a operação dentro do tempo limite fornecido:

pymongo.errors.ServerSelectionTimeoutError: No servers found yet, Timeout: -0.00202266700216569s, Topology Description: <TopologyDescription id: 63698e87cebfd22ab1bd2ae0, topology_type: Unknown, servers: [<ServerDescription ('localhost', 27017) server_type: Unknown, rtt: None>]>

Esse erro indica que o cliente não conseguiu estabelecer uma conexão dentro do tempo limite fornecido ou que a operação foi enviada, mas o servidor não responde a tempo:

pymongo.errors.NetworkTimeout: localhost:27017: timed out

Esse erro pode indicar que o servidor cancelou a operação porque excedeu o tempo limite fornecido. Mesmo que o PyMongo gere essa exceção, a operação pode ter sido concluída parcialmente no servidor.

pymongo.errors.ExecutionTimeout: operation exceeded time limit, full error: {'ok': 0.0, 'errmsg': 'operation exceeded time limit', 'code': 50, 'codeName': 'MaxTimeMSExpired'}

Isso também pode indicar que o cliente cancelou a operação porque não foi possível concluí-la dentro do tempo limite fornecido:

pymongo.errors.ExecutionTimeout: operation would exceed time limit, remaining timeout:0.00196 <= network round trip time:0.00427

Este erro indica que o servidor não conseguiu concluir a operação de gravação solicitada dentro do tempo limite fornecido e após a write concern especificada:

pymongo.errors.WTimeoutError: operation exceeded time limit, full error: {'code': 50, 'codeName': 'MaxTimeMSExpired', 'errmsg': 'operation exceeded time limit', 'errInfo': {'writeConcern': {'w': 1, 'wtimeout': 0}}}

Esse erro indica que o servidor não conseguiu concluir um método insert_many() ou bulk_write() dentro do tempo limite fornecido e após a write concern especificada:

pymongo.errors.BulkWriteError: batch op errors occurred, full error: {'writeErrors': [], 'writeConcernErrors': [{'code': 50, 'codeName': 'MaxTimeMSExpired', 'errmsg': 'operation exceeded time limit', 'errInfo': {'writeConcern': {'w': 1, 'wtimeout': 0}}}], 'nInserted': 2, 'nUpserted': 0, 'nMatched': 0, 'nModified': 0, 'nRemoved': 0, 'upserted': []}

Uma instância do MongoClient gera vários threads para executar tarefas em segundo plano, como monitorar servidores conectados. Essas threads compartilham um estado protegido por instâncias da threading.Lock classe , que não são seguras para bifurcações . O PyMongo está sujeito às mesmas limitações que qualquer outro código multithread que use a classe threading.Lock ou quaisquer mutexes.

Uma dessas limitações é que as travas se tornam inúteis após chamar o método fork() . Quando o fork() é executado, o driver copia todos os bloqueios do processo pai para o processo filho no mesmo estado em que estavam no pai. Se estiverem bloqueados no processo pai, também estarão bloqueados no processo filho. O processo filho criado por fork() tem apenas um thread, portanto, quaisquer bloqueios criados por outros threads no processo pai nunca são liberados no processo filho. Da próxima vez que o processo filho tentar adquirir uma dessas travas, ocorrerá um impasse.

A partir da versão 4.3 do PyMongo, depois de chamar o método os.fork() , o driver usa o método os.register_at_fork() para redefinir suas travas e outros estados compartilhados no processo filho. Embora isso reduza a probabilidade de um impasse, o PyMongo depende de bibliotecas que não são seguras para fork em aplicativos de várias threads, incluindo OpenSSL e getaddrinfo(3). Portanto, um impasse ainda pode ocorrer.

A página de manual do Linux para fork(2) também impõe a seguinte restrição:

Após um fork() em um programa de várias threads, a criança pode chamar com segurança somente funções seguras com sinal assíncrono (consulte segurança de sinal(7)) até o momento em que chama execve(2).

Como o PyMongo depende de funções que não são seguras contra sinais assíncronos, ele pode causar impasses ou falhas ao ser executado em um processo filho.

Dica

Para obter um exemplo de um impasse em um processo filho, consulte PYTHON-3406 em Jira.

Para obter mais informações sobre os problemas causados por bloqueios do Python em contextos multithread com fork(), consulte Problema 6721 no Rastreador de problemas do Python.

Voltar

Perguntas frequentes