* fixes on replication * trying to fix #778 #issuecomment-907319726 * code spacing fixes * codestyle fixes * trigger workflow locally
This commit is contained in:
parent
491d164118
commit
171480dee2
|
|
@ -272,10 +272,10 @@ class LocalChannelManager implements ChannelManager
|
||||||
return $channel->getName() === $channelName;
|
return $channel->getName() === $channelName;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
->flatMap(function (Channel $channel) {
|
->flatMap(function (Channel $channel) {
|
||||||
return collect($channel->getConnections())->pluck('socketId');
|
return collect($channel->getConnections())->pluck('socketId');
|
||||||
})
|
})
|
||||||
->unique()->count();
|
->unique()->count();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -429,9 +429,7 @@ class LocalChannelManager implements ChannelManager
|
||||||
*/
|
*/
|
||||||
public function connectionPonged(ConnectionInterface $connection): PromiseInterface
|
public function connectionPonged(ConnectionInterface $connection): PromiseInterface
|
||||||
{
|
{
|
||||||
$connection->lastPongedAt = Carbon::now();
|
return $this->pongConnectionInChannels($connection);
|
||||||
|
|
||||||
return $this->updateConnectionInChannels($connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -441,23 +439,47 @@ class LocalChannelManager implements ChannelManager
|
||||||
*/
|
*/
|
||||||
public function removeObsoleteConnections(): PromiseInterface
|
public function removeObsoleteConnections(): PromiseInterface
|
||||||
{
|
{
|
||||||
if (! $this->lock()->acquire()) {
|
$lock = $this->lock();
|
||||||
return Helpers::createFulfilledPromise(false);
|
try {
|
||||||
}
|
if (! $lock->acquire()) {
|
||||||
|
return Helpers::createFulfilledPromise(false);
|
||||||
$this->getLocalConnections()->then(function ($connections) {
|
|
||||||
foreach ($connections as $connection) {
|
|
||||||
$differenceInSeconds = $connection->lastPongedAt->diffInSeconds(Carbon::now());
|
|
||||||
|
|
||||||
if ($differenceInSeconds > 120) {
|
|
||||||
$this->unsubscribeFromAllChannels($connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return Helpers::createFulfilledPromise(
|
$this->getLocalConnections()->then(function ($connections) {
|
||||||
$this->lock()->forceRelease()
|
foreach ($connections as $connection) {
|
||||||
);
|
$differenceInSeconds = $connection->lastPongedAt->diffInSeconds(Carbon::now());
|
||||||
|
|
||||||
|
if ($differenceInSeconds > 120) {
|
||||||
|
$this->unsubscribeFromAllChannels($connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Helpers::createFulfilledPromise(true);
|
||||||
|
} finally {
|
||||||
|
optional($lock)->forceRelease();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pong connection in channels.
|
||||||
|
*
|
||||||
|
* @param ConnectionInterface $connection
|
||||||
|
* @return PromiseInterface[bool]
|
||||||
|
*/
|
||||||
|
public function pongConnectionInChannels(ConnectionInterface $connection): PromiseInterface
|
||||||
|
{
|
||||||
|
return $this->getLocalChannels($connection->app->id)
|
||||||
|
->then(function ($channels) use ($connection) {
|
||||||
|
foreach ($channels as $channel) {
|
||||||
|
if ($conn = $channel->getConnection($connection->socketId)) {
|
||||||
|
$conn->lastPongedAt = Carbon::now();
|
||||||
|
$channel->saveConnection($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\ChannelManagers;
|
namespace BeyondCode\LaravelWebSockets\ChannelManagers;
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Channels\Channel;
|
|
||||||
use BeyondCode\LaravelWebSockets\DashboardLogger;
|
use BeyondCode\LaravelWebSockets\DashboardLogger;
|
||||||
use BeyondCode\LaravelWebSockets\Helpers;
|
use BeyondCode\LaravelWebSockets\Helpers;
|
||||||
use BeyondCode\LaravelWebSockets\Server\MockableConnection;
|
use BeyondCode\LaravelWebSockets\Server\MockableConnection;
|
||||||
|
|
@ -145,31 +144,18 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
*/
|
*/
|
||||||
public function unsubscribeFromChannel(ConnectionInterface $connection, string $channelName, stdClass $payload): PromiseInterface
|
public function unsubscribeFromChannel(ConnectionInterface $connection, string $channelName, stdClass $payload): PromiseInterface
|
||||||
{
|
{
|
||||||
return $this->getGlobalConnectionsCount($connection->app->id, $channelName)
|
return parent::unsubscribeFromChannel($connection, $channelName, $payload)
|
||||||
|
->then(function () use ($connection, $channelName) {
|
||||||
|
return $this->decrementSubscriptionsCount($connection->app->id, $channelName);
|
||||||
|
})
|
||||||
->then(function ($count) use ($connection, $channelName) {
|
->then(function ($count) use ($connection, $channelName) {
|
||||||
if ($count === 0) {
|
$this->removeConnectionFromSet($connection);
|
||||||
// Make sure to not stay subscribed to the PubSub topic
|
// If the total connections count gets to 0 after unsubscribe,
|
||||||
// if there are no connections.
|
// try again to check & unsubscribe from the PubSub topic if needed.
|
||||||
|
if ($count < 1) {
|
||||||
|
$this->removeChannelFromSet($connection->app->id, $channelName);
|
||||||
$this->unsubscribeFromTopic($connection->app->id, $channelName);
|
$this->unsubscribeFromTopic($connection->app->id, $channelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->decrementSubscriptionsCount($connection->app->id, $channelName)
|
|
||||||
->then(function ($count) use ($connection, $channelName) {
|
|
||||||
// If the total connections count gets to 0 after unsubscribe,
|
|
||||||
// try again to check & unsubscribe from the PubSub topic if needed.
|
|
||||||
if ($count < 1) {
|
|
||||||
$this->unsubscribeFromTopic($connection->app->id, $channelName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
->then(function () use ($connection, $channelName) {
|
|
||||||
return $this->removeChannelFromSet($connection->app->id, $channelName);
|
|
||||||
})
|
|
||||||
->then(function () use ($connection) {
|
|
||||||
return $this->removeConnectionFromSet($connection);
|
|
||||||
})
|
|
||||||
->then(function () use ($connection, $channelName, $payload) {
|
|
||||||
return parent::unsubscribeFromChannel($connection, $channelName, $payload);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,6 +349,16 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
{
|
{
|
||||||
// This will update the score with the current timestamp.
|
// This will update the score with the current timestamp.
|
||||||
return $this->addConnectionToSet($connection, Carbon::now())
|
return $this->addConnectionToSet($connection, Carbon::now())
|
||||||
|
->then(function () use ($connection) {
|
||||||
|
$payload = [
|
||||||
|
'socketId' => $connection->socketId,
|
||||||
|
'appId' => $connection->app->id,
|
||||||
|
'serverId' => $this->getServerId(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->publishClient
|
||||||
|
->publish($this->getPongRedisHash($connection->app->id), json_encode($payload));
|
||||||
|
})
|
||||||
->then(function () use ($connection) {
|
->then(function () use ($connection) {
|
||||||
return parent::connectionPonged($connection);
|
return parent::connectionPonged($connection);
|
||||||
});
|
});
|
||||||
|
|
@ -375,18 +371,23 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
*/
|
*/
|
||||||
public function removeObsoleteConnections(): PromiseInterface
|
public function removeObsoleteConnections(): PromiseInterface
|
||||||
{
|
{
|
||||||
$this->lock()->get(function () {
|
$lock = $this->lock();
|
||||||
$this->getConnectionsFromSet(0, now()->subMinutes(2)->format('U'))
|
try {
|
||||||
->then(function ($connections) {
|
$lock->get(function () {
|
||||||
foreach ($connections as $socketId => $appId) {
|
$this->getConnectionsFromSet(0, now()->subMinutes(2)->format('U'))
|
||||||
$connection = $this->fakeConnectionForApp($appId, $socketId);
|
->then(function ($connections) {
|
||||||
|
foreach ($connections as $socketId => $appId) {
|
||||||
|
$connection = $this->fakeConnectionForApp($appId, $socketId);
|
||||||
|
|
||||||
$this->unsubscribeFromAllChannels($connection);
|
$this->unsubscribeFromAllChannels($connection);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return parent::removeObsoleteConnections();
|
return parent::removeObsoleteConnections();
|
||||||
|
} finally {
|
||||||
|
optional($lock)->forceRelease();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -404,6 +405,12 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($redisChannel == $this->getPongRedisHash($payload->appId)) {
|
||||||
|
$connection = $this->fakeConnectionForApp($payload->appId, $payload->socketId);
|
||||||
|
|
||||||
|
return parent::connectionPonged($connection);
|
||||||
|
}
|
||||||
|
|
||||||
$payload->channel = Str::after($redisChannel, "{$payload->appId}:");
|
$payload->channel = Str::after($redisChannel, "{$payload->appId}:");
|
||||||
|
|
||||||
if (! $channel = $this->find($payload->appId, $payload->channel)) {
|
if (! $channel = $this->find($payload->appId, $payload->channel)) {
|
||||||
|
|
@ -429,6 +436,16 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
$channel->broadcastLocallyToEveryoneExcept($payload, $socketId, $appId);
|
$channel->broadcastLocallyToEveryoneExcept($payload, $socketId, $appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function find($appId, string $channel)
|
||||||
|
{
|
||||||
|
if (! $channelInstance = parent::find($appId, $channel)) {
|
||||||
|
$class = $this->getChannelClassName($channel);
|
||||||
|
$this->channels[$appId][$channel] = new $class($channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::find($appId, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the Redis connection URL from Laravel database config.
|
* Build the Redis connection URL from Laravel database config.
|
||||||
*
|
*
|
||||||
|
|
@ -601,6 +618,20 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if channel is on the list.
|
||||||
|
*
|
||||||
|
* @param string|int $appId
|
||||||
|
* @param string $channel
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function isChannelInSet($appId, string $channel): PromiseInterface
|
||||||
|
{
|
||||||
|
return $this->publishClient->sismember(
|
||||||
|
$this->getChannelsRedisHash($appId), $channel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set data for a topic. Might be used for the presence channels.
|
* Set data for a topic. Might be used for the presence channels.
|
||||||
*
|
*
|
||||||
|
|
@ -729,6 +760,16 @@ class RedisChannelManager extends LocalChannelManager
|
||||||
return $hash;
|
return $hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the pong Redis hash.
|
||||||
|
*
|
||||||
|
* @param string|int $appId
|
||||||
|
*/
|
||||||
|
public function getPongRedisHash($appId): string
|
||||||
|
{
|
||||||
|
return $this->getRedisKey($appId, null, ['pong']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the statistics Redis hash.
|
* Get the statistics Redis hash.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,17 @@ class Channel
|
||||||
return $this->connections;
|
return $this->connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connection by socketId.
|
||||||
|
*
|
||||||
|
* @param string socketId
|
||||||
|
* @return ?ConnectionInterface
|
||||||
|
*/
|
||||||
|
public function getConnection(string $socketId): ?ConnectionInterface
|
||||||
|
{
|
||||||
|
return $this->connections[$socketId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the channel has connections.
|
* Check if the channel has connections.
|
||||||
*
|
*
|
||||||
|
|
@ -159,6 +170,7 @@ class Channel
|
||||||
collect($this->getConnections())
|
collect($this->getConnections())
|
||||||
->each(function ($connection) use ($payload) {
|
->each(function ($connection) use ($payload) {
|
||||||
$connection->send(json_encode($payload));
|
$connection->send(json_encode($payload));
|
||||||
|
$this->channelManager->connectionPonged($connection);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($replicate) {
|
if ($replicate) {
|
||||||
|
|
@ -196,12 +208,13 @@ class Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($socketId)) {
|
if (is_null($socketId)) {
|
||||||
return $this->broadcast($appId, $payload, $replicate);
|
return $this->broadcast($appId, $payload, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
collect($this->getConnections())->each(function (ConnectionInterface $connection) use ($socketId, $payload) {
|
collect($this->getConnections())->each(function (ConnectionInterface $connection) use ($socketId, $payload) {
|
||||||
if ($connection->socketId !== $socketId) {
|
if ($connection->socketId !== $socketId) {
|
||||||
$connection->send(json_encode($payload));
|
$connection->send(json_encode($payload));
|
||||||
|
$this->channelManager->connectionPonged($connection);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue