読者です 読者をやめる 読者になる 読者になる

Tomcat + DynamoDBでSesison管理を任せてみる

JavaTomcatなWebアプリケーションを作っている時に、Sessionの情報をどう管理しようか、というのが問題になります。
認証済み状態であることをSession上に設定しておき、認証されていないリクエストが来た時にログイン画面に遷移させる、というアプリケーションは多いと思います。
Tomcatのメモリ上にSession情報を配置するのはいいけど、もし、そのTomcatが死んでしまった時、Session情報も無くなってしまいます。
別のTomcatインスタンスにリクエストが飛ばされた時でもユーザに影響が無いようにSession情報を引き継ぎたい、という要望が少なくありません。


イマドキのシステムならDynamoDBで管理なのでしょうか。AWSもそんな事言ってます。

Manage Tomcat Session State with DynamoDB - AWS SDK for Java

少し前の記事ですが、クラスメソッドさんでも取り上げてます。
Amazon DynamoDBによるTomcatセッション永続化とフェイルオーバー | Developers.IO


確かにDynamoDBならEC2インスタンスを使っていなくてもアクセスできるので幅広いシステムに適応できそうです。
また、Tomcatの設定ファイルを変更するだけで、アプリケーション側では特に意識することは無さそうです。

ということで導入してみようと思ったのですが、思いの外ハマったので備忘録を残しておきます。

タイムアウトでないSessionが多数存在する場合、Writeのしきい値を軽く超える

数十ユーザがログイン状態なだけで、DynamoDBに関するエラーログが見られるようになりました。どうも書き込み上限を超えてしまった模様。えっ、早くね?

明らかに有効なSessionのはずなのに存在しないと判断される

また、最後にアクセスしてから明らかにSessionタイムアウトの時間を超えていないにも関わらずログイン画面へ強制遷移されることも発生しました。
DynamoDBと連携するようにしてからです。連携しないようにすると問題は全く起きません。




こんな使い物にならないものをAmazonさんが使えというわけもない、設定方法が悪いんだろうということでいろいろ調査をしてみることにしました。


maxIdleBackupの値を見直す

maxIdleBackupの値を設定しないようにしていました。この期間を超えたものはSessionが最後にアクセスされてからバックアップされる時間です。

今回のSession管理の方針としては、

ということにして、Tomcatが停止した時のSession情報の退避先としてDynamoDBを使用することにしているので、ぶっちゃけた話、稼働している間はDynamoDBに更新する必要はありません。
ということで、maxIdleBackupの値をSessionタイムアウトの時間に設定して、Tomcatが停止しない限りDynamoDBに書き込まないようにしました。

これでめでたしめでたし…とはいかなかった

上記設定を行うことで、

あるTomcatが停止→DynamoDBに書かれる→ロードバランサーが別のTomcatインスタンスにアクセス→メモリ上にSessionが無いのでDynamoDBに問い合わせ、メモリ上に展開→別Tomcatに振り分けられても問題なくアクセス可能

という当初の図式が完成しました。良かった良かった。

ですが、しばらくアクセスしていると、まだ明らかに有効なSessionのはずなのに存在しないと判断されてしまいます。これはおかしい。

設定上だけの問題ではないということで、ライブラリを見てみることにしました。問題は2つ。

一度DynamoDBにputされてしまうとDynamoDBの値でメモリ上のSessionが書き換えられてしまう

DynamoDBにputされたSessionに対して、値の同期をとる処理が走るが、DynamoDBの値でメモリ上のSessionを書き換える処理も走ってしまう。

1台のTomcatでSession情報をDynamoDBに保存する設定をして、簡単なJSPを使って実験してみました。
(AmazonDynamoDBSessionManagerForTomcat-1.0.1.jarを使用しています)

f:id:nemuzuka:20141223145639p:plain




更新する度にカウンタが上がります。
f:id:nemuzuka:20141223145650p:plain
ですが、しばらく繰り返していると…



f:id:nemuzuka:20141223145704p:plain
!?


カウンタが巻き戻っています。これはいけません。
ライブラリが一定時間アクセスされていないSessionをDynamoDBにputする処理の他に、定期的にDynamoDBに登録された情報をSessionに置き換えているようです。
今回のSessionの管理方針としては、Sessionの最新データはTomcat側で持つ、ですからこの処理は不要です。

DynamoDBからgetしてきた時のSessionの最終更新日時の値が不正

DynamoDBの値を元に生成したSessionの情報の最終更新日時の初期値が、「Sessionの生成日時」になっていました。
これだと、別のTomcatに振り分けられた時に生成日時からの経過時間がSessionタイムアウトの時間を超えた場合にSessionタイムアウトと判定されてしまいます。これもマズイ。DynamoDBに設定するのを、Sessionの最終更新日時に変更しました。



というわけで、本当にこれを運用しているところがあるのか?という疑問を持ちながらですが、TomcatのSession管理の仕組みを構築することができました。
ソースも公開しておきますが、根本的に使い方が間違っているとか無いですかね?心配になってきました。
悪いことは言わないからElastiCache使え、ってことでしょうか。
(そもそもTomcatを使うなんて…ということでしょうか)
他所様のシステムの設定情報を拝見したいものです。


どちらにせよ、有効なSessionデータが多数存在する時にTomcatが停止する際に、書き込み上限を超える可能性、というのはあるのですが
DynamoDBにおけるスループット超過対策 〜 Fallback-Queueingパターン | Developers.IO
を組み込んでおけばいいのかな、とも思います。*1

同じようなことで時間を取られている方の助けになれば幸いです。

*1:本当にクラスメソッドさんにはお世話になっております