Skip to content

feat: added Redis-ready session scaling#3978

Merged
thorsten merged 3 commits intomainfrom
feat/session-scaling
Feb 14, 2026
Merged

feat: added Redis-ready session scaling#3978
thorsten merged 3 commits intomainfrom
feat/session-scaling

Conversation

@thorsten
Copy link
Owner

@thorsten thorsten commented Feb 14, 2026

Summary by CodeRabbit

  • New Features

    • Added Redis-backed session support and runtime-selectable session handler (files or Redis).
  • Infrastructure

    • Upgraded container PHP runtime to 8.5 and added a Redis service with persistent storage; application services linked to Redis.
  • Documentation

    • Updated README and installation docs to reflect PHP version updates and Redis usage.
  • Tests

    • Added tests covering session configuration and Redis connectivity/validation.

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

Adds Redis-backed session support: Docker images updated to PHP 8.5 and install the Redis PHP extension; new Redis service in docker-compose; runtime session configuration and a new RedisSessionHandler class; default config and migrations updated; tests added to cover session and Redis connection behavior.

Changes

Cohort / File(s) Summary
Dockerfiles
\.docker/apache/Dockerfile, \.docker/frankenphp/Dockerfile, \.docker/php-fpm/Dockerfile
Bump base images to PHP 8.5 (apache/fpm), install & enable the Redis PHP extension via PECL; small header/comment updates.
Compose & Docs
docker-compose.yml, README.md, docs/installation.md
Add redis:7-alpine service (volume, port, appendonly), link Redis from apache, php-fpm, frankenphp; document optional Redis store and update PHP version references to 8.5.
Bootstrap & configurator
phpmyfaq/src/phpMyFAQ/Bootstrap/PhpConfigurator.php, phpmyfaq/src/phpMyFAQ/Bootstrapper.php
configureSession() signature now accepts ?Configuration; reads session.handler and session.redisDsn, sets file handler or delegates to RedisSessionHandler; Bootstrapper now passes $this->faqConfig.
Redis session handler
phpmyfaq/src/phpMyFAQ/Session/RedisSessionHandler.php
New class with DEFAULT_DSN, configure() to set session.save_handler/session.save_path, DSN parsing, and validateConnection() using stream_socket_client; throws descriptive RuntimeExceptions.
Default config & migrations
phpmyfaq/src/phpMyFAQ/Setup/Installation/DefaultDataSeeder.php, phpmyfaq/src/phpMyFAQ/Setup/Migration/Versions/Migration420Alpha.php
Add default keys session.handler = 'files' and session.redisDsn = 'tcp://redis:6379?database=0'.
Tests
tests/phpMyFAQ/Bootstrap/PhpConfiguratorTest.php, tests/phpMyFAQ/Session/RedisSessionHandlerTest.php, tests/phpMyFAQ/Setup/Installation/DefaultDataSeederTest.php, tests/phpMyFAQ/Setup/Migration/Versions/Migration420AlphaTest.php
Add/extend tests for session configuration (files vs redis, unsupported handler, persistence) and Redis connection validation (invalid DSN, unreachable target), plus config key assertions.

Sequence Diagram

sequenceDiagram
    participant App as Application Bootstrap
    participant Config as Configuration
    participant PC as PhpConfigurator
    participant RSH as RedisSessionHandler
    participant Redis as Redis Service

    App->>PC: configureSession(faqConfig)
    PC->>Config: get('session.handler')
    Config-->>PC: 'redis' / 'files' / other

    alt handler == 'redis'
        PC->>Config: get('session.redisDsn')
        Config-->>PC: 'tcp://redis:6379?database=0'
        PC->>RSH: configure(dsn, validate=true)
        RSH->>RSH: buildSocketTarget(dsn)
        RSH->>Redis: stream_socket_client(tcp://redis:6379)
        Redis-->>RSH: connect / fail
        RSH->>RSH: ini_set('session.save_handler','redis')
        RSH->>RSH: ini_set('session.save_path', dsn)
    else handler == 'files'
        PC->>PC: ini_set('session.save_handler','files')
    else
        PC-->>App: throw RuntimeException("Unsupported session handler")
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I sniffed a Redis tunnel bright,
Carrots cached in memory light,
Sessions bound no longer to files,
I hop through sockets, do cartwheels and smiles,
Cheer — the stash is fast and spry!

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding Redis support for session handling and scaling capabilities across the application.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/session-scaling

No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @.docker/apache/Dockerfile:
- Around line 64-66: The Dockerfile currently installs the Redis PHP extension
without a version lock via the RUN command `pecl install redis &&
docker-php-ext-enable redis`; change this to pin the extension to the known
stable version by specifying the version in the pecl install command (e.g.,
`pecl install redis-6.3.0`) and keep the `docker-php-ext-enable redis` step so
builds are reproducible and unaffected by future upstream major releases.

In `@phpmyfaq/src/phpMyFAQ/Bootstrap/PhpConfigurator.php`:
- Around line 76-89: The switch on $sessionHandler currently includes a
'database' case that falls through to ini_set('session.save_handler', 'files'),
which misleads admins; update the switch in PhpConfigurator so 'database' is
treated as unsupported: remove the 'database' label (or replace its branch) and
let the default branch throw the RuntimeException that lists allowed values, or
explicitly throw a RuntimeException for 'database' with the same message; keep
RedisSessionHandler::configure($redisDsn) unchanged.

In `@phpmyfaq/src/phpMyFAQ/Session/RedisSessionHandler.php`:
- Around line 28-39: The configure() method currently calls
validateConnection($redisDsn) on every invocation causing extra TCP
connect/close and TOCTOU risk; remove that proactive connectivity check from
configure() (leave ini_set('session.save_handler','redis') and
ini_set('session.save_path',$redisDsn) intact) and instead make validation
opt-in (e.g., add a new method validateAndSaveDsnForSetup or accept a boolean
$validate = false parameter) so that validateConnection is only invoked during
setup/admin actions; reference the configure(), validateConnection(),
DEFAULT_DSN, and the session.save_handler/session.save_path ini settings when
making this change.

In `@README.md`:
- Line 78: Update the stale PHP version mentions in README.md by replacing each
occurrence of the string "PHP 8.4" with "PHP 8.5" (the Dockerfiles were bumped),
ensuring entries that reference PHP versions (look for literal "PHP 8.4"
occurrences near the service list around the Redis entry) are updated so the
README matches the current Dockerfiles.
🧹 Nitpick comments (3)
docker-compose.yml (1)

31-38: Redis has no authentication configured.

While the file header says "development only," the Redis instance has no password (requirepass) and its port is published to the host. If this compose file is ever used in a less-trusted network, the Redis instance is openly writable. Consider adding a password via the command or environment, consistent with how MariaDB/Postgres are configured.

   redis:
     image: redis:7-alpine
     restart: always
-    command: redis-server --appendonly yes
+    command: redis-server --appendonly yes --requirepass phpmyfaq
     ports:
       - '6379:6379'
     volumes:
       - ./volumes/redis:/data

If a password is added, the DSN default would need updating to include ?auth=phpmyfaq (e.g., tcp://redis:6379?auth=phpmyfaq&database=0).

tests/phpMyFAQ/Setup/Migration/Versions/Migration420AlphaTest.php (1)

83-109: Test method name is misleading — it now verifies more than rate-limit entries.

testUpAddsRateLimitConfigEntries also asserts queue.transport, session.handler, and session.redisDsn. Consider renaming to something like testUpAddsExpectedConfigEntries to accurately reflect the broader scope.

tests/phpMyFAQ/Bootstrap/PhpConfiguratorTest.php (1)

118-152: Temp directory cleanup may be skipped if the assertion fails.

If assertSame on line 145 throws, lines 147–151 won't execute, leaving the temp session directory behind. Consider moving cleanup into a finally block or using tearDown.

Proposed fix using try/finally
-        $this->assertSame('ok', $value);
-
-        $sessionFile = $sessionDirectory . '/sess_' . $sessionId;
-        if (file_exists($sessionFile)) {
-            unlink($sessionFile);
+        try {
+            $this->assertSame('ok', $value);
+        } finally {
+            $sessionFile = $sessionDirectory . '/sess_' . $sessionId;
+            if (file_exists($sessionFile)) {
+                unlink($sessionFile);
+            }
+            if (is_dir($sessionDirectory)) {
+                rmdir($sessionDirectory);
+            }
         }
-        rmdir($sessionDirectory);

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@README.md`:
- Line 11: The README has inconsistent PHP version requirements: change the PHP
requirement text so both occurrences match (either update the phrase on line 36
that currently says "only supported on PHP 8.3 and up" to "requires PHP 8.5+" or
revert the new text on line 11 back to "PHP 8.3 and up"); locate the two strings
in the README (the sentence at the top mentioning "requires PHP 8.5+" and the
sentence around line 36 mentioning "only supported on PHP 8.3 and up") and make
them identical, keeping formatting and punctuation consistent.
🧹 Nitpick comments (1)
phpmyfaq/src/phpMyFAQ/Bootstrap/PhpConfigurator.php (1)

69-88: PMF_SESSION_SAVE_PATH is set before the handler switch, so Redis will silently overwrite it.

When session.handler is redis, RedisSessionHandler::configure() overwrites session.save_path (set on line 70) with the Redis DSN. The earlier ini_set is harmless but misleading. Consider moving the PMF_SESSION_SAVE_PATH block inside the 'files' case so the intent is clearer.

♻️ Suggested restructuring
-            if (defined('PMF_SESSION_SAVE_PATH') && PMF_SESSION_SAVE_PATH !== '') {
-                ini_set('session.save_path', value: PMF_SESSION_SAVE_PATH);
-            }
-
             $sessionHandler = strtolower((string) ($configuration?->get('session.handler') ?? 'files'));
             $redisDsn = trim((string) ($configuration?->get('session.redisDsn') ?? ''));

             switch ($sessionHandler) {
                 case 'files':
                     ini_set('session.save_handler', value: 'files');
+                    if (defined('PMF_SESSION_SAVE_PATH') && PMF_SESSION_SAVE_PATH !== '') {
+                        ini_set('session.save_path', value: PMF_SESSION_SAVE_PATH);
+                    }
                     break;
                 case 'redis':
                     RedisSessionHandler::configure($redisDsn);
                     break;

@thorsten thorsten merged commit f85255e into main Feb 14, 2026
10 checks passed
@thorsten thorsten deleted the feat/session-scaling branch February 14, 2026 14:27
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.

1 participant