
net.spy.memcached.KetamaNodeLocator Maven / Gradle / Ivy
/*
* arcus-java-client : Arcus Java client
* Copyright 2010-2014 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.spy.memcached;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.util.DefaultKetamaNodeLocatorConfiguration;
import net.spy.memcached.util.KetamaNodeLocatorConfiguration;
/**
* This is an implementation of the Ketama consistent hash strategy from
* last.fm. This implementation may not be compatible with libketama as
* hashing is considered separate from node location.
*
* Note that this implementation does not currently supported weighted nodes.
*
* @see RJ's blog post
*/
public final class KetamaNodeLocator extends SpyObject implements NodeLocator {
final TreeMap ketamaNodes;
final Collection allNodes;
final HashAlgorithm hashAlg;
final KetamaNodeLocatorConfiguration config;
public KetamaNodeLocator(List nodes, HashAlgorithm alg) {
this(nodes, alg, new DefaultKetamaNodeLocatorConfiguration());
}
public KetamaNodeLocator(List nodes, HashAlgorithm alg, KetamaNodeLocatorConfiguration conf) {
super();
allNodes = nodes;
hashAlg = alg;
ketamaNodes=new TreeMap();
config= conf;
int numReps= config.getNodeRepetitions();
for(MemcachedNode node : nodes) {
// Ketama does some special work with md5 where it reuses chunks.
if(alg == HashAlgorithm.KETAMA_HASH) {
for(int i=0; i smn,
Collection an, HashAlgorithm alg, KetamaNodeLocatorConfiguration conf) {
super();
ketamaNodes=smn;
allNodes=an;
hashAlg=alg;
config=conf;
}
public Collection getAll() {
return allNodes;
}
public MemcachedNode getPrimary(final String k) {
MemcachedNode rv=getNodeForKey(hashAlg.hash(k));
assert rv != null : "Found no node for key " + k;
return rv;
}
long getMaxKey() {
return ketamaNodes.lastKey();
}
MemcachedNode getNodeForKey(long hash) {
final MemcachedNode rv;
if(!ketamaNodes.containsKey(hash)) {
Long nodeHash = ketamaNodes.ceilingKey(hash);
if (nodeHash == null) {
hash = ketamaNodes.firstKey();
} else {
hash = nodeHash.longValue();
}
// Java 1.6 adds a ceilingKey method, but I'm still stuck in 1.5
// in a lot of places, so I'm doing this myself.
/*
SortedMap tailMap=ketamaNodes.tailMap(hash);
if(tailMap.isEmpty()) {
hash=ketamaNodes.firstKey();
} else {
hash=tailMap.firstKey();
}
*/
}
rv=ketamaNodes.get(hash);
return rv;
}
public Iterator getSequence(String k) {
return new KetamaIterator(k, allNodes.size());
}
public NodeLocator getReadonlyCopy() {
TreeMap smn=new TreeMap(
ketamaNodes);
Collection an=
new ArrayList(allNodes.size());
// Rewrite the values a copy of the map.
for(Map.Entry me : smn.entrySet()) {
me.setValue(new MemcachedNodeROImpl(me.getValue()));
}
// Copy the allNodes collection.
for(MemcachedNode n : allNodes) {
an.add(new MemcachedNodeROImpl(n));
}
return new KetamaNodeLocator(smn, an, hashAlg, config);
}
public void update(Collection toAttach, Collection toDelete) {
throw new UnsupportedOperationException("update not supported");
}
class KetamaIterator implements Iterator {
final String key;
long hashVal;
int remainingTries;
int numTries=0;
public KetamaIterator(final String k, final int t) {
super();
hashVal=hashAlg.hash(k);
remainingTries=t;
key=k;
}
private void nextHash() {
// this.calculateHash(Integer.toString(tries)+key).hashCode();
long tmpKey=hashAlg.hash((numTries++) + key);
// This echos the implementation of Long.hashCode()
hashVal += (int)(tmpKey ^ (tmpKey >>> 32));
hashVal &= 0xffffffffL; /* truncate to 32-bits */
remainingTries--;
}
public boolean hasNext() {
return remainingTries > 0;
}
public MemcachedNode next() {
try {
return getNodeForKey(hashVal);
} finally {
nextHash();
}
}
public void remove() {
throw new UnsupportedOperationException("remove not supported");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy