This cannot be implemented for previous versions of PHP because we
need socket_import_stream() to extract the underlying socket resource
from the stream in order to be able to set the TCP_NODELAY flag.
This commit reflects the recent change from the redis unstable branch
in which the number of hash slots was increased from 4096 to 16384.
See https://github.com/antirez/redis/commit/ebd666d for reference.
This class works just like Predis\Connection\PhpiredisConnection but
it does not require the socket extensions since it relies on PHP's
native streams thus allowing the use of persistent connections.
It is not possible to get the default value of a client option using
either its name or instance:
$options = array(
'profile' => function ($options, $option) {
// instance of Predis\Option\OptionInterface
$profile = $options->getDefault($option);
// string representing an option handled by $options,
// returns NULL if the specified name is not handled.
$profile = $options->getDefault('profile');
return $profile;
},
);
This addition makes it less awkward to get the default value of an
option, especially when not in the context of a callable option
initializer.
This bug was actually introduced right before pushing the stable release
of v0.8.0 in which we moved the responsibility of parsing raw replies
with command parser to consumer classes.
This commit closes#101.
This is mainly in response to the longstanding issue #36 in which my
proposed solution was fine in terms of functionalities, but eventually
never made into the repository since it was far from being clean enough
for my taste.
Now developers can optionally pass a callable object when creating the
hashring instance to decide how the distributor should extract the hash
from a node (really a connection instance) to populate the ring:
use Predis\Cluster\Distribution\HashRing;
use Predis\Connection\PredisCluster;
$servers = array(
'tcp://10.0.0.1?alias=node01',
'tcp://10.0.0.2?alias=node02',
);
$options = array(
'nodehash' => function ($connection) {
return $connection->getParameters()->alias;
},
'cluster' => function ($options) {
$replicas = HashRing::DEFAULT_REPLICAS;
$hashring = new HashRing($replicas, $options->nodehash);
$cluster = new PredisCluster($hashring);
return $cluster;
},
);
$client = new Predis\Client($servers, $options);
Both HashRing and KetamaPureRing in the Predis\Cluster\Distribution
namespace support this new approach.
Previously the getClientFor() method in a subclass of Predis\Client
returned an instance of Predis\Client instead of a new instance of
the subclass. The new behaviour is more correct.
This is just a convention implemented to client options supporting
callable initializers such as "profile", "cluster" and "replication".
This is useful to get a fully-initialized default value and perform
additional operations before returning it. An example with "profile":
$options = array(
'commands' => array(
'test1' => 'Predis\Command\ConnectionEcho',
'test2' => 'Predis\Command\ConnectionEcho',
),
'profile' => function ($options, $option) {
$profile = $option->getDefault($options);
if (is_array($options->commands)) {
foreach ($options->commands as $id => $cmd) {
$profile->defineCommand($id, $cmd);
}
}
return $profile;
},
);
Redis >= 2.8 returns -WRONGTYPE errors instead of -ERR when executing
operations on wrong key type (such as trying to LPUSH on a string key).
Luckily for us, phpunit's @expectedExceptionMessage annotation actually
does not perform an exact match but works on a substring so we just omit
the initial part of the exception message to make the test work.
When using UNSUBSCRIBE without a list of channels (which actually means
"unsubscribe from all the subscribed channels") Redis 2.6 does not
guarantee that channels are returned following the same order of
subscription.
Connection classes should just handle, convert and return simple Redis
types while parsing and transforming structured replies should be done
by consumers (see Predis\Client or Predis\Transaction\MultiExecContext).
This actually makes more sense considering that parsing a complex response
with the associated command parser may require different actions. As an
example, the result of EXEC is a multibulk that holds the actual responses,
so we really need to parse each one of its elements and we should also
make sure that iterable multibulks are consumed. We already did that
previously, but it was weird knowing that command parsers were applied
by the connection class.
This also moves some duplicated logic away from each connection class
implementation which is a nice bonus.
Multibulk response iterators will not be passed anymore to the response
parser method of the command that generated the response. Pipeline and
transaction abstractions still consume interators returned as response
items.
When no server profile is specified, the connection factory will not push
any initialization command to the newly created connection object.
This change is mainly useful when using redis-cluster and also makes it
possible to easily inject commands such as "SCRIPT LOAD" at initialization
time by grouping everything into one common place simply by extending the
connection factory class.
We extract the keys from commands using the second argument of EVAL /
EVALSHA which specifies the number of arguments that must be treated
as keys (used to populate the KEYS table in the Lua script) and then
we check if there is only one key since redis-cluster right now does
not support multi-keys requests.
Our scripted command abstraction is also supported.
We extract the keys from commands using the second argument of EVAL /
EVALSHA which specifies the number of arguments that must be treated
as keys (used to populate the KEYS table in the Lua script) and then
we check if all the keys generate the same hash using the usual method.
Our scripted command abstraction is also supported.
It comes without saying that accessing or setting keys from within the
Lua script is something that might not work as expected, so you should
be careful when using EVAL and EVALSHA in the context of client-side
sharding.
This should make things a tiny bit faster (we are speaking about micro
optimizations anyway) but more importantly it results in a better
encapsulation.
This is needed because redis-cluster does not support the same commands
or operations that can be performed with our client-side managed predis
cluster.
For example redis-cluster does not support key tagging (that is, parts
of a key enclosed by {...} to hash only that specific part of a key)
and multiple-key operations suchs as MGET, MSET, SDIFF, SUNION or SINTER.
Some multiple-key operations can be performed anyway if the command has
only one key (e.g. "MGET foo" and "MSET foo bar" will not fail).
The reason for this change is to support the upcoming redis-cluster since
it has different behaviors compared to the client-side cluster implementation
provided by Predis. For example redis-cluster will not support key tags or
certain operations currently available with our client-side implementation.