This is more consistent with Predis\Client::executeRaw() and its more
explicit since simply "raw" as a method name was a bit too vague even
despite being nicely short.
Status response objects are needed mostly to make it possible from the
client perspective to differentiate a status response with the payload
"OK" from a normale bulk reply containing "OK".
The biggest change is for commands returning +OK responses: these were
previously translated to TRUE (bool value), but they are now returned
as instances of Predis\Response\Status. Just to illustrate an example
of the possibilities with this change we will use SET since it is the
most widely used command returning +OK:
$response = $client->set('foo', 'bar');
echo $response; // 'OK'
$response == 'OK'; // TRUE
isset($response->ok); // TRUE
$response == true; // TRUE
$response === true; // FALSE
$response instanceof Predis\Response\ObjectInterface; // TRUE
$response instanceof Predis\Response\Status; // TRUE
For those checking responses returned by commands such as SET or PONG,
the breaking change basically lies in the usage of strict comparison:
doing $response === true will now evaluate to FALSE instead of TRUE.
By default Predis caches common status responses such as OK or QUEUED
to lower the memory usage when using pipelines or transactions.
When sending raw commands their arguments are not filtered, responses
are not parsed and key prefixes are not applied. The client also does
not throw any exception on Redis errors regardless of its settings.
The first parameter takes the raw arguments of the command (included
its identifier) as defined by the Redis documentation while the second
optional parameter is always populated by reference to indicate when
Redis actually returned an error response.
$client->raw(['PING']); // "PONG"
$client->raw(['SET','foo','bar']); // "OK"
$client->raw(['GET','foo'], $err); // "bar", $err=FALSE
$client->raw(['LPUSH','foo',1], $err); // "WRONGTYPE...", $err=TRUE
Internally, this method creates instances of Predis\Command\RawCommand
that get passed to the underlying connection instance for execution as
if they were usual commands defined by Predis.
Raw commands work in both cluster and replication scenarios since they
are recognized by their command ID, but key prefixing is not supported
since it is done by the profile instance when instantiating commands.
We also changed our wording to indentify this kind of abstraction so
instead of using "scripted commands" (kind of broken English) we now
use "scriptable commands".
Only two options available for now, used to specify which kind of
pipeline object the client should use or return:
- "atomic": returns a pipeline wrapped in a MULTI / EXEC transaction
(class: Predis\Pipeline\Atomic).
- "fire-and-forget": returns a pipeline that does not read back
responses from the server (class: Predis\Pipeline\FireAndForget).
We might add more options in the future.
First of all we completely removed the concept of pipeline executors.
Now pipelines can be easily customized by extending our default class
Predis\Pipeline\Pipeline.
Tests coverage for the Predis\Pipeline namespace is decent but can be
definitely improved while test cases can be beautified.
This option must return a callable object that is used to override how
the client aggregates connections when passing an array of parameters
to its constructor.
When specified, this option overrides both "cluster" and "replication"
as it allows to make use of your own code to aggregate multiple nodes.
This is, for example, how you can mimic the standard initialization of
a cluster that relies on client-side sharding:
$parameters = ['tcp://127.0.0.1:6380', 'tcp://127.0.0.1:6381'];
$options = [
'aggregate' => function () {
return function ($parameters, $options) {
$connection = new Predis\Connection\PredisCluster();
$options->connections->aggregate($connection, $parameters);
return $connection;
};
},
];
$client = new Predis\Client($parameters, $options);
When invoked by the client, the specified callable must always return
a Predis\Connection\ConnectionInterface instance or the client will
throw an UnexpectedValueException.
The main reason behind that code duplication was performance related
as we tried to reduce method calls when possible, even at the cost of
falling into the realm of early optimizations. Apparently we just lose
~400 req/sec on a 21000 req/sec basis ("SET foo bar") using PHP 5.5.3
(packaged by Ubuntu 13.10) on an Intel Q6600, so we will most likely
stick with this change for the sake of best practices.
The "profile" member is actually used for caching purposes as fetching
its value from the options instance would add noticeable overhead in a
part of the client where every bit of optimization matters, for this
we decided to keep it private.
This commit is a complete rewrite of the classes previously contained
in the Predis\Option namespace aimed at lowering the initialization
overhead while bringing in more consistency. The overall idea is still
the same with a mini DI container, Predis\Configuration\Options, which
carries options with values that can be initialized lazily.
The first difference with our previous implementation is that now even
user-defined options can be initialized lazily, everything needed is
an object responding to the __invoke() magic method such as a closure.
Other kind of callable arguments (strings, arrays) will be treated as
plain values. The only drawback is that we cannot pass any instance of
classes implementing __invoke() as an option value, but considered the
limited scope of our use case we can say it's more of an acceptable
compromise. Callbacks used for lazy initialization will receive two
arguments upon invokation:
- The current instance of Predis\Configuration\Option ($options)
- A string containing the name of the option ($option)
This is an example in actual code:
$options = new Predis\Configuration\Options([
'exceptions' => true,
'profile' => '2.8',
'distributor' => function () {
return new Predis\Cluster\Distribution\KetamaPureRing();
},
'cluster' => function ($options) {
$distr = $options->distributor;
$strategy = new Predis\Cluster\PredisClusterHashStrategy($distr);
$cluster = new Predis\Connection\PredisCluster();
return $cluster;
},
'connections' => function ($options, $option) {
$factory = $options->getDefault($option);
$factory->define('tcp', 'Predis\Connection\PhpiredisConnection');
return $factory;
},
]);
As you can see there's very little difference compared to before in
the actual usage as most changes are under the hood. Some options such
as "exceptions" and "replication" can now correctly parse bool values
from strings (so the string "false" is not evaluated as boolean true).
While options were initially conceived to configure the client and its
behavior, the concept has matured and it's perfectly fine to consider
the use of Predis\Configuration\Options to propagate configurations to
inner parts of the library.
This commit marks the start of works for the next major release of
Predis which will bring various breaking changes needed to polish the
internal design making the library even more flexible to use or extend
and, more importantly, almost stable in terms of API.
The Redis commands API exposed by Predis\Client is not going to change
much if not at all which is a good news. The most immediate changes
affecting developers will involve the renaming of a few namespaces and
classes, the removal of some previously deprecated classes and methods
and some tweaks to the current abstractions.
Right now the plan is to have a fast paced development to release this
version as soon as possible, ideally a few weeks later than Redis 2.8,
then wait to see the final definition of redis-cluster so that we can
tweak our code if needed and finally hit the v1.0.0 milestone with as
few changes as possible. Furthermore, v1.x will most likely be the
last version of Predis supporting PHP 5.3 as we will start migrating
to PHP 5.4 (or even 5.5) with v2.x, which is not going to happen soon
anyway.
Client::pubSub() still works like usual by returning a new pub/sub
context, but it is now considered an alias of Client::pubSubLoop().
This change is necessary in preparation for the next major version
of Predis where Client::pubSub() will be used for the new PUBSUB
command introduced in Redis 2.8.