実際のサービス開発中のモンゴルDBレプリケーションによる書き込み直後のリードデータの不一致
実際のサービスを開発してMongoDBを使用して問題に遭遇したことがあります。 Write直後に同じデータをReadするとデータが確認されない現象でした。当時はその内容をよく知らず、時間をかなり使って愛していました。この問題が発生した状況と原因について書きました。
問題の状況
*当時の会社では、主に多くのサービスでMongoDBを使用する環境 *グローバル発売のために新しいサービスを作成し、立ち上げ準備をしていたところ問題点発見。 stageテスト後、 production にサービスを上げると異常動作発生
- 断続的にサーバーに要求されたクエリが失 敗する (Client から呼び出されたサーバークエリで内部的に mongodb write 後、そのデータを再読み込みする動作を実行)
- 正常動作なら write したデータを再読み込みして次の動作を行う必要がありますが、失敗時にはクライアントでエラーに出会うことになる
*クエリは以下のようなフローで行われます
*クライアントコール
- サーバーから aws サービス要求後の結果待機
- aws サービス完了時に mongodb に結果 write
- write 後に再び mongodb からそのデータを読み込んで次の動作を実行する *サーバーでAWSサービスを照会する部分を見てみると、AWSサービスでは正常に動作完了
- サーバーの write 部分を調べるために mongodb を直接入って確認してみると、データが実際に存在した。つまり、write 動作に異常がないことが確認された。 *サーバークエリの読み取り部分が失敗したため、dbには書き込みデータがあります。正確な原因を知らず、修正が難しかった状況
- 既存のテスト環境で点検したときは異常なし *テスト環境はstaging。テスト時には全く発生しなかった現象がproductで発生
解決方法
- 最初は環境差だと認識できず、コード上のバグなので、ロジックを確認。何度も確認しても大きな問題が見つかりません。
- 確認し続けると解決策が見えなくてもっと知っている 同僚に問い合わせて一緒に確認することになる
*仲間の石もよく分からない問題なので解決に多少時間がかかる
*当時分散db関連内 容を少し習得し、問題点を推論してみる。
- 問題認識のため、stage と production で一つ一つどんな違いがあるかを比較。違いを比較してみると、次のような違いがありました。
- stageでは、MongoDBは複写なしで単一ノードで動作します
- production では MongoDB が replication で 2 つの Secondary ノードを保持
- production環境では、可用性のためにレプリカセットが設定されていました。
- replication では production MongoDB は write 後、secondary ノードに該当データを伝播
- 別途の設定がなかったので、 db で write してすぐに読めば secondary にデータが伝播される前に読み込み動作を実行。 secondary は既存のデータを応答値に渡す
- 伝播時間は約2〜4秒程度で確認された。 *後でmongodb compassでデータを確認するときは伝播時間が経過し、読み込み時に書き込みしたデータが正しく見えた *上記問題を解決するために当時浮上した方法は以下の通りである。
- clientでpollingしてdbの状態が変わるのを待ちます。最も簡単なように見えました
- server で polling して db 状態が変わるまで待機する。 clientの立場では、変更なく既存のクエリを使用可能
- mongodbクエリへの伝播が完了することを保証し、読みやすく、クエリやコレクションなどに設定 *上記の方法の2番目の方法を試してみました。 write 後 read 動作が直ちに発生するのではなく、write 後 read まで delay を少し入れてズーム
- delay を追加してから正常に動作することを確認
- これにより、CAP理論についてもう少し知りました。
- 分散型構造を持つ場合、一貫性(Consistency)、可用性(Availability)、分散許容(Partitioning Tolerance)という3つの特徴を持つ
- **一貫性と可用性の1つを満たすには、他の1つはあきらめるしかありません。
- 一貫性を満たす場合 : 分散ノードのどれがどのノードにアクセスしても、データ値は同じでなければならない。一貫性が壊れた場合は、クエリを要求したときに他のデータを渡すことができます
- 可用性を満たす場合:分散ノードの1つ以上のノードが同期に失敗した場合でも正常に要求を処理する機能を提供する。 *現在のdb構造は可用性を満足しており、一貫性(Consistency)が壊れるケース
クリーンアップ
- db が複写設定になっている場合は、write と read 時点が異なるときに注意しよう
- CAP理論を直接目で見て感じることができ、良い経験だった。 *私が開発しているサービスについて時間がかかるたびに勉強をしてみよう。すべてを知ることはできませんが、多くを知るほどトラブルシューティングに役立ちます。
- stageとproductionの環境が異なる可能性があることを認識しましょう。つまり、テスト環境と実際の運用環境との間に違いがあると問題が生じる可能性があり、これを事前に考えてみるとより良いようだ。
- デバッグ時間を短縮するためには production と stage 環境をほぼ同じか同じにしておくのも良いようです。 *時間もコストです。人件費、サービスオープン遅延など多くのものが遅延したり、さらに消費されることがある。サーバーコストと比較して大きすぎるコストでなければ、stage と production の設定を同じにしても大丈夫だと思われた。