Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rfc - a preferred slave list in a sentinel setup #38

Closed
jedi4ever opened this issue May 19, 2015 · 8 comments
Closed

rfc - a preferred slave list in a sentinel setup #38

jedi4ever opened this issue May 19, 2015 · 8 comments
Milestone

Comments

@jedi4ever
Copy link

We want to run a sentinel setup:

  • each webserver has it's own redis slave (will NOT become master - using slave-priority = 0)
  • a set of sentinels running to protect us from partioning and handle the quorum
  • a master and a few redis slaves for failover of master

In the current implementation , the slaves can be selected using the role slave but it picks a rather arbitrary slave. We would propose to have a way to indicate a list of preferred slaves for a client.

This is useful is in multi-AZ aws setups, where we want to keep the latency to app<->slave low .
A new syntax could look like:

var redis = new Redis({
  sentinels: [{ host: 'localhost', port: 26379 }, { host: 'localhost', port: 26380 }],
  preferred_slaves: [ { prio: 10 , host: 'localhost', port: 6381 }]
  name: 'mymaster'
});
@luin
Copy link
Collaborator

luin commented May 19, 2015

Makes sense. Besides a list of preferred slaves, I think we may also accept a function to let user pick up the slave:

var redis = new Redis({
  sentinels: [{ host: 'localhost', port: 26379 }, { host: 'localhost', port: 26380 }],
  preferredSlaves: function (availableSlaves) {
    return _.sample(availableSlaves);
  },
  name: 'mymaster'
});

@luin luin added this to the 2.0.0 milestone Sep 16, 2015
@luin luin modified the milestones: v3, 2.0.0 May 29, 2016
@tangxinfa
Copy link

This feature is what i am looking for.

@doublesharp
Copy link
Contributor

This is exactly the setup I need as well - any idea when it will be available?

@luin
Copy link
Collaborator

luin commented Sep 24, 2016

Aha, my fault. It seems that slave priority have to be set on the Redis side. See http://redis.io/topics/sentinel#slaves-priority for details.

@luin luin closed this as completed Sep 24, 2016
@doublesharp
Copy link
Contributor

I think there is a misunderstanding - the goal isn't to pick a Slave for failover but rather for reads. Assume there are 6 servers, all running Redis and Sentinel.

  • 1 Redis Master
  • 2 Redis Replicas (a slave, but available for failover)
  • 3 Redis Slaves (a slave, but not available for failover)

In the above setup with the 3 "Slaves" running on the application server reads can be made locally to the Slave instance while writes are made to the remote Master. It's assumed that the reads will not always be in sync with the writes based on the asynchronous Redis replication, but in cases where this is OK you get much better performance.

I actually updated the ioredis code/tests to handle this already, and was planning on submitting a pull request.

It accepts either a function (as you suggested) with the available slaves provided as an argument or an array of objects with properties ip, port, and optionally prio (with a default of 1). I used ip rather than host to match the output from the SENTINEL slaves command. The first available "preferred" slave is returned, otherwise the default behavior of returning a random available slave is preserved. The prio isn't the actual slave priority, it is your preference priority - i want to connect to priority 1 because it is local, priority 2 because it is in my network segment, or worst case something else because 1 and 2 are not available. This would not affect failover or Sentinel functionality.

Let me know if you see any issues with the code below in sentinel_connector.js lines 150-204, with var _this = this; at the top of the function on line 136;

// allow the options to prefer particular slave(s)
if (_this.options.preferredSlaves) {
  var preferredSlaves = _this.options.preferredSlaves;
  switch (typeof preferredSlaves) {
  case 'function':
    // use function from options to filter preferred slave
    selectedSlave = _this.options.preferredSlaves(availableSlaves);
    break;
  case 'object':
    if (!Array.isArray(preferredSlaves)) {
      preferredSlaves = [preferredSlaves];
    } else {
      // sort by priority
      preferredSlaves.sort(function (a, b) {
        // default the priority to 1
        if (!a.prio) {
          a.prio = 1;
        }
        if (!b.prio) {
          b.prio = 1;
        }

        // lowest priority first
        if (a.prio < b.prio) {
          return -1;
        }
        if (a.prio > b.prio) {
          return 1;
        }
        return 0;
      });
    }
    // loop over preferred slaves and return the first match
    for (var p = 0; p < preferredSlaves.length; p++) {
      for (var a = 0; a < availableSlaves.length; a++) {
        if (availableSlaves[a].ip === preferredSlaves[p].ip) {
          if (availableSlaves[a].port === preferredSlaves[p].port) {
            selectedSlave = availableSlaves[a];
            break;
          }
        }
      }
      if (selectedSlave) {
        break;
      }
    }
    // if none of the preferred slaves are available, a random available slave is returned
    break;
  }
}
if (!selectedSlave) {
  // get a random available slave
  selectedSlave = _.sample(availableSlaves);
}

@doublesharp
Copy link
Contributor

Check out the commit in my fork here: doublesharp@8dfbe32

@luin
Copy link
Collaborator

luin commented Sep 24, 2016

I got your point. The code looks great. Feel free to send a pull request and I'll definitely merge it.

@doublesharp
Copy link
Contributor

@tangxinfa @jedi4ever this is now included in 2.4.0 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants