MongoDB 8.2 以降では、チャンクの移行によりドキュメントが欠落するリスクがある場合、シャーディングされたクラスターのセカンダリ読み取りが自動的に終了することがあります。
この新しい動作をサポートするために、 MongoDB 8.2 では次の変更が導入されています。
terminateSecondaryReadsOnOrphanCleanupパラメータを追加しました(デフォルト:true)注意
terminateSecondaryReadsOnOrphanCleanupがfalseに設定されている場合、サーバーは読み取りを終了せず、チャンクの移行によってシャーディングされたコレクション内のドキュメントが失われる可能性があります。これはMongoDB 8.1 またはそれ以前のバージョンのデフォルトの動作です。詳細については、「セカンダリ読み取りの終了を無効にする」を参照してください。orphanCleanupDelaySecsのデフォルト値を900秒から3600秒(1 時間)に増加させます
動作
デフォルトでは 、シャーディングされたクラスターは、チャンク移行がコミットされるときに次の操作を実行します。
ソース シャードは、別のシャードに移行されたドキュメントを削除するために 孤立したクリーンアップ プロセスを開始します。
シャードは、プライマリに存在する既存の読み取りが完了するのを待機します。
シャードは追加の
orphanCleanupDelaySecs秒(デフォルト: 1 時間)待機します。シャードは 孤立したドキュメント を削除します。
セカンダリ は、移行が完了する前に開始された読み取りを終了します。
セカンダリ は孤立したドキュメントの削除 を複製します。
孤立したドキュメントを削除する前にセカンダリ読み取りを終了することで、長時間実行されているセカンダリ読み取りでも、クリーンアップ プロセスによって削除されたドキュメントを見逃しません。
モニタリング
孤立したクリーンアップによって終了したセカンダリ読み取りは、次の方法で監視できます。
Check the server status of your secondary node with the following
mongoshcommand:db.serverStatus().metrics.operation.killedDueToRangeDeletionmongodログを確認します。終了のたびに、次の例のようなログエントリが作成されます。
{ "t": { "$date": "2025-06-11T12:11:43.361+02:00" }, "s": "I", "c": "SHARDING", "id": 10016300, "svc": "S", "ctx": "conn93", "msg": "Read has been terminated due to orphan range cleanup", "attr": { "type": "command", ... "workingMillis": 0, "durationMillis": 0, "orphanCleanupDelaySecs": 3600 } }
長時間実行セカンダリ読み取りの管理
アプリケーションが、チャンク移行を実行するシャーディングされたクラスターで 1 時間を超えるセカンダリ読み取りを実行すると、読み取りの終了によって QueryPlanKilled エラー(エラー コード 175)が発生する可能性があります。
長時間実行されているセカンダリ読み取りを管理するための推奨方法は、アプリケーションに再開メカニズムを実装することです。
また、次の代替戦略を使用して、実行時間が長いセカンダリ読み取りを管理することもできます。
再開メカニズムの実装
再開メカニズムを使用すると、アプリケーションは新しい読み取り操作を作成し、前の読み取り操作が終了した場所から開始できます。
効果的な再開メカニズムを実装するには、アプリケーションでクエリ結果に対して一貫したソート順序を使用する必要があります。再開メカニズムでソート順序を選択する際には、次の要素を考慮してください。
クエリを効率的に実行するために、ソート操作ではインデックス付きフィールドを利用する必要があります。
ソートフィールドには一意の値を含める必要があります。
- ソートフィールドの値が一意でない場合、アプリケーションは、同じソート値を共有するドキュメントを処理するための追加のロジックを実装する必要があります。
例
次の構造を持つ zipcodesコレクションを含む citiesデータベースを考えてみましょう。
{ "state": "NY", "city": "NEW YORK", "zipcode": "00501" }
この例では 、zipcodeフィールド値は一意であると仮定しています。
次のJavaScriptコードでは、セカンダリ読み取り操作を実行して、state が NY であるすべてのドキュメントを取得し、QueryPlanKilled エラーを処理するための再開メカニズムを実装します。
let readDoc; let latestZip; let cursor = db.getSiblingDB("cities").zipcodes.find({ state: "NY" }) .sort({zipcode: 1}) .readPref("secondary"); while(cursor.hasNext()) { try { readDoc = cursor.next(); // process `readDoc` here latestZip = readDoc.zipcode; } catch (err) { if (err.code === 175 && err.errmsg.includes("Read has been terminated due to orphan range cleanup")) { console.log("Query terminated, resuming from zipcode:", latestZip); cursor = db.getSiblingDB("cities").zipcodes.find({ state: "NY", zipcode: {$gt: latestZip} }) .sort({zipcode: 1}) .readPref("secondary"); } else { throw err; // Rethrow non-termination errors } } }
例データベースとアプリケーションロジックを確認する際には、次の点を考慮してください。
この例コードでは、
zipcodeでソートする再開メカニズムを使用してQueryPlanKilledエラーを処理します。zipcodeフィールドでソートすると、各ドキュメントに対して一貫した順序と一意のソート値が確保されます。これにより、アプリケーションは読み取り操作が終了した場所で正確に再開できるようになります。cities.zipcodesコレクションには、再開メカニズム クエリの効率を確保するために{state: 1, zipcode: 1}複合インデックスが実装されています。この複合インデックスを実装すると、コレクションスキャンとメモリ内ソートの両方が防止され、フィルター操作とソート操作がサポートされます。効果的なインデックスの作成の詳細については、ESR(Equality、Sort、Range)ガイドラインを参照してください。QueryPlanKilledエラー(エラー コード175)は、セカンダリ読み取りが終了した以外の理由で発生する可能性があります。QueryPlanKilledエラーを正確に処理するには、errmsgフィールドを解析する必要があります。MongoDB は、セカンダリ読み取りを終了するときに次のエラー メッセージを返します。
{ code: 175, name: QueryPlanKilled, categories: [CursorInvalidatedError], errmsg: "Read has been terminated due to orphan range cleanup" }
- アプリケーションで孤立した範囲のクリーンアップが原因で
QueryPlanKilledエラーが発生した場合、再開されたクエリの開始点として最後に正常に処理された郵便番号を使用します。$gt演算子により、アプリケーションは重複するドキュメントを処理しないようになります。
Test your resume mechanisms in a test environment and monitor your production cluster to understand how often secondary reads are terminated. If terminations occur frequently, you might need to adjust your query patterns, or consider alternative data access approaches. To learn how to monitor your cluster for these errors, see Monitoring.
orfhanCreanupDelaySecs を増やす
orphanCleanupDelaySecsサーバーパラメータは、移行された チャンク をソース シャードから削除するまでにMongoDB が待機する時間を制御します。
orphanCleanupDelaySecs を増やすと、セカンダリ読み取り操作がより長時間実行できるようになります。orphanCleanupDelaySecs は、スタートアップと実行時に設定できます。
次のコマンドは、orphanCleanupDelaySecs を 2 時間に設定します。
db.adminCommand({ setParameter: 1, orphanCleanupDelaySecs: 7200 })
重要
orphanCleanupDelaySecs を増加させると、孤立したドキュメントがノード上に一定期間残ります。この値を増やすと、インデックスを使用するがシャードキーを含まないクエリを実行すると、クエリが結果を返す前に孤立したドキュメントをフィルタリングする必要があるため、パフォーマンスが低下する可能性があります。
セカンダリ読み取りの終了を無効化
注意
MongoDB 8.1 またはそれ以前のバージョンでは、シャーディングされたクラスターは長時間実行されているセカンダリ読み取りを自動的に終了しません。MongoDB 8.2 以降でこの動作を実現するには、セカンダリ読み取りの終了を無効にします。
terminateSecondaryReadsOnOrphanCleanupサーバーパラメータは、孤立したドキュメントが削除される前に、長時間実行されているセカンダリ読み取りを自動的に終了するかどうかを制御します。
terminateSecondaryReadsOnOrphanCleanup を false に設定することで、セカンダリ読み取りの終了を無効にすることができます。このパラメーターは、スタートアップ時に設定できます。
次のコマンドは、terminateSecondaryReadsOnOrphanCleanup を false に設定します。
db.adminCommand({ setParameter: 1, terminateSecondaryReadsOnOrphanCleanup: false })
警告
この機能が無効になっており、チャンクの移行が ターゲットコレクションに影響する場合、セカンダリ読み取りがすべてのドキュメントを返さない可能性があります。
バランサーを無効にする
バランサーを無効にして手動移行を実行しないことで、実行時間の長いセカンダリ読み取りが自動的に終了しないようにできます。
特定のコレクションのバランサーを無効にするには、configureCollectionBalancing コマンドの enableBalancingフィールドを使用します。
バランサー操作を特定の時間に制限するには、バランシング ウィンドウのスケジュールを参照してください。
警告
バランサーを長期間無効にすると、シャードのバランスが取れず、クラスターのパフォーマンスが低下する可能性があります。ユースケースで必要な場合のみ、バランサーを無効にします。