private void assertAllLocationsWithRebalance(int numOwners) {
    ch = chf.create(MurmurHash3.getInstance(), numOwners, numSegments, chMembers, capacityFactors);

    List<Address> membersWithLoad = computeNodesWithLoad(chMembers);
    assertAllLocations(numOwners, membersWithLoad);
    assertDistribution(numOwners, membersWithLoad);

    ch =
        chf.create(
            MurmurHash3.getInstance(),
            numOwners,
            numSegments,
            chMembers.subList(0, 1),
            capacityFactors);
    assertAllLocations(numOwners, chMembers.subList(0, 1));

    for (int i = 2; i <= chMembers.size(); i++) {
      List<Address> currentMembers = chMembers.subList(0, i);
      log.debugf("Created CH with numOwners %d, members %s", numOwners, currentMembers);
      ch = chf.updateMembers(ch, currentMembers, capacityFactors);
      ch = chf.rebalance(ch);

      membersWithLoad = computeNodesWithLoad(currentMembers);
      assertAllLocations(numOwners, membersWithLoad);
    }
  }
  public void testConsistencyWhenNodeLeaves() {
    addNode(testAddresses[0], "m2", "r0", "s1");
    addNode(testAddresses[1], "m1", "r0", "s0");
    addNode(testAddresses[2], "m1", "r0", "s1");
    addNode(testAddresses[3], "m1", "r1", "s0");
    addNode(testAddresses[4], "m0", "r0", "s1");
    addNode(testAddresses[5], "m0", "r1", "s1");
    addNode(testAddresses[6], "m0", "r1", "s0");
    addNode(testAddresses[7], "m0", "r0", "s3");
    addNode(testAddresses[8], "m0", "r0", "s2");
    addNode(testAddresses[9], "m0", "r0", "s0");

    int numOwners = 3;
    updateConsistentHash(numOwners);
    assertAllLocations(numOwners, chMembers);
    assertDistribution(numOwners, chMembers);

    for (Address addr : chMembers) {
      log.debugf("Removing node %s", addr);
      List<Address> addressCopy = new ArrayList<Address>(chMembers);
      addressCopy.remove(addr);
      DefaultConsistentHash newCH = chf.updateMembers(ch, addressCopy, null);
      newCH = chf.rebalance(newCH);

      // Allow a small number of segment moves, even though this is a leave, because the CH factory
      // generates extra moves trying to balance the CH.
      AtomicInteger movedSegmentsCount = new AtomicInteger(0);
      for (int segment = 0; segment < numSegments; segment++) {
        checkConsistency(segment, numOwners, addr, newCH, movedSegmentsCount);
      }
      assert movedSegmentsCount.get() <= numSegments * numOwners * 0.1
          : String.format(
              "Too many moved segments after leave: %d. CH after leave is: %s\nPrevious: %s",
              movedSegmentsCount.get(), newCH, ch);
    }
  }
 protected void updateConsistentHash(int numOwners) {
   ch = chf.create(MurmurHash3.getInstance(), numOwners, numSegments, chMembers, null);
   log.debugf("Created CH with numOwners %d, members %s", numOwners, chMembers);
 }