You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by bs...@apache.org on 2018/01/08 19:07:07 UTC

[geode] branch develop updated: GEODE-4148 Locator threshold before rebalancing clients

This is an automated email from the ASF dual-hosted git repository.

bschuchardt pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new ad74d98  GEODE-4148 Locator threshold before rebalancing clients
ad74d98 is described below

commit ad74d980604291533c85929a854b3003f379df74
Author: Bruce Schuchardt <bs...@pivotal.io>
AuthorDate: Mon Jan 8 10:59:56 2018 -0800

    GEODE-4148 Locator threshold before rebalancing clients
    
    The server location service is now less aggressive in moving clients
    from the most loaded server to the least loaded server.  It will wait
    until the load difference is 10 times the load-per-connection value,
    which defaults to 1.  Once this threshold is reached clients will be
    moved until balance has been restored.
    
    The threshold can be changed from 10 to some other floating point value
    with the system property
    
        gemfire.locator-load-imbalance-threshold
    
    when starting locators.  The setting is not needed in clients or
    servers.
    
    This closes #1241
---
 .../distributed/internal/LocatorLoadSnapshot.java  | 128 +++-
 .../apache/geode/internal/cache/properties.html    | 686 +--------------------
 .../internal/LocatorLoadSnapshotJUnitTest.java     |  66 ++
 3 files changed, 206 insertions(+), 674 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/LocatorLoadSnapshot.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/LocatorLoadSnapshot.java
