Skip to content

#5876 broken pipe handling #6356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions extensions/redis/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use yii\base\Component;
use yii\db\Exception;
use yii\helpers\Inflector;
use yii\base\ErrorException;

/**
* The redis connection class is used to establish a connection to a [redis](http://redis.io/) server.
Expand Down Expand Up @@ -291,11 +292,16 @@ public function close()
$connection = ($this->unixSocket ?: $this->hostname . ':' . $this->port) . ', database=' . $this->database;
\Yii::trace('Closing DB connection: ' . $connection, __METHOD__);
$this->executeCommand('QUIT');
stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR);
$this->_socket = null;
$this->releaseSocket();
}
}

protected function releaseSocket()
{
stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR);
$this->_socket = null;
}

/**
* Initializes the DB connection.
* This method is invoked right after the DB connection is established.
Expand Down Expand Up @@ -370,9 +376,19 @@ public function executeCommand($name, $params = [])
}

\Yii::trace("Executing Redis Command: {$name}", __METHOD__);
fwrite($this->_socket, $command);

return $this->parseResponse(implode(' ', $params));
try {
fwrite($this->_socket, $command);
return $this->parseResponse(implode(' ', $params));
}
catch (ErrorException $e) {
\Yii::warning("Got error: '" . $e->getMessage() . "', reinit connection", __METHOD__);
// handle network errors such as broken pipe
$this->releaseSocket(); // don't send QUIT - socket already broken
$this->open();
// don't catch second exception - it means connection reinit doesn't take effect
fwrite($this->_socket, $command);
return $this->parseResponse(implode(' ', $params));
}
}

/**
Expand All @@ -383,7 +399,7 @@ public function executeCommand($name, $params = [])
private function parseResponse($command)
{
if (($line = fgets($this->_socket)) === false) {
throw new Exception("Failed to read from socket.\nRedis command was: " . $command);
throw new NetworkException("Failed to read from socket.\nRedis command was: " . $command);
}
$type = $line[0];
$line = mb_substr($line, 1, -2, '8bit');
Expand Down Expand Up @@ -427,3 +443,9 @@ private function parseResponse($command)
}
}
}

/**
* Report a socket error
*/
class NetworkException extends ErrorException
{}