Skip to content

Support consistent hashing in cache.NewMemcachedClient #1554

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

Merged

Conversation

rfratto
Copy link
Contributor

@rfratto rfratto commented Aug 2, 2019

This PR adds support for consistent hashing of memcached servers by server DNS name.
Support for this is behind a boolean configuration variable in the client config, available in the flags as -memcached.consistent-hash

The use of jump hash enables consistent hashing when adding or removing a memcached server only requires 1/N keys being moved. A downside to jump hash in particular is that the order of DNS names used for the servers must be predictable to maximize efficiency. A good example of a predictable DNS name is memcached-[pod index].cortex.svc.cluster.local, which is what Kubernetes would create when running memcached as a StatefulSet on a headless service.

/cc @tomwilkie

@rfratto rfratto force-pushed the memcached-client-consistent-hashing branch from aeb25bf to 38ea79e Compare August 2, 2019 14:52
@tomwilkie
Copy link
Contributor

I have a question about the sort of the hostnames returns from DNS. Are the numbers zero padded? Does the sort handled going from 10 to 11 servers correctly?

Otherwise LGTM

@rfratto
Copy link
Contributor Author

rfratto commented Aug 7, 2019

@tomwilkie The numbers aren't zero-padded; the sort won't handle it correctly and a lot of keys will start being moved after scaling past 10 servers.

If that's a problem that needs to be addressed, most flexible solution would be to abandon the jump hash and use a different consistent hashing algorithm that doesn't depend on the order of the DNS names. Switching over to the rendezvous hash would handle arbitrarily ordered servers and allow to scale past 10 servers cleanly (i.e., only 1/N keys moved).

@bboreham
Copy link
Contributor

bboreham commented Aug 7, 2019

FWIW I would look at something like https://github.com/facebook/mcrouter to scale memcached - this allows multiple replicas of the same data and back-filling data that is in one replica but not another.

@tomwilkie
Copy link
Contributor

@rfratto could we use a natural sort on the domain names instead? https://godoc.org/bitbucket.org/zombiezen/cardcpx/natsort

@bboreham Yeah I checked that out before we started this work - I don't think mcrouter actually helps the case we're looking at here; it suffers from the same problem that increasing the size of the pool resulting in a complete invalidation of the cache. And it doesn't do service discovery... If it was in go I'd have said add these features there, but this seems easier.

@tomwilkie tomwilkie self-requested a review August 7, 2019 11:24
@rfratto
Copy link
Contributor Author

rfratto commented Aug 7, 2019

@tomwilkie Ah, that’s cool, I didn’t know about natural sorts. That will fix our problem here.

@bboreham
Copy link
Contributor

bboreham commented Aug 7, 2019

And [mcrouter] doesn't do service discovery

There are 3rd-party (4th-party?) projects that do that, e.g. https://github.com/mlaccetti/mcrouter-kubernetes-provisioner, https://github.com/ianlewis/memcached-operator

@rfratto rfratto force-pushed the memcached-client-consistent-hashing branch 3 times, most recently from accb411 to 8764c0c Compare August 12, 2019 16:17
@rfratto
Copy link
Contributor Author

rfratto commented Aug 12, 2019

@gouthamve @tomwilkie PTAL, the natural sort works as expected with StatefulSet DNS names.

@tomwilkie
Copy link
Contributor

Can you find out what the result of an SRV lookup looks like on k8s, and make sure the natsort does the right thing? A unit test would be great.

@rfratto rfratto force-pushed the memcached-client-consistent-hashing branch from 4a42f81 to cdbada5 Compare August 13, 2019 13:55
@rfratto
Copy link
Contributor Author

rfratto commented Aug 13, 2019

@tomwilkie PTAL. SRV lookup for a headless service backed by a stateful set in k8s looks like this:

;; QUESTION SECTION:
;memcached.cortex.svc.cluster.local. IN SRV

;; ANSWER SECTION:
memcached.cortex.svc.cluster.local. 30 IN SRV   10 25 0 memcached-0.memcached.cortex.svc.cluster.local.
memcached.cortex.svc.cluster.local. 30 IN SRV   10 25 0 memcached-1.memcached.cortex.svc.cluster.local.
memcached.cortex.svc.cluster.local. 30 IN SRV   10 25 0 memcached-2.memcached.cortex.svc.cluster.local.

@rfratto rfratto force-pushed the memcached-client-consistent-hashing branch 2 times, most recently from ba17eae to f29c818 Compare August 13, 2019 13:59
@tomwilkie tomwilkie requested a review from gouthamve August 13, 2019 14:47
rfratto and others added 4 commits August 14, 2019 09:59
cache.MemcachedClientConfig has been updated with a new boolean
variable ConsistentHash, available as consistent_hash in yaml and
memcached.consistent-hash as a flag.

When ConsistentHash is true, the MemcachedClient will use the
newly created cache.MemcachedJumpHashSelector for server distribution.

Jump hash is a consistent hashing algorithm that given a key and a
number of buckets, returns a bucket number in the range [0, numBuckets).
Adding or removing a bucket only results in 1/N keys being moved.

A downside to using jump hash is that buckets can not be arbitrarily
removed from the list; it effectively acts as a stack and only supports
adding or removing buckets from the end. Therefore, jump hash is most
effective when the servers are ordered and where the order is
predicable. A good example of this is Kubernete's StatefulSet with a
headless service. DNS names will be in the form memcached-[pod number],
where the pod number will grow and shrink in the way that numBuckets
does. There will never be a gap in the servers when scaling up or down.

Signed-off-by: Robert Fratto <[email protected]>
TestNatSort has been added to validate that the natsort package works as
expected when sorting a list of servers that are returned from SRV
lookups. The example used corresponds to SRV records that would be
returned for a k8s headless service backed by a StatefulSet.

Signed-off-by: Robert Fratto <[email protected]>
Signed-off-by: Tom Wilkie <[email protected]>
@tomwilkie tomwilkie force-pushed the memcached-client-consistent-hashing branch from f29c818 to 1b76cee Compare August 14, 2019 09:01
@tomwilkie tomwilkie merged commit b3b0d86 into cortexproject:master Aug 14, 2019
@rfratto rfratto deleted the memcached-client-consistent-hashing branch September 30, 2019 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants