リクエストごとに、問題の概要と解決方法を以下に示します。
私たちのシステムでは、カスタムのドキュメントロックルーチン(redis-lockを使用)を作成しました。このルーチンでは、次のことがこの正確な(誤った)順序で発生しました。
操作の順序が正しくありません:
- クライアントリクエストを受信しました
- ドキュメントがロックされています
- 取得したドキュメント
- 編集されたドキュメント
- ドキュメントのロックが解除されました
- クライアントリクエストが解決されました
- 保存されたドキュメント
書き出されているのを見ると、問題は明らかです。ドキュメントをドキュメントロックの外に保存していました。
#6がシステムで100msかかると仮定しましょう。これは100ミリ秒のウィンドウであり、他のリクエストが同じドキュメントを取得すると、保存の競合が発生します(この質問のタイトル付きエラーは基本的に保存の競合IMHOです)。
言い換えると、例:私たちのシステムでは、リクエストAがドキュメントXのバージョン1を取得し、編集してからロックを解除しましたが、リクエストAがドキュメントを保存する前に、リクエストBがドキュメントXを取得してバージョン2にインクリメントしました(Mongoで確認してください)。詳細については、バージョンを参照してください)。次に、リクエストAはクライアントリクエストを解決してドキュメントXを保存しようとしますが、バージョン1を保存しようとしているため、バージョン2であることがわかり、上記のエラーが発生します。
したがって、修正は簡単です。ドキュメントをロック内に保存します。 (上記の例では、#7を#5の前に移動します。以下を参照してください。)
正しい/固定された操作の順序
- クライアントリクエストを受信しました
- ドキュメントがロックされています
- 取得したドキュメント
- 編集されたドキュメント
- 保存されたドキュメント
- ドキュメントのロックが解除されました
- クライアントリクエストが解決されました
(#6と#7を交換する必要があると主張することもできますが、それはMongo / Mongoose /この質問の範囲外です。)
私はこの質問にしばらく答えないままにし、関連するコードを分離してこの問題をトラブルシューティングするためのより良い方法に誰かが光を当てることができるかどうかを確認します。私たちの場合、これは非常に体系的な問題であり、当時のスキルレベルのトラブルシューティングは非常に困難でした。