はじめに
- カナリアリリースとは複数サーバのうちの一部にだけ新しいバージョンのアプリケーションをリリースするデプロイ手法である
- 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 が取得できなくなる。