Giter Site home page Giter Site logo

predis's Introduction

Predis

Software license Latest stable Latest development Monthly installs Build status Coverage Status

A flexible and feature-complete Redis client for PHP 7.2 and newer.

More details about this project can be found on the frequently asked questions.

Main features

  • Support for Redis from 3.0 to 7.2.
  • Support for clustering using client-side sharding and pluggable keyspace distributors.
  • Support for redis-cluster (Redis >= 3.0).
  • Support for master-slave replication setups and redis-sentinel.
  • Transparent key prefixing of keys using a customizable prefix strategy.
  • Command pipelining on both single nodes and clusters (client-side sharding only).
  • Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
  • Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between EVALSHA or EVAL.
  • Abstraction for SCAN, SSCAN, ZSCAN and HSCAN (Redis >= 2.8) based on PHP iterators.
  • Connections are established lazily by the client upon the first command and can be persisted.
  • Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
  • Support for custom connection classes for providing different network or protocol backends.
  • Flexible system for defining custom commands and override the default ones.

How to install and use Predis

This library can be found on Packagist for an easier management of projects dependencies using Composer. Compressed archives of each release are available on GitHub.

composer require predis/predis

Loading the library

Predis relies on the autoloading features of PHP to load its files when needed and complies with the PSR-4 standard. Autoloading is handled automatically when dependencies are managed through Composer, but it is also possible to leverage its own autoloader in projects or scripts lacking any autoload facility:

// Prepend a base path if Predis is not available in your "include_path".
require 'Predis/Autoloader.php';

Predis\Autoloader::register();

Connecting to Redis

When creating a client instance without passing any connection parameter, Predis assumes 127.0.0.1 and 6379 as default host and port. The default timeout for the connect() operation is 5 seconds:

$client = new Predis\Client();
$client->set('foo', 'bar');
$value = $client->get('foo');

Connection parameters can be supplied either in the form of URI strings or named arrays. The latter is the preferred way to supply parameters, but URI strings can be useful when parameters are read from non-structured or partially-structured sources:

// Parameters passed using a named array:
$client = new Predis\Client([
    'scheme' => 'tcp',
    'host'   => '10.0.0.1',
    'port'   => 6379,
]);

// Same set of parameters, passed using an URI string:
$client = new Predis\Client('tcp://10.0.0.1:6379');

Password protected servers can be accessed by adding password to the parameters set. When ACLs are enabled on Redis >= 6.0, both username and password are required for user authentication.

It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case the parameters must use the unix scheme and specify a path for the socket file:

$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
$client = new Predis\Client('unix:/path/to/redis.sock');

The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on various cloud hosting providers. Encryption can be enabled with using the tls scheme and an array of suitable options passed via the ssl parameter:

// Named array of connection parameters:
$client = new Predis\Client([
  'scheme' => 'tls',
  'ssl'    => ['cafile' => 'private.pem', 'verify_peer' => true],
]);

// Same set of parameters, but using an URI string:
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');

The connection schemes redis (alias of tcp) and rediss (alias of tls) are also supported, with the difference that URI strings containing these schemes are parsed following the rules described on their respective IANA provisional registration documents.

The actual list of supported connection parameters can vary depending on each connection backend so it is recommended to refer to their specific documentation or implementation for details.

Predis can aggregate multiple connections when providing an array of connection parameters and the appropriate option to instruct the client about how to aggregate them (clustering, replication or a custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations for each node:

$client = new Predis\Client([
    'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
], [
    'cluster' => 'predis',
]);

See the aggregate connections section of this document for more details.

Connections to Redis are lazy meaning that the client connects to a server only if and when needed. While it is recommended to let the client do its own stuff under the hood, there may be times when it is still desired to have control of when the connection is opened or closed: this can easily be achieved by invoking $client->connect() and $client->disconnect(). Please note that the effect of these methods on aggregate connections may differ depending on each specific implementation.

Client configuration

Many aspects and behaviors of the client can be configured by passing specific client options to the second argument of Predis\Client::__construct():

$client = new Predis\Client($parameters, ['prefix' => 'sample:']);

Options are managed using a mini DI-alike container and their values can be lazily initialized only when needed. The client options supported by default in Predis are:

  • prefix: prefix string applied to every key found in commands.
  • exceptions: whether the client should throw or return responses upon Redis errors.
  • connections: list of connection backends or a connection factory instance.
  • cluster: specifies a cluster backend (predis, redis or callable).
  • replication: specifies a replication backend (predis, sentinel or callable).
  • aggregate: configures the client with a custom aggregate connection (callable).
  • parameters: list of default connection parameters for aggregate connections.
  • commands: specifies a command factory instance to use through the library.

Users can also provide custom options with values or callable objects (for lazy initialization) that are stored in the options container for later use through the library.

Aggregate connections

Aggregate connections are the foundation upon which Predis implements clustering and replication and they are used to group multiple connections to single Redis nodes and hide the specific logic needed to handle them properly depending on the context. Aggregate connections usually require an array of connection parameters along with the appropriate client option when creating a new client instance.

Cluster

Predis can be configured to work in clustering mode with a traditional client-side sharding approach to create a cluster of independent nodes and distribute the keyspace among them. This approach needs some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually when nodes are added or removed:

$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options    = ['cluster' => 'predis'];

$client = new Predis\Client($parameters);

Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form of redis-cluster. This kind of approach uses a different algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not necessarily complete since it will automatically discover new nodes if necessary) and the cluster client options set to redis:

