Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kaigi_on_Rails_2022_Talk-hogelog.pdf

hogelog
October 22, 2022
1.3k

 Kaigi_on_Rails_2022_Talk-hogelog.pdf

hogelog

October 22, 2022
Tweet

More Decks by hogelog

Transcript

  1. 自己紹介 2 @hogelog (Sunao Komuro) - STORES 株式会社 CTO室 -

    2021年6月入社 - サービス開発、基盤領域、マネージャーなど を経て、現在はいちバックエンドエンジニア
  2. Railsのセッションストア 5 - `rails new myapp` しただけでセッションが利用可能 - デフォルトセッションストアはCookieStore 1

    │ class CountController < ApplicationController 2 │ def show 3 │ session[:count] = session[:count].to_i + 1 4 │ render plain: "hello: #{session[:count]}" 5 │ end 6 │ end
  3. CookieStoreの嬉しくないところ 7 - セッションの実体がクライアントにある - セッション無効化などの制御も難しい - セキュリティ的にもサーバサイドに置くことがベストプラクティス - >

    However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Rails Guides <https://guides.rubyonrails.org/security.html> - > The best practice is to use a database based session OWASP Cheet Sheet Series: Ruby on Rails Cheet Sheet <https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html#sessions> - セッションをサーバサイドに切り替えよう
  4. 要件 9 - 対象サービス: STORES(ネットショップ) - 2012年8月リリース - ほぼモノリシックRailsアプリケーション -

    セッション数は1000万オーダー - セッション期限 - ログインユーザ: 2週間 - 非ログインユーザ: セッションクッキー(すぐ揮発) - セッションのリセットはしたくない
  5. 技術選定 10 - ストレージ: Amazon MemoryDB for Redis - Redis

    プロトコルを喋る Aurora 的な存在 - セッションストア: redis-rails (redis-actionpack) gem - 技術選定の理由は STORES Product Blog にて - https://product.st.inc/entry/2022/07/07/142350 - https://product.st.inc/entry/2022/07/07/142350
  6. 移行戦略 11 - セッションは維持したまま - ダブルライトによる段階的な移行 1. クッキーのみ 2. ダブルライト

    (Write: クッキー&Redis, Read: クッキー) 3. ダブルライト (Write: クッキー&Redis, Read: Redis) 4. Redisのみ
  7. 必要な部品 12 - ダブルライト (Write: クッキー&Redis, Read: クッキー) - ほぼ

    CookieStore だが書き込みは RedisStore にも実行 - ダブルライト (Write: クッキー&Redis, Read: Redis) - ほぼ RedisStore だが書き込みは CookieStore にも実行
  8. ダブルライト (Write: クッキー & Redis, Read: クッキー) 13 - `class

    CookieToRedis < ActionDispatch::Session::CookieStore` - delete_session, commit_session でダブルライト 1 │ class CookieToRedis < ActionDispatch::Session::CookieStore 2 │ def initialize(app, options = {}) 3 │ super(app, options[:cookie_store]) 4 │ @redis_store = ActionDispatch::Session::RedisStore.new(app, options[:redis_store]) 5 │ end 6 │ 7 │ def delete_session(req, session_id, options) 8 │ super 9 │ @redis_store = delete_session(req, session_id, options) 10 │ end 11 │ 12 │ def commit_session(req, res) 13 │ super 14 │ @redis_store.commit_session(req, res) 15 │ end 16 │ end 17 │ 18 │ Rails.application.config.session_store CookieToRedis, { 19 │ cookie_store: { key: "cookie_id" }, 20 │ redis_store: { key: "redis_id", servers: ..., expire_after: 2.weeks }, 21 │ }
  9. ダブルライト (Write: クッキー & Redis, Read: Redis) 14 - `class

    RedisToCookie < ActionDispatch::Session::RedisStore` - delete_session, commit_session でダブルライト 1 │ class RedisToCookie < ActionDispatch::Session::RedisStore 2 │ def initialize(app, options = {}) 3 │ super(app, options[:redis_store]) 4 │ @cookie_store = ActionDispatch::Session::CookieStore.new(app, options[:cookie_store]) 5 │ end 6 │ 7 │ def delete_session(req, session_id, options) 8 │ super 9 │ @cookie_store = delete_session(req, session_id, options) 10 │ end 11 │ 12 │ def commit_session(req, res) 13 │ super 14 │ @cookie_store.commit_session(req, res) 15 │ end 16 │ end 17 │ 18 │ Rails.application.config.session_store RedisToCookie, { 19 │ cookie_store: { key: "cookie_id" }, 20 │ redis_store: { key: "redis_id", servers: ..., expire_after: 2.weeks }, 21 │ }
  10. MemoryDB 設定の調整 18 - maxmemory-policy の設定 - MemoryDB のキーが揮発しメモリに余裕が -

    しかし、しばらくすると揮発しなくなる - キーが増え続けメモリが溢れる💣💥
  11. なぜキーの増加が止まらなかったのか(その2) 19 - 調査 - TTLなしキー(揮発しないキー)でいっぱいになっていた - Redis monitor コマンドで調査

    - ダブルライト実装、rack、redis-rails の精査 - 原因 - CookieToRedisダブルライト実装の不備 - STORES のセッション期限仕様 - redis-rails gem の仕様(不具合?)
  12. CookieToRedisダブルライト実装の不備 20 - CookieToRedisはRedisStoreに対しcookie_store設定値で書き込 んでいた 373 │ def commit_session(req, res)

    374 │ session = req.get_header RACK_SESSION 375 │ options = session.options … 388 │ if not data = write_session(req, session_id, session_data, options) rack-2.2.3.1/lib/rack/session/abstract/id.rb
  13. STORES のセッション期限仕様 21 - セッション期限 - ログインユーザ: 2週間 - 非ログインユーザ:

    セッションクッキー(すぐ揮発) - CookieStoreではログインセッション期限は2週間に伸ばしている がデフォルトは`expire_after: nil` Rails.application.config.session_store CookieToRedis, { cookie_store: { key: "cookie_id" }, redis_store: { key: "redis_id", servers: ..., expire_after: 2.weeks }, }
  14. RedisStore における `expire_after: nil` の挙動 23 - セッションキーはセッションクッキーに書き込まれる - ブラウザ終了などによりすぐに揮発する

    - セッション内容はRedisにTTLなしで書き込まれる - TTLなしのキーは揮発しない - どこからも読まれない揮発しないRedisキーが生まれる
  15. ダブルライト実装の修正 26 - CookieStore も `expire_after: 2.weeks` に統一 - CookieStore非ログインユーザのセッション期限も2週間に

    Rails.application.config.session_store CookieToRedis, { cookie_store: { key: "cookie_id", expire_after: 2.weeks }, redis_store: { key: "redis_id", servers: ..., expire_after: 2.weeks }, }
  16. redis-rails gem 32 - 人気の gem だが開発は活発ではない - `expire_after: nil`

    仕様(不具合?)のIssueも反応なし - STORES 及び世界中で広く使われているgem - Issue を投げるだけではない、より積極的なコントリビュートの必 要性 - @hogelog の今後の活躍にご期待ください
  17. 33