Solução de problemas
Nesta página
Nesta página, você pode encontrar soluções para problemas comuns encontrados ao usar o PyMongo com o MongoDB.
Conexão
Servidor MongoDB relata versão X de fio, PyMongo requer Y
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.
Reconexão automática
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.
Tempo limite ao acessar o MongoDB do PyMongo com tunelamento
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.
Operações de leitura e escrita
AutoReconnect
Erro
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.
Aviso de depreciação: a contagem está obsoleta
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'
Erro de configuração do MongoClient
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.
Nenhum resultado ao fazer query de um documento por ObjectId em aplicativos web
É 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__) 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()
A query funciona no shell, mas não no PyMongo
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 .
Cursors
O objeto 'Cursor' não tem atributo '_Cursor__killed'
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.
"CursorNotFound ID de cursor inválido no servidor"
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.
Projeções
'Não é possível fazer exclusão no campo <field> na projeção de inclusão'
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.
Indexes
DuplicateKeyException
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
Data Formats
ValueError: não é possível codificar uuid.UUID nativo com UuidRepresentation.UNSPECIFIED
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})
OverflowError ao decodificar datas armazenadas pelo driver de outro idioma
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})
TLS
CERTIFICATE_VERIFY_FAILED
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.
TLSV1_ALERT_PROTOCOL_VERSION
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.
Resposta de status inválida
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.
SSLV3_ALERT_HANDSHAKE_FAILURE
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
Renegociação legada insegura desativada
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:
Use a UnsafeLegacyServerConnect
opção
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
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.
Tempo limite de operação do lado do cliente
ServerSelectionTimeoutError
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>]>
NetworkTimeout
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
ExecutionTimeout
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
WTimeoutError
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}}}
BulkWriteError
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': []}
Processos de bifurcação
Bifurcar um processo causa um impasse
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.