$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options    = ['cluster' => 'redis'];

$client = new Predis\Client($parameters, $options);

Replication

The client can be configured to operate in a single master / multiple slaves setup to provide better service availability. When using replication, Predis recognizes read-only commands and sends them to a random slave in order to provide some sort of load-balancing and switches to the master as soon as it detects a command that performs any kind of operation that would end up modifying the keyspace or the value of a key. Instead of raising a connection error when a slave fails, the client attempts to fall back to a different slave among the ones provided in the configuration.

The basic configuration needed to use the client in replication mode requires one Redis server to be identified as the master (this can be done via connection parameters by setting the role parameter to master) and one or more slaves (in this case setting role to slave for slaves is optional):

$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options    = ['replication' => 'predis'];

$client = new Predis\Client($parameters, $options);

The above configuration has a static list of servers and relies entirely on the client's logic, but it is possible to rely on redis-sentinel for a more robust HA environment with sentinel servers acting as a source of authority for clients for service discovery. The minimum configuration required by the client to work with redis-sentinel is a list of connection parameters pointing to a bunch of sentinel instances, the replication option set to sentinel and the service option set to the name of the service:

$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options   = ['replication' => 'sentinel', 'service' => 'mymaster'];

$client = new Predis\Client($sentinels, $options);

If the master and slave nodes are configured to require an authentication from clients, a password must be provided via the global parameters client option. This option can also be used to specify a different database index. The client options array would then look like this:

$options = [
    'replication' => 'sentinel',
    'service' => 'mymaster',
    'parameters' => [
        'password' => $secretpassword,
        'database' => 10,
    ],
];

While Predis is able to distinguish commands performing write and read-only operations, EVAL and EVALSHA represent a corner case in which the client switches to the master node because it cannot tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior, when certain Lua scripts do not perform write operations it is possible to provide an hint to tell the client to stick with slaves for their execution:

$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options    = ['replication' => function () {
    // Set scripts that won't trigger a switch from a slave to the master node.
    $strategy = new Predis\Replication\ReplicationStrategy();
    $strategy->setScriptReadOnly($LUA_SCRIPT);

    return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
}];

$client = new Predis\Client($parameters, $options);
$client->eval($LUA_SCRIPT, 0);             // Sticks to slave using `eval`...
$client->evalsha(sha1($LUA_SCRIPT), 0);    // ... and `evalsha`, too.

The examples directory contains a few scripts that demonstrate how the client can be configured and used to leverage replication in both basic and complex scenarios.

Command pipelines

Pipelining can help with performances when many commands need to be sent to a server by reducing the latency introduced by network round-trip timings. Pipelining also works with aggregate connections. The client can execute the pipeline inside a callable block or return a pipeline instance with the ability to chain commands thanks to its fluent interface:

// Executes a pipeline inside the given callable block:
$responses = $client->pipeline(function ($pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", str_pad($i, 4, '0', 0));
        $pipe->get("key:$i");
    }
});

// Returns a pipeline that can be chained thanks to its fluent interface:
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();

Transactions

The client provides an abstraction for Redis transactions based on MULTI and EXEC with a similar interface to command pipelines:

// Executes a transaction inside the given callable block:
$responses = $client->transaction(function ($tx) {
    $tx->set('foo', 'bar');
    $tx->get('foo');
});

// Returns a transaction that can be chained thanks to its fluent interface:
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();

This abstraction can perform check-and-set operations thanks to WATCH and UNWATCH and provides automatic retries of transactions aborted by Redis when WATCHed keys are touched. For an example of a transaction using CAS you can see the following example.

Adding new commands

While we try to update Predis to stay up to date with all the commands available in Redis, you might prefer to stick with an old version of the library or provide a different way to filter arguments or parse responses for specific commands. To achieve that, Predis provides the ability to implement new command classes to define or override commands in the default command factory used by the client:

// Define a new command by extending Predis\Command\Command:
class BrandNewRedisCommand extends Predis\Command\Command
{
    public function getId()
    {
        return 'NEWCMD';
    }
}

// Inject your command in the current command factory:
$client = new Predis\Client($parameters, [
    'commands' => [
        'newcmd' => 'BrandNewRedisCommand',
    ],
]);

$response = $client->newcmd();

There is also a method to send raw commands without filtering their arguments or parsing responses. Users must provide the list of arguments for the command as an array, following the signatures as defined by the Redis documentation for commands:

$response = $client->executeRaw(['SET', 'foo', 'bar']);

Script commands

While it is possible to leverage Lua scripting on Redis 2.6+ using directly EVAL and EVALSHA, Predis offers script commands as an higher level abstraction built upon them to make things simple. Script commands can be registered in the command factory used by the client and are accessible as if they were plain Redis commands, but they define Lua scripts that get transmitted to the server for remote execution. Internally they use EVALSHA by default and identify a script by its SHA1 hash to save bandwidth, but EVAL is used as a fall back when needed:

