Docs Menu

Docs Homeアプリケーションの開発Python ドライバーPyMongo

トラブルシューティング

項目一覧

  • 接続
  • 読み取り操作と書込み操作
  • Cursors
  • プロジェクション
  • Indexes
  • Data Formats
  • TLS
  • クライアント側操作タイムアウト
  • プロセスのフォーク

このページでは、MongoDB で PyMongo を使用する際に発生する一般的な問題の解決策を見つけることができます。

MongoDB Server v 3.4またはそれ以前のバージョンに接続しようとすると、PyMongo は次のエラーを発生させる可能性があります。

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

これは、接続先のサーバーに対してドライバーのバージョンが新しすぎる場合に発生します。 この問題を解決するには、MongoDB の配置を v 3.6以降にアップグレードするか、MongoDB Server v 2.6以降をサポートする PyMongo v 3 .x にダウングレードします。

AutoReconnect例外は、 フェイルオーバーが発生したことを示します。 これは、PyMongo がレプリカセットの元のプライマリ メンバーへの接続を失い、最後の操作が失敗した可能性があることを意味します。

このエラーが発生すると、PyMongo は自動的に後続の操作のために新しいプライマリ メンバーを検索しようとします。 エラーを処理するには、アプリケーションは次のいずれかのアクションを実行する必要があります。

  • 失敗した可能性のある操作を再試行する

  • 操作が失敗した可能性を認識しながら実行を続行

重要

PyMongo は、レプリカセットが新しいプライマリ メンバーを選出するまで、すべての操作でAutoReconnectエラーを発生させます。

SSH トンネル経由で MongoDB レプリカセットに接続しようとすると、次のエラーが表示されます。

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

これは、PyMongo が他のレプリカセット ノードのアドレスとポートを含むisMasterコマンドの応答を使用してレプリカセット ノードを検出するために発生します。 ただし、SSH トンネル経由でこれらのアドレスとポートにアクセスすることはできません。

代わりに、SSH トンネルでdirectConnection=Trueオプションを使用することで、単一の MongoDB ノードに直接接続できます。

読み込み設定(read preference)でtag-setsを指定しており、MongoDB が指定されたタグを持つレプリカセット メンバーを見つけられない場合は、このエラーが表示されます。 このエラーを回避するには、タグセット リストの最後に空の辞書( {} )を含めます。 これにより、一致するタグが見つからない場合は、読み取り参照モードに一致するノードから読み取るように PyMongo に指示します。

PyMongo はcount()メソッドのサポートを終了しました。 代わりに、 Collectionクラスのcount_documents()メソッドを使用します。

重要

count_documents()メソッドはCollectionクラスに属します。 Cursor.count_documents()を呼び出しようとすると、PyMongo は次のエラーを発生させます。

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

無効なキーワード引数名を指定すると、ドライバーはこのエラーを発生させます。

指定したキーワード引数が存在し、正しくスペルされていることを確認します。

次のコード例に示すように、ウェブ アプリケーションでは URL でドキュメントの ObjectId をエンコードするのが一般的です。

"/posts/50b3bda58a02fb9a84d8991e"

ウェブ フレームワークは、URL の ObjectId 部分を string としてリクエスト ハンドラーに渡します。 string をfind_one()メソッドに渡す前に、 ObjectIdインスタンスに変換する必要があります。

次のコード例は、 Flask でこの変換を実行する方法を示しています。 アプリケーション。プロセスは他のウェブ フレームワークでも同様です。

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()

常に最初である_idフィールドの後は、BSON ドキュメント内のキーと値のペアは任意の順序にすることができます。 次のコード例のフィールド「b」と「a」に示されているように、 mongo shell は、データの読み取りと書き込み中にキーの順序を保持します。

// 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 } }

PyMongo はデフォルトで BSON ドキュメントを Python 辞書として表現し、辞書内のキーの順序は定義されていません。 Python では、最初に "a" キーで宣言された辞書は、最初に "b" キーを使用して宣言された辞書と同じです。 次の例では、 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}

同様に、Python 辞書では、BSON に保存される順序でキーが表示されない場合があります。 次の例は、前の例に挿入されたドキュメントを印刷した結果を示しています。

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

BSON を読み取るときにキーの順序を保持するには、キーの順序を記憶する辞書であるSONクラスを使用します。

次のコード例は、 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)

前述のサブドキュメントが見つかると、ドライバーはSONオブジェクトを含むクエリ結果を表し、キーの順序を保持します。

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

サブドキュメントの実際のストレージ 配置が表示されるようになりました。"b" は "a" の前にあります。

Because a Python dictionary's key order is not defined, you cannot predict how it will be serialized to BSON. ただし、MongoDB では、キーの順序が同じ場合にのみ、サブドキュメントが と等しいと見なされます。 Python 辞書を使用してサブドキュメントをクエリする場合、一致しない可能性があります。

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

Python は 2 つの辞書を同じと見なすため、クエリ内でキーの順序を入れ替えても、違いはありません。

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

これは 2 つの方法で解決できます。 まず、サブドキュメントを フィールドごとに照合します。

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