index 364b92a..353fad9 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/LocatorLoadSnapshot.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/LocatorLoadSnapshot.java
@@ -44,6 +44,12 @@ import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID;
  *
  */
 public class LocatorLoadSnapshot {
+
+  public static final String LOAD_IMBALANCE_THRESHOLD_PROPERTY_NAME =
+      "gemfire.locator-load-imbalance-threshold";
+
+  public static final float DEFAULT_LOAD_IMBALANCE_THRESHOLD = 10;
+
   private final Map/* <ServerLocation, String[]> */ serverGroupMap = new HashMap();
 
   private final Map/* <String(server group), Map<ServerLocation, LoadHolder> */
@@ -55,6 +61,19 @@ public class LocatorLoadSnapshot {
   private final ConcurrentMap/* <EstimateMapKey, LoadEstimateTask> */
   estimateMap = new ConcurrentHashMap();
 
+  /**
+   * when replacing a client's current server we do not move a client from a highly loaded server to
+   * a less loaded server until imbalance reaches this threshold. Then we aggressively move clients
+   * until balance is achieved.
+   */
+  private float loadImbalanceThreshold;
+
+  /**
+   * when the loadImbalanceThreshold is hit this variable will be true and it will remain true until
+   * balance is achieved.
+   */
+  private boolean rebalancing;
+
   private final ScheduledThreadPoolExecutor estimateTimeoutProcessor =
       new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
         public Thread newThread(final Runnable r) {
@@ -64,10 +83,17 @@ public class LocatorLoadSnapshot {
         }
       });
 
+
   public LocatorLoadSnapshot() {
     connectionLoadMap.put(null, new HashMap());
     queueLoadMap.put(null, new HashMap());
     this.estimateTimeoutProcessor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+    String property = System.getProperty(LOAD_IMBALANCE_THRESHOLD_PROPERTY_NAME);
+    if (property != null) {
+      loadImbalanceThreshold = Float.parseFloat(property);
+    } else {
+      loadImbalanceThreshold = DEFAULT_LOAD_IMBALANCE_THRESHOLD;
+    }
   }
 
   public void addServer(ServerLocation location, String[] groups, ServerLoad initialLoad) {
@@ -141,7 +167,12 @@ public class LocatorLoadSnapshot {
     return isBalanced(groupServers);
   }
 
-  private synchronized boolean isBalanced(Map groupServers) {
+  private synchronized boolean isBalanced(Map<ServerLocation, LoadHolder> groupServers) {
+    return isBalanced(groupServers, false);
+  }
+
+  private synchronized boolean isBalanced(Map<ServerLocation, LoadHolder> groupServers,
+      boolean withThresholdCheck) {
     if (groupServers == null || groupServers.isEmpty()) {
       return true;
     }
@@ -150,9 +181,8 @@ public class LocatorLoadSnapshot {
     float largestLoadPerConnection = Float.MIN_VALUE;
     float worstLoad = Float.MIN_VALUE;
 
-    for (Iterator itr = groupServers.entrySet().iterator(); itr.hasNext();) {
-      Map.Entry next = (Entry) itr.next();
-      LoadHolder nextLoadReference = (LoadHolder) next.getValue();
+    for (Entry<ServerLocation, LoadHolder> loadHolderEntry : groupServers.entrySet()) {
+      LoadHolder nextLoadReference = loadHolderEntry.getValue();
       float nextLoad = nextLoadReference.getLoad();
       float nextLoadPerConnection = nextLoadReference.getLoadPerConnection();
 
@@ -167,7 +197,52 @@ public class LocatorLoadSnapshot {
       }
     }
 
-    return (worstLoad - bestLoad) <= largestLoadPerConnection;
+    boolean balanced = (worstLoad - bestLoad) <= largestLoadPerConnection;
+
+    if (withThresholdCheck) {
+      balanced = thresholdCheck(bestLoad, worstLoad, largestLoadPerConnection, balanced);
+    }
+
+    return balanced;
+  }
+
+
+  /**
+   * In order to keep from ping-ponging clients around the cluster we don't move a client unless
+   * imbalance is greater than the loadImbalanceThreshold.
+   * <p>
+   * When the threshold is reached we report imbalance until proper balance is achieved.
+   * </p>
+   * <p>
+   * This method has the side-effect of setting the <code>rebalancing</code> instance variable
+   * which, at the time of this writing, is only used by this method.
+   * </p>
+   */
+  synchronized boolean thresholdCheck(float bestLoad, float worstLoad,
+      float largestLoadPerConnection, boolean balanced) {
+    if (rebalancing) {
+      if (balanced) {
+        rebalancing = false;
+      }
+      return balanced;
+    }
+
+    // see if we're out of balance enough to trigger rebalancing or whether we
+    // should tolerate the imbalance
+    if (!balanced) {
+      float imbalance = worstLoad - bestLoad;
+      if (imbalance >= (largestLoadPerConnection * loadImbalanceThreshold)) {
+        rebalancing = true;
+      } else {
+        // we're not in balance but are within the threshold
+        balanced = true;
+      }
+    }
+    return balanced;
+  }
+
+  synchronized boolean isRebalancing() {
+    return rebalancing;
   }
 
   /**
@@ -246,13 +321,13 @@ public class LocatorLoadSnapshot {
       group = null;
     }
 
-    Map groupServers = (Map) connectionLoadMap.get(group);
+    Map<ServerLocation, LoadHolder> groupServers = (Map) connectionLoadMap.get(group);
     if (groupServers == null || groupServers.isEmpty()) {
       return null;
     }
 
     // check to see if we are currently balanced
-    if (isBalanced(groupServers)) {
+    if (isBalanced(groupServers, true)) {
       // if we are then return currentServer
       return currentServer;
     }
@@ -347,7 +422,7 @@ public class LocatorLoadSnapshot {
    * Test hook to get the current load for all servers Returns a map of ServerLocation->Load for
    * each server.
    */
-  public synchronized Map getLoadMap() {
+  public synchronized Map<ServerLocation, ServerLoad> getLoadMap() {
     Map connectionMap = (Map) connectionLoadMap.get(null);
     Map queueMap = (Map) queueLoadMap.get(null);
     Map result = new HashMap();
@@ -412,7 +487,16 @@ public class LocatorLoadSnapshot {
     }
   }
 
-  private List/* <LoadHolder> */ findBestServers(Map groupServers, Set excludedServers, int count) {
+  /**
+   *
+   * @param groupServers the servers to consider
+   * @param excludedServers servers to exclude
+   * @param count how many you want. a negative number means all of them in order of best to worst
+   * @return a list of best...worst server LoadHolders
+   */
+  private List /* <LoadHolder> */ findBestServers(Map<ServerLocation, LoadHolder> groupServers,
+      Set excludedServers, int count) {
+
     TreeSet bestEntries = new TreeSet(new Comparator() {
       public int compare(Object o1, Object o2) {
         LoadHolder l1 = (LoadHolder) o1;
@@ -427,19 +511,21 @@ public class LocatorLoadSnapshot {
       }
     });
 
+    boolean retainAll = (count < 0);
     float lastBestLoad = Float.MAX_VALUE;
-    for (Iterator itr = groupServers.entrySet().iterator(); itr.hasNext();) {
-      Map.Entry next = (Entry) itr.next();
-      ServerLocation location = (ServerLocation) next.getKey();
+
+    for (Map.Entry<ServerLocation, LoadHolder> loadEntry : groupServers.entrySet()) {
+      ServerLocation location = loadEntry.getKey();
       if (excludedServers.contains(location)) {
         continue;
       }
-      LoadHolder nextLoadReference = (LoadHolder) next.getValue();
+
+      LoadHolder nextLoadReference = loadEntry.getValue();
       float nextLoad = nextLoadReference.getLoad();
 
-      if (bestEntries.size() < count || count == -1 || nextLoad < lastBestLoad) {
+      if ((bestEntries.size() < count) || retainAll || (nextLoad < lastBestLoad)) {
         bestEntries.add(nextLoadReference);
-        if (count != -1 && bestEntries.size() > count) {
+        if (!retainAll && (bestEntries.size() > count)) {
           bestEntries.remove(bestEntries.last());
         }
         LoadHolder lastBestHolder = (LoadHolder) bestEntries.last();
@@ -453,18 +539,18 @@ public class LocatorLoadSnapshot {
   /**
    * If it is most loaded then return its LoadHolder; otherwise return null;
    */
-  private LoadHolder isCurrentServerMostLoaded(ServerLocation currentServer, Map groupServers) {
-    final LoadHolder currentLH = (LoadHolder) groupServers.get(currentServer);
+  private LoadHolder isCurrentServerMostLoaded(ServerLocation currentServer,
+      Map<ServerLocation, LoadHolder> groupServers) {
+    final LoadHolder currentLH = groupServers.get(currentServer);
     if (currentLH == null)
       return null;
     final float currentLoad = currentLH.getLoad();
-    for (Iterator itr = groupServers.entrySet().iterator(); itr.hasNext();) {
-      Map.Entry next = (Entry) itr.next();
-      ServerLocation location = (ServerLocation) next.getKey();
+    for (Map.Entry<ServerLocation, LoadHolder> loadEntry : groupServers.entrySet()) {
+      ServerLocation location = loadEntry.getKey();
       if (location.equals(currentServer)) {
         continue;
       }
-      LoadHolder nextLoadReference = (LoadHolder) next.getValue();
+      LoadHolder nextLoadReference = loadEntry.getValue();
       float nextLoad = nextLoadReference.getLoad();
       if (nextLoad > currentLoad) {
         // found a guy who has a higher load than us so...
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/properties.html b/geode-core/src/main/java/org/apache/geode/internal/cache/properties.html
index 31cf184..1369b8a 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/properties.html
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/properties.html
@@ -112,7 +112,7 @@ with random port selection.
 <p>
 See <code>org.apache.geode.internal.AvailablePort#isPortAvailable</code>.
 <p>
-When establishing a JGroups locator, this sets the <code>SO_TIMEOUT</code>
+When establishing a locator, this sets the <code>SO_TIMEOUT</code>
 characteristic on the UDP port that we attempt to test.
 <p>
 Units are in milliseconds.
@@ -337,20 +337,6 @@ Enables logging for this class.
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>DistributionManager.DEBUG_JAVAGROUPS</strong></dt>
-<dd>
-<em>Public:</em> yes (general debugging)
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager</code>.
-<p>
-See <code>com.gemstone.org.jgroups.stack.Protocol#trace/<code>.
-<p>
-Enables JGroups-package debug logging.
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>DistributionManager.DISCONNECT_WAIT</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -391,26 +377,6 @@ See <code>org.apache.geode.distributed.internal.DistributionManager#INCOMING_QUE
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>DistributionManager.JAVAGROUPS_CONFIG</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>String</em>
-<p>
-If multicast is enabled, the default is org/apache/geode/distributed/internal/javagroups-mcast.txt.
-<p>
-If multicast is not enabled, the default is org/apache/geode/distributed/internal/javagroups-config.txt.
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-<pre>
-   The system property that specifies the name of a file from which to read
-   Jgroups configuration information
-</pre>
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>DistributionManager.MAX_FE_THREADS</strong></dt>
@@ -635,7 +601,7 @@ Enables certain additional (fine-level?) logging.
 <p>
 <em>Boolean</em> (default is false)
 <p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#VERBOSE_VIEWS</code>.
+See <code>org.apache.geode.distributed.internal.membership.gms.GMSMembershipManager#VERBOSE_VIEWS</code>.
 <p>
 Enables additional info-level logging
 (see <code>#addShunnedMember</code> and <code>#addSurpriseMember</code>).
@@ -829,13 +795,13 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>JGroups.SHUN_SUNSET</strong></dt>
+<dt><strong>gemfire.shunned-member-timeout</strong></dt>
 <dd>
 <em>Public:</em> false
 <p>
 <em>Integer</em> (default is 90)
 <p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#isShunned</code>.
+See <code>org.apache.geode.distributed.internal.membership.gms.GMSMembershipManager#isShunned</code>.
 <p>
 This is the length of time during which incoming messages from a departed peer
 are regarded as out-of-order messages and are hence ignored (shunned).
@@ -844,18 +810,6 @@ Units are in seconds.
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>Locator.forceAdminDMType</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.InternalLocator#FORCE_ADMIN_DM_TYPE</code>.
-<p>
-No longer exists as of 7.0.
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>Locator.forceLocatorDMType</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -945,22 +899,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>bind.address</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>String</em> (See <code>InetAddress.getByName</code>)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.FD_SOCK#setProperties(Properties)</code>.
-<p>
-See <code>com.gemstone.org.jgroups.protocols.MPING#setProperties(Properties)</code>.
-<p>
-See <code>com.gemstone.org.jgroups.protocols.TP#setProperties(Properties)</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>java.util.concurrent.NanoTimerProvider</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -1030,32 +968,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>enable_canonicalization</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.Message#DISABLE_CANONICALIZATION</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>force.properties</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>String</em>
-<p>
-See <code>com.gemstone.org.jgroups.conf.ConfiguratorFactory class init</code>.
-<p>
-A JGroups configuration string?
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>GemFire.ALWAYS_REPLICATE_UPDATES</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -1532,25 +1444,6 @@ See <code>org.apache.geode.internal.cache.LocalRegion#EXPIRY_UNITS_MS</code>.
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.FD_TIMEOUT</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Long</em> (default is 0)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-If <code>gemfire.useFD</code> is true or this property's value is non-zero,
-the <code>FD</code> protocol is inserted on the JGroups stack.
-<p>
-Furthermore, the <code>MEMBER_TIMEOUT</code> string is substituted with this
-value in the JGroups default config, which in turn sets the
-<code>timeout</code> property in the <code>VERIFY_SUSPECT</code> protocol,
-which otherwise defaults to 2000 milliseconds.
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.IDLE_THREAD_TIMEOUT</strong></dt>
@@ -1579,6 +1472,23 @@ Tell admin API whether the member is a dedicated cache server
 </dd>
 
 <!-- -------------------------------------------------------  -->
+    <dt><strong>gemfire.locator-load-imbalance-threshold</strong></dt>
+    <dd>
+        <em>Public:</em> true
+        <p>
+            <em>Float</em> (default is 10.0)
+        <p>
+            See <code>org.apache.geode.distributed.internal.LocatorLoadSnapshot</code>
+        <p>
+            Sets the connection count threshold for rebalancing clients.  When a
+        client asks the locator whether it should switch to a less loaded server
+        the locator will respond "no" if the connection-count gap between the
+        highest-loaded server and the least-loaded server is within this threshold.
+        If the threshold is reached the locator will aggressivley reassign clients
+        until balance is re-established.
+    </dd>
+
+<!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.memoryEventTolerance</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -1802,23 +1712,6 @@ See <code>org.apache.geode.internal.cache.partitioned.RegionAdvisor#VOLUNTERING_
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.VIEW_BUNDLING_WAIT_TIME</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default is 150)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.pbcast.GMS#BUNDLE_WAITTIME</code>.
-<p>
-<pre>
-     GemStoneAddition - amount of time to wait for additional join/leave
-     requests before processing.  Set gemfire.VIEW_BUNDLING_WAIT_TIME to
-     the number of milliseconds.  Defaults to 150ms.
-</pre>
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.VM_OWNERSHIP_WAIT_TIME</strong></dt>
@@ -1980,18 +1873,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.debug-frag2</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.FRAG2#DEBUG_FRAG2</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.disableAccessTimeUpdateOnPut</strong></dt>
 <dd>
 <em>Public:</em> true
@@ -2153,23 +2034,6 @@ See java.lang.management.ThreadMXBean
 for more information.
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.fast-member-timeout</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default is 1000)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.VERIFY_SUSPECT#suspect</code>.
-<p>
-<pre>
-  artificially age the entry for faster processing
-</pre>
-<p>
-Units are in milliseconds.
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.gateway-conserve-sockets-allowed</strong></dt>
@@ -2290,17 +2154,6 @@ Causes the code used by gfsh to launch a server or locator to install
 signal handlers using sun.misc.Signal.
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.lightLocators</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.internal.gui.RemoteCommandManager#getLocatorStart</code>.
-<p>
-Adds the "-lightweight" argument when creating the locator.
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.loadLibrary.debug</strong></dt>
@@ -2342,7 +2195,7 @@ See <code>org.apache.geode.internal.gui.RemoteCommandManager#getLocatorStart</co
 See <code>org.apache.geode.internal.gui.StartStopManager#startLocalLocator</code>.
 <p>
 A JVM property, esp. "-Xmx400m"
-<p>
+</p>
 TBA
 </dd>
 
@@ -2497,26 +2350,13 @@ TBA
 <p>
 <pre>
    This property limits the number of threads that the locator will
-	use for processing gossip messages and server location
-	requests.
+	use for processing messages.
 </pre>
 <p>
 TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>gemfire.useFD</strong></dt>
-<dd>
-<em>Public:</em> true
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>gemfire.validateMessageSize</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -2568,10 +2408,12 @@ See <code>org.apache.geode.distributed.DistributedSystem#PROPERTY_FILE</code>.
      system path then the above search is done.  If it is an absolute
      file system path then that file must exist; no search for it is
      done.
+     </p>
      @since GemFire 5.0
 </pre>
 <p>
 TBA
+</p>
 </dd>
 
 <!-- -------------------------------------------------------  -->
@@ -2661,22 +2503,6 @@ are saved a close.
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>ignore.bind.address</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.MPING#setProperties(Properties)</code>.
-<p>
-See <code>com.gemstone.org.jgroups.util.Util#isBindAddressPropertyIgnored</code>.
-<p>
-If <code>true</code>, the value of the
-<code>bind.address</code> property is ignored.
-<p>
-This is used in MPING, FD_SOCK, and TP.
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>install</strong></dt>
@@ -2693,52 +2519,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>java.net.preferIPv6Addresses</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.util.Util#getFirstNonLoopbackAddress</code>.
-<p>
-TBA
-<p>
-Is this a standard Java property?
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>jboss.partition.udpGroup</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>String</em> (See <code>InetAddress.getByName</code>)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.UDP#setProperties</code>.
-<p>
-<pre>
-  The multicast address used for sending and receiving packets 
-</pre>
-If you manage to get here without setting mcast_addr_name, the default
-is 228.8.8.8.
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>jboss.partition.udpPort</strong></dt>
-<em>Integer</em>
-<p>
-See <code>com.gemstone.org.jgroups.protocols.UDP#setProperties</code>.
-<dd>
-<em>Public:</em> false
-<p>
-<p>
-<pre>
-  The multicast port used for sending and receiving packets
-</pre>
-<p>
-If you manage to get here without setting mcast_port, the default value is 7600.
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>jta.VERBOSE</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -2766,19 +2546,6 @@ Units are in seconds.
 TBA Is this a standard JTA property?
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>local_addr.timeout</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Long</em> (default is 30000)
-<p>
-See <code>com.gemstone.org.jgroups.JChannel#LOCAL_ADDR_TIMEOUT</code>.
-<p>
-Units are in milliseconds.
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>mergelogs.TRIM_TIMESTAMPS</strong></dt>
@@ -2833,18 +2600,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.DEBUG_FAIL_FIRST</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.pbcast.ClientGmsImpl#join(Address)</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.VERBOSE</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -2857,20 +2612,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.ackSenderThread</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.UNICAST#start</code>.
-<p>
-Creates an <code>AckSender</code> thread.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.backlog</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -2904,17 +2645,10 @@ TBA
 <dd>
 <em>Public:</em> false
 <p>
-<em>Long</em> in JGroups, <em>Integer</em> in Connection.
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code> (default is 20).
+<em>Integer</em> in Connection.
 <p>
 See <code>org.apache.geode.internal.tcp.Connection#BATCH_FLUSH_MS</code>.
 (Default is 50)
-<p>
-Replaces <code>BUNDLING_TIMEOUT</code> in the JGroups config template.
-This in turn sets the <code>max_bundle_timeout</code> property in the
-<code>TP</code> JGroups protocol:
-<p>
 <pre>
     Max number of milliseconds until queued messages are sent. Messages are
     sent when max_bundle_size or
@@ -2937,18 +2671,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.debugConnect</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.defaultConcurrencyLevel</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -2980,64 +2702,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.directAckTimeout</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default is 15000)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.DirAck#DIRACK_TIMEOUT</code>.
-<p>
-<pre>
-  warning timer period for log message while waiting for acks
-</pre>
-<p>
-Units are in milliseconds.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.disableBatching</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-If true, sets <code>ENABLE_BUNDLING</code> to false and
-<code>BUNDLING_TIMEOUT</code> to 30 in the JGroups config template; otherwise
-it sets <code>ENABLE_BUNDLING</code> to true and <code>BUNDLING_TIMEOUT</code>
-to <code>p2p.batchFlushTime</code> (30 if not set).
-<p>
-<code>ENABLE_BUNDLING</code> maps to the <code>enable_bundling</code> property
-in <code>UDP</code>, and <code>BUNDLING_TIMEOUT</code> maps to the 
-<code>max_bundle_timeout</code> property in <code>UDP</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.disableDirAckBypass</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (defaults to false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.DirAck#DIRACK_BYPASS</code>.
-<p>
-See <code>com.gemstone.org.jgroups.protocols.TP#dirackBypass</code>.
-<p>
-<pre>
-   set -Dp2p.disableDirAckBypass=true to disengage bypassing most of the stack
-   on outgoing messages
-</pre>
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.disableSocketWrite</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3061,105 +2725,13 @@ TBA
 <p>
 <em>Integer</em> (default is 3000)
 <p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#destroyMember</code>.
+See <code>org.apache.geode.distributed.internal.membership.gms.GMSMembershipManager#destroyMember</code>.
 <p>
 TBA Workaround for bug 34010: small pause inserted before closing
 reader threads for a departed member.
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.discoveryProbes</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default is 2)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-This substitutes the <code>NUM_PING_REQUESTS</code> field in the JGroups
-config template, which in turn sets the <code>num_ping_requests</code> property
-in the <code>Discovery</code> protocol.
-<p>
-<pre>
-Number of GET_MBRS_REQ messages to be sent (min=1), distributed over timeout ms
-</pre>
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.discoveryTimeout</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Long</em> (default is 1000)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-Sets the <code>DISCOVERY_TIMEOUT</code> field in the JGroups template, which
-in turn sets the <code>timeout</code> property in the <code>Discovery</code>
-protocol.
-<p>
-Units are in milliseconds.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.enableInitialCoordinator</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-See <code>com.gemstone.org.jgroups.protocols.pbcast.GMS#setProperties</code>.
-<p>
-The boolean inverse of this property substitutes <code>DISABLE_COORD</code> in
-the JGroups template file, which in turn sets the
-<code>disable_initial_coord property</code> in <code>pbcast.GMS</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.enableJgStackStats</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#enableJgStackStats</code>.
-<p>
-<pre>
-  whether time-based statistics should be gathered for up/down events
-</pre>
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.enableUcastFlush</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#DISABLE_UCAST_FLUSH</code>.
-<p>
-Regarding the <code>DISABLE_UCAST_FLUSH = !Boolean.getBoolean()</code>:
-<pre>
-      Sometimes the jgroups channel is blocked until unicast messages
-      are all ack'd.  This boolean disables that blocking and can
-      increase performance in situations where it's okay to deliver
-      point-to-point and multicast messages out of order
-</pre>
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.handshakeTimeoutMs</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3181,35 +2753,18 @@ TBA
 See <code>org.apache.geode.internal.tcp</code>.
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.incomingPacketHandler</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-Substitutes <code>INCOMING_PACKET_HANDLER</code> in the JGroups template
-string, which in turn sets the <code>use_incoming_packet_handler</code>
-property in <code>UDP</code>.
-<p>
-If set, a separate thread is used to collect incoming UDP messages and to
-dispatch JGroups events.
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>p2p.joinTimeout</strong></dt>
 <dd>
 <em>Public:</em> false
 <p>
-<em>Long</em> (default is 10000)
+<em>Long</em> (default is 60000 for a server and 24000 for a locator)
 <p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
+See <code>org.apache.geode.distributed.internal.membership.gms.ServiceConfig</code>.
 <p>
-Sets the <code>JOIN_TIMEOUT</code> field in the JGroups config template, which
-in turn sets the <code>join_timeout</code> property in the
-<code>pbcast.GMS protocol</code>.
+    Establishes the timeout for waiting for a join response when connecting to
+    the cluster
 <p>
 Units are in milliseconds.
 <p>
@@ -3280,27 +2835,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.maxSentMsgsSize</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Long</em> (default is 100000)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-This substitutes the <code>MAX_SENT_MSGS_SIZE</code> field in the JGroups
-config template, which in turn sets the <code>max_sent_msgs_size</code>
-property in the <code>pbcast.NAKACK</code> protocol.
-<p>
-<pre>
-  maximum number of sent messages
-</pre>
-<p>
-This controls uncontrolled backlog (memory growth) in the NAKACK JGroups
-protocol layer (this is a GemStone addition).
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.nodirectBuffers</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3316,25 +2850,6 @@ See <code>org.apache.geode.internal.tcp.TCPConduit#useDirectBuffers</code>.
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.numInitialMembers</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default is 1)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-This substitutes the <code>NUM_INITIAL_MEMBERS</code> string in the JGroups
-config template, which in turn sets the <code>num_initial_members</code>
-property in the <code>PING</code> protocol.
-<p>
-<pre>
-  the minimum number of initial members for a FIND_INITAL_MBRS, default is 2
-</pre>
-<p>
-TBA
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>p2p.oldIO</strong></dt>
@@ -3349,23 +2864,6 @@ If set, don't use java.nio.
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>p2p.outgoingPacketHandler</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-Sets <code>OUTGOING_PACKET_HANDLER</code> in the JGroups config template,
-which in turn sets the <code>use_outgoing_packet_handler</code> property in
-<code>UDP</code>.
-<p>
-If set, JGroups uses a separate thread to manage outgoing messages and
-related events.
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>p2p.readerBufferSize</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3377,78 +2875,6 @@ See <code>org.apache.geode.internal.tcp.Connection#INITIAL_CAPACITY</code>.
 TBA
 </dd>
 
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.retransmissionAcks</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>com.gemstone.org.jgroups.protocols.DirAck#ALWAYS_RELEASE</code>.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.simulateDiscard</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-If true, inserts a <code>DISCARD</code> protocol into the JGroups stack.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.simulateDiscard.received</strong></dt>
-<em>Double</em> (between 0 and 1, default is 0)
-<p>
-<dd>
-<em>Public:</em> false
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-Sets the <code>up</code> property in the JGroups <code>DISCARD</code> protocol.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.simulateDiscard.sent</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Double</em> (between 0 and 1, default is 0.05)
-<p>
-See <code>org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel</code>.
-<p>
-Sets the <code>down</code> property in the JGroups <code>DISCARD</code>
-protocol.
-<p>
-TBA
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>p2p.socket_timeout</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Long</em> (default is 60000)
-<p>
-See org.apache.geode.distributed.internal.membership.jgroup.JGroupMembershipManager#createChannel.
-<p>
-Substitutes the <code>SOCKET_TIMEOUT</code> field in the JGroups config
-template, which in turn sets the <code>socket_timeout</code> field in the
-<code>DirAck</code> protocol.
-<p>
-Units are in milliseconds.
-<p>
-TBA 
-</dd>
 
 <!-- -------------------------------------------------------  -->
 <dt><strong>p2p.tcpBufferSize</strong></dt>
@@ -3492,20 +2918,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>persist.properties</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>String</em>
-<p>
-See com.gemstone.org.jgroups.persistence.DBPersistenceManager#DBPersistenceManager(String).
-<p>
-This is the name of a file that is read when an instance is created.
-<p>
-Note: this class is not used by GemFire. 
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>query.disableIndexes</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3546,38 +2958,6 @@ TBA
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strong>resolve.dns</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Boolean</em> (default is false)
-<p>
-See com.gemstone.org.jgroups.stack.IpAddress class init.
-<p>
-See com.gemstone.org.jgroups.util.Util class init.
-<p>
-If set to true, InetAddress's may be printed by JGroups using getHostName instead of getHostAddress.
-<p>
-TBA 
-</dd>
-
-<!-- -------------------------------------------------------  -->
-<dt><strong>scheduler.max.threads</strong></dt>
-<dd>
-<em>Public:</em> false
-<p>
-<em>Integer</em> (default 128)
-<p>
-See com.gemstone.org.jgroups.util.Scheduler#Scheduler().
-<p>
-<pre>
-      max number of threads, will only be allocated when needed
-</pre>
-<p>
-TBA Is this class even used? 
-</dd>
-
-<!-- -------------------------------------------------------  -->
 <dt><strong>skipConnection</strong></dt>
 <dd>
 <em>Public:</em> false
@@ -3585,7 +2965,7 @@ TBA Is this class even used?
 <em>Boolean</em> (default is false)
 <p>
 Removed in Geode 1.0 with removal of deprecated Bridge classes.
-<p>
+</p>
 TBA 
 </dd>
 
@@ -3890,7 +3270,7 @@ default keystore password.  See the JSSE documentation.
 </dd>
 
 <!-- -------------------------------------------------------  -->
-<dt><strongjavax.net.ssl.trustStore></strong></dt>
+<dt><strong>javax.net.ssl.trustStore></strong></dt>
 <dd>
 <em>Public:</em> false
 <p>
diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/LocatorLoadSnapshotJUnitTest.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/LocatorLoadSnapshotJUnitTest.java
index aaf7b15..9641901 100644
--- a/geode-core/src/test/java/org/apache/geode/distributed/internal/LocatorLoadSnapshotJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/LocatorLoadSnapshotJUnitTest.java
@@ -287,4 +287,70 @@ public class LocatorLoadSnapshotJUnitTest {
     assertFalse(sn.hasBalancedConnections("b"));
   }
 
+  @Test
+  public void testThatReplacementServerIsSelected() {
+    final LocatorLoadSnapshot loadSnapshot = new LocatorLoadSnapshot();
+
+    final ServerLocation l1 = new ServerLocation("localhost", 1);
+    final ServerLocation l2 = new ServerLocation("localhost", 2);
+    final ServerLocation l3 = new ServerLocation("localhost", 3);
+
+    float defaultLoadImbalanceThreshold = LocatorLoadSnapshot.DEFAULT_LOAD_IMBALANCE_THRESHOLD;
+
+    float l1ConnectionLoad = 50 + defaultLoadImbalanceThreshold;
+    float l2ConnectionLoad = 50;
+    float l3ConnectionLoad = 50 - defaultLoadImbalanceThreshold;
+    loadSnapshot.addServer(l1, new String[] {"a"}, new ServerLoad(l1ConnectionLoad, 1, 0, 1));
+    loadSnapshot.addServer(l2, new String[] {"a", "b"}, new ServerLoad(l2ConnectionLoad, 1, 0, 1));
+    loadSnapshot.addServer(l3, new String[] {"b"}, new ServerLoad(l3ConnectionLoad, 1, 0, 1));
+
+    // a new server should be selected until the load-imbalance-threshold is reached
+    ServerLocation newServer = null;
+    do {
+      newServer = loadSnapshot.getReplacementServerForConnection(l1, "", Collections.EMPTY_SET);
+      if (newServer == l3) {
+        // the threshold check should have initiated client rebalancing
+        assertTrue(loadSnapshot.isRebalancing());
+      }
+    } while (newServer == l3);
+
+    // once balance is achieved we should have received the same server and
+    // rebalancing should have ended
+    assertEquals(l1, newServer);
+    assertFalse(loadSnapshot.isRebalancing());
+
+    // all load snapshots should now be balanced
+    Map<ServerLocation, ServerLoad> loadMap = loadSnapshot.getLoadMap();
+    ServerLoad l1Load = loadMap.get(l1);
+    assertEquals(50, l1Load.getConnectionLoad(), 0.01);
+    ServerLoad l2Load = loadMap.get(l2);
+    assertEquals(50, l1Load.getConnectionLoad(), 0.01);
+    ServerLoad l3Load = loadMap.get(l3);
+    assertEquals(50, l3Load.getConnectionLoad(), 0.01);
+  }
+
+  @Test
+  public void testThatReplacementServerIsNotSelectedIfThresholdNotReached() {
+    final LocatorLoadSnapshot loadSnapshot = new LocatorLoadSnapshot();
+
+    final ServerLocation l1 = new ServerLocation("localhost", 1);
+    final ServerLocation l2 = new ServerLocation("localhost", 2);
+    final ServerLocation l3 = new ServerLocation("localhost", 3);
+
+    float defaultLoadImbalanceThreshold = LocatorLoadSnapshot.DEFAULT_LOAD_IMBALANCE_THRESHOLD;
+
+    float l1ConnectionLoad = 50 + defaultLoadImbalanceThreshold - 1;
+    float l2ConnectionLoad = 50;
+    float l3ConnectionLoad = 50 + (defaultLoadImbalanceThreshold / 2);
+    loadSnapshot.addServer(l1, new String[] {"a"}, new ServerLoad(l1ConnectionLoad, 1, 0, 1));
+    loadSnapshot.addServer(l2, new String[] {"a", "b"}, new ServerLoad(l2ConnectionLoad, 1, 0, 1));
+    loadSnapshot.addServer(l3, new String[] {"b"}, new ServerLoad(l3ConnectionLoad, 1, 0, 1));
+
+    ServerLocation newServer =
+        loadSnapshot.getReplacementServerForConnection(l1, "", Collections.EMPTY_SET);
+    assertEquals(l1, newServer);
+    Map<ServerLocation, ServerLoad> loadMap = loadSnapshot.getLoadMap();
+    ServerLoad l1Load = loadMap.get(l1);
+    assertEquals(l1ConnectionLoad, l1Load.getConnectionLoad(), 0.01);
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].