// Define a new script command by extending Predis\Command\ScriptCommand:
class ListPushRandomValue extends Predis\Command\ScriptCommand
{
    public function getKeysCount()
    {
        return 1;
    }

    public function getScript()
    {
        return <<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
return rnd
LUA;
    }
}

// Inject the script command in the current command factory:
$client = new Predis\Client($parameters, [
    'commands' => [
        'lpushrand' => 'ListPushRandomValue',
    ],
]);

$response = $client->lpushrand('random_values', $seed = mt_rand());

Customizable connection backends

Predis can use different connection backends to connect to Redis. The builtin Relay integration leverages the Relay extension for PHP for major performance gains, by caching a partial replica of the Redis dataset in PHP shared runtime memory.

$client = new Predis\Client('tcp://127.0.0.1', [
    'connections' => 'relay',
]);

Developers can create their own connection classes to support whole new network backends, extend existing classes or provide completely different implementations. Connection classes must implement Predis\Connection\NodeConnectionInterface or extend Predis\Connection\AbstractConnection:

class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
{
    // Implementation goes here...
}

// Use MyConnectionClass to handle connections for the `tcp` scheme:
$client = new Predis\Client('tcp://127.0.0.1', [
    'connections' => ['tcp' => 'MyConnectionClass'],
]);

For a more in-depth insight on how to create new connection backends you can refer to the actual implementation of the standard connection classes available in the Predis\Connection namespace.

Development

Reporting bugs and contributing code

Contributions to Predis are highly appreciated either in the form of pull requests for new features, bug fixes, or just bug reports. We only ask you to adhere to issue and pull request templates.

Test suite

ATTENTION: Do not ever run the test suite shipped with Predis against instances of Redis running in production environments or containing data you are interested in!

Predis has a comprehensive test suite covering every aspect of the library and that can optionally perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify the correct behavior of the implementation of each command. Integration tests for unsupported Redis commands are automatically skipped. If you do not have Redis up and running, integration tests can be disabled. See the tests README for more details about testing this library.

Predis uses GitHub Actions for continuous integration and the history for past and current builds can be found on its actions page.

License

The code for Predis is distributed under the terms of the MIT license (see LICENSE).

predis's People

Contributors

abraovic avatar andrew-demb avatar b1rdex avatar bobrik avatar chayim avatar clslrns avatar drealecs avatar franmomu avatar glaubinix avatar grahamcampbell avatar hannesvdvreken avatar irvyne avatar krydos avatar matthewbaggett avatar nrk avatar orvice avatar qusonann avatar raphaelstolt avatar remicollet avatar seldaek avatar snc avatar srmklive avatar szepeviktor avatar thbourlove avatar tillkruss avatar tobiasbengtsson avatar udat avatar vjik avatar vladvildanov avatar zaherg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

predis's Issues

Detect no connection without producing a warning?

Is there a way to detect if the connection to redis was established successfully, and if not, perform a different action? Right now, if it can't connect to the redis server, it produces a run-time warning.

Setting predis database parameter not working?

I have this as a setting.

$single_server = array(
    'host'     => '127.0.0.1',
    'port'     => 6379,
    'database' => 15
);

$remote_single_server = array(
    'host'     => '10.241.54.128',
    'port'     => 6379,
    'database' => 15
);

then when I call predis using the following code:
$r = new Predis\Client($single_server);

whenever I push a value to the key value store it pushes it to database 0
so i have to this then it works.

$r->select($single_server['database']);

However when I do this on a remote client.

$r = new Predis\Client($remote_single_server);
$r->select($remote_single_server['database']);

It pushes to database 0. even though I already specified to select 15

What seems to be the problem here? Thanks

accidentally hit the close issue button

createSingleFile.php not working

hello,

it seems that createSinglwFile.php doesn't work ;(

bodik@bodik:~/meta/predis/bin$ php createSingleFile.php
PHP Notice: Undefined offset: 0 in /home/bodik/meta/predis/bin/createSingleFile.php on line 187
PHP Fatal error: Call to a member function getDependencies() on a non-object in /home/bodik/meta/predis/bin/createSingleFile.php on line 80

php5-cli: 5.3.3-7+squeeze3
predis: current head as of 62744fb

thanks for any help

bodik

Possible Autoloader problem?

Hi, I have Predis installed to /usr/share/php (which is a php include path). I am running PHP 5.3.8.

$ php -a
Interactive shell

php > require 'Predis/Autoloader.php';
php > use \Predis;
php > Predis\Autoloader::register();
php > $r = new Predis\Client();
PHP Fatal error:  Class '\Predis\Profiles\ServerVersion24' not found in /usr/share/php/Predis/Profiles/ServerProfile.php on line 124
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. Predis\Client->__construct() php shell code:1
PHP   3. Predis\ClientOptions->__get() php shell code:46
PHP   4. Predis\Options\ClientProfile->getDefault() /usr/share/php/Predis/ClientOptions.php:134
PHP   5. Predis\Profiles\ServerProfile::getDefault() /usr/share/php/Predis/Options/ClientProfile.php:47
PHP   6. Predis\Profiles\ServerProfile::get() /usr/share/php/Predis/Profiles/ServerProfile.php:53

Unable to catch exceptions

Version 0.7.1 and 0.7.2-dev. It is impossible to catch Predis\Network\ConnectionException exceptions. I have tried multiple ways I can think of to fix this, but so far are unable to.

        include_once('Predis/Autoloader.php');
        Predis\Autoloader::register();
        try {
            $this->redis = new Predis\Client($this->config['redis_servers'], array('prefix' => $this->config['db']['sql_database'].':', 'throw_errors' => false));
        } catch (Predis\Network\ConnectionException $e) {
            var_dump($e);
        }

lowercase vs uppercase

Hi,

it should be mentioned or fixed, because this works:

$cmdSet = clsRedis::getRedis()->createCommand('info');

and this doesn't work:

$cmdSet = clsRedis::getRedis()->createCommand('INFO');

which is quite strange.

'Predis_ClientException' with message 'Cannot assign requested address'

I'm using predis and from time to time I have these messages in php.err:

PHP Fatal error: Uncaught exception 'Predis_ClientException' with message
'Cannot assign requested address' in /home/meshok/predis.php:648
Stack trace:
#0 /home/meshok/predis.php(708): Predis_Connection->connect()
#1 /home/meshok/predis.php(680): Predis_Connection->getSocket()
#2 /home/meshok/predis.php(116):

Predis_Connection->writeCommand(Object(Predis_Commands_SetAdd))
#3 /home/meshok/predis.php(124):

Predis_Client->executeCommandInternal(Object(Predis_Connection),
Object(Predis_Commands_SetAdd))
#4 /home/meshok/predis.php(108):

Predis_Client->executeCommand(Object(Predis_Commands_SetAdd))
#5 [internal function]: Predis_Client->__call('sadd', Array)
#6 /home/meshok/bounce.php(55): Predis_Client->sadd('emails_550',

'storm@radioener...')

What version of the product are you using? On what operating system?
redis 1.2.2 32 bit on 64 bit debian

What can couse that

feature: add an out-of-band interface to pipeline

in order to read from redis while writing to it via a pipeline, it is sometime convenient to re-use the underlying connection object in a pipeline. I did a little patch to predis like this:

//PipelineContext.php

public function __get($name) {
return ($name == 'oob') ? $this->_client : null;
}

while using it:

$value = $pipe->oob->get('key');

Unexpected character in input: '\' (ASCII=92) state=1

I am getting errors with Predis under Centos 5. I am running PHP 5.2.13. I get following error message when I try to instantiate Redis\Client.

Warning: Unexpected character in input: '' (ASCII=92) state=1 in

Is there any particular reason why backslashes are used ?

Fatal Error on connection time out

Hi, I'm using the 5.2 branch and get this error whenever there's connection problem:

PHP Fatal error: Uncaught exception 'Predis_CommunicationException' with message 'Connection timed out' in redis.class.php:1387

This is the code i use:

    try {
        $r = new Predis_Client();
    } catch (PredisException $e) {
        error_log($e->getMessage());
    }

Anyone have any idea? Would like to catch the error instead of getting fatal errors. Thank you!

ERR operation not permitted with "auth" command

Hi,

I use authentication on my Redis box and when I try to reach it using $redis->auth("My passowrd") it crashes with an Exception

Here is the code to reproduce

<?php
require("autoload.php");
$redis = new Predis\Client(array(
     'host'     => '1.2.3.4',
     'port'     => 6379,
     'database' => 15));
$result = $redis->auth("my password");
?>

And here is the result

PHP Warning: Uncaught exception 'Predis\ServerException' with message 'ERR operation not permitted' in /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php:257
Stack trace:

0 /usr/share/nginx/www/Core/predis/lib/Predis/Network/ConnectionBase.php(142): Predis\Network\StreamConnection->read()

1 /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php(160): Predis\Network\ConnectionBase->readResponse(Object(Predis\Commands\ConnectionSelect))

2 /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php(135): Predis\Network\StreamConnection->sendInitializationCommands()

3 /usr/share/nginx/www/Core/predis/lib/Predis/Network/ConnectionBase.php(197): Predis\Network\StreamConnection->connect()

4 /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php(172): Predis\Network\ConnectionBase->getResource()

5 /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php(285): Predis\Network\StreamConnection->writeBytes('*2??$4??AUTH??$...')

6 /usr/share/ in /usr/share/nginx/www/Core/predis/lib/Predis/Network/StreamConnection.php on line 257

Did I miss something or is there a bug?

My password is long and complicated but even with a password like 12345 the issue is the same.

I tried to authenticate using

array(
    'host'     => '1.2.3.4',
    'port'     => 6379,
    'database' => 15,
    'password' => "my password")

and it works, nonetheless, I can't use auth as the first command just after a plain connection.

Thank you.

Ugly code when using pipleline

This really isn't an issue with predis but i'm looking for some direction on how I can abstract this code more and not make it look so ugly. The self thing is because of 5.3. Maybe you could put some more complex examples in the wiki?

 function increment($field, $increment=1) {
    $self = &$this;
    $replies = $this->redis->pipeline(function($pipe) use(&$self, &$field, &$increment) {
      $pipe->hincrby($self->dailyKey(), $field, $increment);
      $pipe->hincrby($self->monthlyKey(), $field, $increment);
      $pipe->hincrby($self->totalKey(), $field, $increment);
    });
    $this->daily->$$field = $replies[0];
    $this->monthly->$$field = $replies[1];
    $this->total->$$field = $replies[2];
  }

  function serverSync() {
    $self = &$this;
    $replies = $this->redis->pipeline(function($pipe) use(&$self) {
      $pipe->hgetall($self->dailyKey());
      $pipe->hgetall($self->monthlyKey());
      $pipe->hgetall($self->totalKey());
    }); 
  }

Slowdown against redis 2.0.x on localhost for outgoing data >8192 bytes?

Edit: Somehow I had missed that the problem only occurs when using setex() and not with regular set(). Since setex() is a feature new to redis 2.0 it consequently seems more likely that the problem lies in the server and not in the predis library. I am not allowed to delete the issue (as far as I can see) so please deal with it as you find appropriate.

I've been testing predis 0.6.1 (php-5.2 version, primarily, but verified with 5.3 as well) against a redis server (2.0.0-rc2 and rc3) on localhost and run up against a somewhat weird performance degradation issue, in certain circumstances.

Conditions:

  • redis 2.0 (rc2 or rc3) server running on the same host as the client program
  • outgoing data (via writeBytes) forced to be 8193 bytes or larger

Result:

  • roundtrip time increases by a factor ~100 and performance suffers horribly

More specifically: In my case, roundtrip time goes from ~0.4ms to 0.04s. After some pinpointing the unexpected slowdown appears to come almost exclusively from the call to fgets() in readLine(). There is no measurable cpu load when this happens. It looks like a network hang. I cannot reproduce the problem with the redis-benchmark program from the redis server distribution. Everything works fine when outgoing data is 8192 bytes or less.

Tested on/with:

  • set() and setex()
  • redis 2.0.0 rc2 and rc3
  • virtual and physical hardware
  • 32-bit and 64-bit hardware
  • CentOS 4 and 5, and Fedora 13
  • different network environments

The test works fine with either the redis server (any version) on a different host, or with a redis 1.2.6 server. This might very well be a redis server issue, but since I have been unable to reproduce the problem with other tools, I thought I'd start with reporting it here.

lpush does not appear to be working

Firstly thanks for the good looking library :)

$client->lpush('logs', 'testing 123')

when querying the logs key i get :ResponseError: ERR Operation against a key holding the wrong kind of value

Something im doing wrong or a bug?

regards
ross

hmset returns server error: 'Protocol error: too big bulk count string'

In our production system, I'm seeing occasional failures with hmsets with large arguments. For some items, the failure rate in production is 20% or so.

PHP Fatal error: Uncaught exception 'Predis\ServerException' with message 'ERR Protocol error: too big bulk count string' in phar:///predis_0.7.1.phar/Predis/Network/StreamConnection.php:257
Stack trace:

0 phar:///predis_0.7.1.phar/Predis/Network/ConnectionBase.php(142): Predis\Network\StreamConnection->read()

1 phar:///predis_0.7.1.phar/Predis/Network/ConnectionBase.php(134): Predis\Network\ConnectionBase->readResponse(Object(Predis\Commands\HashSetMultiple))

2 phar:///predis_0.7.1.phar/Predis/Client.php(219): Predis\Network\ConnectionBase->executeCommand(Object(Predis\Commands\HashSetMultiple))

3 [internal function]: Predis\Client->__call('hmset', Array)

4 bugtest.php(42): Predis\Client->hmset('bugtest', Array)

5 {main}

thrown in phar:///predis_0.7.1.phar/Predis/Network/StreamConnection.php on line 257

I've been able to reproduce it in a standalone test, but at a much lower rate - usually much less than 1%.

Test code to reproduce:

<?
require_once 'predis_0.7.1.phar';
for ($i = 0; $i < 10000; $i++) {
  if ($i % 100 == 0) {
    print "$i\n";
  }
  $redis = new Predis\Client('tcp://redis_server:6379');
  $a = array('f' => str_repeat('a', 74707),
             'r' => str_repeat('a', 2),
             'm' => str_repeat('a', 800));
  $redis->hmset('bugtest', $a);
  unset($redis);
}

Eliciting the bug seems to require a new client instance with each loop. I also couldn't reproduce the bug unless connecting to a Redis server on a different machine.

I see that the error string 'too big bulk count string' occurs in the Redis source (2.4.7), and is returned when this test in networking.c succeeds:

if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) /* 1024 * 64 */

php --version:
PHP 5.3.2-1ubuntu4.7 with Suhosin-Patch (cli)

listTrim differs from the official syntax LTRIM

I found that LTRIM is implemented as listTrim, but only by looking at the source. I propose one of the following:
changing it to ltrim
having redundancy and supporting both methods, or
prominently displaying this discrepancy somewhere in the introductory documentation.

thanks.

Transit to a more classical PHP library model

The current monolithic implementation with all the classes in a 3000-lines long file is really not too great to work with. Is there a good reason why this is not following a more classic model like this?

Name\Space\Class => Name/Space/Class.php

I'm happy to help with the transition, but I don't want to do it just to have my patch rejected so I'm asking upfront.

ZSetRange should support LIMIT

Is there any way to support a LIMIT modifier with Z* statements?

With PRedis, how to issue something like:

ZRANGEBYSCORE key -inf 1234 LIMIT 5 10 WITHSCORES

?

If this is not implemented, this would be really a nice addition.

Hset command implemented in Predis ?

Hi ,
First thing first , great job with Predis , I have successfull integrated it a cache system and coutner on a CI project.

I also need to store some hashes with the hset http://redis.io/commands/hset

I know you could just set and the value can be encoded with json_encode and on get with json_decode .

DO you plan to add this later on or there is a work around to this ?

support for failover

This isn't an issue (predis is awesome :D), but rather a question. Does predis support failover? Like if a node goes down, it sends to another server /slave, etc.?

Error while reading line from the server

Using redis for php 5.2 branch. I'm getting random "Error while reading line from the server" error. The command i used is lpush a serialized string.

I'm using predis on a php daemon script, meaning it's up all the time. I've already set the timeout for redis.conf to 0.

Predis not Serializing Objects

It appears that this fails:

// Set Array
$redis->set( 'key', array(1,2,3) );

This is unanticipated with such a complete library. Where is the best place to put an object/array serializer for all Reids Commands?

Infinite recursion

Hello,

Apparently, revision 78f11f1 somehow introduced an infinite recursive loop.

I'm going to try to write a test case.

Clean up namespaces.

Currently there are a lot of verbose namespace definitions in your code. You can save a lot of kilobytes by using the use statement. For example,

use \Predis\MultiBulkCommand;

class Ping extends MultiBulkCommand {}
class Doecho extends MultiBulkCommand {}

instead of

class Ping extends \Predis\MultiBulkCommand {}
class Doecho extends \Predis\MultiBulkCommand {}

this should also be done for exceptions:

use \InvalidArgumentException;
throw new InvalidArgumentException('...');

this change makes the code cleaner and should speed up parsing of this library.

what is the correct syntax of BLPOP on Predis?

I do it like this:

$r = new Predis\Client($single_server, $options);
$retval = $r->blpop('queue:query');

But I get this error:

ERR wrong number of arguments for 'blpop' command

Whenever I do this

$r = new Predis\Client($single_server, $options);
$retval = $r->blpop('queue:query',0);

I get the error:

Error while reading line from the server

Doing it from redis-cli

redis 127.0.0.1:6379> BLPOP queue:query
(error) ERR wrong number of arguments for 'blpop' command
redis 127.0.0.1:6379> BLPOP queue:query 0
1) "queue:query"
2) "hello world"

[PATCH] Minor bugs in getNodeKey method

Hi,

I found some minor bugs in the getNodeKey() function.

The first one is that if you happen to match a key in the while loop ($item == $key) the return result is invalid. It should return $ringKeys[$index] instead of $index.

Second, the binary search implementation is inefficient. On an array of 128 keys (the default replicas), it always finds a key within 7 iterations. This can be corrected by rounding the index.

On the other hand, if you round the index you'll end up with an edge case which produces an incorrect result when the value of $key is lower than the smallest value in the $ringKeys array. It essentially tries to find a key with index -1 (which doesnt exist), so you have to account for that.

I've added a patch below:


diff -urN predis.git/lib/Predis.php predis.git.new/lib/Predis.php
--- predis.git/lib/Predis.php   2010-02-26 02:25:34.000000000 -0500
+++ predis.git.new/lib/Predis.php       2010-02-26 02:36:01.000000000 -0500
@@ -1113,7 +1113,7 @@
         $index = 0;

         while ($lower <= $upper) {
-            $index = ($lower + $upper) / 2;
+            $index = round(($lower + $upper) / 2);
             $item  = $ringKeys[$index];
             if ($item > $key) {
                 $upper = $index - 1;
@@ -1122,9 +1122,13 @@
                 $lower = $index + 1;
             }
             else {
-                return $index;
+                return $ringKeys[$index];
             }
         }
+
+       if ($upper < 0)
+               $upper = count($ringKeys) - 1;
+
         return $ringKeys[$upper];
     }
 }

"keys" command results in error: "ClientException not found"

Hi,

Calling:
$cmdSet = $redis->createCommand('KEYS');
Results in:
PHP Fatal error: Uncaught exception 'Predis\ClientException' with message ''KEYS' is not a registered Redis command' in lib/Predis.php:1994

How do I register a command?

Notice that I am able to set and get key-value pairs to/from our Redis installation with no problems.

Maybe the issue I am having is related to the fact that I am very new to Predis and I wasn't able to find any documentation for Predis other than the Quick Tour in the Wiki page, which is what I used to write these very few simple script.

Are there are other documentation?

Can somebody please help me. It's very urgent.

Thanks,

Frank

Error while reading line from the server

CommunicationExceptions are thrown with the message 'Error while reading line from the server' from time to time. The Predis part of an example stack trace:

Message: Error while reading line from the server
Thrown at: /var/www/lib/redis/Predis.php:1568
Trace:
#0 /var/www/lib/redis/Predis.php(1636): Predis\Connection->onCommunicationException('Error while rea...')
#1 /var/www/lib/redis/Predis.php(724): Predis\Connection->readLine()
#2 /var/www/lib/redis/Predis.php(1577): Predis\FastResponseReader->read(Object(Predis\Connection))
#3 /var/www/lib/redis/Predis.php(1586): Predis\Connection->readResponse(Object(Predis\Commands\Auth))
#4 /var/www/lib/redis/Predis.php(225): Predis\Connection->executeCommand(Object(Predis\Commands\Auth))
#5 /var/www/lib/redis/RedisProxy.php(21): Predis\Client->__call('auth', Array)
#6 /var/www/lib/redis/RedisProxy.php(21): Predis\Client->auth('password')

We couldn't find anything in common in the affected scripts. Some errors are from a local Redis server, some from a remote one. An example that sometimes generates this error:

The script is run by Apache, browsers call it directly by URL, so there can be a lot of instances running at once. The following Redis commands are run via Predis (in this order; a, b , c and d are different tables):

  • AUTH
  • SELECT a
  • EXISTS
  • (if a condition is met) SETEX
  • SELECT b
  • RPUSH
  • if a condition is met
    • SELECT b
    • INCR
    • SELECT c
    • GET
    • SELECT d
    • RPUSH

The bold commands are those that have ever generated the error. The majority (99%) is on AUTH.

Environment:

$ redis-server -v
Redis server version 2.2.4 (00000000:0)

Predis version 0.6.6

$ php -v
PHP 5.3.3-0.dotdeb.1 with Suhosin-Patch (cli) (built: Oct  1 2010 08:49:29) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

$ /usr/sbin/apache2ctl -v
Server version: Apache/2.2.9 (Debian)
Server built:   Apr 20 2010 15:40:17

feature: add an option to skip execute the pipe

the flushPipeline() function executes the pipe then clears it, I think an optional boolean parameter $execute is useful (default to true). While this parameter is set to false, flushPipeline simply clears the pipe without actually sending it to redis. This is useful while reusing a pipeline after execute(). (Call this method with $execute set to false to clear the pipe in case the previous pipeline execution is not clear).

ServerException in command SMEMBERS

i have tried out sadd command, it works very fine.
As i am going to check out the member of a set using $redis->smembers($key);

i got this Exception:

Predis\ServerException: ERR Operation against a key holding the wrong kind of value in /usr/share/php5/PEAR/Predis/Network/StreamConnection.php on line 257

maybe you know whats going there. I am using Predis 0.7.1 and redis 2.4.6

best regards,
JD

SORT method with parameter GET don't work

Hi
when try to execute the code below the result is an array with empty values (keys are present). if delete the get parameter the command work corretly.
$res=$r->sort('key',array(
'get'=>array('otherkey:*'),
'limit'=>array(10,20)));
PS: With redis-cli the same command work.

Master/Slave replication handling

I'm wondering if it would be possible to include this inside Predis, a way to create a virtual connection maybe, that would contain many sub-connections (lazy-initialized, we don't want to connect to every slave for nothing) to slave(s) and the master server.

Then whenever you read stuff, it connects to one (round robin or preference list, not sure) of the slaves. If you write data, it connects to the master, writes it, and then uses the master for subsequent reads - to avoid replication lag issues.

I think it would be great if this could be handled at the library level.

connection_timeout not working as expected

Using predis via RedisBundle. I have set connection_timeout and read_write_timeout to 1. I can see that they are set to 1 in predis. Everything works fine.

Now when I kill my Redis server inside StreamConnection::tcpStreamInitializer() the connect fails. However something weird happens inside the call to $this->onConnectionError(trim($errstr), $errno);. Basically it ends up not throwing the exception and instead using up the max execution time. Is the Exception caught somewhere and a try attempt is made in an infinite loop?

Named Connections

After reading Jeremy's article, I wonder if the following will ever be possible in Predis:

$redis = new Predis\Client(
    array(
        'node01' => array(
            'host' => '10.0.0.1',
            'port' => 6379
        ),
        'node02' => array(
            'host' => '10.0.0.2',
            'port' => 6380
        )
    )
);

I ask because it isn't clear from the documentation whether or not changing one of your server addresses or ports (like say, when promoting a slave, or splitting existing instances on a single machine onto several machines) will affect which servers get which keys.

For example, if I were to change "node02" to refer to a different IP address and port (promoting a slave), won't the hashing routine get confused?

I'd like to see named connections (even including the master/slave nomenclature from saldeak's issue so that addresses can be changed more or less at will without affecting the consistent hashing mechanism.

This would be particularly important in the situation where I have 8 Redis instances (plus 8 slaves) running on 4 machines (2 masters and 2 slaves per machine) now, but then want to double the number of machines and keep the same number of instances (1 master and 1 slave per machine).

installing through pear doesn't work

when performing

pear channel-discover pearhub.org
pear install pearhub/predis

i receive an error:

No releases available for package "pearhub.org/predis"
install failed

Attempting to use MultiExec with CAS fails

I ran into the issue trying to use MultiExec that i would get an exception: 'DISCARD without MULTI'

It seems there are two places where a discard can happen before the multi is issued, both in MultiExecContext::execute()

Reproduce: fresh redis installation(no keys)

  1. Downloaded redis 2.2.2,
  2. start with default redis.conf, no custom configuration
  3. Run examples/MultiExecTransactionsWithCAS.php without setting up the key
  4. Exception thrown: Uncaught exception 'Predis\ServerException' with message 'DISCARD without MULTI'

It appears from my debugging that when $tx->zrange() returns null there ends up being no commands and a discard is attempted, but because the multi is only done when zrange returns a result there is no multi when discard is called.

From what i can tell, the same thing can happen in two locations of MultiExecContext::execute()
the catch(\Exception $exception) block, and the if(count($commands) === 0) block

Test Output:

/predis/examples$ php MultiExecTransactionsWithCAS.php 

PHP Fatal error:  Uncaught exception 'Predis\ServerException' with message 'DISCARD without MULTI' in /predis/lib/Predis/Network/StreamConnection.php:173
Stack trace:
#0 /predis/lib/Predis/Network/StreamConnection.php(199): Predis\Network\StreamConnection->read()
#1 /predis/lib/Predis/Network/ConnectionBase.php(71): Predis\Network\StreamConnection->readResponse(Object(Predis\Commands\Discard))
#2 /predis/lib/Predis/Client.php(137): Predis\Network\ConnectionBase->executeCommand(Object(Predis\Commands\Discard))
#3 [internal function]: Predis\Client->__call('discard', Array)
#4 /predis/lib/Predis/MultiExecContext.php(183): Predis\Client->discard()
#5 /predis/lib/Predis/Client.php(209): Predis\MultiExecContext->execute in /predis/lib/Predis/Network/StreamConnection.php on line 173

unix socket

make predis to connect to redis by unix socet

''..' is not a registered Redis command

Hi,

this works:

$cmdSet = clsRedis::getRedis()->createCommand('set', array('library', 'predis'));
echo $info = clsRedis::getRedis()->executeCommand($cmdSet);

this doesnt:

$cmdSet = clsRedis::getRedis()->createCommand('debug object', array('library'));
$info = clsRedis::getRedis()->executeCommand($cmdSet);

$cmdSet = clsRedis::getRedis()->createCommand('config get', array('*'));
$info = clsRedis::getRedis()->executeCommand($cmdSet);

What I am doing wrong ?

Fatal error: Uncaught exception

Is there a way to check using predis if Redis server is running ?

I intentionally try to use predis when Redis server hasn't been started. When i do this i keep receiving blank page with fatal error.
(when Redis is down i don't want my users see a blank page)

Is there a way to troubleshoot this ?

The error is below:
Fatal error: Uncaught exception 'Predis_CommunicationException' with message 'Попытка установить соединение была безуспешной, т.к. от другого компьютера за требуемое время не получен нужный отклик, или было разорвано уже установленное соединение из-за неверного отклика уже подключенного компьютера.' in C:\wamp\www\test\html\predis\lib\Predis.php:1303
Stack trace:
#0 C:\wamp\www\test\html\predis\lib\Predis.php(1268): Predis_Connection->onCommunicationException('??????? ???????...', 10060)
#1 C:\wamp\www\test\html\predis\lib\Predis.php(1382): Predis_Connection->connect()
#2 C:\wamp\www\test\html\predis\lib\Predis.php(1335): Predis_Connection->getSocket()
#3 C:\wamp\www\test\html\predis\lib\Predis.php(1308): Predis_Connection->writeBytes('*2??$3??GET??$6...')
#4 C:\wamp\www\test\html\predis\lib\Predis.php(1318): Predis_Connection->writeCommand(Object(Predis_Commands_Get))
#5 C:\wamp\www\test\html\predis\lib\Predis.php(207): Predis_Connection->executeCommand(Object(Predis_Commands_Get))
#6 [internal function]: Predis_C in C:\wamp\www\test\html\predis\lib\Predis.php on line 1303

Predis/PHP namespace issue

Hi,

I was trying out the custom PHP session handler session_set_save_handler to store session
information in Redis (https://github.com/ivanstojic/redis-session-php)

I keep getting "Class 'Predis\Client' not found" when I try to make calls from the read or write session callbacks with and without a leading "/".

I can call Predis\Client elsewhere with no problems but inside the callbacks the classes
do not appear to be available.

I am using PHP 5.3.2-1ubuntu4.5 with Suhosin-Patch (cli) (built: Sep 17 2010 13:41:55)

Regards,
George.

predis cluster problem when migrate server

Hi, basically I have a redis cluster setup. For example:

$conn = array(
'0' => array('host' => localhost, 'port' => 6379),
'1' => array('host' => localhost, 'port' => 6378),
'2' => array('host' => localhost, 'port' => 6377),
);

I'm moving one of the node to a bigger server with a different IP. So i moved the data using redis replication, so in this example it would be 10.0.0.1 will be slaveof localhost. And the new config once replication completes becomes:

$conn = array(
'0' => array('host' => '10.0.0.1', 'port' => 6379),
'1' => array('host' => localhost, 'port' => 6378),
'2' => array('host' => localhost, 'port' => 6377),
);

However, the problem now is that predis doesn't seems to get the data it got previously from 10.0.0.1, but instead from other server. It seems like the whole cluster distribution got changed.

Any idea?

Throw exception on 'Connection refused'?

If Redis isn't running, the first command run through Predis seems to exit with "Connection refused" - would it be possible to have it throw an exception instead?

If an exception was thrown, right after the connection I would basically do:

try {
    $predis->ping();
}
catch(\Exception $e) { ... }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.