クエリは、Python で指定する順序や BSON に保存される順序に関係なく、"a" が1.0 1.0で、かつ "b" を持つ 任意の サブドキュメントと一致します。 このクエリは、「a」と「b」以外の追加のキーを持つサブドキュメントも一致するようになりましたが、以前のクエリでは完全一致が必要でした。

2 つ目の解決策は、 ~bson.son.SONオブジェクトを使用してキーの順序を指定することです。

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

ドライバーは、~bson.son.SON を BSON に直列化し、それをクエリとして使用する場合に、 を作成したときに使用したキーの順序を保持します。 したがって、 コレクション内のサブドキュメントと完全に一致するサブドキュメントを作成できます。

注意

サブドキュメントの一致の詳細については、 ドキュメントの 「埋め込み/ネストされたドキュメントへのクエリ」MongoDB Server ガイドを参照してください。

PyMongo v 3.8またはそれ以前のバージョンでは、 Cursorコンストラクターに無効な引数を指定するとTypeErrorAttributeErrorが発生します。 AttributeErrorは関係ありませんが、 TypeErrorには次の例に示すようにデバッグ情報が含まれています。

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

これを修正するには、適切なキーワード引数を指定していることを確認してください。 また、PyMongo v 3.9以降にアップグレードすることで、関連性のないエラーも排除されます。

MongoDB のカーソルは、サーバー上で操作が実行されずに長時間開いていると、サーバー上でタイムアウトすることがあります。 これにより、カーソルを反復処理しようとすると、 CursorNotFoundの例外が発生する可能性があります。

単一のプロジェクションにフィールドを含め、除外しようとすると、ドライバーはこのメッセージとともにOperationFailureを返します。 プロジェクションが、含めるフィールドまたは除外するフィールドのみを指定していることを確認します。

一意なインデックスに違反する重複値を保存する書込み操作を実行すると、ドライバーはDuplicateKeyExceptionを発生させ、MongoDB は次のようなエラーを返します。

E11000 duplicate key error index

このエラーは、次のコード例に示すように、UUID 表現がUNSPECIFIEDの場合に、ネイティブUUIDオブジェクトをBinaryオブジェクトにエンコードしようとした結果です。

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.

代わりに、次の例に示すように、 Binary.from_uuid()メソッドを使用してネイティブ UUID をBinaryオブジェクトに明示的に変換する必要があります。

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

PyMongo は BSON datetime値を Python のdatetime.datetimeクラスのインスタンスにデコードします。 datetime.datetimeのインスタンスは、 datetime.MINYEAR ( 1 )からdatetime.MAXYEAR ( 9999 )までの年に制限されています。 一部の MongoDB ドライバーは、 datetime.datetimeでサポートされている値より大幅に外側の年値を持つ BSON 日時を保存できます。

この問題を回避する方法がいくつかあります。 PyMongo 4.3以降、 bson.decodeは 4 つの方法のいずれかで BSON datetime値を復号化できます。 変換方法は、 ~bson.codec_options.CodecOptionsdatetime_conversionパラメータを使用して指定できます。

デフォルトの変換オプションは~bson.codec_options.DatetimeConversion.DATETIMEです。これにより、値がdatetime.datetimeとしてデコードされ、範囲外の日付に対して~builtin.OverflowErrorが発生できるようになります。 ~bson.codec_options.DatetimeConversion.DATETIME_AUTOはこの動作を変更して、表現が範囲外の場合には代わりに~bson.datetime_ms.DatetimeMSを返し、以前のように~datetime.datetimeオブジェクトを返します。

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)}

その他のオプションについては、 DatetimeConversion の API ドキュメントを参照してください クラス。

datetime_conversionの設定に関係のないもう 1 つのオプションは、 ~datetime.datetimeでサポートされている範囲外のドキュメント値をフィルタリングで除外することです。

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

datetimeの値が必要ない場合は、そのフィールドのみをフィルタリングで除外できます。

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

次のようなエラー メッセージは、OpenSSL がサーバーの証明書を検証できなかったことを意味します。

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

これは、OpenSSL がシステムのルート証明書にアクセスできないか、証明書が古くなっているために多く発生します。

Linux を使用する場合は、Linux ベンダーから最新のルート証明書の更新がインストールされていることを確認してください。

macOS を使用しており、python.org からダウンロードした Python v 3.7以降を実行している場合は、 ルート証明書をインストールするには、次のコマンドを実行します。

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

Tip

この問題の詳細については、 Python の問題29065 を参照してください。

port-pypy を使用する場合は、OpenSSL にルート証明書を見つける場所を指示するために環境変数を設定する必要がある場合があります。 次のコード例は、 認証モジュール をインストールする方法を示しています から Pypi に接続し、SSL_CERT_FILE 環境変数をエクスポートします。

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

Tip

この問題の詳細については、 port-pypy の問題15 を参照してください。

次のようなエラーメッセージが表示された場合、Python で使用される OpenSSL バージョンでは、サーバーに接続するのに十分な新しい TLS プロトコルがサポートされていないことを意味します。

