はじめに
- カナリアリリースとは複数サーバのうちの一部にだけ新しいバージョンのアプリケーションをリリースするデプロイ手法である
- Rails 4.2.x から 5.0.x にアップグレードする際にカナリアリリースをする、つまり Rails 4.2.x と 5.0.x のアプリケーションが混在した環境を作ると session が取得できなくなる不具合が発生するのでこれを回避したい
この時世に Rails 5.0.x へのアップグレード(しかもカナリアリリースを挟んで)をする方がいらっしゃるのかは不明ですが、どなたかの役に立てば幸いです
Ruby や Rails をある程度触ってる方向けの内容なので、Rails における session や rack などの基礎的な説明は省きます。
解決方法
- rack gem のバージョンを
2.0.7
に固定する
原因を一言で
Rails 4.2.x -> 5.0.x のアップグレードに引きずられて rack gem が 2.0.8 以上にアップデートされるのが原因。(rack の 2.0.7 -> 2.0.8 で session_id
の生成ロジックに破壊的な変更がある)
See. https://github.com/rack/rack/blob/master/CHANGELOG.md#208---2019-12-08
説明
rack の 2.0.7...2.0.8 の差分 のlib/rack/session/abstract/id.rb
, lib/rack/session/memcache.rb
辺りを見るととても分かりやすい。
- rack 2.0.7 まではブラウザの cookie に保存されてる
_session_id
(Rails 側で命名) の値をそのままセッションストア(Redis や Memcached など)のキーにしていた - rack 2.0.8 からは
_session_id
をDigest::SHA256.hexdigest
した値をキーにするようになった(コードはこの辺り、public_id
が_session_id
に等しい) - 各セッションストアの gem で
#get_session_with_fallback
という fallback 用のメソッドを用意している(redis-rack の場合, Memcache の場合)ので、新しいバージョンの rack から古いバージョンの rack で生成したセッションデータを取得することはできるが、逆はできない。つまり、後方互換性はあるが、前方互換性がない
上記より、新しいバージョンの rack gem を全サーバに一度にデプロイすれば問題ないが、各サーバで 2.0.7 以下と 2.0.8 以上の rack gem が混在する環境(カナリアリリース環境)だと session
が取得できなくなる。