We should actually make use of more protected methods in this
class instead of the private ones, this is most likely going
to be addressed in future commits.
See also issue #86 on GitHub for the reason behind this change.
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.
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.
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 makes it possible to easily assign a connection to a slot range
using the key "slots" in connection parameters like in the following
example:
$parameters = array(
'tcp://127.0.0.1:6379?slots=0-1364',
'tcp://127.0.0.1:6380?slots=1365-2729',
'tcp://127.0.0.1:6381?slots=2730-4095',
);
$options = array('cluster' => 'redis');
$client = new Predis\Client($parameters, $options);
It is possible to get the full list of slot ranges for each node of
the cluster using redis-cli connected to one of the nodes:
./redis-cli -h 127.0.0.1 -p 6379 CLUSTER NODES
The last column in the resulting output contains the slots assigned
to each instance participating to the redis cluster.
This is useful to avoid guessing the correct server instance at runtime
thus reducing the overhead of a guess-and-try approach since relying on
-ASK or -MOVED responses returned by a node is a costly operation.
For now, you can pre-associate slots using the "cluster" client option:
$parameters = array(
'tcp://127.0.0.1:6379',
'tcp://127.0.0.1:6380',
'tcp://127.0.0.1:6381',
);
$options = array(
'cluster' => function ($options) {
$cluster = new Predis\Connection\RedisCluster();
$cluster->setSlots(0, 1364, '127.0.0.1:6379');
$cluster->setSlots(1365, 2729, '127.0.0.1:6380');
$cluster->setSlots(2730, 4095, '127.0.0.1:6381');
return $cluster;
},
);
$client = new Predis\Client($parameters, $options);
In the future we will make the slots configuration easier by fetching
the configuration from a node using the command "CLUSTER NODES" or by
pre-computing the slices of slots associated to a node when adding it
to the cluster connection object.
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).
This implementation is capable of handling "ASK" and "MOVED" replies
returned by Redis when one of the nodes asks the client for temporary
or permanent redirects of a slot to a different node.
Performances look almost on par with our client-side sharding solution
and everything looks already relatively stable.
Tests are still missing.
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.
The "throw_errors" connection parameter has been removed and replaced by the
new "exceptions" client option since exceptions on -ERR replies returned by
Redis are not generated by connection classes anymore but are thrown by the
client class and other abstractions such as pipeline contexts.
This change does not affect much people using the Predis\Client class (aside
from the different configuration) but gives much more flexibility to those
building their own pieces of code around the internal classes of Predis.
The reason for this change is that not every cluster implementation can support
this behaviour, think of Redis cluster for example. We moved the implementation
of this method in Predis\Connection\PredisCluster since it can still be useful.