public function testDefaultHandler() { $this->redis = new TryCatchWrapper(new PhpIRedisClient('someCircle')); $this->redis->setDefaultHandlerStrategy(ExceptionStrategy::RETHROW()); try { $this->redis->set('testDefaultHandler', 213); $this->redis->lpop('testDefaultHandler'); $this->fail('no exception thrown'); } catch (Exception $e) { // all good } $strategy = ExceptionStrategy::RETURN_VALUE(); $strategy->returnValue = 'abc'; $this->redis->setDefaultHandlerStrategy($strategy); $this->assertSame('abc', $this->redis->lpop('testDefaultHandler')); }
/** * @param CoreException $e * @param ExceptionContext $ec * @return ExceptionStrategy * @throws FailoverException */ public function handleException(CoreException $e, ExceptionContext $ec = null) { // we only want to handle ConnectionErrors if (!$e instanceof ConnectionError) { return ExceptionStrategy::RETHROW(); } $this->log->warning(sprintf("Handling exception for connection %s: %s, trying to reconnect", $this->curConn->getUniqueId(), $e->getMessage())); if ($this->inMultiMode) { // usually we would throw an exception here, because data was lost, so the application should decide what to do. // ITE however wants us to just continue in this case as well, so we try to reset the multi mode and continue, // as if this were a regular error $this->log->warning(sprintf("connection error while multi mode was active, data was likely lost. msg: %s", $e->getMessage())); try { // we cannot use $this->curConn here as its a TCWrapper and would catch the exception directly, invoking this method // again and leading to a recursive loop of death $this->connections[$this->curConnKey]->getClient()->discard(); } catch (Exception $e) { // we just drop the exception } } // disconnect first to reset the internal state in the instance, we set $force to true here, // in order to remove persistent connections as well $this->curConn->disconnect(true); // sleep for 50 ms to give the network a chance to restore itself usleep(50000); // try to reconnect once if (!$this->connectInstance($this->curConn)) { $this->log->warning("Reconnecting failed, deactivating connection"); // not possible to reconnect, deactivate and continue $this->connections->deactivateConnection($this->curConnKey); $this->curConnKey = -1; $this->curConn = null; } // at this point either the old connection is active again, or we have to look for a new one if ($this->curConn === null) { $this->log->warning(sprintf("Trying to find new connection, configured connections: %s", $this->connections->getUniqueId())); // we need to find a functioning connection try { $this->activateNextConnection(); } catch (FailoverException $foe) { // no more healthy connections, we catch the exception so we can link it to the original one throw new FailoverException("while handling a connection problem in the FailoverWrapper: " . $e->getMessage(), FailoverException::ERROR_NO_HEALTHY_CONNECTIONS, $e); } } $this->log->warning(sprintf("New connection %s active, reissuing command %s", $this->curConn->getUniqueId(), $ec->getFunction())); // at this point we (hopefully) have a functioning connection so we retry the command // TODO: this recursive call may lead to a very nasty deadlock, if a connection keeps breaking right after connecting // this is what the UNSTABLE status was used for. maybe instead of using $this, we should call directly on the connection object $retVal = call_user_func_array(array($this, $ec->getFunction()), $ec->getArguments()); $strategy = ExceptionStrategy::RETURN_VALUE(); $strategy->returnValue = $retVal; return $strategy; }
/** * @param Connection $conn * @param array $config * @return TryCatchWrapper */ private function wrapInTryCatch(Connection $conn, array $config) { $wrapper = new TryCatchWrapper($conn, $this->logger); if (isset($config['defaultHandlerStrategy'])) { $strategy = ExceptionStrategy::fromValue((string) $config['defaultHandlerStrategy']); $wrapper->setDefaultHandlerStrategy($strategy); } return $wrapper; }
/** * @param Exception $e the Exception that was thrown * @param string $command the command that was being executed * @param array $args the arguments of the command * @throws \Plista\Core\Redis\Exception * @return bool */ private function handleException(Exception $e, $command, $args) { $excBrief = "{$e->getCode()}: {$e->getMessage()} ({$e->getFile()}:{$e->getLine()}) Stacktrace:\n" . $e->getTraceAsString(); // $excHash = md5($excBrief); // $curTime = Registry::getSystem()->getTime()->current(); // // $this->excLog[$excHash]['count']++; // $this->excLog[$excHash]['time'] = $curTime; // log the exception $this->log->warning("Caught Redis exception: {$excBrief}"); if ($this->exceptionHandler) { // notify the handler and get the strategy $context = ExceptionContext::fromFunctionAndArguments($command, $args); $strategy = $this->exceptionHandler->handleException($e, $context); } else { // use the default strategy $strategy = $this->defaultHandlerStrategy; } // default is to discard the exception if (!$strategy) { $strategy = ExceptionStrategy::DISCARD(); } // let's see what the handler wants us to do if ($strategy->equals(ExceptionStrategy::RETHROW)) { throw $e; } // this case is used by the FailoverWrapper if ($strategy->equals(ExceptionStrategy::RETURN_VALUE)) { return $strategy->returnValue; } // return false to signal failure. maybe interpreted by some callers as a valid value (which it // is in some cases), but that's not for us to worry about. return false; }
function handleException(Exception $e, ExceptionContext $ec = null) { $es = ExceptionStrategy::RETURN_VALUE(); $es->returnValue = $ec; return $es; }