Package net.spy.memcached

Source Code of net.spy.memcached.KetamaNodeLocator

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.SortedMap;
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 <a href="http://www.last.fm/user/RJ/journal/2007/04/10/392555/">RJ's blog post</a>
*/
public final class KetamaNodeLocator extends SpyObject implements NodeLocator {


  private volatile TreeMap<Long, MemcachedNode> ketamaNodes;
  final Collection<MemcachedNode> allNodes;

  final HashAlgorithm hashAlg;
  final KetamaNodeLocatorConfiguration config;


  /**
   * Create a new KetamaNodeLocator using specified nodes and the specifed hash algorithm.
   *
   * @param nodes The List of nodes to use in the Ketama consistent hash continuum
   * @param alg The hash algorithm to use when choosing a node in the Ketama consistent hash continuum
   */
  public KetamaNodeLocator(List<MemcachedNode> nodes, HashAlgorithm alg) {
        this(nodes, alg, new DefaultKetamaNodeLocatorConfiguration());
  }

  /**
   * Create a new KetamaNodeLocator using specified nodes and the specifed hash algorithm and configuration.
   *
   * @param nodes The List of nodes to use in the Ketama consistent hash continuum
   * @param alg The hash algorithm to use when choosing a node in the Ketama consistent hash continuum
   * @param conf
   */
  public KetamaNodeLocator(List<MemcachedNode> nodes, HashAlgorithm alg, KetamaNodeLocatorConfiguration conf) {
    super();
    allNodes = nodes;
    hashAlg = alg;
    config = conf;

    setKetamaNodes(nodes);

    }

  private KetamaNodeLocator(TreeMap<Long, MemcachedNode> smn,
      Collection<MemcachedNode> an, HashAlgorithm alg, KetamaNodeLocatorConfiguration conf) {
    super();
    ketamaNodes=smn;
    allNodes=an;
    hashAlg=alg;
        config=conf;
  }

  public Collection<MemcachedNode> 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 getKetamaNodes().lastKey();
  }

  MemcachedNode getNodeForKey(long hash) {
    final MemcachedNode rv;
    if(!ketamaNodes.containsKey(hash)) {
      // 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<Long, MemcachedNode> tailMap=getKetamaNodes().tailMap(hash);
      if(tailMap.isEmpty()) {
        hash=getKetamaNodes().firstKey();
      } else {
        hash=tailMap.firstKey();
      }
    }
    rv=getKetamaNodes().get(hash);
    return rv;
  }

  public Iterator<MemcachedNode> getSequence(String k) {
    // Seven searches gives us a 1 in 2^7 chance of hitting the
    // same dead node all of the time.
    return new KetamaIterator(k, 7, getKetamaNodes(), hashAlg);
  }

  public NodeLocator getReadonlyCopy() {
    TreeMap<Long, MemcachedNode> smn=new TreeMap<Long, MemcachedNode>(
      getKetamaNodes());
    Collection<MemcachedNode> an=
      new ArrayList<MemcachedNode>(allNodes.size());

    // Rewrite the values a copy of the map.
    for(Map.Entry<Long, MemcachedNode> 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);
  }

    /**
     * @return the ketamaNodes
     */
    protected TreeMap<Long, MemcachedNode> getKetamaNodes() {
  return ketamaNodes;
    }

    /**
     * Setup the KetamaNodeLocator with the list of nodes it should use.
     *
     * @param nodes a List of MemcachedNodes for this KetamaNodeLocator to use in its continuum
     */
    protected void setKetamaNodes(List<MemcachedNode> nodes) {
  TreeMap<Long, MemcachedNode> newNodeMap = new TreeMap<Long, MemcachedNode>();
  int numReps= config.getNodeRepetitions();
  for(MemcachedNode node : nodes) {
    // Ketama does some special work with md5 where it reuses chunks.
    if(hashAlg == HashAlgorithm.KETAMA_HASH) {
      for(int i=0; i<numReps / 4; i++) {
        byte[] digest=HashAlgorithm.computeMd5(config.getKeyForNode(node, i));
        for(int h=0;h<4;h++) {
          Long k = ((long)(digest[3+h*4]&0xFF) << 24)
            | ((long)(digest[2+h*4]&0xFF) << 16)
            | ((long)(digest[1+h*4]&0xFF) << 8)
            | (digest[h*4]&0xFF);
          newNodeMap.put(k, node);
          getLogger().debug("Adding node %s in position %d", node, k);
        }

      }
    } else {
      for(int i=0; i<numReps; i++) {
        newNodeMap.put(hashAlg.hash(config.getKeyForNode(node, i)), node);
      }
    }
  }
  assert newNodeMap.size() == numReps * nodes.size();
  ketamaNodes = newNodeMap;

    }

}
TOP

Related Classes of net.spy.memcached.KetamaNodeLocator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.