Skip to content

perf: php session is always written because of __MTIME value - #9885

Closed
tpayen wants to merge 1 commit into
roundcube:masterfrom
tpayen:perf/session-php_mtime
Closed

perf: php session is always written because of __MTIME value#9885
tpayen wants to merge 1 commit into
roundcube:masterfrom
tpayen:perf/session-php_mtime

Conversation

@tpayen

@tpayen tpayen commented Jun 11, 2025

Copy link
Copy Markdown
Contributor

On each request the session __MTIME variable is updated with the time() value so the session is written on each request.
It can be a performance issue when using shared session handler as Redis, as we can see a lot of network traffic to update all this data.
To reduce the network consumption we choose to update __MTIME only a time to time, and a little arbitrary we choose to do it each 1/10 session lifetime. As __MTIME acted probably as a keep alive for the session, it's steel a good idea to update it regulary, but not too regulary

In our infrastructure, this change reduce the network consumption of Redis by half, so I think it worth the change

@alecpl

alecpl commented Jun 11, 2025

Copy link
Copy Markdown
Member

As far as I can see the $changed property is not used in php session handler. It has it's purpose in other handlers. I think anything referencing $changed and __MTIME can be safely removed from the rcube_session_php code.

ps. why don't you use the redis session handler?

@tpayen

tpayen commented Jun 11, 2025

Copy link
Copy Markdown
Contributor Author

Yeah ok I thought the $changed property wasn't being used. If I update the PR to remove use of $changed and __MTIME in rcube_session_php it's ok for you ?

ps. why don't you use the redis session handler?

We are using a Redis backend as session handler, so we don't have choice ;-) But we also have lock session issues with redis so we are thinking to change maybe for redis sentinel

@alecpl

alecpl commented Jun 11, 2025

Copy link
Copy Markdown
Member

Yeah ok I thought the $changed property wasn't being used. If I update the PR to remove use of $changed and __MTIME in rcube_session_php it's ok for you ?

Yes, I suppose.

ps. why don't you use the redis session handler?

We are using a Redis backend as session handler, so we don't have choice ;-)

$config['session_storage'] can be set to 'redis', but it seems you use 'php'.

@tpayen

tpayen commented Jun 11, 2025

Copy link
Copy Markdown
Contributor Author

$config['session_storage'] can be set to 'redis', but it seems you use 'php'.

Yes, we use 'php' as 'session_storage' with session_handler configured with 'RedisCluster' 'cause we use a redis cluster (multiple masters) and it's not supported in roundcube :

        // only allow 1 host for now until we support clustering
        if (count($hosts) > 1) {
            rcube::raise_error([
                'code' => 604,
                'type' => 'redis',
                'line' => __LINE__,
                'file' => __FILE__,
                'message' => 'Redis cluster not yet supported',
            ], true, true);
        }

But as RedisCluster doesn't support session locking, this isn't the best option. If we go to Redis Sentinel, I'll probably make a PR to support sentinel in roundcube (the client needs to know the sentinels).

We need clustering as we have 100k-150k very actives users

@MiMoHo

MiMoHo commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Since this stalled after the agreement above, I picked it up and opened #10248 implementing exactly what was agreed here: removing the __MTIME update entirely (rather than throttling it), while keeping __IP (which is required for ip_check and, being idempotent, doesn't prevent lazy_write). @tpayen you are credited as co-author there — I hope that's fine with you; if you'd rather update this PR yourself instead, happy to close mine.

One note complementing the discussion above: the $changed property mentioned earlier has meanwhile been removed from the session classes entirely (the expires_at rework), so on current master the __MTIME removal is all that's left to do. I verified with a counting save handler that with __MTIME PHP calls write() on every request, and without it the second request only triggers updateTimestamp() (TTL refresh) — details in #10248.

MiMoHo added a commit to MiMoHo/roundcube-roundcubemail that referenced this pull request Jul 19, 2026
…9885)

The php session driver updated $_SESSION['__MTIME'] on every request.
The variable is not read anywhere, but mutating session data on each
request prevents PHP's session.lazy_write optimization from skipping
storage writes for unchanged sessions - every request caused a full
session write. With a network-backed session.save_handler (e.g.
Redis) this is significant unnecessary traffic; the original reporter
measured about half of their Redis traffic caused by it.

Remove the __MTIME update. Session keep-alive does not need it: with
lazy_write, PHP calls the save handler's updateTimestamp() for
unchanged sessions, which refreshes the TTL (phpredis) or mtime
(files). The __IP variable is kept, it is required for ip_check and
is idempotent, so it does not prevent lazy_write.

Verified with a counting save handler over two simulated requests:
with __MTIME PHP calls write() on both requests; without it, the
second request triggers updateTimestamp() only.

Implements what was agreed with the maintainer in PR roundcube#9885.

Co-authored-by: Thomas P <[email protected]>
Co-Authored-By: Claude Fable 5 <[email protected]>
alecpl pushed a commit that referenced this pull request Jul 19, 2026
…0248)

The php session driver updated $_SESSION['__MTIME'] on every request.
The variable is not read anywhere, but mutating session data on each
request prevents PHP's session.lazy_write optimization from skipping
storage writes for unchanged sessions - every request caused a full
session write. With a network-backed session.save_handler (e.g.
Redis) this is significant unnecessary traffic; the original reporter
measured about half of their Redis traffic caused by it.

Remove the __MTIME update. Session keep-alive does not need it: with
lazy_write, PHP calls the save handler's updateTimestamp() for
unchanged sessions, which refreshes the TTL (phpredis) or mtime
(files). The __IP variable is kept, it is required for ip_check and
is idempotent, so it does not prevent lazy_write.

Verified with a counting save handler over two simulated requests:
with __MTIME PHP calls write() on both requests; without it, the
second request triggers updateTimestamp() only.

Implements what was agreed with the maintainer in PR #9885.

Co-authored-by: Thomas P <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
alecpl pushed a commit that referenced this pull request Jul 19, 2026
…0248)

The php session driver updated $_SESSION['__MTIME'] on every request.
The variable is not read anywhere, but mutating session data on each
request prevents PHP's session.lazy_write optimization from skipping
storage writes for unchanged sessions - every request caused a full
session write. With a network-backed session.save_handler (e.g.
Redis) this is significant unnecessary traffic; the original reporter
measured about half of their Redis traffic caused by it.

Remove the __MTIME update. Session keep-alive does not need it: with
lazy_write, PHP calls the save handler's updateTimestamp() for
unchanged sessions, which refreshes the TTL (phpredis) or mtime
(files). The __IP variable is kept, it is required for ip_check and
is idempotent, so it does not prevent lazy_write.

Verified with a counting save handler over two simulated requests:
with __MTIME PHP calls write() on both requests; without it, the
second request triggers updateTimestamp() only.

Implements what was agreed with the maintainer in PR #9885.

Co-authored-by: Thomas P <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
@alecpl alecpl closed this Jul 19, 2026
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.

3 participants