mirror of
https://github.com/newnius/YAO-portal.git
synced 2025-12-18 02:36:43 +00:00
init & add agent & add job
This commit is contained in:
45
predis/src/Transaction/AbortedMultiExecException.php
Normal file
45
predis/src/Transaction/AbortedMultiExecException.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Transaction;
|
||||
|
||||
use Predis\PredisException;
|
||||
|
||||
/**
|
||||
* Exception class that identifies a MULTI / EXEC transaction aborted by Redis.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class AbortedMultiExecException extends PredisException
|
||||
{
|
||||
private $transaction;
|
||||
|
||||
/**
|
||||
* @param MultiExec $transaction Transaction that generated the exception.
|
||||
* @param string $message Error message.
|
||||
* @param int $code Error code.
|
||||
*/
|
||||
public function __construct(MultiExec $transaction, $message, $code = null)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
$this->transaction = $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction that generated the exception.
|
||||
*
|
||||
* @return MultiExec
|
||||
*/
|
||||
public function getTransaction()
|
||||
{
|
||||
return $this->transaction;
|
||||
}
|
||||
}
|
||||
461
predis/src/Transaction/MultiExec.php
Normal file
461
predis/src/Transaction/MultiExec.php
Normal file
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Transaction;
|
||||
|
||||
use Predis\ClientContextInterface;
|
||||
use Predis\ClientException;
|
||||
use Predis\ClientInterface;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\CommunicationException;
|
||||
use Predis\Connection\AggregateConnectionInterface;
|
||||
use Predis\NotSupportedException;
|
||||
use Predis\Protocol\ProtocolException;
|
||||
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||
use Predis\Response\ServerException;
|
||||
use Predis\Response\Status as StatusResponse;
|
||||
|
||||
/**
|
||||
* Client-side abstraction of a Redis transaction based on MULTI / EXEC.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class MultiExec implements ClientContextInterface
|
||||
{
|
||||
private $state;
|
||||
|
||||
protected $client;
|
||||
protected $commands;
|
||||
protected $exceptions = true;
|
||||
protected $attempts = 0;
|
||||
protected $watchKeys = array();
|
||||
protected $modeCAS = false;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Client instance used by the transaction.
|
||||
* @param array $options Initialization options.
|
||||
*/
|
||||
public function __construct(ClientInterface $client, array $options = null)
|
||||
{
|
||||
$this->assertClient($client);
|
||||
|
||||
$this->client = $client;
|
||||
$this->state = new MultiExecState();
|
||||
|
||||
$this->configure($client, $options ?: array());
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the passed client instance satisfies the required conditions
|
||||
* needed to initialize the transaction object.
|
||||
*
|
||||
* @param ClientInterface $client Client instance used by the transaction object.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
private function assertClient(ClientInterface $client)
|
||||
{
|
||||
if ($client->getConnection() instanceof AggregateConnectionInterface) {
|
||||
throw new NotSupportedException(
|
||||
'Cannot initialize a MULTI/EXEC transaction over aggregate connections.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$client->getProfile()->supportsCommands(array('MULTI', 'EXEC', 'DISCARD'))) {
|
||||
throw new NotSupportedException(
|
||||
'The current profile does not support MULTI, EXEC and DISCARD.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the transaction using the provided options.
|
||||
*
|
||||
* @param ClientInterface $client Underlying client instance.
|
||||
* @param array $options Array of options for the transaction.
|
||||
**/
|
||||
protected function configure(ClientInterface $client, array $options)
|
||||
{
|
||||
if (isset($options['exceptions'])) {
|
||||
$this->exceptions = (bool) $options['exceptions'];
|
||||
} else {
|
||||
$this->exceptions = $client->getOptions()->exceptions;
|
||||
}
|
||||
|
||||
if (isset($options['cas'])) {
|
||||
$this->modeCAS = (bool) $options['cas'];
|
||||
}
|
||||
|
||||
if (isset($options['watch']) && $keys = $options['watch']) {
|
||||
$this->watchKeys = $keys;
|
||||
}
|
||||
|
||||
if (isset($options['retry'])) {
|
||||
$this->attempts = (int) $options['retry'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of the transaction.
|
||||
*/
|
||||
protected function reset()
|
||||
{
|
||||
$this->state->reset();
|
||||
$this->commands = new \SplQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the transaction context.
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
if ($this->state->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->modeCAS) {
|
||||
$this->state->flag(MultiExecState::CAS);
|
||||
}
|
||||
|
||||
if ($this->watchKeys) {
|
||||
$this->watch($this->watchKeys);
|
||||
}
|
||||
|
||||
$cas = $this->state->isCAS();
|
||||
$discarded = $this->state->isDiscarded();
|
||||
|
||||
if (!$cas || ($cas && $discarded)) {
|
||||
$this->call('MULTI');
|
||||
|
||||
if ($discarded) {
|
||||
$this->state->unflag(MultiExecState::CAS);
|
||||
}
|
||||
}
|
||||
|
||||
$this->state->unflag(MultiExecState::DISCARDED);
|
||||
$this->state->flag(MultiExecState::INITIALIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically invokes a Redis command with the specified arguments.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
return $this->executeCommand(
|
||||
$this->client->createCommand($method, $arguments)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Redis command bypassing the transaction logic.
|
||||
*
|
||||
* @param string $commandID Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function call($commandID, array $arguments = array())
|
||||
{
|
||||
$response = $this->client->executeCommand(
|
||||
$this->client->createCommand($commandID, $arguments)
|
||||
);
|
||||
|
||||
if ($response instanceof ErrorResponseInterface) {
|
||||
throw new ServerException($response->getMessage());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified Redis command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @throws AbortedMultiExecException
|
||||
* @throws CommunicationException
|
||||
*
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
if ($this->state->isCAS()) {
|
||||
return $this->client->executeCommand($command);
|
||||
}
|
||||
|
||||
$response = $this->client->getConnection()->executeCommand($command);
|
||||
|
||||
if ($response instanceof StatusResponse && $response == 'QUEUED') {
|
||||
$this->commands->enqueue($command);
|
||||
} elseif ($response instanceof ErrorResponseInterface) {
|
||||
throw new AbortedMultiExecException($this, $response->getMessage());
|
||||
} else {
|
||||
$this->onProtocolError('The server did not return a +QUEUED status response.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes WATCH against one or more keys.
|
||||
*
|
||||
* @param string|array $keys One or more keys.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
* @throws ClientException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function watch($keys)
|
||||
{
|
||||
if (!$this->client->getProfile()->supportsCommand('WATCH')) {
|
||||
throw new NotSupportedException('WATCH is not supported by the current profile.');
|
||||
}
|
||||
|
||||
if ($this->state->isWatchAllowed()) {
|
||||
throw new ClientException('Sending WATCH after MULTI is not allowed.');
|
||||
}
|
||||
|
||||
$response = $this->call('WATCH', is_array($keys) ? $keys : array($keys));
|
||||
$this->state->flag(MultiExecState::WATCH);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the transaction by executing MULTI on the server.
|
||||
*
|
||||
* @return MultiExec
|
||||
*/
|
||||
public function multi()
|
||||
{
|
||||
if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) {
|
||||
$this->state->unflag(MultiExecState::CAS);
|
||||
$this->call('MULTI');
|
||||
} else {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes UNWATCH.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*
|
||||
* @return MultiExec
|
||||
*/
|
||||
public function unwatch()
|
||||
{
|
||||
if (!$this->client->getProfile()->supportsCommand('UNWATCH')) {
|
||||
throw new NotSupportedException(
|
||||
'UNWATCH is not supported by the current profile.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->state->unflag(MultiExecState::WATCH);
|
||||
$this->__call('UNWATCH', array());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the transaction by UNWATCH-ing the keys that are being WATCHed and
|
||||
* DISCARD-ing pending commands that have been already sent to the server.
|
||||
*
|
||||
* @return MultiExec
|
||||
*/
|
||||
public function discard()
|
||||
{
|
||||
if ($this->state->isInitialized()) {
|
||||
$this->call($this->state->isCAS() ? 'UNWATCH' : 'DISCARD');
|
||||
|
||||
$this->reset();
|
||||
$this->state->flag(MultiExecState::DISCARDED);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the whole transaction.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function exec()
|
||||
{
|
||||
return $this->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the state of the transaction before execution.
|
||||
*
|
||||
* @param mixed $callable Callback for execution.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws ClientException
|
||||
*/
|
||||
private function checkBeforeExecution($callable)
|
||||
{
|
||||
if ($this->state->isExecuting()) {
|
||||
throw new ClientException(
|
||||
'Cannot invoke "execute" or "exec" inside an active transaction context.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($callable) {
|
||||
if (!is_callable($callable)) {
|
||||
throw new \InvalidArgumentException('The argument must be a callable object.');
|
||||
}
|
||||
|
||||
if (!$this->commands->isEmpty()) {
|
||||
$this->discard();
|
||||
|
||||
throw new ClientException(
|
||||
'Cannot execute a transaction block after using fluent interface.'
|
||||
);
|
||||
}
|
||||
} elseif ($this->attempts) {
|
||||
$this->discard();
|
||||
|
||||
throw new ClientException(
|
||||
'Automatic retries are supported only when a callable block is provided.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the actual execution of the whole transaction.
|
||||
*
|
||||
* @param mixed $callable Optional callback for execution.
|
||||
*
|
||||
* @throws CommunicationException
|
||||
* @throws AbortedMultiExecException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function execute($callable = null)
|
||||
{
|
||||
$this->checkBeforeExecution($callable);
|
||||
|
||||
$execResponse = null;
|
||||
$attempts = $this->attempts;
|
||||
|
||||
do {
|
||||
if ($callable) {
|
||||
$this->executeTransactionBlock($callable);
|
||||
}
|
||||
|
||||
if ($this->commands->isEmpty()) {
|
||||
if ($this->state->isWatching()) {
|
||||
$this->discard();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$execResponse = $this->call('EXEC');
|
||||
|
||||
if ($execResponse === null) {
|
||||
if ($attempts === 0) {
|
||||
throw new AbortedMultiExecException(
|
||||
$this, 'The current transaction has been aborted by the server.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while ($attempts-- > 0);
|
||||
|
||||
$response = array();
|
||||
$commands = $this->commands;
|
||||
$size = count($execResponse);
|
||||
|
||||
if ($size !== count($commands)) {
|
||||
$this->onProtocolError('EXEC returned an unexpected number of response items.');
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $size; ++$i) {
|
||||
$cmdResponse = $execResponse[$i];
|
||||
|
||||
if ($cmdResponse instanceof ErrorResponseInterface && $this->exceptions) {
|
||||
throw new ServerException($cmdResponse->getMessage());
|
||||
}
|
||||
|
||||
$response[$i] = $commands->dequeue()->parseResponse($cmdResponse);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the current transaction object to a callable block for execution.
|
||||
*
|
||||
* @param mixed $callable Callback.
|
||||
*
|
||||
* @throws CommunicationException
|
||||
* @throws ServerException
|
||||
*/
|
||||
protected function executeTransactionBlock($callable)
|
||||
{
|
||||
$exception = null;
|
||||
$this->state->flag(MultiExecState::INSIDEBLOCK);
|
||||
|
||||
try {
|
||||
call_user_func($callable, $this);
|
||||
} catch (CommunicationException $exception) {
|
||||
// NOOP
|
||||
} catch (ServerException $exception) {
|
||||
// NOOP
|
||||
} catch (\Exception $exception) {
|
||||
$this->discard();
|
||||
}
|
||||
|
||||
$this->state->unflag(MultiExecState::INSIDEBLOCK);
|
||||
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for protocol errors encountered inside the transaction.
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
private function onProtocolError($message)
|
||||
{
|
||||
// Since a MULTI/EXEC block cannot be initialized when using aggregate
|
||||
// connections we can safely assume that Predis\Client::getConnection()
|
||||
// will return a Predis\Connection\NodeConnectionInterface instance.
|
||||
CommunicationException::handle(new ProtocolException(
|
||||
$this->client->getConnection(), $message
|
||||
));
|
||||
}
|
||||
}
|
||||
166
predis/src/Transaction/MultiExecState.php
Normal file
166
predis/src/Transaction/MultiExecState.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Transaction;
|
||||
|
||||
/**
|
||||
* Utility class used to track the state of a MULTI / EXEC transaction.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class MultiExecState
|
||||
{
|
||||
const INITIALIZED = 1; // 0b00001
|
||||
const INSIDEBLOCK = 2; // 0b00010
|
||||
const DISCARDED = 4; // 0b00100
|
||||
const CAS = 8; // 0b01000
|
||||
const WATCH = 16; // 0b10000
|
||||
|
||||
private $flags;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->flags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal state flags.
|
||||
*
|
||||
* @param int $flags Set of flags
|
||||
*/
|
||||
public function set($flags)
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal state flags.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets one or more flags.
|
||||
*
|
||||
* @param int $flags Set of flags
|
||||
*/
|
||||
public function flag($flags)
|
||||
{
|
||||
$this->flags |= $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets one or more flags.
|
||||
*
|
||||
* @param int $flags Set of flags
|
||||
*/
|
||||
public function unflag($flags)
|
||||
{
|
||||
$this->flags &= ~$flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the specified flag or set of flags is set.
|
||||
*
|
||||
* @param int $flags Flag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check($flags)
|
||||
{
|
||||
return ($this->flags & $flags) === $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of a transaction.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->flags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the RESET flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isReset()
|
||||
{
|
||||
return $this->flags === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the INITIALIZED flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInitialized()
|
||||
{
|
||||
return $this->check(self::INITIALIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the INSIDEBLOCK flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExecuting()
|
||||
{
|
||||
return $this->check(self::INSIDEBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the CAS flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCAS()
|
||||
{
|
||||
return $this->check(self::CAS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if WATCH is allowed in the current state.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWatchAllowed()
|
||||
{
|
||||
return $this->check(self::INITIALIZED) && !$this->check(self::CAS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the WATCH flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWatching()
|
||||
{
|
||||
return $this->check(self::WATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the DISCARDED flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDiscarded()
|
||||
{
|
||||
return $this->check(self::DISCARDED);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user