ステートレス
プロセスが内部に状態を持たず、リクエストごとに独立して処理する設計原則
ステートレスとは
ステートレスは、プロセスが内部に状態を持たず、リクエストごとに独立して処理する設計原則である。Twelve-Factor App の第 6 原則「プロセス」が、アプリを 1 つ以上のステートレスかつ share-nothing なプロセスとして実行し、永続させる必要のあるデータはデータベースなどのバッキングサービスへ置くことを求めている。原典が禁じているのは「次のリクエストも同じプロセスに届く」という前提で、メモリやファイルシステムは 1 回の処理の中だけの一時置き場として扱う。プロセスが 1 つしかなくても、デプロイや設定変更、実行環境の移動による再起動でローカルの状態は消える。この前提を守ることが、インスタンスを自由に入れ替えられる状態、つまり水平スケーリングと耐障害性の土台になる。
ステートフル vs ステートレス
同じ機能を実装しても、状態をプロセスの内側に置くか外側に置くかで、スケールと運用の性質が変わる。
| 観点 | ステートフル | ステートレス |
|---|---|---|
| 状態の保持 | プロセス内 | 外部ストレージ |
| スケーリング | 困難 (状態の同期が必要) | 容易 (インスタンスを追加するだけ) |
| 障害復旧 | 処理中の状態が失われる | 別のインスタンスが同じリクエストを引き継げる |
| テスト | 状態に依存して複雑 | 入出力だけでテスト |
状態の保存先
状態を外へ出すときは、失われたときに何が壊れるかで置き場所を決める。作り直せる状態はキャッシュ層で足り、失うと業務が壊れる状態は永続層へ置く。セッションのように期限で消えてよいものは、自動失効を持つ保存先を選ぶ。
| 状態 | 保存先 |
|---|---|
| セッション | DynamoDB |
| キャッシュ | ElastiCache (Redis) |
| ファイル | S3 |
| 一時データ | Lambda /tmp (実行環境ごとに独立・512 MB から 10,240 MB で設定) |
| ジョブの進捗 | Step Functions |
Lambda のステートレス性
Lambda の実行環境は、処理中に別のリクエストを受け付けない。同時に届いた数だけ環境が並ぶため、どの環境が処理しても結果が変わらない設計にしておけば、そのまま同時実行数を増やせる。
リクエスト 1 → Lambda インスタンス A → DynamoDB
リクエスト 2 → Lambda インスタンス B → DynamoDB
リクエスト 3 → Lambda インスタンス C → DynamoDB
→ どのインスタンスが処理しても同じ結果
→ インスタンスが破棄されてもデータは DynamoDB に残る
ただし実行環境は破棄されるとは限らない。保留イベントが無くなった時点で凍結され、次の呼び出しで解凍して再利用される。ハンドラーの外で初期化した DB 接続や設定はそのまま引き継がれ、これは接続確立のコストを節約する仕組みとして使える。逆に、ユーザー固有の値をグローバル変数へ置くと後続の呼び出しから見えてしまう。/tmp も同じ環境が生きている間は前回の書き込みが残る。ステートレスは基盤が保証する性質ではなく、書き手が保つ性質である。
ステートレスのメリット
インスタンスが状態を持たないため、水平スケーリングでインスタンスを自由に増減できる。1 つのインスタンスが壊れても処理中のリクエストの再試行で済み、テストも入出力だけで完結する。デプロイ時に新旧インスタンスが共存できるので、段階的な切り替えも取りやすい。
外部化でつまずく点
最初に踏むのがスティッキーセッションである。ロードバランサーで同じ訪問者を同じインスタンスへ固定すればメモリ上のセッションが当面動くが、原典はこれを第 6 原則の違反と明記している。スケールインとデプロイのたびにログイン状態が切れ、負荷も偏る。
次に来るのが外部化した状態の読み出しコストである。リクエストごとにセッションを取りに行くため往復が増える。署名付きトークンを利用者側に持たせれば読み出しは消えるが、失効させたいときに取り消す場所が手元に無くなる。有効期限を短くして再発行で回すか、取り消し一覧だけを保存先に残す形が落としどころになる。
ステートレスと冪等性を同じものとして扱う混同にも注意する。プロセスが状態を持たないことと、同じリクエストが 2 回届いても結果が壊れないことは別の性質で、後者は処理する側で識別子ごとの重複排除を用意しないと成り立たない。ステートレス化して再試行が増えるほど、この差が表に出る。
どこまで外へ出すか
判断の順序は、まず対象の状態を失ったときに何が壊れるかを列挙し、作り直せるならキャッシュ層、失えないなら永続層と失効設計へ振り分ける。WebSocket の接続や長時間実行のジョブのように状態を持たざるを得ない面は、状態を抱える範囲を接続管理やワークフローの層に閉じ込め、その外側をステートレスに保つ。全面ステートレスを目標にするのではなく、状態を持つ場所の数を数えられるところまで減らすのが実務の目安になる。
この記事は役に立ちましたか?
関連用語
水平スケーリング
サーバーの台数を増やして処理能力を向上させるスケーリング手法
Lambda
AWS のサーバーレスコンピューティングサービスで、コードをイベント駆動で実行する
プロセスタイプ
Twelve-Factor App のプロセスモデルで、アプリケーションの実行形態を分類する概念
シェアードナッシング
各ノードが独立したリソースを持ち、共有状態を排除するアーキテクチャ
ステートマシン
有限個の状態と遷移で振る舞いを定義する計算モデルで、ワークフローや UI の状態管理に使う
Aurora
AWS のクラウドネイティブ RDB で、MySQL/PostgreSQL 互換で高可用性と高パフォーマンスを実現する