【要約】「1プロセス前提」はコードのどこにも書いていない ― 個人開発アプリを水平スケールさせたら9箇所壊れた話 [Zenn_Python] | Summary by TechDistill
> Source: Zenn_Python
Execute Primary Source
// Problem
開発者が、1台構成で運用していた個人開発アプリを、ロードバランサ配下の複数インスタンス構成へ拡張しようとした際、メモリ上の状態に依存していた箇所が動作不全に陥った。具体的には、以下の問題に直面した。
- ・メモリ上のdictに保持したデータが、別インスタンスからのリクエストで参照できない。
- ・既存のユニットテストは単一プロセスで実行されるため、並行性の問題を検知できなかった。
- ・エラーを出さずに処理がスキップされるなど、デバッグが困難な「静かな失敗」が発生した。
// Approach
開発者は、プロセスが再起動すると消失する「状態」をすべて棚卸しし、状態の所在をプロセス内から外部(DB)へ移すアプローチを採用した。解決策は以下の4つの型に集約される。
1.状態のDB移行とCAS(Compare-and-Swap)による条件付き更新。
2.TTL(生存期間)付きのClaimによる、期限付きの所有権管理。
3.pg_notify / LISTENを用いた、設定変更の全プロセスへの通知。
4.PostgreSQLのadvisory lockによる、起動時の処理の直列化。
// Result
開発者は、水平スケールに伴う9箇所の不整合を解消し、スケーラブルなシステム基盤を構築できた。具体的な成果は以下の通りである。
- ・メモリ依存のバグを特定し、DBを用いた整合性確保を実現した。
- ・testcontainersを活用し、実DBを用いた並行性を検証する統合テストを導入した。
- ・Fencing問題などの既知の制限を明文化し、運用の透明性を向上させた。
Senior Engineer Insight
> スケーラビリティを追求する際、エンジニアが必ず直面する「暗黙の1プロセス前提」を鋭く突いた良記事だ。特に、ユニットテストが並行性の問題を検知できないという指摘は、大規模システム開発において極めて重要である。解決策として提示されたCASやTTL付きClaimは、分散システムにおける定石であり、実装の複雑性と整合性のトレードオフを正しく理解している。実戦投入時は、DBの負荷増大とロック名の衝突回避、およびFencing問題への考慮に細心の注意を払うべきである。