Skip to content

feat: move Symfony sessions to Redis, env-configurable#440

Merged
turegjorup merged 2 commits into
release/3.0.0from
feature/redis-session-handler
May 7, 2026
Merged

feat: move Symfony sessions to Redis, env-configurable#440
turegjorup merged 2 commits into
release/3.0.0from
feature/redis-session-handler

Conversation

@turegjorup
Copy link
Copy Markdown
Contributor

Summary

framework.session.handler_id now reads from a new SESSION_HANDLER_DSN env var, which defaults to the existing REDIS_CACHE_DSN. Symfony's built-in SessionHandlerFactory auto-creates a RedisSessionHandler from the redis://... DSN — no service wiring or new dependencies. Operators can set SESSION_HANDLER_DSN= empty to fall back to PHP's native file handler.

Why

  • Removes the per-session flock that serialises parallel session-touching requests on the file handler (visible as inconsistent tail latency when the React admin fires concurrent fetches).
  • Sessions survive container restarts without mounting /tmp as a volume.
  • Multi-pod deployments share session state without sticky routing — the OIDC handshake works regardless of which pod handles the callback.

Files Changed

  • config/packages/framework.yamlhandler_id: '%env(SESSION_HANDLER_DSN)%' in the global config; when@test forces it back to null since MockFileSessionStorage bypasses handlers and we don't want the test container compiling a Redis handler against an env that may not point at a reachable Redis.
  • .env — adds SESSION_HANDLER_DSN=${REDIS_CACHE_DSN} with comment explaining the override path.
  • CHANGELOG.md[Unreleased] entry.

Key isolation

The auto-built RedisSessionHandler prefixes keys with sf_s. Cache uses Symfony's own cache.adapter.redis prefix machinery driven by REDIS_CACHE_PREFIX=DisplayApiService. So sharing one Redis DB between cache and sessions is safe — keys don't collide. Operators wanting full DB separation can override SESSION_HANDLER_DSN=redis://redis:6379/1 (or whatever DB).

Local verification (already run)

  • docker compose run phpfpm bin/console debug:container session.handler reports the handler is built via SessionHandlerFactory::createHandler — Symfony's auto-build path for DSNs.
  • HTTP request to /v2/authentication/oidc/urls (the OIDC URL endpoint, which writes the session for state tracking) returns 200 and sets a PHPSESSID cookie. Inspecting Redis post-request: exactly one key, sf_s<session-id>, matching the cookie.
  • Full PHPUnit suite passes (143 tests, 607 assertions). Redis DBSIZE=0 after the suite, confirming the when@test override keeps tests off Redis.

Test Plan

  • Pull the branch, docker compose down -v && docker compose up -d.
  • curl -s -c /tmp/c.txt -o /dev/null -w '%{http_code}\n' http://nginx:8080/v2/authentication/oidc/urls?providerKey=internal → expect 200.
  • docker compose exec redis redis-cli DBSIZE → expect 1 (one new sf_s<id> key).
  • Override path: SESSION_HANDLER_DSN= docker compose run --rm phpfpm bin/console debug:config framework session → handler_id should resolve to empty/null and Symfony falls back to native file handler.
  • docker compose run --rm phpfpm composer run test → expect 143/143 green.

🤖 Generated with Claude Code

@turegjorup turegjorup requested a review from tuj May 6, 2026 20:41
@turegjorup turegjorup self-assigned this May 6, 2026
@turegjorup turegjorup force-pushed the feature/redis-session-handler branch from 2784d94 to e4abf91 Compare May 7, 2026 08:49
framework.session.handler_id now reads from SESSION_HANDLER_DSN, which
defaults to the existing REDIS_CACHE_DSN so dev (and prod that has Redis
available) gets Redis-backed sessions out of the box. Operators can set
SESSION_HANDLER_DSN= (empty) to fall back to PHP's native file handler.

Why move:
- Removes the per-session flock that serialises parallel session-touching
  requests on the file handler (visible as inconsistent tail latency
  when the React admin fires concurrent fetches).
- Sessions survive container restarts without mounting /tmp as a volume.
- Multi-pod deployments share session state without sticky routing —
  the OIDC handshake works regardless of which pod handles the callback.

The new RedisSessionHandler is auto-built by Symfony from the DSN; it
prefixes keys with `sf_s` so they don't collide with cache keys on the
same Redis DB. when@test forces handler_id back to null, since
MockFileSessionStorage doesn't go through a handler and we don't want
the test container to compile a Redis handler against an env that may
not point at a reachable Redis.

Verified locally: HTTP request to /v2/authentication/oidc/urls writes a
`sf_s<id>` key into Redis; full PHPUnit suite (143 tests, 607
assertions) passes with redis DBSIZE=0 after, confirming the test
override works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@turegjorup turegjorup force-pushed the feature/redis-session-handler branch from e4abf91 to b204a23 Compare May 7, 2026 09:10
The previous rebase commit silently committed CHANGELOG.md with
`<<<<<<<` / `=======` / `>>>>>>>` markers still in place — the Edit
that resolved them lost a race against the git rebase tooling
touching the same file, but `git add CHANGELOG.md` accepted the
broken bytes and `git rebase --continue` succeeded. CI's markdownlint
job (MD032) caught it. Both bullets are kept.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@turegjorup turegjorup merged commit c0f0586 into release/3.0.0 May 7, 2026
18 checks passed
@turegjorup turegjorup deleted the feature/redis-session-handler branch May 7, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants