This commit is contained in:
Fabian @ Blax Software 2026-03-21 10:27:03 +01:00
parent d091ebbd82
commit f191c44634
1 changed files with 15 additions and 8 deletions

View File

@ -146,8 +146,13 @@ class Handler implements MessageComponentInterface
return false; return false;
} }
// Update connection timestamp directly on connection object (no promise chain) // Update connection pong timestamp in BOTH local memory AND Redis sorted set.
$connection->lastPongedAt = time(); // This is critical: removeObsoleteConnections() checks the Redis set score,
// so a direct $connection->lastPongedAt assignment alone is insufficient —
// the Redis-based cleanup would still unsubscribe channels after 120s.
// connectionPonged() is async (returns a Promise resolved by the event loop),
// so this does not block the ping response.
$this->channelManager->connectionPonged($connection);
// Send pre-encoded pong response immediately // Send pre-encoded pong response immediately
$connection->send(self::$PONG_RESPONSE); $connection->send(self::$PONG_RESPONSE);
@ -181,10 +186,9 @@ class Handler implements MessageComponentInterface
MessageInterface $message, MessageInterface $message,
string $payload string $payload
): void { ): void {
// Any received message proves the client is alive — update pong timestamp // Any received message proves the client is alive — update local pong timestamp.
// to prevent removeObsoleteConnections() from unsubscribing active connections. // This is a safety net for LocalChannelManager::removeObsoleteConnections().
// This is critical because heartbeat pings with unique suffixes (e.g. pusher.ping[abc]) // The primary Redis score update happens in tryHandlePingFast() via connectionPonged().
// bypass tryHandlePingFast() and handlePusherEvent() doesn't call connectionPonged().
$connection->lastPongedAt = time(); $connection->lastPongedAt = time();
// Set remote address once (moved from per-message to reduce overhead) // Set remote address once (moved from per-message to reduce overhead)
@ -322,8 +326,11 @@ class Handler implements MessageComponentInterface
return; return;
} }
// Initialize lastPongedAt with unix timestamp (faster than Carbon) // Register connection pong in both local memory and Redis sorted set.
$connection->lastPongedAt = time(); // The Redis score is checked by removeObsoleteConnections() every 10s.
// Without this, the connection wouldn't have a Redis score until the
// first channel subscription, leaving a window for stale removal.
$this->channelManager->connectionPonged($connection);
$this->channelManager->subscribeToApp($connection->app->id); $this->channelManager->subscribeToApp($connection->app->id);