Skip to content

perf: improve session handling - #2318

Open
brendt wants to merge 6 commits into
3.xfrom
perf-router-container
Open

brendt wants to merge 6 commits into
3.xfrom
perf-router-container

Conversation

@brendt

@brendt brendt commented Oct 23, 2026

Copy link
Copy Markdown
Member

This PR addresses the session-related part of #2309. Clients that don't send cookies, like load testing tools, APIs and crawlers, got a new session file on every request, skewing benchmarks.

What changed:

  • Cleanups are optimized: RANDOM_REQUESTS now runs at most once per minute. Database cleanup uses a single DELETE instead of one per session.
  • Sessions are only persisted when needed to. If there's only changes to the previous URL session, we don't need to persist the session because Back falls back to the Referer header.
  • Session changes are comparing to the serialized data to prevent unnecessary writes.
  • Claude also detected and fixed a bug where ClientTest left a php -S server running after the suite.

brendt and others added 5 commits October 23, 2026 09:20
…se sessions at once

The `RANDOM_REQUESTS` cleanup strategy went over all sessions on roughly one in fifty requests. As the amount of sessions grows with traffic, this made throughput collapse under load (#2309). Random cleanups now run at most once per minute.

Expired database sessions are now removed with a single query instead of one query per session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clients that don't send a session cookie, like API consumers, crawlers and load testing tools, got a new session persisted on every request. Besides the write itself, this made the amount of sessions grow along with traffic, which in turn made cleaning them up increasingly expensive (#2309).

New sessions are now only persisted once something is stored in them. The previous URL alone doesn't count, since it is tracked on every request; `Back` falls back to the `Referer` header when it's missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sessions were serialized and written to storage at the end of every request, even when nothing changed. `Session::isDirty()` now compares a fingerprint of the session's state against the one taken when it was loaded or last persisted, so values that are modified in place are still detected.

File sessions that didn't change are only touched, their last activity being read from the modification time when loading. This also prevents an unchanged session from overwriting changes made by a concurrent request. Database sessions that didn't change only have their `last_active_at` updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test started the server through `tempest serve`, which runs `php -S` in a child process. Stopping the command left that child running after the test suite finished. Starting the server was also slower than the time the test waits for it, so the tests were usually skipped.

The server is now started directly, so it's stopped along with the process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Benchmark Results

Comparison of perf-router-container against 3.x (c68e0ac2c31364125c367ffa8ed9f26b0d2b28cd).

Open to see the benchmark results

No benchmark changes above ±5%.

Generated by phpbench against commit b295213

$data = $this->session->all();

// The previous URL is tracked on every request, so it alone doesn't warrant persisting a new session.
unset($data[PreviousUrl::PREVIOUS_URL_SESSION_KEY]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This assumes that the browser sends Referer request header, though. If it sends none, this will break Back() redirects.
Same issue happens if Referrer-Policy: no-referrer is set as a response header.
This was not an issue on base, because we always saved the session.

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