TL;DR: クイックフィックス
このエラーは、プライマリ(Primary)ノードへの書き込みは成功したものの、セカンダリ(Secondary)ノードが十分な速さで保存を完了できなかったことを意味します。他のノードからの「受信完了」が戻る前に、タイマー(wtimeout)が終了してしまいました。
まず以下の手順を試してください:
- ノードの状態を確認する: シェルで
rs.status()を実行します。すべてのノードがPRIMARYまたはSECONDARY状態でなければなりません。RECOVERING状態で停止しているノードは、書き込みの確認を返しません。 - タイムアウトを増やす: アプリケーションで
wtimeout: 1000(1秒)を使用している場合は、5000に増やしてみてください。トラフィックが急増した際、ネットワーク全体にデータを伝播させるには1秒以上かかることがよくあります。 - マジョリティ(過半数)を確認する: 3ノード構成で
w: "majority"を使用している場合、少なくとも2つの健全なデータ保持ノードがオンラインである必要があります。
なぜこのエラーが発生するのか
レプリカセットを使用している場合、MongoDB は単に「書き込んで終わり」ではありません。通常、データが複数のマシンで安全に保持されているという保証が必要です。majority のような書き込み確認(write concern)を指定すると、プライマリはセカンダリがローカルの oplog にデータを取り込み、確認(ACK)を返すのを待ちます。
「Write concern specified was not met」というメッセージが表示された場合、プライマリへの書き込み自体は成功していますが、クラスターが制限時間内に安全要件を満たせなかったことを意味します。主な原因は以下の通りです:
- 深刻なレプリケーション遅延: セカンダリのディスク I/O 待機率が高い(多くの場合 10% 以上)と、処理が遅れます。わずか2秒の遅延でも、1秒の
wtimeoutをトリガーします。 - ネットワークの不安定(フラッピング): データセンター間の一時的なマイクロアウトージにより、ノードが技術的に「稼働中」であっても確認パケットの到着が遅れることがあります。
- リソースの競合: セカンダリでのバックグラウンドのインデックス作成や重い集計クエリが、レプリケーションプロセスの CPU サイクルを奪うことがあります。
ステップバイステップのトラブルシューティング
1. クラスターの健全性を監査する
まずは「ゾンビ」ノードの特定から始めましょう。Mongo シェルに入り、ハートビートを確認します:
rs.status()
出力の中から health: 0 や STARTUP2 のような状態を探します。ノードが欠けていると、書き込み確認のロジックから見えなくなります。よくある原因は、ディスク容量不足で同期が完全に停止したノードです。
2. 遅延を数値化する
「健全」に見えるノードでも、動作が遅い場合があります。セカンダリがプライマリから何秒遅れているかを正確に確認してください:
rs.printSecondaryReplicationInfo()
5 secs behind the primary と表示されているのにアプリケーションコードで wtimeout: 2000 を使用している場合、そこがボトルネックです。より高速な NVMe ストレージを導入するか、タイムアウト設定を緩和する必要があります。
3. アプリケーションコードの調整
多くの場合、クラスターの物理的な状態は問題ありませんが、アプリケーションがせっかちすぎることがあります。高負荷時にはレプリケーションに時間がかかるのは自然なことです。ドライバーの書き込み確認設定を調整して、クラスターに余裕を持たせましょう。
Node.js (Mongoose):
// wtimeout をデフォルトから 5 秒に増やす
await User.create([{ name: 'Alice' }], {
writeConcern: {
w: 'majority',
wtimeout: 5000
}
});
Python (PyMongo):
from pymongo import WriteConcern
# 特定の操作に 5 秒のタイムアウトを適用する
coll.with_options(
write_concern=WriteConcern(w='majority', wtimeout=5000)
).insert_one({'status': 'active'})
4. ノードの投票権を確認する
rs.conf() を実行し、votes フィールドを確認します。5つのノードがあっても、そのうち3つが votes: 0 に設定されている場合、「マジョリティ」の計算が予期しない挙動を示すことがあります。書き込み確認を期待するすべてのデータ保持ノードに、実際に投票権があることを確認してください。
解決策のテスト
修正が機能することを期待するだけでなく、本番と同じ安全設定を使用してシェルでテストしてください:
db.heartbeat.insertOne(
{ ts: new Date() },
{ writeConcern: { w: "majority", wtimeout: 10000 } }
)
これが即座に acknowledged: true を返せば、クラスターのパフォーマンスは良好です。完了までに数秒かかる場合は、ハードウェアやネットワークの混雑など、根本的な問題に対処する必要があります。