[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version

業界のベストプラクティスでは、一部の MongoDB の配置で古い TLS プロトコルを無効にすることが推奨されています。また、一部の規則では、古い TLS プロトコルを無効にすることが推奨されています。 一部の配置では TLS 1.0が無効になり、他の配置では TLS 1.0および TLS 1.1が無効になる場合があります。

PyMongo が最新の TLS バージョンを使用するためにアプリケーションを変更する必要はありませんが、一部のオペレーティング システムのバージョンでは、それらをサポートするのに十分な新しい OpenSSL バージョンが提供されていない場合があります。

macOS v 10.12を使用している場合 (High Sierra)またはそれ以前の場合は、 python.org から Python をインストールします。 Homebrew、macports、または同様のソース。

Linux またはその他の macOS Unix を使用している場合は、次のコマンドを使用して OpenSSL バージョンを確認します。

$ openssl version

上記のコマンドで1.0.1未満のバージョン番号が表示されている場合、 TLS 1.1以降のサポートは利用できません。 新しいバージョンにアップグレードするか、OS ベンダーに解決策について問い合わせてください。

Python インタープリタの TLS バージョンを確認するには、 requestsモジュールをインストールして次のコードを実行します。

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

TLS 1.1以降が表示されます。

次のようなエラー メッセージが表示された場合、証明書失効チェックに失敗したことを意味します。

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

詳細については、このガイドの「 OCSP 」セクションを参照してください。

Python v 3.10以降を v 4.0より前のバージョンの MongoDB で使用する場合、 次のメッセージのようなエラーが表示される場合があります。

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)

MongoDB Server ログには、次のエラーも表示される場合があります。

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

Python v で ssl モジュールに加えられた変更3.10は v 4より前の MongoDB バージョンとの互換性がない可能性があります。 0 。 この問題を解決するには、次の手順の 1 つ以上を試してください。

  • Python を v 3.9またはそれ以前のバージョンにダウングレード

  • MongoDB Server を v 4.2以降にアップグレードする

  • OCSPオプションを使用して PyMongo をインストールする(PyOpenSSL に依存する)

このエラーは、クライアントが指定されたタイムアウト内に操作を実行するために利用できるサーバーを見つけられなかったことを示しています。

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>]>

このエラーは、クライアントが指定されたタイムアウト内に接続を確立できなかったこと、または操作が送信されたがサーバーが時間内に応答しなかったことを示しています。

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

このエラーは、指定されたタイムアウトを超えたため、サーバーが操作をキャンセルしたことを示している可能性があります。 PyMongo がこの例外を発生させた場合でも、サーバー上で操作が部分的に完了している可能性があります。

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

また、指定されたタイムアウト内に操作を完了できなかったため、クライアントが操作をキャンセルしたことを示す場合もあります。

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

このエラーは、サーバーが指定されたタイムアウト内に、指定された書込み保証 (write concern) に従って要求された書込み操作を完了できなかったことを示しています。

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

このエラーは、サーバーが指定されたタイムアウト内に、指定された書込み保証 (write concern) に従い、 メソッドまたは メソッドを完了できなかったことを示しています。insert_many()bulk_write()

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': []}

MongoClientインスタンスは、接続されたサーバーの監視などのバックグラウンド タスクを実行するために複数のスレッドを生成します。 これらのスレッドは、 フォークセーフではない クラスのインスタンスによって保護されている状態を共有しますthreading.Lock 。PyMongo には、 threading.Lockクラスまたはミューテックスを使用する他のマルチスレッド コードと同じ制限が適用されます。

これらの制限の 1 つは、 fork()メソッドを呼び出した後にロックが使用できなくなることです。 fork()が実行されると、ドライバーは親プロセスのすべてのロックを、親と同じ状態で子プロセスにコピーします。 親プロセスでロックされている場合は、子プロセスでもロックされます。 fork()によって作成された子プロセスにはスレッドが 1 つしかないため、親プロセスの他のスレッドによって作成されたロックは、子プロセスでは解放されません。 子プロセスが次にこれらのロックのいずれかを取得しようとすると、デッドロックが発生します。

PyMongo バージョン4.3以降では、 os.fork()メソッドを呼び出すと、ドライバーはos.register_at_fork()メソッドを使用して子プロセス内のロックやその他の共有状態をリセットします。 これによりデッドロックの可能性は減りますが、PyMongo は OpenSSL などのマルチスレッド アプリケーションではフォークセーフでないライブラリに依存しています3 と getaddrinfo( )。そのため、デッドロックは引き続き発生する可能性があります。

fork 用の Linux マニュアルページ(2 ) また、次の制限も課されます。

fork()マルチスレッド プログラムで の後、子は非同期シグナルセーフな関数のみを安全に呼び出せるようになります( シグナル安全性(7 ) を参照してください )を、 execVE(2 )を呼び出すまで。

PyMongo は非同期シグナルセーフでない関数に依存しているため、子プロセスで実行するとデッドロックやクラッシュが発生する可能性があります。

Tip

子プロセスにおけるデッドロックの例については、 Python-3406 を参照してください。 Jira の。

Pythonfork()を使用したマルチスレッド6721 Pythonコンテキストでの ロックが発生する問題の詳細については、 の問題 を参照してください を指定します。

← よくある質問