PHPでWEBサイトを構築していれば
当たり前のように使うのがセッション管理の仕組みです。
PHPセッションを管理する方法としては、3種類あります。
- ファイル(デフォルト)
- データベース
- KVS
昔は1か2が主流でしたが、
最近では、KVS(Redisなど)が当たり前に使われるようになってきましたよね。
AWSでRedisが標準サポートされたのが大きいですね。
大規模サイトの場合、サーバを冗長化するのが一般的です。
その場合、どのサーバからアクセスしてもセッションの一意性を担保したり、
DBかKVSでセッションを共有するのが最もシンプルです。
2016年くらいに私が構築に関わった
とある大規模サイトがあります。
そのサイトは、年間売上100億級なので
パフォーマンス命です。
インフラ環境はAWSだったのですが、
当時のシステム制約上、KVSが気軽に利用できる状況ではありませんでした。
とはいえ、セッション管理がDBだと過剰負荷となり、
パフォーマンス的によろしくないです。
ということで、選択したのはファイル管理です。
前置きが長くてすみません(><);
ここからが本題です(笑)
このシステムでは、セッション読み込み時に
独自のカスタマイズが必要でした。
PHPでセッション読み込みの内容をカスタマイズする場合
「session_set_save_handler」関数を使います。
読み込み処理の場合、例えば下記のような自作ハンドラクラスを作成します。
その他、書き込みやクローズ処理なども定義します。
class MySession
{
function __construct()
{
// セッションハンドラ関数
session_set_save_handler(
array(&$this, 'open'),
array(&$this, 'close'),
array(&$this, 'read'),
array(&$this, 'write'),
array(&$this, 'destroy'),
array(&$this, 'gc')
);
}
// セッション読み込み処理
public function read($id)
{
// カスタム処理
// ~~~~
// セッション読み込み処理
return (string)@file_get_contents("$this->savePath/sess_$id");
}
// セッション書き込み処理
public function write($id, $sess_data)
{
return file_put_contents("$this->savePath/sess_$id", $sess_data) === false ? false : true;
}
// 以下略
}
このコードでも一応動いていたのですが、
ある時問題が発生しました。
セッションに保存していたデータを削除したはずなのに、
なぜか突然復活してしまうという悲劇が起こりました><;
実際の案件では、ECサイトのカートで削除したはずの商品が
20%くらいの確率で突然カートに復活する現象でした。
これは、普通にデフォルトのPHPセッションの仕組みを使っていれば発生しない現象です。
というのは、PHPセッションではファイルの排他ロックの仕組みが備わっています。
PHPは何でも気軽に実装できてしまう分、
こういった細かい部分をあまり意識しなくなりがちなデメリットもあると感じます。
よく考えたら、排他ロックしてないと想定外の書き込みに対応できなくなりますよね...
結局、SessionHandlerクラスを拡張すれば
簡単にデフォルトのセッション機構の仕組みを呼び出せることが分かったので、
ロジックを書き換えました。
class MySession extends SessionHandler
{
function __construct()
{
// セッションハンドラ関数
session_set_save_handler($this, true);
}
// セッション読み込み処理
public function read($id)
{
// カスタム処理
// ~~~~
// セッション読み込み処理
return parent::read($id);
}
// 他はデフォルトの仕組みを使うので定義しない
}
何事にも通じることですが、
デフォルトの仕組みをきちんと使うのが
間違いがないと肝に銘じた事例でした。
まぁ、今時のシステムでパフォーマンス命の場合、
KVS一択でしょうから、何の参考にもならないと思いますが、
こういう奇妙なこともあったというネタでした(笑)