(fix): purge cached user document before Realtime session verification

Cross-process read-after-write race: the HTTP worker writes a new session
into the user document on /account login endpoints, then the client sends
an authentication frame over a different Swoole process (Realtime). Cache
propagation between processes is not guaranteed to be observed on the
very next read, so sessionVerify() occasionally fails with a stale user
document whose sessions array does not yet contain the just-created one.

Purge the cached user locally before the read so sessionVerify sees the
freshly-written session deterministically. Overhead is bounded: one DEL
on cache + one extra primary-key read, executed once per WebSocket
authentication frame (not per message).

Surfaces on PR appwrite-labs/cloud#3214 as an intermittent
'Session is not valid.' failure in RealtimeConsoleClientTest +
RealtimeCustomClientTest manual-authentication cases in dedicated mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jake Barnby
2026-04-17 22:27:28 +12:00
co-authored by Claude Opus 4.7
parent 082110bba8
commit 4da9873b83
+12 -1
View File
@@ -916,8 +916,19 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$store->decode($message['data']['session']);
$userId = $store->getProperty('id', '');
// Read-after-write across processes: the HTTP worker writes the session
// to the user document on /account endpoints, then the client sends an
// authentication frame here over a different Swoole process. Cache-layer
// propagation isn't guaranteed to be observed on the next read — purge
// locally so sessionVerify sees the freshly-written session.
if (!empty($userId)) {
$database->purgeCachedDocument('users', $userId);
}
/** @var User $user */
$user = $database->getDocument('users', $store->getProperty('id', ''));
$user = $database->getDocument('users', $userId);
/**
* TODO: