Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Named Connections #36

Closed
indyarmy opened this Issue · 4 comments

2 participants

Russ Porosky Daniele Alessandri
Russ Porosky

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).

Daniele Alessandri
Owner
Adding support for _named connections_ that uses an alias instead of the __ip:port__ pair for the hashring should be quite easy with the development version of Predis by creating a subclass of [PredisCluster](http://github.com/nrk/predis/blob/master/lib/Predis/Network/PredisCluster.php) that overrides `Predis\Network\PredisCluster::add()` to use the already existing (but optional) `alias` property for a connection. The configuration of the client would then look something like the following snippet:

SNIPPET REMOVED

Might end up adding this one as an optional cluster connection since it's quite easy.

UPDATE: well, I got it wrong... but it's still something easy to implement :-) See my next comment for more details.

Regarding a transparent master/slave configuration (@Seldaek's request), I'm still not sure how to implement this in a good way and, more than anything else, I don't think it could work decently alongside a clustered scenario (at least due to how Predis is structured right now).

Comments?

EDIT: I forgot to give you a clear answer about the case when the cluster configuration is changed! With the default implementation of a client-side cluster, when you change an ip:port pair of one of your Redis instances then you inevitably incur in the rehashing of some keys. With the proposed named-nodes approach, reashing occurs only when the number of nodes in your cluster configuration changes.

Russ Porosky

Thanks for the response, and I'm glad to hear it's not a hard thing to do - That would be awesome!

Any estimate on when we'd see the cluster functionality released?

Daniele Alessandri
Owner

Heh I got things a bit wrong, that's how it goes when spending a whole month without touching a single line of code :-) The distribution algorithm used by Predis\Network\PredisCluster can be already customized using different distribution strategies, by default it's Predis\Distribution\HashRing which uses the classic ip:port pair.

After pushing this slight change it's now possible to reuse most of the code of Predis\Distribution\HashRing when, in a subclass, we just need to change how we get the hash of a node. With this change we can implement our strategy that calculates the hash of a node against its alias with very few lines of code:

<?php

class AliasBasedHashRing extends Predis\Distribution\HashRing {
    public function add($node, $weight = null) {
        $parameters = $node->getParameters();
        if (!$parameters->isSetByUser('alias')) {
            throw new \InvalidArgumentException(
                "The 'alias' property must be set for {$node} to use " .
                "this kind of distribution strategy"
            );
        }
        parent::add($node, $weight);
    }

    protected function getNodeHash($nodeObject) {
        return $nodeObject->getParameters()->alias;
    }
}

You can find a complete and usable example in this gist. If you change the configuration of the nodes (e.g. you move one of them to a new port, or a completely new host) you'll notice that the number of keys for each node won't change.

I'm still not sure if, when and how I will merge this into the main repository (btw, the name AliasBasedHashRing is terrible!). It's something quite small and very specific that it could live in a future side-project related to Predis. In the meanwhile you can just get this class and use it in your project just like I did in the linked example.

I'll just leave this issue open for now until I decide how to proceed.

Daniele Alessandri nrk referenced this issue from a commit
Daniele Alessandri Add optional callable to drive extraction of node hash in distributor.
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.
09de7be
Daniele Alessandri
Owner

It's been a while but I finally came up with something easy enough to set up in user's code and way more flexible than my previously proposed solution.

As explained in the message of 09de7be, you can now instruct the client to use the alias parameter instead the default ip:port pair using options and without the need to extend a base class:

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);

This change will make it into Predis v0.8.1.

Daniele Alessandri nrk closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.