diff --git a/core/src/main/java/hudson/util/ConsistentHash.java b/core/src/main/java/hudson/util/ConsistentHash.java index 83bdb3f53d..0919975ac5 100644 --- a/core/src/main/java/hudson/util/ConsistentHash.java +++ b/core/src/main/java/hudson/util/ConsistentHash.java @@ -64,6 +64,7 @@ public class ConsistentHash { * All the items in the hash, to their replication factors. */ private final Map items = new HashMap(); + private int numPoints; private final int defaultReplication; private final Hash hash; @@ -100,8 +101,13 @@ public class ConsistentHash { private final Object[] owner; // really T[] private Table() { + int r=0; + for (Point[] v : items.values()) + r+=v.length; + numPoints = r; + // merge all points from all nodes and sort them into a single array - Point[] allPoints = new Point[countAllPoints()]; + Point[] allPoints = new Point[numPoints]; int p=0; for (Point[] v : items.values()) { System.arraycopy(v,0,allPoints,p,v.length); @@ -211,23 +217,20 @@ public class ConsistentHash { } public int countAllPoints() { - int r=0; - for (Point[] v : items.values()) - r+=v.length; - return r; + return numPoints; } /** * Adds a new node with the default number of replica. */ - public void add(T node) { + public synchronized void add(T node) { add(node,defaultReplication); } /** * Calls {@link #add(Object)} with all the arguments. */ - public void addAll(T... nodes) { + public synchronized void addAll(T... nodes) { for (T node : nodes) addInternal(node,defaultReplication); refreshTable(); @@ -236,7 +239,7 @@ public class ConsistentHash { /** * Calls {@link #add(Object)} with all the arguments. */ - public void addAll(Collection nodes) { + public synchronized void addAll(Collection nodes) { for (T node : nodes) addInternal(node,defaultReplication); refreshTable(); @@ -245,7 +248,7 @@ public class ConsistentHash { /** * Calls {@link #add(Object,int)} with all the arguments. */ - public void addAll(Map nodes) { + public synchronized void addAll(Map nodes) { for (Map.Entry node : nodes.entrySet()) addInternal(node.getKey(),node.getValue()); refreshTable(); @@ -254,23 +257,20 @@ public class ConsistentHash { /** * Removes the node entirely. This is the same as {@code add(node,0)} */ - public void remove(T node) { + public synchronized void remove(T node) { add(node, 0); } /** * Adds a new node with the given number of replica. - * - *

- * This is the only function that manipulates {@link #items}. */ public synchronized void add(T node, int replica) { addInternal(node, replica); refreshTable(); } - private void addInternal(T node, int replica) { - if(replica==0) { + private synchronized void addInternal(T node, int replica) { + if (replica==0) { items.remove(node); } else { Point[] points = new Point[replica]; @@ -281,11 +281,10 @@ public class ConsistentHash { } } - private void refreshTable() { + private synchronized void refreshTable() { table = new Table(); } - /** * Compresses a string into an integer with MD5. */ diff --git a/core/src/test/java/hudson/util/ConsistentHashTest.java b/core/src/test/java/hudson/util/ConsistentHashTest.java index ed0704a49c..0d10fff42a 100644 --- a/core/src/test/java/hudson/util/ConsistentHashTest.java +++ b/core/src/test/java/hudson/util/ConsistentHashTest.java @@ -122,11 +122,24 @@ public class ConsistentHashTest { @Test public void emptyBehavior() { ConsistentHash hash = new ConsistentHash(); + assertEquals(0, hash.countAllPoints()); assertFalse(hash.list(0).iterator().hasNext()); assertNull(hash.lookup(0)); assertNull(hash.lookup(999)); } + @Test + public void countAllPoints() { + ConsistentHash hash = new ConsistentHash(); + assertEquals(0, hash.countAllPoints()); + hash.add("foo", 10); + assertEquals(10, hash.countAllPoints()); + hash.add("bar", 5); + assertEquals(15, hash.countAllPoints()); + hash.remove("foo"); + assertEquals(5, hash.countAllPoints()); + } + /** * This test doesn't fail but it's written to measure the performance of the consistent hash function with large data set. */