Implemented the WATCH command for RedisCluster. This command can
take any number of keys, so phpredis splits the request across the
cluster in the best way it can.
For every key in a multiple key command, Redis Cluster requires
that they all hash to the same SLOT, else it will return a
CROSSLOT error and fail.
For WATCH in RedisCluster, a few things to note:
* The command will fail if phpredis is out of sync with the keyspace.
This is because we'll need to know where to deliver each command,
or can't possibly deliver them correctlhy.
* The command will fail if any command delivery failures occur on any
node. This is the case either for a normal communication error or
if RedisCluster returns to us MOVED/ASK redirection.
This is the initial commit supporting transactions in RedisCluster
via MULTI..EXEC. Given that the data can be distributed across
many nodes, we accomplish transactions as follows.
1. When entering MULTI mode, the cluster context state is changed
to MULTI
2. When we send a command to a node, we check that node's RedisSock
state, and if it's still ATOMIC, deliver the MULTI command,
updating the node's state to MULTI.
3. When reading replies, we check if the RedisSock* is in a MULTI
state, which means we need to issue the EXEC command. We do
this and then flip the state back to ATOMIC.
4. On completion, everything is reverted to ATOMIC state and our
reply callbacks are freed and set to NULL.
For transactions, we enforce writes such that they MUST be able to
be processed by the node in question (e.g. they can't work during
a reshard, because there is no way to know what has failed, etc).
This is the same as RedisCluster which will also cancel transactions
in the case where a request was made for data that is somewhere else.
Added distribution option to RedisCluster in preperation for
implementation of commands which can be distributed by the client.
The two distribution modes will be:
* DIST_OOE : Maintain order of execution
* DIST_SPEED : Most efficient delivery
Either way, the RedisCluster object will need to return values that
were gathered in the same way they were sent in, but might execute
them in a different order if the option is set to DIST_SPEED.
Added context void pointer that we pass around, which is
(thus far) really just for hmget (as we need to keep the
keys around to bind them with the returned values).
This requires every command construction routine and each
response callback be passed this void pointer, which is always
NULL except for HMGET
Added HMGET and HMSET commands
Added support for the DUMP command and created a Cluster based
RAW bulk handler. Thinking we should do like MULTI BULK and
create a generic handler, with various callbacks for encoding
We need to figure out how the KEYS command should work in the
context of cluster. We could either send it to all nodes, or
send it to just one slot.
Added TYPE command and cluster response processor
Added a mechanism to our RedisCluster context structure to retain
the last reply_type, single line reply, and/or integer response
which could either be the length of a BULK/MULTIBULK payload, or
an actual integer.
This makes command processing functions simpler, as we just have
to check that the reply type is correct, and for single line/int
responses, the values themselves.
Updated node parsing to understand that clusters can serve
1-N sequential slots, rather than just one contiguous series.
Updated how we detected masters by using that column in CLUSTER NODES
rather than counting them, as a master can be set up that serves no
slots (e.g. newly added or data was migrated away from it).
This is the initial draft of the logic we'll use in phpredis to
direct requests at a cluster, and then upon getting a moved or
ask response, try at the node delivered.
Redis Cluster instance, as well as formalized a generic
macro we will use for any return type.
Further work to be done creating the response parsing generics
and then later in combining them with standard phpredis response
parsing.
Every time we communicate with the cluster we have to determine
which slot to try, and then if we get a MOVED or ASK redirection,
retry at the new slot.
In the case of MOVED, we'll update the node mapping we cache locally.
For ASK, we'll leave it unchanged but try at the slot where directed
This is an initial commmit which adds a RedisCluster class as well
as the framework around which we'll be building proper cluster
support.
The first commit just contains the code to set up and use our new
RedisCluster class as well as parsing logic to handline CLUSTER NODES
such that we can map the keyspace.
Next up, command processing and then pipelining in a sane way.