package io.fathom.cloud.blobs.replicated;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import com.fathomdb.SimpleIterator;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.protobuf.ByteString;
/**
* A now-traditional ConsistentHash ring.
*
* Note that this data structure is immutable. When the ring changes, build a
* new one.
*/
public class ConsistentHash<T> {
private final HashFunction hashFunction;
private final int numberOfReplicas;
private final TreeMap<Integer, T> circle = new TreeMap<Integer, T>();
private final Map<String, T> nodeMap;
public ConsistentHash(int numberOfReplicas, Map<String, T> nodeMap) {
this.numberOfReplicas = numberOfReplicas;
this.nodeMap = nodeMap;
this.hashFunction = Hashing.murmur3_32();
for (Entry<String, T> entry : nodeMap.entrySet()) {
add(entry.getKey(), entry.getValue());
}
}
private void add(String key, T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.put(hashFunction.hashString(key + "_@_" + i).asInt(), node);
}
}
// public void remove(String key, T node) {
// for (int i = 0; i < numberOfReplicas; i++) {
// circle.remove(hashFunction.hashString(key + "_@_" + i).asInt());
// }
// }
// public T get(HashCode hash) {
// int hashValue = hash.asInt();
// return get(hashValue);
// }
//
// public T get(int hashValue) {
// if (circle.isEmpty()) {
// return null;
// }
//
// Entry<Integer, T> entry = circle.ceilingEntry(hashValue);
// if (entry == null) {
// entry = circle.firstEntry();
// }
//
// return entry.getValue();
// }
class RingIterator extends SimpleIterator<T> {
// final TreeMap<Integer, T> circle;
final int start;
// final Set<T> done = Sets.newCopyOnWriteArraySet();
Iterator<T> tail;
Iterator<T> head;
public RingIterator(
// TreeMap<Integer, T> circle,
int start) {
// this.circle = circle;
this.start = start;
this.tail = circle.tailMap(start, true).values().iterator();
}
@Override
protected T getNext(T current) {
while (true) {
if (this.tail != null) {
if (tail.hasNext()) {
T next = tail.next();
// if (done.contains(next)) {
// done.add(next);
// return next;
// } else {
// continue;
// }
return next;
} else {
this.tail = null;
this.head = circle.headMap(start, false).values().iterator();
}
}
if (this.head != null) {
if (head.hasNext()) {
T next = head.next();
// if (done.contains(next)) {
// done.add(next);
// return next;
// } else {
// continue;
// }
return next;
} else {
this.head = null;
}
}
return null;
}
}
}
private Iterator<T> walkRing(int hashValue) {
return new RingIterator(hashValue);
}
public Iterable<T> all() {
return nodeMap.values();
}
public Iterator<T> walkRing(ByteString key) {
return walkRing(key.hashCode());
}
}