All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.cache.partitioned.PartitionedRegionLoadModelJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.gemstone.gemfire.internal.cache.partitioned;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.cache.partition.PartitionMemberInfo;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.BucketOperator.Completion;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.CompositeDirector;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.PartitionedRegionLoadModel;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.PartitionedRegionLoadModel.AddressComparor;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.RebalanceDirector;
import com.gemstone.gemfire.internal.cache.partitioned.rebalance.SimulatedBucketOperator;
import com.gemstone.gemfire.internal.cache.persistence.PersistentMemberID;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * @author dsmith
 *
 */
@SuppressWarnings("synthetic-access")
public class PartitionedRegionLoadModelJUnitTest {
  private static final int MAX_MOVES = 5000;
  private static final boolean DEBUG = true;
  
  private MyBucketOperator bucketOperator;
  
  @Before
  public void setUp() {
    this.bucketOperator = new MyBucketOperator();
  }
  
  /**
   * This test checks basic redundancy satisfaction. It creates two
   * buckets with low redundancy and 1 bucket with full redundancy and excepts
   * copies of the low redundancy buckets to be made. 
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfaction() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(
        bucketOperator, 2, 4, getAddressComparor(false),
        Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,0}, new long[] {1,1,1,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,1,0,1}, new long[] {0,0,0,1});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    Set details = model.getPartitionedMemberDetails("a");
    assertEquals(2, details.size());
    
    //TODO - make some assertions about what's in the details

    //we expect three moves
    assertEquals(3, doMoves(new CompositeDirector(true, true, false, false), model));
    
    //TODO - make some assertions about what's in the details
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member2, 2));
    expectedCreates.add(new Create(member1, 3));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  /**
   * This test creates buckets with low redundancy, but only 1 of the buckets
   * is small enough to be copied. The other bucket should be rejected because
   * it is too big..
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionWithSizeLimit() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 3, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    
    //A member with 1 bucket with low redundancy, but it is too big to copy anywhere
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50, 50, new long[] {30,0,0}, new long[] {1,0,0});
    //A member with 2 buckets with low redundancy that can be copied
    PartitionMemberInfoImpl details2 = buildDetails(member2, 40, 40, new long[] {0,10,10}, new long[] {0,1,1});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    //we expect 2 moves
    assertEquals(2, doMoves(new CompositeDirector(true, true, false, false), model));
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member1, 1));
    expectedCreates.add(new Create(member1, 2));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  @Test
  public void testRedundancySatisfactionWithCriticalMember() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(
        bucketOperator, 2, 3, getAddressComparor(false),
        Collections. singleton(member1), null);

    //this member has critical heap
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50, 50, new long[] {10,0,0}, new long[] {1,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 40, 40, new long[] {0,10,10}, new long[] {0,1,1});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    //we expect 2 moves
    assertEquals(1, doMoves(new CompositeDirector(true, true, false, false),model));
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member2, 0));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  /**
   * This test makes sure we ignore the size limit if requested
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionDoNotEnforceLocalMaxMemory() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 3, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    
    //A member with 1 bucket with low redundancy, but it is too big to copy anywhere
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50, 50, new long[] {30,0,0}, new long[] {1,0,0});
    //A member with 2 buckets with low redundancy that can be copied
    PartitionMemberInfoImpl details2 = buildDetails(member2, 40, 40, new long[] {0,10,10}, new long[] {0,1,1});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), false);
    
    //we expect 2 moves
    assertEquals(3, doMoves(new CompositeDirector(true, true, false, false), model));
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member1, 1));
    expectedCreates.add(new Create(member1, 2));
    assertEquals(expectedCreates, bucketOperator.creates);
    Set afterDetails = model.getPartitionedMemberDetails("a");
    assertEquals(afterDetails.size(), 2);
    for(PartitionMemberInfo member: afterDetails) {
      if(member.getDistributedMember().equals(member1)) {
        assertEquals(details1.getConfiguredMaxMemory(), member.getConfiguredMaxMemory());
      } else { 
        assertEquals(details2.getConfiguredMaxMemory(), member.getConfiguredMaxMemory());
      }
    }
  }
  
  /**
   * Tests to make sure that redundancy satisfaction prefers to
   * make redundant copies on members with remote IP addresses.
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionPreferRemoteIp() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1,  3, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getLocalHost(), 3);

    //Create some buckets with low redundancy on members 1 and 2
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {30,0,0}, new long[] {1,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,30,30}, new long[] {0,1,1});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0}, new long[] {0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    //we expect 3 moves
    assertEquals(3, doMoves(new CompositeDirector(true, true, false, false), model));
    
    //The buckets should all be created on member 3 because
    //it has a different ip address
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member3, 0));
    expectedCreates.add(new Create(member3, 1));
    expectedCreates.add(new Create(member3, 2));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  @Test
  public void testRedundancySatisfactionEnforceRemoteIp() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 3, getAddressComparor(true), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);

    //Create some buckets with low redundancy on members 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {30,30,30}, new long[] {1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0}, new long[] {0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    //we expect 0 moves, because we're enforcing that we can't create
    //copies on the same IP.
    assertEquals(0, doMoves(new CompositeDirector(true, true, false, false), model));

    assertEquals(Collections.emptyList(), bucketOperator.creates);
  }
  
  @Test
  public void testMoveBucketsEnforceRemoteIp() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 3, getAddressComparor(true), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);

    //Create some buckets with low redundancy on members 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {30,30,30}, new long[] {1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0}, new long[] {0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    //we expect 0 moves, because we're enforcing that we can't create
    //copies on the same IP.
    assertEquals(1, doMoves(new CompositeDirector(true, true, true, true), model));

    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    assertEquals(expectedMoves, bucketOperator.bucketMoves);
  }
  
  /**
   * Tests to make sure that redundancy satisfaction balances
   * between nodes to ensure an even load.
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionBalanced() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    //we expect 4 moves
    assertEquals(4, doMoves(new CompositeDirector(true, true, false, false), model));
    
    //The bucket creates should alternate between members
    //so that the load is balanced.
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member3, 1));
    expectedCreates.add(new Create(member2, 2));
    expectedCreates.add(new Create(member3, 3));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  /**
   * Tests to make sure that redundancy satisfaction balances
   * between nodes to ensure an even load.
   * @throws Exception
   */
  @Test
  public void testColocatedRegions() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 12, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {1,1,1,1,1,1,1,1,1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 250, 250, new long[] {0,0,0,0,0,0,0,0,0,0,0,0}, new long[] {0,0,0,0,0,0,0,0,0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 250, 250, new long[] {0,0,0,0,0,0,0,0,0,0,0,0}, new long[] {0,0,0,0,0,0,0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    model.addRegion("b", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    //we expect 4 moves
    assertEquals(18, doMoves(new CompositeDirector(true, true, false, true), model));
    
    //The bucket creates should alternate between members
    //so that the load is balanced.
    Set expectedCreates = new HashSet();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member3, 1));
    expectedCreates.add(new Create(member2, 2));
    expectedCreates.add(new Create(member3, 3));
    expectedCreates.add(new Create(member2, 4));
    expectedCreates.add(new Create(member3, 5));
    expectedCreates.add(new Create(member2, 6));
    expectedCreates.add(new Create(member3, 7));
    expectedCreates.add(new Create(member2, 8));
    expectedCreates.add(new Create(member3, 9));
    expectedCreates.add(new Create(member2, 10));
    expectedCreates.add(new Create(member3, 11));
    assertEquals(expectedCreates, new HashSet(bucketOperator.creates));
    
    Set expectedMoves= new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member3));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member3));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member3));
    assertEquals(expectedMoves, new HashSet(bucketOperator.primaryMoves));
  }
  
  @Test
  public void testIncompleteColocation() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {1,1,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    model.addRegion("b", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    model.addRegion("c", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    //Add a region which is not created on all of the members.
    
    //Member 3 should not be considered a target for any moves. 
    
    assertEquals(6, doMoves(new CompositeDirector(true, true, false, true), model));
    
    //Everything should be creatd on member2
    Set expectedCreates = new HashSet();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member2, 1));
    expectedCreates.add(new Create(member2, 2));
    expectedCreates.add(new Create(member2, 3));
    assertEquals(expectedCreates, new HashSet(bucketOperator.creates));
    
    Set expectedMoves= new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    assertEquals(expectedMoves, new HashSet(bucketOperator.primaryMoves));
  }
  
  /**
   * Test that we enforce local max memory on a per region basis
   * IE if one of the regions has a low lmm, it will prevent a bucket move
   * @throws Exception
   */
  @Test
  public void testColocationEnforceLocalMaxMemory() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    
    //Member 2 has a lmm of 2, so it should only accept 2 buckets
    PartitionMemberInfoImpl bDetails1 = buildDetails(member1, 2, 2, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl bDetails2 = buildDetails(member2, 2, 2, new long[] {0,0,0,0}, new long[] {0,0,0,0});    
    model.addRegion("b", Arrays.asList(bDetails1, bDetails2), new FakeOfflineDetails(), true);
    
    
    assertEquals(4, doMoves(new CompositeDirector(true, true, false, true), model));
    
    //Everything should be create on member2
    Set expectedCreates = new HashSet();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member2, 1));
    assertEquals(expectedCreates, new HashSet(bucketOperator.creates));
    
    Set expectedMoves= new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    assertEquals(expectedMoves, new HashSet(bucketOperator.primaryMoves));
  }
  
  /**
   * Test that each region indivually honors it's enforce local max
   * memory flag.
   * @throws Exception
   */
  @Test
  public void testColocationIgnoreEnforceLocalMaxMemory() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    
    //Member 2 has a lmm of 2, so it should only accept 2 buckets
    PartitionMemberInfoImpl bDetails1 = buildDetails(member1, 2, 2, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl bDetails2 = buildDetails(member2, 2, 2, new long[] {0,0,0,0}, new long[] {0,0,0,0});    
    model.addRegion("b", Arrays.asList(bDetails1, bDetails2), new FakeOfflineDetails(), false);
    
    
    assertEquals(6, doMoves(new CompositeDirector(true, true, false, true), model));
    
    //Everything should be created on member2
    Set expectedCreates = new HashSet();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member2, 1));
    expectedCreates.add(new Create(member2, 2));
    expectedCreates.add(new Create(member2, 3));
    assertEquals(expectedCreates, new HashSet(bucketOperator.creates));
    
    Set expectedMoves= new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    assertEquals(expectedMoves, new HashSet(bucketOperator.primaryMoves));
  }
  
  /**
   * Test which illustrates the problem with our greedy algorithm. It doesn't
   * necessarily end up with a balanced result.
   * 
   *  TODO rebalance - change this test of fix the algorithm?
   * @throws Exception
   */
  @Ignore
  @Test
  public void testFoolGreedyAlgorithm() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 50, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, new long[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    doMoves(new CompositeDirector(true, true, false, false), model);

    //we'd like to have 20 buckets per member, but what we'll find is that member 1
    //will have 15 and 2 and 3 will have 17 and 18.
    for(PartitionMemberInfo details : model.getPartitionedMemberDetails("a")) {
      assertEquals(20, details.getBucketCount());
    }
  }
  
  /**
   * Tests to make sure that redundancy satisfaction balances
   * between nodes to ensure an even load.
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionWithFailures() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    final InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    MyBucketOperator op = new MyBucketOperator() {
      @Override
      public void createRedundantBucket(
          InternalDistributedMember targetMember, int i,
          Map colocatedRegionBytes, Completion completion) {
        if(targetMember.equals(member2)) {
          completion.onFailure();
        } else {
          super.createRedundantBucket(targetMember, i, colocatedRegionBytes, completion);
        }
      }
      
    };
    
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(op ,1, 4, getAddressComparor(false), Collections.emptySet(), null);
    
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    //we expect 8 moves
    assertEquals(8, doMoves(new CompositeDirector(true, true, false, false), model));
    
    //The bucket creates should do all of the creates on member 3
    //because member2 failed.
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member3, 0));
    expectedCreates.add(new Create(member3, 1));
    expectedCreates.add(new Create(member3, 2));
    expectedCreates.add(new Create(member3, 3));
    assertEquals(expectedCreates, op.creates);
  }
  
  /**
   * Test that redundancy satisfation can handle asynchronous failures
   * and complete the job correctly. 
   * @throws Exception
   */
  @Test
  public void testRedundancySatisfactionWithAsyncFailures() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    
    BucketOperatorWithFailures operator = new BucketOperatorWithFailures();
    operator.addBadMember(member2);
    bucketOperator = operator;
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(
        bucketOperator, 1, 6, getAddressComparor(false),
        Collections.emptySet(), null);
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1,1,1}, new long[] {1,1,1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    
    Set details = model.getPartitionedMemberDetails("a");
    assertEquals(3, details.size());
    
    //TODO - make some assertions about what's in the details

    //we expect 6 moves (3 of these will fail)
    assertEquals(6, doMoves(new CompositeDirector(true, true, false, false), model));
    
    assertEquals(3, bucketOperator.creates.size());
    for(Completion completion: operator.pendingSuccesses) {
      completion.onSuccess();
    }
    for(Completion completion: operator.pendingFailures) {
      completion.onFailure();
    }
    
    //Now the last two moves will get reattempted to a new location (because the last location failed)
    assertEquals(3, doMoves(new CompositeDirector(true, true, false, false), model));
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member3, 1));
    expectedCreates.add(new Create(member3, 3));
    expectedCreates.add(new Create(member3, 5));
    expectedCreates.add(new Create(member3, 0));
    expectedCreates.add(new Create(member3, 2));
    expectedCreates.add(new Create(member3, 4));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  /**
   * Very basic test of moving primaries. Creates two nodes and four buckets, with a copy
   * of each bucket on both nodes. All of the primaries are on one node. It expects half the
   * primaries to move to the other node.
   * @throws Exception
   */
  @Test
  public void testMovePrimaries() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    //Create some imbalanced primaries
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {1,1,1,1}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    assertEquals(2, doMoves(new CompositeDirector(false, false, false, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //Two of the primaries should move to member2
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, bucketOperator.primaryMoves);
  }

  /**
   * Test that we can move primaries if failures occur during the move.
   * In this case member2 is bad, so primaries should move
   * to member3 instead.
   * @throws Exception
   */
  @Test
  public void testMovePrimariesWithFailures() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    final InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    MyBucketOperator op = new MyBucketOperator() {
      
      @Override
      public boolean movePrimary(InternalDistributedMember source,
          InternalDistributedMember target, int bucketId) {
        if(target.equals(member2)) {
          return false;
        }
        return super.movePrimary(source, target, bucketId);
      }
    };
    
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(op ,2, 4, getAddressComparor(false), Collections.emptySet(), null);
    //Create some imbalanced primaries
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {1,1,1,1}, new long[] {0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {1,1,1,1}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);

    assertEquals(8, doMoves(new CompositeDirector(false, false, false, true), model));
    
    assertEquals(Collections.emptyList(), op.creates);

    //Two of the primaries should move to member2
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member3));
    expectedMoves.add(new Move(member1, member3));

    assertEquals(expectedMoves, op.primaryMoves);
  }
  
  /**
   * Test of moving primaries when nodes are weighted relative to each other
   * @throws Exception
   */
  @Test
  public void testMovePrimariesWithWeights() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    
    //member 1 has a lower weight, and all of the primaries
    PartitionMemberInfoImpl details1 = buildDetails(member1, 1, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    //member 2 has a higher weight
    PartitionMemberInfoImpl details2 = buildDetails(member2, 3, 500, new long[] {1,1,1,1}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);
    
    assertEquals(3, doMoves(new CompositeDirector(false, false, false, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //Three of the primaries should move to member2, because it has a higher weight
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, bucketOperator.primaryMoves);
  }
  
  /**
   * This is a more complicated test of primary moving
   * where moving primaries for some buckets then forces 
   * other primaries to move.
   * 
   * P = primary
   * R = redundant
   * X = not hosting
   * 
   * Bucket       0  1  2  3  4  5  6  7  8 
   * Member1   P  P P  P P  P  X  X  X
   * Member2   R  R R R R  R  P  P  R
   * Member3   X  X  X X X  X  R  R  P
   * 
   * 
   * @throws Exception
   */
  @Test
  public void testPrimaryShuffle() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 9, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    
    PartitionMemberInfoImpl details1 = buildDetails(member1, 1, 500, new long[] {1,1,1,1,1,1,0,0,0}, new long[] {1,1,1,1,1,1,0,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 1, 500, new long[] {1,1,1,1,1,1,1,1,1}, new long[] {0,0,0,0,0,0,1,1,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 1, 500, new long[] {0,0,0,0,0,0,1,1,1}, new long[] {0,0,0,0,0,0,0,0,1});
    
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);

    
    int moves = doMoves(new CompositeDirector(false, false, false, true), model);
    assertEquals("Actual Moves" + bucketOperator.primaryMoves, 3, moves);
    

    //Two of the primaries should move off of member1 to member2. And
    //One primaries should move from member 2 to member 3.
    Set expectedMoves = new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member2, member3));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, new HashSet(bucketOperator.primaryMoves));
  }

  /**
   * Test a case where we seem to get into an infinite loop while balancing primaries.
   * @throws Exception
   */
  @Test
  public void testBug39953() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 113, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    InternalDistributedMember member4 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 4);
    //Create some imbalanced primaries
    PartitionMemberInfoImpl details1 = buildDetails(member1, 216, 216, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,1,1,1,0,0,0,0,0
});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 216, 216, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {1,0,0,0,1,1,0,1,0,0,0,1,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0
});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 216, 216, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {0,1,0,0,0,0,1,0,0,1,1,0,1,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1
});
    PartitionMemberInfoImpl details4 = buildDetails(member4, 216, 216, new long[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, new long[] {0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0
});
    model.addRegion("a", Arrays.asList(details1, details2,details3, details4), new FakeOfflineDetails(), true);

    assertEquals(0, doMoves(new CompositeDirector(false, false, false, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);


    assertEquals(Collections.emptyList(), bucketOperator.primaryMoves);
  }
  
  /**
   * Very basic test of moving buckets. Creates two nodes and four buckets, with buckets only on one node. Half of the buckets should move to the other node. 
   * @throws Exception
   */
  @Test
  public void testMoveBuckets() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    assertEquals(2, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);
    assertEquals(Collections.emptyList(), bucketOperator.primaryMoves);

    //Two of the buckets should move to member2
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, bucketOperator.bucketMoves);
  }
  
  /**
   * Test that moving buckets will work if there are failures while moving buckets
   * member2 refuses the buckets, so the buckets should move to member3
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithFailures() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    final InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    MyBucketOperator op = new MyBucketOperator() {
      
      @Override
      public boolean moveBucket(InternalDistributedMember source,
          InternalDistributedMember target, int id,
          Map colocatedRegionBytes) {
        if(target.equals(member2)) {
          return false;
        }
        return super.moveBucket(source, target, id, colocatedRegionBytes);
      }
    };
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(op ,0, 4, getAddressComparor(false), Collections.emptySet(), null);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);

    assertEquals(8, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), op.creates);
    assertEquals(Collections.emptyList(), op.primaryMoves);

    //Two of the buckets should move to member2
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member3));
    expectedMoves.add(new Move(member1, member3));

    assertEquals(expectedMoves, op.bucketMoves);
  }
  
  /**
   * Test to make sure that we honor the weight
   * of a node while moving buckets. 
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithWeights() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 6, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 250, 250, new long[] {1,1,1,1, 1, 1}, new long[] {1,1,1,1, 1, 1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0, 0, 0 }, new long[] {0,0,0,0, 0, 0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    assertEquals(4, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);
    assertEquals(Collections.emptyList(), bucketOperator.primaryMoves);

    //Four of the buckets should move to member2, because
    //member2 has twice the weight as member1.
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, bucketOperator.bucketMoves);
  }
  
  /**
   * Test to make sure we honor the size of buckets when
   * choosing which buckets to move.
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithSizes() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 6, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {3,1,1,1,1,1}, new long[] {1,1,1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2), new FakeOfflineDetails(), true);

    assertEquals(4, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);
    assertEquals(Collections.emptyList(), bucketOperator.primaryMoves);

    //Four of the buckets should move to member2, because
    //member1 has 1 bucket that is size 3.
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, bucketOperator.bucketMoves);
  }
  
  /**
   * Test to move buckets with redundancy.
   * Makes sure that buckets and primaries are balanced
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithRedundancy() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    InternalDistributedMember member4 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 4);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, 500, new long[] {1,1,1,1}, new long[] {1,1,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, 500, new long[] {1,1,1,1}, new long[] {0,0,1,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, 500, new long[] {1,1,1,1}, new long[] {0,0,0,1});
    PartitionMemberInfoImpl details4 = buildDetails(member4, 500, 500, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3, details4), new FakeOfflineDetails(), true);

    doMoves(new CompositeDirector(false, false, true, true), model);
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //One bucket should move from each member to member4
    Set expectedMoves = new HashSet();
    expectedMoves.add(new Move(member1, member4));
    expectedMoves.add(new Move(member2, member4));
    expectedMoves.add(new Move(member3, member4));

    assertEquals(expectedMoves, new HashSet(bucketOperator.bucketMoves));
    
    //We don't know how many primaries will move, because
    //the move buckets algorithm could move the primary or
    //it could move a redundant copy. But after we're done, we should
    //only have one primary per member.
    Set detailSet = model.getPartitionedMemberDetails("a");
    for(PartitionMemberInfo member: detailSet) {
      assertEquals(1, member.getPrimaryCount());
      assertEquals(3, member.getBucketCount());
    }
  }
  
  /**
   * Test to move buckets with some large buckets (to make sure there are no issues with buffer overflow);
   * Makes sure that buckets and primaries are balanced
   * @throws Exception
   */
  @Test
  public void testMoveLargeBucketsWithRedundancy() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,2, 4, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    InternalDistributedMember member4 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 4);
    //Create some imbalanced nodes
    
    long bigBucket = Integer.MAX_VALUE * 5L + 10L;
    PartitionMemberInfoImpl details1 = buildDetails(member1, 500, Long.MAX_VALUE, new long[] {bigBucket,bigBucket,bigBucket,bigBucket}, new long[] {1,1,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 500, Long.MAX_VALUE, new long[] {bigBucket,bigBucket,bigBucket,bigBucket}, new long[] {0,0,1,0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 500, Long.MAX_VALUE, new long[] {bigBucket,bigBucket,bigBucket,bigBucket}, new long[] {0,0,0,1});
    PartitionMemberInfoImpl details4 = buildDetails(member4, 500, Long.MAX_VALUE, new long[] {0,0,0,0}, new long[] {0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3, details4), new FakeOfflineDetails(), true);

    doMoves(new CompositeDirector(false, false, true, true), model);
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //One bucket should move from each member to member4
    Set expectedMoves = new HashSet();
    expectedMoves.add(new Move(member1, member4));
    expectedMoves.add(new Move(member2, member4));
    expectedMoves.add(new Move(member3, member4));

    assertEquals(expectedMoves, new HashSet(bucketOperator.bucketMoves));
    
    //We don't know how many primaries will move, because
    //the move buckets algorithm could move the primary or
    //it could move a redundant copy. But after we're done, we should
    //only have one primary per member.
    Set detailSet = model.getPartitionedMemberDetails("a");
    for(PartitionMemberInfo member: detailSet) {
      assertEquals(1, member.getPrimaryCount());
      assertEquals(3, member.getBucketCount());
    }
  }
  
  /**
   * Test to make sure that moving buckets
   * honors size restrictions for VMs.
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithSizeLimits() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 6, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50, 50, new long[] {30,30,30,0,0,0}, new long[] {1,1,1,0,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 50, 50, new long[] {0,0,0,10,10,10}, new long[] {0,0,0,1,1,1});
    //this member has a lower size that can't fit buckets of size 30
    PartitionMemberInfoImpl details3 = buildDetails(member3, 50, 20, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);

    assertEquals(3, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //One bucket should move from each member to member4
    Set expectedMoves = new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member2, member3));
    expectedMoves.add(new Move(member2, member3));

    assertEquals(expectedMoves, new HashSet(bucketOperator.bucketMoves));
    
    Set detailSet = model.getPartitionedMemberDetails("a");
    for(PartitionMemberInfo member: detailSet) {
      assertEquals(2, member.getPrimaryCount());
      assertEquals(2, member.getBucketCount());
    }
  }
  
  /**
   * Test to make sure that moving buckets
   * honors size critical members
   * @throws Exception
   */
  @Test
  public void testMoveBucketsWithCriticalMember() throws Exception {
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(
        bucketOperator, 0, 6, getAddressComparor(false),
        Collections. singleton(member3), null);
    //Create some imbalanced nodes
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50, 50, new long[] {10,10,10,10,10,10}, new long[] {1,1,1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 50, 50, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    //this member has a critical heap
    PartitionMemberInfoImpl details3 = buildDetails(member3, 50, 50, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    model.addRegion("a", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);

    assertEquals(3, doMoves(new CompositeDirector(false, false, true, true), model));
    
    assertEquals(Collections.emptyList(), bucketOperator.creates);

    //The buckets should only move to member2, because member3 is critical
    Set expectedMoves = new HashSet();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));

    assertEquals(expectedMoves, new HashSet(bucketOperator.bucketMoves));
  }

  /** Test to make sure two runs with the same information
   * perform the same moves. 
   */
  @Test
  public void testRepeatableRuns() throws UnknownHostException {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,0, 113, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 22893);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 25655);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 22959);
    InternalDistributedMember member4 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 22984);
    InternalDistributedMember member5 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 28609);
    InternalDistributedMember member6 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 22911);
    InternalDistributedMember member7 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 29562);
    PartitionMemberInfoImpl details1 = buildDetails(member1, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {23706, 0, 23347, 23344, 0, 0, 0, 11386, 0, 0, 0, 0, 0, 10338, 0, 9078, 6413, 10411, 5297, 1226, 0, 2594, 2523, 0, 1297, 0, 3891, 2523, 0, 0, 2594, 0, 1297, 0, 1297, 2594, 1, 0, 10375, 5188, 9078, 0, 1297, 0, 0, 1226, 1, 1, 0, 0, 1297, 11672, 0, 0, 0, 0, 7782, 0, 11673, 0, 2594, 1, 0, 2593, 3891, 1, 0, 7711, 7710, 2594, 0, 6485, 0, 1, 7711, 6485, 7711, 3891, 1297, 0, 10303, 2594, 3820, 0, 2523, 3999, 0, 1, 0, 2522, 1, 5188, 5188, 0, 2594, 3891, 2523, 2594, 0, 1297, 1, 1, 1226, 0, 1297, 0, 3891, 1226, 2522, 11601, 10376, 0, 2594}, new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {0, 24674, 0, 23344, 0, 19312, 19421, 11386, 7889, 0, 0, 6413, 12933, 10338, 18088, 9078, 0, 0, 0, 1226, 0, 2594, 0, 0, 0, 2594, 0, 2523, 0, 1, 0, 0, 1297, 0, 0, 0, 0, 2594, 0, 5188, 9078, 0, 0, 0, 1, 1226, 1, 0, 1297, 5187, 0, 0, 0, 0, 0, 1, 0, 11602, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7710, 0, 10304, 6485, 0, 0, 0, 0, 0, 3891, 0, 0, 10303, 0, 0, 1, 2523, 3999, 0, 0, 1, 0, 0, 5188, 0, 5116, 2594, 3891, 2523, 0, 2522, 1297, 1, 0, 0, 1297, 0, 1297, 3891, 1226, 2522, 0, 10376, 0, 0}, new long[] {0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {23706, 24674, 0, 0, 20901, 0, 19421, 0, 7889, 11708, 0, 0, 12933, 10338, 18088, 0, 6413, 10411, 5297, 0, 7782, 2594, 0, 1297, 0, 2594, 3891, 0, 2523, 1, 0, 2523, 1297, 1297, 1297, 0, 1, 2594, 0, 0, 0, 1297, 0, 1297, 1, 0, 0, 1, 1297, 5187, 0, 0, 13007, 0, 11672, 0, 7782, 11602, 0, 0, 0, 0, 2594, 2593, 3891, 1, 7782, 7711, 0, 0, 10304, 0, 7711, 0, 7711, 6485, 7711, 0, 1297, 1297, 10303, 2594, 3820, 1, 2523, 0, 1, 0, 1, 2522, 1, 5188, 5188, 5116, 2594, 3891, 2523, 2594, 0, 0, 0, 1, 1226, 1297, 1297, 1297, 0, 0, 2522, 0, 0, 2523, 0}, new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0});
    PartitionMemberInfoImpl details4 = buildDetails(member4, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {23706, 24674, 23347, 0, 20901, 19312, 0, 0, 7889, 11708, 12933, 6413, 0, 0, 0, 9078, 6413, 10411, 5297, 1226, 7782, 0, 2523, 1297, 0, 0, 0, 2523, 0, 0, 2594, 2523, 0, 1297, 0, 2594, 1, 0, 10375, 0, 0, 1297, 1297, 1297, 1, 1226, 1, 0, 1297, 0, 1297, 0, 13007, 7781, 11672, 1, 7782, 11602, 11673, 5225, 2594, 1, 2594, 2593, 3891, 0, 7782, 0, 7710, 0, 10304, 0, 0, 1, 7711, 6485, 7711, 0, 0, 0, 0, 0, 3820, 1, 0, 3999, 1, 1, 1, 0, 1, 0, 5188, 0, 0, 3891, 0, 0, 2522, 1297, 1, 0, 0, 0, 1297, 1297, 0, 0, 2522, 11601, 10376, 2523, 2594}, new long[] {1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0});
    PartitionMemberInfoImpl details5 = buildDetails(member5, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {23706, 24674, 0, 23344, 0, 0, 19421, 0, 0, 11708, 12933, 6413, 12933, 10338, 18088, 0, 0, 10411, 0, 1226, 7782, 2594, 2523, 1297, 1297, 2594, 3891, 0, 2523, 1, 2594, 2523, 0, 1297, 1297, 2594, 0, 2594, 10375, 0, 0, 1297, 0, 1297, 0, 1226, 1, 1, 0, 5187, 1297, 11672, 13007, 7781, 11672, 1, 0, 11602, 11673, 5225, 2594, 1, 0, 2593, 3891, 1, 7782, 0, 0, 2594, 0, 6485, 7711, 1, 7711, 0, 7711, 3891, 0, 1297, 0, 2594, 3820, 0, 2523, 0, 1, 1, 0, 2522, 0, 0, 0, 5116, 0, 0, 0, 0, 2522, 0, 0, 1, 0, 1297, 1297, 1297, 3891, 0, 0, 0, 0, 0, 2594}, new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0});
    PartitionMemberInfoImpl details6 = buildDetails(member6, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {0, 0, 23347, 0, 20901, 19312, 0, 11386, 7889, 0, 12933, 6413, 0, 0, 18088, 0, 6413, 0, 5297, 0, 7782, 0, 2523, 0, 1297, 2594, 0, 0, 2523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5188, 9078, 0, 1297, 0, 1, 0, 0, 1, 0, 0, 1297, 11672, 13007, 7781, 0, 0, 0, 0, 0, 5225, 0, 0, 2594, 0, 0, 0, 7782, 7711, 0, 2594, 0, 0, 7711, 0, 0, 0, 0, 0, 1297, 1297, 0, 0, 0, 1, 0, 3999, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2523, 2594, 0, 0, 0, 0, 1226, 1297, 0, 0, 3891, 1226, 0, 11601, 10376, 2523, 0}, new long[] {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0});
    PartitionMemberInfoImpl details7 = buildDetails(member7, 50 * 1024 * 1024, 50 * 1024 * 1024, new long[] {0, 0, 23347, 23344, 20901, 19312, 19421, 11386, 0, 11708, 12933, 0, 12933, 0, 0, 9078, 0, 0, 0, 0, 0, 0, 0, 1297, 1297, 0, 3891, 2523, 2523, 1, 2594, 2523, 1297, 1297, 1297, 2594, 1, 2594, 10375, 5188, 9078, 1297, 1297, 1297, 0, 0, 0, 0, 1297, 5187, 0, 11672, 0, 7781, 11672, 1, 7782, 0, 11673, 5225, 2594, 0, 2594, 0, 0, 1, 0, 7711, 7710, 2594, 10304, 6485, 7711, 1, 0, 6485, 0, 3891, 1297, 1297, 10303, 2594, 0, 0, 0, 0, 0, 1, 0, 2522, 0, 5188, 5188, 5116, 2594, 0, 0, 2594, 2522, 1297, 1, 1, 1226, 0, 0, 0, 0, 1226, 0, 11601, 0, 2523, 2594}, new long[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});

    model.addRegion("a", Arrays.asList(details1, details2, details3, details4, details5, details6, details7), new FakeOfflineDetails(), true);
    
    doMoves(new CompositeDirector(false, false, true, true), model);
    List bucketMoves1 = new ArrayList(bucketOperator.bucketMoves);
    List bucketCreates1 = new ArrayList(bucketOperator.creates);
    List primaryMoves1 = new ArrayList(bucketOperator.primaryMoves);
    bucketOperator.bucketMoves.clear();
    bucketOperator.creates.clear();
    bucketOperator.primaryMoves.clear();
    
    
    model = new PartitionedRegionLoadModel(bucketOperator ,0, 113, getAddressComparor(false), Collections.emptySet(), null);
    model.addRegion("a", Arrays.asList(details1, details2, details4, details3, details5, details6, details7), new FakeOfflineDetails(), true);
    
    doMoves(new CompositeDirector(false, false, true, true), model);
    assertEquals(bucketCreates1, bucketOperator.creates);
    assertEquals(bucketMoves1, bucketOperator.bucketMoves);
    assertEquals(primaryMoves1, bucketOperator.primaryMoves);
  }

  /**
   * This is more of a simulation than a test
   */
  @Ignore
  @Test
  public void z_testRandom() throws Exception {
    long seed = System.nanoTime();
    System.out.println("random seed=" + seed);
    try  {
      Random rand = new Random(seed);
      int MAX_MEMBERS = 20;
      int MAX_BUCKETS = 200;
      int MAX_REDUNDANCY = 3;
      float IMAIRED_PERCENTAGE= 0.1f; //probability that a bucket will have impaired redundancy
      int AVERAGE_BUCKET_SIZE = 10;
      int AVERAGE_MAX_MEMORY = 200;
      int members = rand.nextInt(MAX_MEMBERS) + 2;
      int buckets = rand.nextInt(MAX_BUCKETS) + members;
      int redundancy = rand.nextInt(MAX_REDUNDANCY);
      long[][] bucketLocations = new long[members][buckets];
      long[][] bucketPrimaries= new long[members][buckets];
      for(int i = 0; i < buckets; i++) {
        int bucketSize = rand.nextInt(AVERAGE_BUCKET_SIZE * 2);

        int remainingCopies = redundancy + 1;
        if(rand.nextFloat() <= IMAIRED_PERCENTAGE) {
          remainingCopies = redundancy == 0 ? 0 : rand.nextInt(redundancy);
        }

        if(remainingCopies > 0) {
          int primary = rand.nextInt(members);
          bucketLocations[primary][i] = bucketSize;
          bucketPrimaries[primary][i] = 1;
          remainingCopies--;
        }

        while(remainingCopies > 0) {
          int member = rand.nextInt(members);
          if(bucketLocations[member][i] == 0) {
            bucketLocations[member][i] = bucketSize;
            remainingCopies--;
          }
        }
      }

      PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator, redundancy, buckets, getAddressComparor(false), Collections.emptySet(), null);
      PartitionMemberInfoImpl[] details = new PartitionMemberInfoImpl[members]; 
      for(int i = 0; i < members; i++) {
        InternalDistributedMember member = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), i);
        int maxMemory = rand.nextInt(AVERAGE_MAX_MEMORY * 2);
        details[i] = buildDetails(member, maxMemory, maxMemory, bucketLocations[i], bucketPrimaries[i]);
      }

      model.addRegion("a", Arrays.asList(details), new FakeOfflineDetails(), true);

      doMoves(new CompositeDirector(true, true, true, true), model);
    } catch(Throwable e) {
      throw new Exception("Error with seed " + seed, e);
    }
  }
  
  /**
   * This test makes sure that we rebalance correctly
   * with multiple levels of colocation. See bug #40943
   * @throws Exception
   */
  @Test
  public void testManyColocatedRegions() throws Exception {
    for(int i = 0; i < 10; i++) {
      try {
        doManyColocatedRegionsTest(i);
      } catch(Throwable t) {
        throw new RuntimeException("With " + i + " colocated regions, we failed", t);
      } 
    }
  }
  
  private void doManyColocatedRegionsTest(int colocatedRegions) throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 5, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    InternalDistributedMember member3 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 3);
    //Create some buckets with low redundancy on member 1
    PartitionMemberInfoImpl details1 = buildDetails(member1, 200, 200, new long[] {30,23,28,30,23}, new long[] {1,1,1,0,0});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 200, 200, new long[] {30,23,28,30,23}, new long[] {0,0,0,1,1});
    PartitionMemberInfoImpl details3 = buildDetails(member3, 200, 200, new long[] {0,0,0,0,0}, new long[] {0,0,0,0,0});
    model.addRegion("primary", Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    for(int i = 0; i < colocatedRegions; i++) {
      model.addRegion("colocated" + i, Arrays.asList(details1, details2, details3), new FakeOfflineDetails(), true);
    }
    
    //we expect 3 moves
    assertEquals(4, doMoves(new CompositeDirector(true, true, true, true), model));
  }
  
  /**
   * Test to make sure than redundancy satisfaction ignores offline members
   * @throws Exception
   */
  @Test
  public void testRedundancySatisficationWithOfflineMembers() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 5, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    
    PartitionMemberInfoImpl details1 = buildDetails(member1, 200, 200, new long[] {30,0,28,30,23}, new long[] {1,0,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 200, 200, new long[] {0,23,0,0,0}, new long[] {0,1,0,0,0});
    
    
    
    //Two buckets have an offline members
    Set o = Collections.singleton(new PersistentMemberID());
    Set x = Collections.emptySet();
    final OfflineMemberDetailsImpl offlineDetails = new OfflineMemberDetailsImpl(new Set[] {x, x, o, o, x} );
    
    model.addRegion("primary", Arrays.asList(details1, details2), offlineDetails, true);
    
    assertEquals(3, doMoves(new CompositeDirector(true, true, false, false), model));
    
    List expectedCreates = new ArrayList();
    expectedCreates.add(new Create(member2, 0));
    expectedCreates.add(new Create(member1, 1));
    expectedCreates.add(new Create(member2, 4));
    assertEquals(expectedCreates, bucketOperator.creates);
  }
  
  @Test
  public void testRebalancingWithOfflineMembers() throws Exception {
    PartitionedRegionLoadModel model = new PartitionedRegionLoadModel(bucketOperator ,1, 6, getAddressComparor(false), Collections.emptySet(), null);
    InternalDistributedMember member1 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 1);
    InternalDistributedMember member2 = new InternalDistributedMember(InetAddress.getByName("127.0.0.1"), 2);
    
    PartitionMemberInfoImpl details1 = buildDetails(member1, 480, 480, new long[] {1,1,1,1,1,1}, new long[] {1,1,1,1,1,1});
    PartitionMemberInfoImpl details2 = buildDetails(member2, 480, 480, new long[] {0,0,0,0,0,0}, new long[] {0,0,0,0,0,0});
    
    
    
    //Each bucket has an offline member
    Set o = Collections.singleton(new PersistentMemberID());
    Set x = Collections.emptySet();
    final OfflineMemberDetailsImpl offlineDetails = new OfflineMemberDetailsImpl(new Set[] {o, o, o, o, o, o} );
    
    model.addRegion("primary", Arrays.asList(details1, details2), offlineDetails, true);
    assertEquals(3, doMoves(new CompositeDirector(true, true, true, true), model));
    
    List expectedMoves = new ArrayList();
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    expectedMoves.add(new Move(member1, member2));
    assertEquals(expectedMoves, bucketOperator.bucketMoves);
  }
  
  private int doMoves(RebalanceDirector director, PartitionedRegionLoadModel model) {
    int moveCount = 0;
    float initialVariance = model.getVarianceForTest();
    float initialPrimaryVariance = model.getPrimaryVarianceForTest();
    if(DEBUG) {
      System.out.println("Initial Model\n" + model + "\nVariance= " + initialVariance
          + ", Primary variance=" + initialPrimaryVariance + "\n---------------");
    }
    model.initialize();
    director.initialize(model);
    while(director.nextStep() && moveCount < MAX_MOVES) {
      
      moveCount++;
      float variance = model.getVarianceForTest();
      float primaryVariance = model.getPrimaryVarianceForTest();
      if(DEBUG) {
        System.out.println("---------------\nMove " + moveCount + "\n" + model
            + "\nVariance= " + variance + ", Primary variance=" + primaryVariance
            + "\n---------------");
      }
      
      if(MoveType.MOVE_BUCKET.equals(bucketOperator.lastMove)) {
        //Assert that the variance is monotonically descreasing 
        //(ie our balance is improving after each step)
        assertTrue("Moving a bucket did not improve variance. Old Variance "
            + initialVariance + " new variance " + variance,
            initialVariance > variance);
      }
      if(MoveType.MOVE_PRIMARY.equals(bucketOperator.lastMove)) {
        assertTrue("Moving a primary did not improve variance. Old Variance "
            + initialPrimaryVariance + " new variance " + primaryVariance,
            initialPrimaryVariance > primaryVariance);
      }
      
      initialVariance = variance;
      initialPrimaryVariance = primaryVariance;
    }
    
    return moveCount;
  }
  
  private PartitionMemberInfoImpl buildDetails(InternalDistributedMember id, float weight, long localMaxMemory, long[] loads, long[] primaryLoads) {
    PRLoad load1 = new PRLoad(loads.length, weight);
    int size = 0;
    int primaryCount = 0;
    int bucketCount = 0;
    long[] bucketSizes = new long[loads.length];
    for(int i = 0; i < loads.length; i++) {
      load1.addBucket(i, loads[i], primaryLoads[i]);
      bucketSizes[i] = loads[i];
      size+= bucketSizes[i];
      if(loads[i] != 0) {
        bucketCount++;
      } 
      if(primaryLoads[i] != 0) {
        primaryCount++;
      }
    }
    PartitionMemberInfoImpl details1 = new PartitionMemberInfoImpl(id, localMaxMemory, size, bucketCount, primaryCount, load1, bucketSizes);
    return details1;
  }
  
  private static AddressComparor getAddressComparor(final boolean enforceUniqueZones) {
    return new AddressComparor() {
      public boolean areSameZone(InternalDistributedMember member1,
          InternalDistributedMember member2) {
        return member1.getIpAddress().equals(member2.getIpAddress());
      }

      public boolean enforceUniqueZones() {
        return enforceUniqueZones;
      }
    };
  }
 
  public static class Create {
    public final InternalDistributedMember targetMember;
    public final int bucketId;
    
    public Create(InternalDistributedMember targetMember, int bucketId) {
      this.targetMember = targetMember;
      this.bucketId = bucketId;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + this.bucketId;
      result = prime * result
          + ((this.targetMember == null) ? 0 : this.targetMember.hashCode());
      return result;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Create other = (Create) obj;
      if (this.bucketId != other.bucketId)
        return false;
      if (this.targetMember == null) {
        if (other.targetMember != null)
          return false;
      } else if (!this.targetMember.equals(other.targetMember))
        return false;
      return true;
    }
    
    public String toString() {
      return "Create[member=" + targetMember+",bucketId=" + bucketId +"]";
    }
  }
  
  public static class Remove {
    public final InternalDistributedMember targetMember;
    public final int bucketId;
    
    public Remove(InternalDistributedMember targetMember, int bucketId) {
      this.targetMember = targetMember;
      this.bucketId = bucketId;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + this.bucketId;
      result = prime * result
          + ((this.targetMember == null) ? 0 : this.targetMember.hashCode());
      return result;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Create other = (Create) obj;
      if (this.bucketId != other.bucketId)
        return false;
      if (this.targetMember == null) {
        if (other.targetMember != null)
          return false;
      } else if (!this.targetMember.equals(other.targetMember))
        return false;
      return true;
    }
    
    public String toString() {
      return "Remove[member=" + targetMember+",bucketId=" + bucketId +"]";
    }
  }
  
  public static class Move {
    public final InternalDistributedMember sourceMember;
    public final InternalDistributedMember targetMember;

    public Move(InternalDistributedMember sourceMember,
        InternalDistributedMember targetMember) {
      this.sourceMember = sourceMember;
      this.targetMember = targetMember;
    }
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result
          + ((this.sourceMember == null) ? 0 : this.sourceMember.hashCode());
      result = prime * result
          + ((this.targetMember == null) ? 0 : this.targetMember.hashCode());
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Move other = (Move) obj;
      if (this.sourceMember == null) {
        if (other.sourceMember != null)
          return false;
      } else if (!this.sourceMember.equals(other.sourceMember))
        return false;
      if (this.targetMember == null) {
        if (other.targetMember != null)
          return false;
      } else if (!this.targetMember.equals(other.targetMember))
        return false;
      return true;
    }
    
    public String toString() {
      return "Move[source=" + sourceMember+",target=" + targetMember + "]";
    }
  }
  
  public static class MyBucketOperator extends SimulatedBucketOperator {
    public List creates = new ArrayList();
    public List removes = new ArrayList();
    public List primaryMoves = new ArrayList();
    public List bucketMoves = new ArrayList();
    private MoveType lastMove = null;
    

    @Override
    public void createRedundantBucket(
        InternalDistributedMember targetMember, int i, Map colocatedRegionBytes, Completion completion) {
      creates.add(new Create(targetMember, i));
      if(DEBUG) {
        System.out.println("Created bucket " + i + " on " + targetMember);
      }
      lastMove = MoveType.CREATE;
      completion.onSuccess();
    }

    @Override
    public boolean movePrimary(InternalDistributedMember source,
        InternalDistributedMember target, int bucketId) {
      primaryMoves.add(new Move(source, target));
      if(DEBUG) {
        System.out.println("Moved primary " + bucketId + " from " + source + " to " + target);
      }
      lastMove = MoveType.MOVE_PRIMARY;
      return true;
    }

    @Override
    public boolean moveBucket(InternalDistributedMember source,
        InternalDistributedMember target, int id,
        Map colocatedRegionBytes) {
      bucketMoves.add(new Move(source, target));
      if(DEBUG) {
        System.out.println("Moved bucket " + id + " from " + source + " to " + target);
      }
      lastMove = MoveType.MOVE_BUCKET;
      return true;
    }

    @Override
    public boolean removeBucket(InternalDistributedMember memberId, int id,
        Map colocatedRegionSizes) {
      removes.add(new Remove(memberId, id));
      if(DEBUG) {
        System.out.println("Moved bucket " + id + " from " + memberId);
      }
      lastMove = MoveType.REMOVE;
      return true;
    }
    
    
  }
  
  public static class BucketOperatorWithFailures extends MyBucketOperator {
    List pendingSuccesses = new ArrayList();
    List pendingFailures = new ArrayList();
    Set badMembers = new HashSet ();

    public void addBadMember(InternalDistributedMember member) {
      this.badMembers.add(member);
    }
    @Override
    public void createRedundantBucket(InternalDistributedMember targetMember,
        int i, Map colocatedRegionBytes, Completion completion) {
      if(badMembers.contains(targetMember)) {
        pendingFailures.add(completion);
      } else {
        super.createRedundantBucket(targetMember, i, colocatedRegionBytes, new Completion() {
          @Override
          public void onSuccess() {
          }

          @Override
          public void onFailure() {
          }
        });
        
        pendingSuccesses.add(completion);
;      }
    }
  }
  
  private static enum MoveType {
    CREATE,
    MOVE_PRIMARY,
    MOVE_BUCKET,
    REMOVE;
  }
  
  private static class FakeOfflineDetails implements OfflineMemberDetails {
    
    private Set offlineMembers;

    public FakeOfflineDetails() {
      this(Collections.emptySet());
    }
    
    public FakeOfflineDetails(Set offlineMembers) {
      this.offlineMembers = offlineMembers;
    }

    public Set getOfflineMembers(int bucketId) {
      return this.offlineMembers;
    }

    public void fromData(DataInput in) throws IOException,
        ClassNotFoundException {
      offlineMembers = DataSerializer.readObject(in);
    }

    public void toData(DataOutput out) throws IOException {
      DataSerializer.writeObject(offlineMembers, out);
      
    }
    
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy