これは何
Laravel v5.6で、DBのキャッシュとSessionを保存するキャッシュを分離したい。
分離することで、DBのテーブル構造が変わってキャッシュの不整合が起こった時に躊躇なくDBキャッシュを揮発させることができる。分離してあればSessionキャッシュはそのままなのでユーザはログアウトされない。
キャッシュの分離はLaravelのキャッシュ周りの設定を変えれば良いのだが、環境変数名がわかりづらかったりしてハマりどころがあるので記録しておく。
環境
- Laravel 5.6
- PHP 7.1
- Memcached 1.5.11
分離されていない状態の設定
config/cache.php においてもともと以下のようになっているとする。
'default' => env('CACHE_DRIVER', 'file'),
'stores' => [
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', 'memcached'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
この設定はストア名が'memcached'で、かつ driver名が'memcached'で同名になっている。
環境変数 CACHE_DRIVER に'memcached'を設定すると”ストア名”の 'memcached'が使用される。変数名が CACHE_DRIVER であるのだが、指定されるのはストアの方なので理解に苦しむ(初めてlaravel/laravelにIssueを立ててコミッタに聞いてみたい)。
また、セッションの方の設定ファイル config/session.php は
'driver' => env('SESSION_DRIVER', 'file'),
中略
'store' => 'null',
となっており、storeはデフォルトではnullが入っている。
分離するには
分離をするためには以下のように設定をすれば良い
【1】 config/cache.php に以下のように複数のStore設定を書く
'stores' => [
'db_cache_memcached' => [
'driver' => 'memcached',
'persistent_id' => env('DB_CACHE_PERSISTENT_ID'),
'sasl' => [
env('DB_CACHE_MEMCACHED_USERNAME'),
env('DB_CACHE_MEMCACHED_PASSWORD'),
],
'servers' => [
[
'host' => env('DB_CACHE_MEMCACHED_HOST', 'memcached'),
'port' => env('DB_CACHE_MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'session_memcached' => [
'driver' => 'memcached',
'persistent_id' => env('SESSION_CACHE_PERSISTENT_ID'),
'sasl' => [
env('SESSION_MEMCACHED_USERNAME'),
env('SESSION_MEMCACHED_PASSWORD'),
],
'servers' => [
[
'host' => env('SESSION_MEMCACHED_HOST', 'memcached'),
'port' => env('SESSION_MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
【2】 config/session.php の store 設定を追加する
config/session.php の 'store'がnullの場合には SESSION_DRIVER 設定は config/cache.php にある store にあるものを探しに行く。
'store'が非nullであるなら対応するストア名をconfig/cache.phpへ探しに行く。
これについては補足のセクションでコードの動きを軽く書いておきます
'store' => 'session_memcached',
【3】 本番リリースする時には環境変数は忘れずに入れておく
補足
config/session.php の store のあるなしで動きが変わるのはなんで?
この設定がどこのコードで読み込まれているか? と言う話ですね。
'store' => 'session_memcached',
Illuminate/Session/SessionManager.php において以下のようなメソッドがある。(ここへどうやってたどり着くかと言うのは正直まだあまりわかっていませんが、一般的には HogeManager.php が設定と動きを管理していると言うデザインパターンっぽい知識を使ってここを起点に読んでいきます。詳細にわかったらまた補足で記事を書きたいと思います)
protected function createMemcachedDriver()
{
return $this->createCacheBased('memcached');
}
順に飛んで行く
protected function createCacheBased($driver)
{
return $this->buildSession($this->createCacheHandler($driver));
}
createCacheHandler の方を見ると
protected function createCacheHandler($driver)
{
// ★ ここでstore設定があればそれをとり、なかったら driver名と同名のstoreになるようになっている。
$store = $this->app['config']->get('session.store') ?: $driver;
return new CacheBasedSessionHandler(
clone $this->app['cache']->store($store),
$this->app['config']['session.lifetime']
);
}
と言うことで store 設定を書かなくてもstore名'memcached'をそのまま残してストアを新設することで分離しようと思えばできるのですが、store名なのかdriver名なのかはっきり分かっていた方が管理がしやすいので分けた方が良いかと思います。
persistent_id を誤まって一緒にしてしまったことにより分離されなかった話
persistent_id
デフォルトでは、Memcached のインスタンスはリクエストの終了時に破棄されます。 リクエスト間で持続するインスタンスを作成するには、 > persistent_id でそのインスタンスの一意な ID を指定します。 同じ persistent_id で作られたすべてのインスタンスは同じ接続を共有します。
なので、設定で分離したキャッシュだが、同じpersistent_id を使ってしまうと同じキャッシュに接続しに行ってしまう。
まとめ
理解の妨げになっているのは以下のようなところでした
- session は cache.php のストアを使いまわしているが、設定によりdriverであったりstoreであったりする
- 環境変数名が分かりづらいので意味を直感的に理解できない
ドキュメントにははっきり書いてなかったのですが、とりあえずコードを冷静に読めばなんとかなったのでした。