Session database updates race condition/bad logic for keep-alives
Discovered this in the EPEL 1.5.15 version, but despite time working differently in the newer version, the decision to update is still the same.
I realize that the reason for this is to limit updates, but it creates problems in certain configurations.
For example, the default session_lifetime is 10 minutes. If you change when you check mail to 15 minutes, you'll get 2 keepalive tasks, a refresh task, and then another keepalive task. Because the numbers work out to halflife of the session_time, depending on when the request is made, we can exceed the session_lifetime and err=session.
Here is what it looks like in the database from a 2 minute session_lifetime using a 3 minute refresh (same scenario as 10m/15m).
| n79hqq0vr7mqmglctd4hpjktvf | 2026-09-18 15:27:01 | keep-alive | n79hqq0vr7mqmglctd4hpjktvf | 2026-09-18 15:28:08 | keep-alive | n79hqq0vr7mqmglctd4hpjktvf | 2026-09-18 15:28:08 | refresh (didn't update) | n79hqq0vr7mqmglctd4hpjktvf | 2026-09-18 15:30:12 | keep-alive session failure
I changed it to /3 for testing and watched the updates. Notice here that the refresh is 2 seconds too fast and wouldn't have updated with /2. The keep-alive in this case stayed within 2 minutes and wouldn't create an error, but this is a race condition.
| u787n83ij7mgvlb4k2qb85kcbv | 2026-09-18 16:23:33 | keep-alive | u787n83ij7mgvlb4k2qb85kcbv | 2026-09-18 16:24:34 | keep-alive | u787n83ij7mgvlb4k2qb85kcbv | 2026-09-18 16:25:32 | refresh | u787n83ij7mgvlb4k2qb85kcbv | 2026-09-18 16:26:33 | keep-alive
Even with keep-alives only (which will always be session_lifetime/2 if session_lifetime is <= 30 minutes, there is the possibility of time shifts causing sporadic session errors, but I believe why refresh is more consistent at being early is that refresh may use a different timer than keep-alive despite prohibiting the keep-alive and so it can fire earlier than expected more consistently and fail to update the session time.
At a minimum, there should be a buffer, so perhaps subtract 10-30 seconds? Ironically, the 1.5 code before the time was changed to expired uses / 3 for redis, memcached, and memcache, but apparently got changed to / 2 to match db when updated to the new time scheme.
Source: roundcube/roundcubemail