mirror of
https://github.com/predis/predis.git
synced 2026-08-20 20:32:54 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff59f74581 | |||
| 1a0eb7ee85 | |||
| bb6c1a7b6d | |||
| 42c33a406f | |||
| 7ad74217c2 | |||
| 72d7675563 | |||
| 8b5fa92856 | |||
| 076a62e3d3 | |||
| 85dc1752b7 | |||
| 7683215023 | |||
| dd4ddf6504 | |||
| 6468e6cc2e | |||
| 12f6d30502 | |||
| a602fc7425 | |||
| 4ec74cc5fe | |||
| 78fd4cb998 | |||
| 2a8dd6a7f1 | |||
| 7d7b3416a8 |
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
|
||||
redis-versions: ['3', '4', '5', '6', '7']
|
||||
|
||||
steps:
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
## Changelog
|
||||
|
||||
## v2.0.3 (2022-10-11)
|
||||
|
||||
- Improved PHP 8.2 support
|
||||
- Call `is_resource()` before reading/writing
|
||||
- Added partial support for Redis Stream commands
|
||||
- Fixed Sentinel authentication issue
|
||||
|
||||
## v2.0.2 (2022-09-06)
|
||||
|
||||
- Fixed PHP 8.2 deprecation notice: Use of "static" in callables
|
||||
|
||||
## v2.0.1 (2022-09-04)
|
||||
|
||||
- Added retry interval to `RedisCluster` with a default of `10ms`
|
||||
- Avoid PHP 8.2 warning in `Connection\Parameters`
|
||||
- Fixed Sentinel reconnect issue in long-running PHP processes
|
||||
|
||||
## v2.0.0 (2022-06-08)
|
||||
|
||||
- Dropped support for PHP 7.1 and older
|
||||
|
||||
@@ -35,6 +35,10 @@ This library can be found on [Packagist](http://packagist.org/packages/predis/pr
|
||||
management of projects dependencies using [Composer](http://packagist.org/about-composer).
|
||||
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
|
||||
|
||||
```shell
|
||||
composer require predis/predis
|
||||
```
|
||||
|
||||
|
||||
### Loading the library ###
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
*/
|
||||
class Client implements ClientInterface, \IteratorAggregate
|
||||
{
|
||||
const VERSION = '2.0.0';
|
||||
const VERSION = '2.0.3';
|
||||
|
||||
/** @var OptionsInterface */
|
||||
private $options;
|
||||
|
||||
+157
-151
@@ -25,157 +25,163 @@ use Predis\Response\Status;
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string|null dump(string $key)
|
||||
* @method int exists(string $key)
|
||||
* @method int expire(string $key, int $seconds)
|
||||
* @method int expireat(string $key, int $timestamp)
|
||||
* @method array keys(string $pattern)
|
||||
* @method int move(string $key, int $db)
|
||||
* @method mixed object($subcommand, string $key)
|
||||
* @method int persist(string $key)
|
||||
* @method int pexpire(string $key, int $milliseconds)
|
||||
* @method int pexpireat(string $key, int $timestamp)
|
||||
* @method int pttl(string $key)
|
||||
* @method string|null randomkey()
|
||||
* @method mixed rename(string $key, string $target)
|
||||
* @method int renamenx(string $key, string $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort(string $key, array $options = null)
|
||||
* @method int ttl(string $key)
|
||||
* @method mixed type(string $key)
|
||||
* @method int append(string $key, $value)
|
||||
* @method int bitcount(string $key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
|
||||
* @method int bitpos(string $key, $bit, $start = null, $end = null)
|
||||
* @method int decr(string $key)
|
||||
* @method int decrby(string $key, int $decrement)
|
||||
* @method string|null get(string $key)
|
||||
* @method int getbit(string $key, $offset)
|
||||
* @method string getrange(string $key, $start, $end)
|
||||
* @method string|null getset(string $key, $value)
|
||||
* @method int incr(string $key)
|
||||
* @method int incrby(string $key, int $increment)
|
||||
* @method string incrbyfloat(string $key, int|float $increment)
|
||||
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method Status psetex(string $key, $milliseconds, $value)
|
||||
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit(string $key, $offset, $value)
|
||||
* @method Status setex(string $key, $seconds, $value)
|
||||
* @method int setnx(string $key, $value)
|
||||
* @method int setrange(string $key, $offset, $value)
|
||||
* @method int strlen(string $key)
|
||||
* @method int hdel(string $key, array $fields)
|
||||
* @method int hexists(string $key, string $field)
|
||||
* @method string|null hget(string $key, string $field)
|
||||
* @method array hgetall(string $key)
|
||||
* @method int hincrby(string $key, string $field, int $increment)
|
||||
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
|
||||
* @method array hkeys(string $key)
|
||||
* @method int hlen(string $key)
|
||||
* @method array hmget(string $key, array $fields)
|
||||
* @method mixed hmset(string $key, array $dictionary)
|
||||
* @method array hscan(string $key, $cursor, array $options = null)
|
||||
* @method int hset(string $key, string $field, string $value)
|
||||
* @method int hsetnx(string $key, string $field, string $value)
|
||||
* @method array hvals(string $key)
|
||||
* @method int hstrlen(string $key, string $field)
|
||||
* @method array|null blpop(array|string $keys, int|float $timeout)
|
||||
* @method array|null brpop(array|string $keys, int|float $timeout)
|
||||
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
|
||||
* @method string|null lindex(string $key, int $index)
|
||||
* @method int linsert(string $key, $whence, $pivot, $value)
|
||||
* @method int llen(string $key)
|
||||
* @method string|null lpop(string $key)
|
||||
* @method int lpush(string $key, array $values)
|
||||
* @method int lpushx(string $key, array $values)
|
||||
* @method string[] lrange(string $key, int $start, int $stop)
|
||||
* @method int lrem(string $key, int $count, string $value)
|
||||
* @method mixed lset(string $key, int $index, string $value)
|
||||
* @method mixed ltrim(string $key, int $start, int $stop)
|
||||
* @method string|null rpop(string $key)
|
||||
* @method string|null rpoplpush(string $source, string $destination)
|
||||
* @method int rpush(string $key, array $values)
|
||||
* @method int rpushx(string $key, array $values)
|
||||
* @method int sadd(string $key, array $members)
|
||||
* @method int scard(string $key)
|
||||
* @method string[] sdiff(array|string $keys)
|
||||
* @method int sdiffstore(string $destination, array|string $keys)
|
||||
* @method string[] sinter(array|string $keys)
|
||||
* @method int sinterstore(string $destination, array|string $keys)
|
||||
* @method int sismember(string $key, string $member)
|
||||
* @method string[] smembers(string $key)
|
||||
* @method int smove(string $source, string $destination, string $member)
|
||||
* @method string|null spop(string $key, int $count = null)
|
||||
* @method string|null srandmember(string $key, int $count = null)
|
||||
* @method int srem(string $key, string $member)
|
||||
* @method array sscan(string $key, int $cursor, array $options = null)
|
||||
* @method string[] sunion(array|string $keys)
|
||||
* @method int sunionstore(string $destination, array|string $keys)
|
||||
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method int zadd(string $key, array $membersAndScoresDictionary)
|
||||
* @method int zcard(string $key)
|
||||
* @method string zcount(string $key, int|string $min, int|string $max)
|
||||
* @method string zincrby(string $key, int $increment, string $member)
|
||||
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method array zpopmin(string $key, int $count = 1)
|
||||
* @method array zpopmax(string $key, int $count = 1)
|
||||
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
|
||||
* @method int|null zrank(string $key, string $member)
|
||||
* @method int zrem(string $key, string ...$member)
|
||||
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
|
||||
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
|
||||
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
|
||||
* @method int|null zrevrank(string $key, string $member)
|
||||
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method string|null zscore(string $key, string $member)
|
||||
* @method array zscan(string $key, int $cursor, array $options = null)
|
||||
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method int zremrangebylex(string $key, string $min, string $max)
|
||||
* @method int zlexcount(string $key, string $min, string $max)
|
||||
* @method int pfadd(string $key, array $elements)
|
||||
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
|
||||
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array|null exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch(string $key)
|
||||
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth(string $password)
|
||||
* @method string echo(string $message)
|
||||
* @method mixed ping(string $message = null)
|
||||
* @method mixed select(int $database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof(string $host, int $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd(string $key, $longitude, $latitude, $member)
|
||||
* @method array geohash(string $key, array $members)
|
||||
* @method array geopos(string $key, array $members)
|
||||
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
|
||||
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
|
||||
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string|null dump(string $key)
|
||||
* @method int exists(string $key)
|
||||
* @method int expire(string $key, int $seconds)
|
||||
* @method int expireat(string $key, int $timestamp)
|
||||
* @method array keys(string $pattern)
|
||||
* @method int move(string $key, int $db)
|
||||
* @method mixed object($subcommand, string $key)
|
||||
* @method int persist(string $key)
|
||||
* @method int pexpire(string $key, int $milliseconds)
|
||||
* @method int pexpireat(string $key, int $timestamp)
|
||||
* @method int pttl(string $key)
|
||||
* @method string|null randomkey()
|
||||
* @method mixed rename(string $key, string $target)
|
||||
* @method int renamenx(string $key, string $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort(string $key, array $options = null)
|
||||
* @method int ttl(string $key)
|
||||
* @method mixed type(string $key)
|
||||
* @method int append(string $key, $value)
|
||||
* @method int bitcount(string $key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
|
||||
* @method int bitpos(string $key, $bit, $start = null, $end = null)
|
||||
* @method int decr(string $key)
|
||||
* @method int decrby(string $key, int $decrement)
|
||||
* @method string|null get(string $key)
|
||||
* @method int getbit(string $key, $offset)
|
||||
* @method string getrange(string $key, $start, $end)
|
||||
* @method string|null getset(string $key, $value)
|
||||
* @method int incr(string $key)
|
||||
* @method int incrby(string $key, int $increment)
|
||||
* @method string incrbyfloat(string $key, int|float $increment)
|
||||
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method Status psetex(string $key, $milliseconds, $value)
|
||||
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit(string $key, $offset, $value)
|
||||
* @method Status setex(string $key, $seconds, $value)
|
||||
* @method int setnx(string $key, $value)
|
||||
* @method int setrange(string $key, $offset, $value)
|
||||
* @method int strlen(string $key)
|
||||
* @method int hdel(string $key, array $fields)
|
||||
* @method int hexists(string $key, string $field)
|
||||
* @method string|null hget(string $key, string $field)
|
||||
* @method array hgetall(string $key)
|
||||
* @method int hincrby(string $key, string $field, int $increment)
|
||||
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
|
||||
* @method array hkeys(string $key)
|
||||
* @method int hlen(string $key)
|
||||
* @method array hmget(string $key, array $fields)
|
||||
* @method mixed hmset(string $key, array $dictionary)
|
||||
* @method array hscan(string $key, $cursor, array $options = null)
|
||||
* @method int hset(string $key, string $field, string $value)
|
||||
* @method int hsetnx(string $key, string $field, string $value)
|
||||
* @method array hvals(string $key)
|
||||
* @method int hstrlen(string $key, string $field)
|
||||
* @method array|null blpop(array|string $keys, int|float $timeout)
|
||||
* @method array|null brpop(array|string $keys, int|float $timeout)
|
||||
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
|
||||
* @method string|null lindex(string $key, int $index)
|
||||
* @method int linsert(string $key, $whence, $pivot, $value)
|
||||
* @method int llen(string $key)
|
||||
* @method string|null lpop(string $key)
|
||||
* @method int lpush(string $key, array $values)
|
||||
* @method int lpushx(string $key, array $values)
|
||||
* @method string[] lrange(string $key, int $start, int $stop)
|
||||
* @method int lrem(string $key, int $count, string $value)
|
||||
* @method mixed lset(string $key, int $index, string $value)
|
||||
* @method mixed ltrim(string $key, int $start, int $stop)
|
||||
* @method string|null rpop(string $key)
|
||||
* @method string|null rpoplpush(string $source, string $destination)
|
||||
* @method int rpush(string $key, array $values)
|
||||
* @method int rpushx(string $key, array $values)
|
||||
* @method int sadd(string $key, array $members)
|
||||
* @method int scard(string $key)
|
||||
* @method string[] sdiff(array|string $keys)
|
||||
* @method int sdiffstore(string $destination, array|string $keys)
|
||||
* @method string[] sinter(array|string $keys)
|
||||
* @method int sinterstore(string $destination, array|string $keys)
|
||||
* @method int sismember(string $key, string $member)
|
||||
* @method string[] smembers(string $key)
|
||||
* @method int smove(string $source, string $destination, string $member)
|
||||
* @method string|array|null spop(string $key, int $count = null)
|
||||
* @method string|null srandmember(string $key, int $count = null)
|
||||
* @method int srem(string $key, array|string $member)
|
||||
* @method array sscan(string $key, int $cursor, array $options = null)
|
||||
* @method string[] sunion(array|string $keys)
|
||||
* @method int sunionstore(string $destination, array|string $keys)
|
||||
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
|
||||
* @method int xdel(string $key, string ...$id)
|
||||
* @method int xlen(string $key)
|
||||
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
|
||||
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
|
||||
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
|
||||
* @method int zadd(string $key, array $membersAndScoresDictionary)
|
||||
* @method int zcard(string $key)
|
||||
* @method string zcount(string $key, int|string $min, int|string $max)
|
||||
* @method string zincrby(string $key, int $increment, string $member)
|
||||
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method array zpopmin(string $key, int $count = 1)
|
||||
* @method array zpopmax(string $key, int $count = 1)
|
||||
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
|
||||
* @method int|null zrank(string $key, string $member)
|
||||
* @method int zrem(string $key, string ...$member)
|
||||
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
|
||||
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
|
||||
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
|
||||
* @method int|null zrevrank(string $key, string $member)
|
||||
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method string|null zscore(string $key, string $member)
|
||||
* @method array zscan(string $key, int $cursor, array $options = null)
|
||||
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method int zremrangebylex(string $key, string $min, string $max)
|
||||
* @method int zlexcount(string $key, string $min, string $max)
|
||||
* @method int pfadd(string $key, array $elements)
|
||||
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
|
||||
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array|null exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch(string $key)
|
||||
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth(string $password)
|
||||
* @method string echo(string $message)
|
||||
* @method mixed ping(string $message = null)
|
||||
* @method mixed select(int $database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof(string $host, int $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd(string $key, $longitude, $latitude, $member)
|
||||
* @method array geohash(string $key, array $members)
|
||||
* @method array geopos(string $key, array $members)
|
||||
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
|
||||
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
|
||||
@@ -31,142 +31,154 @@ class KeyPrefixProcessor implements ProcessorInterface
|
||||
public function __construct($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
$this->commands = array(
|
||||
|
||||
$prefixFirst = static::class . '::first';
|
||||
$prefixAll = static::class . '::all';
|
||||
$prefixInterleaved = static::class . '::interleaved';
|
||||
$prefixSkipFirst = static::class . '::skipFirst';
|
||||
$prefixSkipLast = static::class . '::skipLast';
|
||||
$prefixSort = static::class . '::sort';
|
||||
$prefixEvalKeys = static::class . '::evalKeys';
|
||||
$prefixZsetStore = static::class . '::zsetStore';
|
||||
$prefixMigrate = static::class . '::migrate';
|
||||
$prefixGeoradius = static::class . '::georadius';
|
||||
|
||||
$this->commands = [
|
||||
/* ---------------- Redis 1.2 ---------------- */
|
||||
'EXISTS' => 'static::all',
|
||||
'DEL' => 'static::all',
|
||||
'TYPE' => 'static::first',
|
||||
'KEYS' => 'static::first',
|
||||
'RENAME' => 'static::all',
|
||||
'RENAMENX' => 'static::all',
|
||||
'EXPIRE' => 'static::first',
|
||||
'EXPIREAT' => 'static::first',
|
||||
'TTL' => 'static::first',
|
||||
'MOVE' => 'static::first',
|
||||
'SORT' => 'static::sort',
|
||||
'DUMP' => 'static::first',
|
||||
'RESTORE' => 'static::first',
|
||||
'SET' => 'static::first',
|
||||
'SETNX' => 'static::first',
|
||||
'MSET' => 'static::interleaved',
|
||||
'MSETNX' => 'static::interleaved',
|
||||
'GET' => 'static::first',
|
||||
'MGET' => 'static::all',
|
||||
'GETSET' => 'static::first',
|
||||
'INCR' => 'static::first',
|
||||
'INCRBY' => 'static::first',
|
||||
'DECR' => 'static::first',
|
||||
'DECRBY' => 'static::first',
|
||||
'RPUSH' => 'static::first',
|
||||
'LPUSH' => 'static::first',
|
||||
'LLEN' => 'static::first',
|
||||
'LRANGE' => 'static::first',
|
||||
'LTRIM' => 'static::first',
|
||||
'LINDEX' => 'static::first',
|
||||
'LSET' => 'static::first',
|
||||
'LREM' => 'static::first',
|
||||
'LPOP' => 'static::first',
|
||||
'RPOP' => 'static::first',
|
||||
'RPOPLPUSH' => 'static::all',
|
||||
'SADD' => 'static::first',
|
||||
'SREM' => 'static::first',
|
||||
'SPOP' => 'static::first',
|
||||
'SMOVE' => 'static::skipLast',
|
||||
'SCARD' => 'static::first',
|
||||
'SISMEMBER' => 'static::first',
|
||||
'SINTER' => 'static::all',
|
||||
'SINTERSTORE' => 'static::all',
|
||||
'SUNION' => 'static::all',
|
||||
'SUNIONSTORE' => 'static::all',
|
||||
'SDIFF' => 'static::all',
|
||||
'SDIFFSTORE' => 'static::all',
|
||||
'SMEMBERS' => 'static::first',
|
||||
'SRANDMEMBER' => 'static::first',
|
||||
'ZADD' => 'static::first',
|
||||
'ZINCRBY' => 'static::first',
|
||||
'ZREM' => 'static::first',
|
||||
'ZRANGE' => 'static::first',
|
||||
'ZREVRANGE' => 'static::first',
|
||||
'ZRANGEBYSCORE' => 'static::first',
|
||||
'ZCARD' => 'static::first',
|
||||
'ZSCORE' => 'static::first',
|
||||
'ZREMRANGEBYSCORE' => 'static::first',
|
||||
'EXISTS' => $prefixAll,
|
||||
'DEL' => $prefixAll,
|
||||
'TYPE' => $prefixFirst,
|
||||
'KEYS' => $prefixFirst,
|
||||
'RENAME' => $prefixAll,
|
||||
'RENAMENX' => $prefixAll,
|
||||
'EXPIRE' => $prefixFirst,
|
||||
'EXPIREAT' => $prefixFirst,
|
||||
'TTL' => $prefixFirst,
|
||||
'MOVE' => $prefixFirst,
|
||||
'SORT' => $prefixSort,
|
||||
'DUMP' => $prefixFirst,
|
||||
'RESTORE' => $prefixFirst,
|
||||
'SET' => $prefixFirst,
|
||||
'SETNX' => $prefixFirst,
|
||||
'MSET' => $prefixInterleaved,
|
||||
'MSETNX' => $prefixInterleaved,
|
||||
'GET' => $prefixFirst,
|
||||
'MGET' => $prefixAll,
|
||||
'GETSET' => $prefixFirst,
|
||||
'INCR' => $prefixFirst,
|
||||
'INCRBY' => $prefixFirst,
|
||||
'DECR' => $prefixFirst,
|
||||
'DECRBY' => $prefixFirst,
|
||||
'RPUSH' => $prefixFirst,
|
||||
'LPUSH' => $prefixFirst,
|
||||
'LLEN' => $prefixFirst,
|
||||
'LRANGE' => $prefixFirst,
|
||||
'LTRIM' => $prefixFirst,
|
||||
'LINDEX' => $prefixFirst,
|
||||
'LSET' => $prefixFirst,
|
||||
'LREM' => $prefixFirst,
|
||||
'LPOP' => $prefixFirst,
|
||||
'RPOP' => $prefixFirst,
|
||||
'RPOPLPUSH' => $prefixAll,
|
||||
'SADD' => $prefixFirst,
|
||||
'SREM' => $prefixFirst,
|
||||
'SPOP' => $prefixFirst,
|
||||
'SMOVE' => $prefixSkipLast,
|
||||
'SCARD' => $prefixFirst,
|
||||
'SISMEMBER' => $prefixFirst,
|
||||
'SINTER' => $prefixAll,
|
||||
'SINTERSTORE' => $prefixAll,
|
||||
'SUNION' => $prefixAll,
|
||||
'SUNIONSTORE' => $prefixAll,
|
||||
'SDIFF' => $prefixAll,
|
||||
'SDIFFSTORE' => $prefixAll,
|
||||
'SMEMBERS' => $prefixFirst,
|
||||
'SRANDMEMBER' => $prefixFirst,
|
||||
'ZADD' => $prefixFirst,
|
||||
'ZINCRBY' => $prefixFirst,
|
||||
'ZREM' => $prefixFirst,
|
||||
'ZRANGE' => $prefixFirst,
|
||||
'ZREVRANGE' => $prefixFirst,
|
||||
'ZRANGEBYSCORE' => $prefixFirst,
|
||||
'ZCARD' => $prefixFirst,
|
||||
'ZSCORE' => $prefixFirst,
|
||||
'ZREMRANGEBYSCORE' => $prefixFirst,
|
||||
/* ---------------- Redis 2.0 ---------------- */
|
||||
'SETEX' => 'static::first',
|
||||
'APPEND' => 'static::first',
|
||||
'SUBSTR' => 'static::first',
|
||||
'BLPOP' => 'static::skipLast',
|
||||
'BRPOP' => 'static::skipLast',
|
||||
'ZUNIONSTORE' => 'static::zsetStore',
|
||||
'ZINTERSTORE' => 'static::zsetStore',
|
||||
'ZCOUNT' => 'static::first',
|
||||
'ZRANK' => 'static::first',
|
||||
'ZREVRANK' => 'static::first',
|
||||
'ZREMRANGEBYRANK' => 'static::first',
|
||||
'HSET' => 'static::first',
|
||||
'HSETNX' => 'static::first',
|
||||
'HMSET' => 'static::first',
|
||||
'HINCRBY' => 'static::first',
|
||||
'HGET' => 'static::first',
|
||||
'HMGET' => 'static::first',
|
||||
'HDEL' => 'static::first',
|
||||
'HEXISTS' => 'static::first',
|
||||
'HLEN' => 'static::first',
|
||||
'HKEYS' => 'static::first',
|
||||
'HVALS' => 'static::first',
|
||||
'HGETALL' => 'static::first',
|
||||
'SUBSCRIBE' => 'static::all',
|
||||
'UNSUBSCRIBE' => 'static::all',
|
||||
'PSUBSCRIBE' => 'static::all',
|
||||
'PUNSUBSCRIBE' => 'static::all',
|
||||
'PUBLISH' => 'static::first',
|
||||
'SETEX' => $prefixFirst,
|
||||
'APPEND' => $prefixFirst,
|
||||
'SUBSTR' => $prefixFirst,
|
||||
'BLPOP' => $prefixSkipLast,
|
||||
'BRPOP' => $prefixSkipLast,
|
||||
'ZUNIONSTORE' => $prefixZsetStore,
|
||||
'ZINTERSTORE' => $prefixZsetStore,
|
||||
'ZCOUNT' => $prefixFirst,
|
||||
'ZRANK' => $prefixFirst,
|
||||
'ZREVRANK' => $prefixFirst,
|
||||
'ZREMRANGEBYRANK' => $prefixFirst,
|
||||
'HSET' => $prefixFirst,
|
||||
'HSETNX' => $prefixFirst,
|
||||
'HMSET' => $prefixFirst,
|
||||
'HINCRBY' => $prefixFirst,
|
||||
'HGET' => $prefixFirst,
|
||||
'HMGET' => $prefixFirst,
|
||||
'HDEL' => $prefixFirst,
|
||||
'HEXISTS' => $prefixFirst,
|
||||
'HLEN' => $prefixFirst,
|
||||
'HKEYS' => $prefixFirst,
|
||||
'HVALS' => $prefixFirst,
|
||||
'HGETALL' => $prefixFirst,
|
||||
'SUBSCRIBE' => $prefixAll,
|
||||
'UNSUBSCRIBE' => $prefixAll,
|
||||
'PSUBSCRIBE' => $prefixAll,
|
||||
'PUNSUBSCRIBE' => $prefixAll,
|
||||
'PUBLISH' => $prefixFirst,
|
||||
/* ---------------- Redis 2.2 ---------------- */
|
||||
'PERSIST' => 'static::first',
|
||||
'STRLEN' => 'static::first',
|
||||
'SETRANGE' => 'static::first',
|
||||
'GETRANGE' => 'static::first',
|
||||
'SETBIT' => 'static::first',
|
||||
'GETBIT' => 'static::first',
|
||||
'RPUSHX' => 'static::first',
|
||||
'LPUSHX' => 'static::first',
|
||||
'LINSERT' => 'static::first',
|
||||
'BRPOPLPUSH' => 'static::skipLast',
|
||||
'ZREVRANGEBYSCORE' => 'static::first',
|
||||
'WATCH' => 'static::all',
|
||||
'PERSIST' => $prefixFirst,
|
||||
'STRLEN' => $prefixFirst,
|
||||
'SETRANGE' => $prefixFirst,
|
||||
'GETRANGE' => $prefixFirst,
|
||||
'SETBIT' => $prefixFirst,
|
||||
'GETBIT' => $prefixFirst,
|
||||
'RPUSHX' => $prefixFirst,
|
||||
'LPUSHX' => $prefixFirst,
|
||||
'LINSERT' => $prefixFirst,
|
||||
'BRPOPLPUSH' => $prefixSkipLast,
|
||||
'ZREVRANGEBYSCORE' => $prefixFirst,
|
||||
'WATCH' => $prefixAll,
|
||||
/* ---------------- Redis 2.6 ---------------- */
|
||||
'PTTL' => 'static::first',
|
||||
'PEXPIRE' => 'static::first',
|
||||
'PEXPIREAT' => 'static::first',
|
||||
'PSETEX' => 'static::first',
|
||||
'INCRBYFLOAT' => 'static::first',
|
||||
'BITOP' => 'static::skipFirst',
|
||||
'BITCOUNT' => 'static::first',
|
||||
'HINCRBYFLOAT' => 'static::first',
|
||||
'EVAL' => 'static::evalKeys',
|
||||
'EVALSHA' => 'static::evalKeys',
|
||||
'MIGRATE' => 'static::migrate',
|
||||
'PTTL' => $prefixFirst,
|
||||
'PEXPIRE' => $prefixFirst,
|
||||
'PEXPIREAT' => $prefixFirst,
|
||||
'PSETEX' => $prefixFirst,
|
||||
'INCRBYFLOAT' => $prefixFirst,
|
||||
'BITOP' => $prefixSkipFirst,
|
||||
'BITCOUNT' => $prefixFirst,
|
||||
'HINCRBYFLOAT' => $prefixFirst,
|
||||
'EVAL' => $prefixEvalKeys,
|
||||
'EVALSHA' => $prefixEvalKeys,
|
||||
'MIGRATE' => $prefixMigrate,
|
||||
/* ---------------- Redis 2.8 ---------------- */
|
||||
'SSCAN' => 'static::first',
|
||||
'ZSCAN' => 'static::first',
|
||||
'HSCAN' => 'static::first',
|
||||
'PFADD' => 'static::first',
|
||||
'PFCOUNT' => 'static::all',
|
||||
'PFMERGE' => 'static::all',
|
||||
'ZLEXCOUNT' => 'static::first',
|
||||
'ZRANGEBYLEX' => 'static::first',
|
||||
'ZREMRANGEBYLEX' => 'static::first',
|
||||
'ZREVRANGEBYLEX' => 'static::first',
|
||||
'BITPOS' => 'static::first',
|
||||
'SSCAN' => $prefixFirst,
|
||||
'ZSCAN' => $prefixFirst,
|
||||
'HSCAN' => $prefixFirst,
|
||||
'PFADD' => $prefixFirst,
|
||||
'PFCOUNT' => $prefixAll,
|
||||
'PFMERGE' => $prefixAll,
|
||||
'ZLEXCOUNT' => $prefixFirst,
|
||||
'ZRANGEBYLEX' => $prefixFirst,
|
||||
'ZREMRANGEBYLEX' => $prefixFirst,
|
||||
'ZREVRANGEBYLEX' => $prefixFirst,
|
||||
'BITPOS' => $prefixFirst,
|
||||
/* ---------------- Redis 3.2 ---------------- */
|
||||
'HSTRLEN' => 'static::first',
|
||||
'BITFIELD' => 'static::first',
|
||||
'GEOADD' => 'static::first',
|
||||
'GEOHASH' => 'static::first',
|
||||
'GEOPOS' => 'static::first',
|
||||
'GEODIST' => 'static::first',
|
||||
'GEORADIUS' => 'static::georadius',
|
||||
'GEORADIUSBYMEMBER' => 'static::georadius',
|
||||
);
|
||||
'HSTRLEN' => $prefixFirst,
|
||||
'BITFIELD' => $prefixFirst,
|
||||
'GEOADD' => $prefixFirst,
|
||||
'GEOHASH' => $prefixFirst,
|
||||
'GEOPOS' => $prefixFirst,
|
||||
'GEODIST' => $prefixFirst,
|
||||
'GEORADIUS' => $prefixGeoradius,
|
||||
'GEORADIUSBYMEMBER' => $prefixGeoradius,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,7 +209,7 @@ class KeyPrefixProcessor implements ProcessorInterface
|
||||
if ($command instanceof PrefixableCommandInterface) {
|
||||
$command->prefixKeys($this->prefix);
|
||||
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
|
||||
call_user_func($this->commands[$commandID], $command, $this->prefix);
|
||||
$this->commands[$commandID]($command, $this->prefix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xadd
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XADD extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XADD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$args = array();
|
||||
|
||||
$args[] = $arguments[0];
|
||||
$options = $arguments[3] ?? [];
|
||||
|
||||
if (isset($options['nomkstream']) && $options['nomkstream']) {
|
||||
$args[] = 'NOMKSTREAM';
|
||||
}
|
||||
|
||||
if (isset($options['trim']) && is_array($options['trim'])) {
|
||||
array_push($args, ...$options['trim']);
|
||||
|
||||
if (isset($options['limit'])) {
|
||||
$args[] = 'LIMIT';
|
||||
$args[] = $options['limit'];
|
||||
}
|
||||
}
|
||||
|
||||
// ID, default to * to let Redis set it
|
||||
$args[] = $arguments[2] ?? '*';
|
||||
if (isset($arguments[1]) && is_array($arguments[1])) {
|
||||
foreach ($arguments[1] as $key => $val) {
|
||||
$args[] = $key;
|
||||
$args[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
parent::setArguments($args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xdel
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XDEL extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XDEL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeVariadic($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xlen
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XLEN extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XLEN';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xrange
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XRANGE extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XRANGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 4) {
|
||||
$arguments[] = $arguments[3];
|
||||
$arguments[3] = 'COUNT';
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($data as $entry) {
|
||||
$processed = array();
|
||||
$count = count($entry[1]);
|
||||
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$processed[$entry[1][$i]] = $entry[1][++$i];
|
||||
}
|
||||
|
||||
$result[$entry[0]] = $processed;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xrevrange
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XREVRANGE extends XRANGE
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XREVRANGE';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/xtrim
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class XTRIM extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'XTRIM';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$args = [];
|
||||
$options = $arguments[3] ?? [];
|
||||
|
||||
$args[] = $arguments[0];
|
||||
// Either e.g. 'MAXLEN' or ['MAXLEN', '~']
|
||||
if (is_array($arguments[1])) {
|
||||
array_push($args, ...$arguments[1]);
|
||||
} else {
|
||||
$args[] = $arguments[1];
|
||||
}
|
||||
|
||||
$args[] = $arguments[2];
|
||||
if (isset($options['limit'])) {
|
||||
$args[] = 'LIMIT';
|
||||
$args[] = $options['limit'];
|
||||
}
|
||||
|
||||
parent::setArguments($args);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ use Predis\Connection\FactoryInterface;
|
||||
use Predis\Connection\NodeConnectionInterface;
|
||||
use Predis\NotSupportedException;
|
||||
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||
use Predis\Response\ServerException;
|
||||
use Predis\Response\Error as ErrorResponse;
|
||||
|
||||
/**
|
||||
* Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
|
||||
@@ -54,6 +56,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
private $strategy;
|
||||
private $connections;
|
||||
private $retryLimit = 5;
|
||||
private $retryInterval = 10;
|
||||
|
||||
/**
|
||||
* @param FactoryInterface $connections Optional connection factory.
|
||||
@@ -82,6 +85,26 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
$this->retryLimit = (int) $retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial retry interval (milliseconds).
|
||||
*
|
||||
* @param int $retryInterval Milliseconds between retries.
|
||||
*/
|
||||
public function setRetryInterval($retryInterval)
|
||||
{
|
||||
$this->retryInterval = (int) $retryInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the retry interval (milliseconds).
|
||||
*
|
||||
* @return int Milliseconds between retries.
|
||||
*/
|
||||
public function getRetryInterval()
|
||||
{
|
||||
return (int) $this->retryInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -207,6 +230,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
private function queryClusterNodeForSlotMap(NodeConnectionInterface $connection)
|
||||
{
|
||||
$retries = 0;
|
||||
$retryAfter = $this->retryInterval;
|
||||
$command = RawCommand::create('CLUSTER', 'SLOTS');
|
||||
|
||||
RETRY_COMMAND: {
|
||||
@@ -226,7 +250,10 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`');
|
||||
}
|
||||
|
||||
usleep($retryAfter * 1000);
|
||||
$retryAfter = $retryAfter * 2;
|
||||
++$retries;
|
||||
|
||||
goto RETRY_COMMAND;
|
||||
}
|
||||
}
|
||||
@@ -482,24 +509,40 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
*/
|
||||
private function retryCommandOnFailure(CommandInterface $command, $method)
|
||||
{
|
||||
$failure = false;
|
||||
$retries = 0;
|
||||
$retryAfter = $this->retryInterval;
|
||||
|
||||
RETRY_COMMAND: {
|
||||
try {
|
||||
$response = $this->getConnectionByCommand($command)->$method($command);
|
||||
} catch (ConnectionException $exception) {
|
||||
$connection = $exception->getConnection();
|
||||
$connection->disconnect();
|
||||
|
||||
$this->remove($connection);
|
||||
if ($response instanceof ErrorResponse) {
|
||||
$message = $response->getMessage();
|
||||
|
||||
if ($failure) {
|
||||
if (strpos($message, 'CLUSTERDOWN') !== false) {
|
||||
throw new ServerException($message);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
usleep($retryAfter * 1000);
|
||||
$retryAfter = $retryAfter * 2;
|
||||
|
||||
if ($exception instanceof ConnectionException) {
|
||||
$connection = $exception->getConnection();
|
||||
|
||||
if ($connection) {
|
||||
$connection->disconnect();
|
||||
$this->remove($connection);
|
||||
}
|
||||
}
|
||||
|
||||
if ($retries === $this->retryLimit) {
|
||||
throw $exception;
|
||||
} elseif ($this->useClusterSlots) {
|
||||
$this->askSlotMap();
|
||||
}
|
||||
|
||||
$failure = true;
|
||||
++$retries;
|
||||
|
||||
goto RETRY_COMMAND;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ class Parameters implements ParametersInterface
|
||||
'port' => 6379,
|
||||
);
|
||||
|
||||
/**
|
||||
* Set of connection paramaters already filtered
|
||||
* for NULL or 0-length string values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* @param array $parameters Named array of connection parameters.
|
||||
*/
|
||||
|
||||
@@ -70,6 +70,11 @@ class SentinelReplication implements ReplicationInterface
|
||||
*/
|
||||
protected $sentinels = array();
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $sentinelIndex = 0;
|
||||
|
||||
/**
|
||||
* @var NodeConnectionInterface
|
||||
*/
|
||||
@@ -264,6 +269,12 @@ class SentinelReplication implements ReplicationInterface
|
||||
$parameters['database'] = null;
|
||||
$parameters['username'] = null;
|
||||
|
||||
// don't leak password from between configurations
|
||||
// https://github.com/predis/predis/pull/807/#discussion_r985764770
|
||||
if (! isset($parameters['password'])) {
|
||||
$parameters['password'] = null;
|
||||
}
|
||||
|
||||
if (!isset($parameters['timeout'])) {
|
||||
$parameters['timeout'] = $this->sentinelTimeout;
|
||||
}
|
||||
@@ -282,11 +293,13 @@ class SentinelReplication implements ReplicationInterface
|
||||
public function getSentinelConnection()
|
||||
{
|
||||
if (!$this->sentinelConnection) {
|
||||
if (!$this->sentinels) {
|
||||
if ($this->sentinelIndex >= count($this->sentinels)) {
|
||||
$this->sentinelIndex = 0;
|
||||
throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
|
||||
}
|
||||
|
||||
$sentinel = array_shift($this->sentinels);
|
||||
$sentinel = $this->sentinels[$this->sentinelIndex];
|
||||
++$this->sentinelIndex;
|
||||
$this->sentinelConnection = $this->createSentinelConnection($sentinel);
|
||||
}
|
||||
|
||||
@@ -307,6 +320,7 @@ class SentinelReplication implements ReplicationInterface
|
||||
);
|
||||
|
||||
$this->sentinels = array();
|
||||
$this->sentinelIndex = 0;
|
||||
// NOTE: sentinel server does not return itself, so we add it back.
|
||||
$this->sentinels[] = $sentinel->getParameters()->toArray();
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ class StreamConnection extends AbstractConnection
|
||||
$socket = $this->getResource();
|
||||
|
||||
while (($length = strlen($buffer)) > 0) {
|
||||
$written = @fwrite($socket, $buffer);
|
||||
$written = is_resource($socket) ? @fwrite($socket, $buffer) : false;
|
||||
|
||||
if ($length === $written) {
|
||||
return;
|
||||
@@ -304,7 +304,7 @@ class StreamConnection extends AbstractConnection
|
||||
$bytesLeft = ($size += 2);
|
||||
|
||||
do {
|
||||
$chunk = fread($socket, min($bytesLeft, 4096));
|
||||
$chunk = is_resource($socket) ? fread($socket, min($bytesLeft, 4096)) : false;
|
||||
|
||||
if ($chunk === false || $chunk === '') {
|
||||
$this->onConnectionError('Error while reading bytes from the server.');
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Util\Test as TestUtil;
|
||||
use Predis\Client;
|
||||
use Predis\Command;
|
||||
use Predis\Connection;
|
||||
@@ -315,7 +316,10 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
|
||||
*/
|
||||
protected function getRequiredRedisServerVersion(): ?string
|
||||
{
|
||||
$annotations = $this->getAnnotations();
|
||||
$annotations = TestUtil::parseTestMethodAnnotations(
|
||||
get_class($this),
|
||||
$this->getName(false)
|
||||
);
|
||||
|
||||
if (isset($annotations['method']['requiresRedisVersion'], $annotations['method']['group']) &&
|
||||
!empty($annotations['method']['requiresRedisVersion']) &&
|
||||
|
||||
@@ -101,6 +101,22 @@ class SREM_Test extends PredisCommandTestCase
|
||||
$this->assertSame(0, $redis->srem('digits', 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.4.0
|
||||
*/
|
||||
public function testRemovesMembersInArrayTypeFromSetVariadic(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->sadd('letters', 'a', 'b', 'c', 'd');
|
||||
|
||||
$this->assertSame(2, $redis->srem('letters', ['b', 'd', 'z']));
|
||||
$this->assertSameValues(array('a', 'c'), $redis->smembers('letters'));
|
||||
|
||||
$this->assertSame(0, $redis->srem('digits', [1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XADD_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider dataFilterArguments
|
||||
*/
|
||||
public function testFilterArguments(array $arguments, array $expected): void
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
public function dataFilterArguments(): array
|
||||
{
|
||||
$data = [];
|
||||
$data[] = [
|
||||
[
|
||||
'stream',
|
||||
['key' => 'val'],
|
||||
'*',
|
||||
['trim' => ['MINID', '~', '0-1'], 'limit' => 5, 'nomkstream' => true]
|
||||
],
|
||||
['stream', 'NOMKSTREAM', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key', 'val'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
[
|
||||
'stream',
|
||||
['key1' => 'val1', 'key2' => 'val2'],
|
||||
'*',
|
||||
['trim' => ['MINID', '~', '0-1'], 'limit' => 5, 'nomkstream' => true]
|
||||
],
|
||||
['stream', 'NOMKSTREAM', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key1', 'val1', 'key2', 'val2'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
[
|
||||
'stream',
|
||||
['key' => 'val'],
|
||||
'*',
|
||||
['trim' => ['MINID', '~', '0-1'], 'limit' => 5]
|
||||
],
|
||||
['stream', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key', 'val'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
[
|
||||
'stream',
|
||||
['key' => 'val'],
|
||||
'*',
|
||||
['trim' => ['MINID', '~', '0-1']]
|
||||
],
|
||||
['stream', 'MINID', '~', '0-1', '*', 'key', 'val'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
[
|
||||
'stream',
|
||||
['key' => 'val'],
|
||||
'*',
|
||||
['trim' => ['MINID', '0-1']]
|
||||
],
|
||||
['stream', 'MINID', '0-1', '*', 'key', 'val'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
['stream', ['key' => 'val'], '2-3'],
|
||||
['stream', '2-3', 'key', 'val'],
|
||||
];
|
||||
|
||||
$data[] = [
|
||||
['stream', ['key' => 'val']],
|
||||
['stream', '*', 'key', 'val'],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XADD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XADD';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testAddsToStreamWithDefaults(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$this->assertSame(1, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testAddsToStreamWithSpecificId(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$id = time() . '-123';
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], $id);
|
||||
|
||||
$response = $redis->xrange('stream', $id, $id);
|
||||
$this->assertCount(1, $response);
|
||||
$this->assertNotNull($response[$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testNomkstreamWhenStreamDoesNotExist(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('new-stream', ['key' => 'val'], '*', ['nomkstream' => true]);
|
||||
|
||||
$this->assertSame(0, $redis->exists('new-stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testNomkstreamWhenStreamExists(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], '*', ['nomkstream' => true]);
|
||||
|
||||
$this->assertSame(1, $redis->exists('stream'));
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMinidExact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$id = $redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MINID', $id]]);
|
||||
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMinidInexact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$id = $redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MINID', '~', $id]]);
|
||||
|
||||
$this->assertSame(3, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testTrimOnMaxlenExact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MAXLEN', 2]]);
|
||||
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testTrimOnMaxlenInexact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MAXLEN', '~', 2]]);
|
||||
|
||||
$this->assertSame(3, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMaxlenWithLimit(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$redis->xadd(
|
||||
'stream',
|
||||
['key' => 'val'],
|
||||
'*',
|
||||
['trim' => ['MAXLEN', '~', 2], 'limit' => 2]
|
||||
);
|
||||
|
||||
$this->assertSame(4, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->xadd('foo', ['key' => 'val']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XDEL_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XDEL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XDEL';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
$arguments = array('stream', 'id1', 'id2', 'id3');
|
||||
$expected = array('stream', 'id1', 'id2', 'id3');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testRemovesSpecifiedMembers(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key0' => 'val0'], '0-1');
|
||||
$redis->xadd('stream', ['key1' => 'val1'], '1-1');
|
||||
$redis->xadd('stream', ['key2' => 'val2'], '2-1');
|
||||
|
||||
$this->assertSame(2, $redis->xdel('stream', '0-1', '2-1', '99-1'));
|
||||
$this->assertSame(['1-1' => ['key1' => 'val1']], $redis->xrange('stream', '-', '+'));
|
||||
|
||||
$this->assertSame(0, $redis->xdel('stream', '0-1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->xdel('foo', 'bar');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XLEN_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XLEN';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XLEN';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
$arguments = array('key');
|
||||
$expected = array('key');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testReturnsLengthOfList(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$this->assertSame(3, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testReturnsZeroLengthOnNonExistingList(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertSame(0, $redis->llen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->xlen('foo');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XRANGE_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XRANGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XRANGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
$arguments = array('stream', '0-1', '1-2', 123);
|
||||
$expected = array('stream', '0-1', '1-2', 'COUNT', 123);
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsNoCount(): void
|
||||
{
|
||||
$arguments = array('stream', '0-1', '1-2');
|
||||
$expected = array('stream', '0-1', '1-2');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$raw = array(array('0-1', ['key', 'val']));
|
||||
$expected = array('0-1' => ['key' => 'val']);
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponseMultipleKeys(): void
|
||||
{
|
||||
$raw = array(array('0-1', ['key1', 'val1', 'key2', 'val2']));
|
||||
$expected = array('0-1' => ['key1' => 'val1', 'key2' => 'val2']);
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testReturnsElementsInRange(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$redis->xadd('stream', ['key' . $i => 'val' . $i], $i . '-1');
|
||||
}
|
||||
|
||||
$this->assertSame(array(), $redis->xrange('stream', '1-1', '0-1'));
|
||||
$this->assertSame(
|
||||
array('0-1' => ['key0' => 'val0']),
|
||||
$redis->xrange('stream', '0-1', '0-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('0-1' => ['key0' => 'val0'], '1-1' => ['key1' => 'val1']),
|
||||
$redis->xrange('stream', '0-1', '1-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('0-1' => ['key0' => 'val0'], '1-1' => ['key1' => 'val1']),
|
||||
$redis->xrange('stream', '-', '1-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('8-1' => ['key8' => 'val8'], '9-1' => ['key9' => 'val9']),
|
||||
$redis->xrange('stream', '8-1', '+')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('5-1' => ['key5' => 'val5'], '6-1' => ['key6' => 'val6']),
|
||||
$redis->xrange('stream', '5-1', '6-1')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testMultipleKeys(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '0-1');
|
||||
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '1-1');
|
||||
|
||||
$this->assertSame(
|
||||
array(
|
||||
'0-1' => ['key1' => 'val1', 'key2' => 'val2'],
|
||||
'1-1' => ['key1' => 'val1', 'key2' => 'val2'],
|
||||
),
|
||||
$redis->xrange('stream', '-', '+')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->xrange('foo', '0-1', '1-1');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XREVRANGE_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XREVRANGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XREVRANGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
$arguments = array('stream', '1-1', '0-1', 123);
|
||||
$expected = array('stream', '1-1', '0-1', 'COUNT', 123);
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsNoCount(): void
|
||||
{
|
||||
$arguments = array('stream', '1-1', '0-1');
|
||||
$expected = array('stream', '1-1', '0-1');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$raw = array(array('0-1', ['key', 'val']));
|
||||
$expected = array('0-1' => ['key' => 'val']);
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponseMultipleKeys(): void
|
||||
{
|
||||
$raw = array(array('0-1', ['key1', 'val1', 'key2', 'val2']));
|
||||
$expected = array('0-1' => ['key1' => 'val1', 'key2' => 'val2']);
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testReturnsElementsInRange(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$redis->xadd('stream', ['key' . $i => 'val' . $i], $i . '-1');
|
||||
}
|
||||
|
||||
$this->assertSame(array(), $redis->xrevrange('stream', '0-1', '1-1'));
|
||||
$this->assertSame(
|
||||
array('0-1' => ['key0' => 'val0']),
|
||||
$redis->xrevrange('stream', '0-1', '0-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('1-1' => ['key1' => 'val1'], '0-1' => ['key0' => 'val0']),
|
||||
$redis->xrevrange('stream', '1-1', '0-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('1-1' => ['key1' => 'val1'], '0-1' => ['key0' => 'val0']),
|
||||
$redis->xrevrange('stream', '1-1', '-')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('9-1' => ['key9' => 'val9'], '8-1' => ['key8' => 'val8']),
|
||||
$redis->xrevrange('stream', '+', '8-1')
|
||||
);
|
||||
$this->assertSame(
|
||||
array('6-1' => ['key6' => 'val6'], '5-1' => ['key5' => 'val5']),
|
||||
$redis->xrevrange('stream', '6-1', '5-1')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testMultipleKeys(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '0-1');
|
||||
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '1-1');
|
||||
|
||||
$this->assertSame(
|
||||
array(
|
||||
'1-1' => ['key1' => 'val1', 'key2' => 'val2'],
|
||||
'0-1' => ['key1' => 'val1', 'key2' => 'val2'],
|
||||
),
|
||||
$redis->xrevrange('stream', '+', '-')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->xrevrange('foo', '1-1', '0-1');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
<?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\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-stream
|
||||
*/
|
||||
class XTRIM_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return 'Predis\Command\Redis\XTRIM';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'XTRIM';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider dataFilterArguments
|
||||
*/
|
||||
public function testFilterArguments(array $arguments, array $expected): void
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
public function dataFilterArguments(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
['stream', ['MINID', '~'], '0-1', ['limit' => 10]],
|
||||
['stream', 'MINID', '~', '0-1', 'LIMIT', 10],
|
||||
],
|
||||
[
|
||||
['stream', ['MINID'], '0-1', ['limit' => 10]],
|
||||
['stream', 'MINID', '0-1', 'LIMIT', 10],
|
||||
],
|
||||
[
|
||||
['stream', 'MINID', '0-1', ['limit' => 10]],
|
||||
['stream', 'MINID', '0-1', 'LIMIT', 10],
|
||||
],
|
||||
[
|
||||
['stream', 'MINID', '0-1'],
|
||||
['stream', 'MINID', '0-1'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testTrimOnMaxlenExact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$res = $redis->xtrim('stream', 'MAXLEN', 2);
|
||||
|
||||
$this->assertSame(1, $res);
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testTrimOnMaxlenInexact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$res = $redis->xtrim('stream', ['MAXLEN', '~'], 2);
|
||||
|
||||
$this->assertSame(2, $res);
|
||||
$this->assertSame(3, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMinidExact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$id = $redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$res = $redis->xtrim('stream', 'MINID', $id);
|
||||
|
||||
$this->assertSame(1, $res);
|
||||
$this->assertSame(2, $redis->xlen('stream'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMinidInexact(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$id = $redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$res = $redis->xtrim('stream', ['MINID', '~'], $id);
|
||||
|
||||
$this->assertSame(2, $res);
|
||||
$this->assertSame(3, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testTrimOnMaxlenWithLimit(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$config = $redis->config('get', 'stream-node-max-entries');
|
||||
$oldStreamNodeMaxEntries = (int) array_pop($config);
|
||||
$redis->config('set', 'stream-node-max-entries', 2);
|
||||
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
$redis->xadd('stream', ['key' => 'val']);
|
||||
|
||||
$res = $redis->xtrim('stream', ['MAXLEN', '~'], 2, ['limit' => 2]);
|
||||
|
||||
$this->assertSame(2, $res);
|
||||
$this->assertSame(4, $redis->xlen('stream'));
|
||||
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 5.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('key', 'foo');
|
||||
$redis->xtrim('key', 'MAXLEN', 2);
|
||||
}
|
||||
}
|
||||
@@ -1301,4 +1301,144 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$this->assertEquals($cluster, $unserialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @group disconnected
|
||||
* @group slow
|
||||
*/
|
||||
public function testRetryCommandSuccessOnClusterDownErrors()
|
||||
{
|
||||
$clusterDownError= new Response\Error("CLUSTERDOWN") ;
|
||||
|
||||
$command = Command\RawCommand::create('get', 'node:1001');
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
||||
$connection1->expects($this->exactly(3))
|
||||
->method('executeCommand')
|
||||
->with($command)
|
||||
->will($this->onConsecutiveCalls(
|
||||
$clusterDownError,
|
||||
$clusterDownError,
|
||||
'foobar'));
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
$cluster->useClusterSlots(false);
|
||||
$cluster->setRetryLimit(2);
|
||||
$cluster->add($connection1);
|
||||
|
||||
$this->assertSame('foobar', $cluster->executeCommand($command));
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @group disconnected
|
||||
* @group slow
|
||||
*/
|
||||
public function testRetryCommandFailureOnClusterDownErrors()
|
||||
{
|
||||
$this->expectException('Predis\Response\ServerException');
|
||||
$this->expectExceptionMessage('CLUSTERDOWN');
|
||||
|
||||
$clusterDownError= new Response\Error("CLUSTERDOWN") ;
|
||||
|
||||
$command = Command\RawCommand::create('get', 'node:1001');
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
||||
$connection1->expects($this->exactly(3))
|
||||
->method('executeCommand')
|
||||
->with($command)
|
||||
->will($this->onConsecutiveCalls(
|
||||
$clusterDownError,
|
||||
$clusterDownError,
|
||||
$clusterDownError
|
||||
));
|
||||
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
$cluster->useClusterSlots(false);
|
||||
$cluster->setRetryLimit(2);
|
||||
$cluster->add($connection1);
|
||||
|
||||
$cluster->executeCommand($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @group disconnected
|
||||
* @group slow
|
||||
*/
|
||||
public function testQueryClusterNodeForSlotMapPauseDurationOnRetry()
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
array(5461, 10922, array('127.0.0.1', 6382), array()),
|
||||
array(10923, 16383, array('127.0.0.1', 6383), array()),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
$connection1
|
||||
->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
'CLUSTER', array('SLOTS')
|
||||
))
|
||||
->willThrowException(
|
||||
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
||||
);
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection2
|
||||
->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
'CLUSTER', array('SLOTS')
|
||||
))
|
||||
->willThrowException(
|
||||
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6383]')
|
||||
);
|
||||
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
|
||||
$connection3
|
||||
->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
'CLUSTER', array('SLOTS')
|
||||
))
|
||||
->willReturn($slotsmap);
|
||||
|
||||
$factory = $this->getMockBuilder('Predis\Connection\FactoryInterface')->getMock();
|
||||
$factory
|
||||
->expects($this->never())
|
||||
->method('create');
|
||||
|
||||
// TODO: I'm not sure about mocking a protected method, but it'll do for now
|
||||
/** @var Connection\Cluster\RedisCluster|MockObject */
|
||||
$cluster = $this->getMockBuilder('Predis\Connection\Cluster\RedisCluster')
|
||||
->onlyMethods(array('getRandomConnection'))
|
||||
->setConstructorArgs(array($factory))
|
||||
->getMock();
|
||||
$cluster
|
||||
->expects($this->exactly(3))
|
||||
->method('getRandomConnection')
|
||||
->willReturnOnConsecutiveCalls($connection1, $connection2, $connection3);
|
||||
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
$cluster->add($connection3);
|
||||
|
||||
$cluster->setRetryInterval(2000);
|
||||
|
||||
$startTime = time() ;
|
||||
$cluster->askSlotMap();
|
||||
$endTime = time();
|
||||
$totalTime=$endTime-$startTime;
|
||||
$t1 = $cluster->getRetryInterval() ;
|
||||
$t2 = $t1 * 2;
|
||||
|
||||
$expectedTime = ($t1 + $t2 )/1000 ; // expected time for 2 retries (fail 1=wait 2s, fail 2=wait 4s , OK)
|
||||
$this->AssertEqualsWithDelta($expectedTime, $totalTime, 1, "Unexpected execution time") ;
|
||||
|
||||
$this->assertCount(16384, $cluster->getSlotMap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class SentinelReplicationTest extends PredisTestCase
|
||||
|
||||
$this->assertArrayNotHasKey('database', $parameters, 'Parameter `database` was expected to not exist in connection parameters');
|
||||
$this->assertArrayNotHasKey('username', $parameters, 'Parameter `username` was expected to not exist in connection parameters');
|
||||
$this->assertArrayNotHasKey('password', $parameters, 'Parameter `password` was expected to not exist in connection parameters');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +118,30 @@ class SentinelReplicationTest extends PredisTestCase
|
||||
$this->assertNotNull($parameters->database);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testConnectionParametersInstanceForSentinelConnectionIsNotModifiedEmptyPassword(): void
|
||||
{
|
||||
$sentinel1 = Connection\Parameters::create('tcp://127.0.0.1:5381?role=sentinel&database=1&password=');
|
||||
$sentinel2 = Connection\Parameters::create('tcp://127.0.0.1:5381?role=sentinel&database=1');
|
||||
|
||||
$replication1 = $this->getReplicationConnection('svc', array($sentinel1));
|
||||
$replication2 = $this->getReplicationConnection('svc', array($sentinel2));
|
||||
|
||||
$parameters1 = $replication1->getSentinelConnection()->getParameters();
|
||||
$parameters2 = $replication2->getSentinelConnection()->getParameters();
|
||||
|
||||
$this->assertSame($sentinel1, $parameters1);
|
||||
$this->assertSame($sentinel2, $parameters2);
|
||||
|
||||
$this->assertNull($parameters1->password);
|
||||
$this->assertNull($parameters2->password);
|
||||
|
||||
$this->assertNotNull($parameters1->database);
|
||||
$this->assertNotNull($parameters2->database);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
@@ -1447,6 +1472,52 @@ class SentinelReplicationTest extends PredisTestCase
|
||||
$this->assertEquals($strategy, $unserialized->getReplicationStrategy());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testMethodGetSentinelConnectionAfterSentinelRestart(): void
|
||||
{
|
||||
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
|
||||
$sentinel1
|
||||
->expects($this->exactly(2))
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
'SENTINEL', array('sentinels', 'svc')
|
||||
))
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->throwException(new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')),
|
||||
array(
|
||||
array(
|
||||
'name', '127.0.0.1:5382',
|
||||
'ip', '127.0.0.1',
|
||||
'port', '5382',
|
||||
'runid', 'f53b52d281be5cdd4873700c94846af8dbe47209',
|
||||
'flags', 'sentinel',
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
|
||||
$sentinel2
|
||||
->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
'SENTINEL', array('sentinels', 'svc')
|
||||
))
|
||||
->willThrowException(
|
||||
new Connection\ConnectionException($sentinel2, 'Unknown connection error [127.0.0.1:5382]')
|
||||
);
|
||||
|
||||
$replication = $this->getReplicationConnection('svc', array($sentinel1, $sentinel2));
|
||||
try {
|
||||
$replication->updateSentinels();
|
||||
} catch (\Predis\ClientException $exception){
|
||||
$this->assertEquals('No sentinel server available for autodiscovery.', $exception->getMessage());
|
||||
}
|
||||
|
||||
$replication->updateSentinels();
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
// ---- HELPER METHODS ------------------------------------------------ //
|
||||
// ******************************************************************** //
|
||||
|
||||
@@ -57,6 +57,29 @@ class StreamConnectionTest extends PredisConnectionTestCase
|
||||
$connection->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testDoesntThrowErrorOnInvalidResource(): void
|
||||
{
|
||||
$this->expectException('Predis\Connection\ConnectionException');
|
||||
|
||||
$cmdSelect = RawCommand::create('SELECT', '1000');
|
||||
$invalidResource = null;
|
||||
|
||||
/** @var NodeConnectionInterface|MockObject */
|
||||
$connection = $this
|
||||
->getMockBuilder($this->getConnectionClass())
|
||||
->onlyMethods(array('getResource'))
|
||||
->setConstructorArgs(array(new Parameters()))
|
||||
->getMock();
|
||||
$connection
|
||||
->method('getResource')
|
||||
->willReturn($invalidResource);
|
||||
|
||||
$connection->writeRequest($cmdSelect);
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
Reference in New Issue
Block a user