Author: stefanegli
Date: Thu Sep 24 10:09:22 2015
New Revision: 1705024

URL: http://svn.apache.org/viewvc?rev=1705024&view=rev
Log:
SLING-5030 part 2: when an instance detects that it is ISOLATED_FROM_TOPOLOGY 
it resets the leaderElectionId - that has the effect that it will be the least 
likely to become leader - which means that it is put at the end of the instance 
ordering. This will ensure that the newly elected leader can stay leader - thus 
reducing the number of leader changes in case of a temporary and then healed 
network-partitioning situation. Note that this also implies that when an 
instance does NOT detect the ISOLATED_FROM_TOPOLOGY that it will NOT step down 
in the leader ordering.

Modified:
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/UndefinedClusterViewException.java
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
    
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
    
sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatTest.java

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
 Thu Sep 24 10:09:22 2015
@@ -59,6 +59,7 @@ import org.apache.sling.discovery.Topolo
 import org.apache.sling.discovery.TopologyView;
 import org.apache.sling.discovery.impl.cluster.ClusterViewService;
 import org.apache.sling.discovery.impl.cluster.UndefinedClusterViewException;
+import 
org.apache.sling.discovery.impl.cluster.UndefinedClusterViewException.Reason;
 import org.apache.sling.discovery.impl.common.DefaultClusterViewImpl;
 import org.apache.sling.discovery.impl.common.DefaultInstanceDescriptionImpl;
 import org.apache.sling.discovery.impl.common.heartbeat.HeartbeatHandler;
@@ -686,8 +687,26 @@ public class DiscoveryServiceImpl implem
             // treat it as being cut off from the entire topology, ie we don't
             // update the announcements but just return
             // the previous oldView marked as !current
-            logger.info("getTopology: undefined cluster view: 
"+e.getClass().getSimpleName()+": "+e);
+            logger.info("getTopology: undefined cluster view: 
"+e.getClass().getSimpleName()+": ["+e.getReason()+"] "+e);
             oldView.markOld();
+            if (e.getReason()==Reason.ISOLATED_FROM_TOPOLOGY) {
+                if (heartbeatHandler!=null) {
+                    // SLING-5030 part 2: when we detect being isolated we 
should
+                    // step at the end of the leader-election queue and 
+                    // that can be achieved by resetting the leaderElectionId
+                    // (which will in turn take effect on the next round of
+                    // voting, or also double-checked when the local instance 
votes)
+                    //
+                    //TODO:
+                    // Note that when the local instance doesn't notice
+                    // an 'ISOLATED_FROM_TOPOLOGY' case, then the 
leaderElectionId
+                    // will not be reset. Which means that it then could 
potentially
+                    // regain leadership.
+                    if (heartbeatHandler.resetLeaderElectionId()) {
+                        logger.info("getTopology: reset leaderElectionId to 
force this instance to the end of the instance order (thus incl not to remain 
leader)");
+                    }
+                }
+            }
             return oldView;
         }
 

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
 Thu Sep 24 10:09:22 2015
@@ -31,6 +31,7 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.discovery.ClusterView;
 import org.apache.sling.discovery.InstanceDescription;
 import org.apache.sling.discovery.impl.Config;
+import 
org.apache.sling.discovery.impl.cluster.UndefinedClusterViewException.Reason;
 import org.apache.sling.discovery.impl.common.View;
 import org.apache.sling.discovery.impl.common.ViewHelper;
 import org.apache.sling.discovery.impl.common.resource.EstablishedClusterView;
@@ -94,7 +95,8 @@ public class ClusterViewServiceImpl impl
     public ClusterView getClusterView() throws UndefinedClusterViewException {
        if (resourceResolverFactory==null) {
                logger.warn("getClusterView: no resourceResolverFactory set at 
the moment.");
-               throw new UndefinedClusterViewException("no 
resourceResolverFactory set");
+               throw new 
UndefinedClusterViewException(Reason.REPOSITORY_EXCEPTION,
+                       "no resourceResolverFactory set");
        }
         ResourceResolver resourceResolver = null;
         try {
@@ -104,7 +106,8 @@ public class ClusterViewServiceImpl impl
             View view = ViewHelper.getEstablishedView(resourceResolver, 
config);
             if (view == null) {
                 logger.debug("getClusterView: no view established at the 
moment. isolated mode");
-                throw new UndefinedClusterViewException("no established view 
at the moment");
+                throw new 
UndefinedClusterViewException(Reason.NO_ESTABLISHED_VIEW,
+                        "no established view at the moment");
             }
 
             EstablishedClusterView clusterViewImpl = new 
EstablishedClusterView(
@@ -124,12 +127,14 @@ public class ClusterViewServiceImpl impl
                 logger.info("getClusterView: the existing established view 
does not incude the local instance ("+getSlingId()+") yet! Assuming isolated 
mode. "
                         + "If this occurs at runtime - other than at startup - 
it could cause a pseudo-network-partition, see SLING-3432. "
                         + "Consider increasing heartbeatTimeout then!");
-                throw new UndefinedClusterViewException("established view does 
not include local instance - isolated");
+                throw new 
UndefinedClusterViewException(Reason.ISOLATED_FROM_TOPOLOGY, 
+                        "established view does not include local instance - 
isolated");
             }
         } catch (LoginException e) {
             logger.error(
                     "handleEvent: could not log in administratively: " + e, e);
-            throw new UndefinedClusterViewException("could not log in 
administratively: "+e);
+            throw new 
UndefinedClusterViewException(Reason.REPOSITORY_EXCEPTION,
+                    "could not log in administratively: "+e);
         } finally {
             if (resourceResolver != null) {
                 resourceResolver.close();

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/UndefinedClusterViewException.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/UndefinedClusterViewException.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/UndefinedClusterViewException.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/UndefinedClusterViewException.java
 Thu Sep 24 10:09:22 2015
@@ -29,11 +29,35 @@ package org.apache.sling.discovery.impl.
  */
 public class UndefinedClusterViewException extends Exception {
 
-    public UndefinedClusterViewException() {
+    public static enum Reason {
+        /** used when the local instance is isolated from the topology
+         * (which is noticed by an established view that does not include
+         * the local instance)
+         */
+        ISOLATED_FROM_TOPOLOGY,
+        
+        /** used when there is no established view yet
+         * (happens on a fresh installation)
+         */
+        NO_ESTABLISHED_VIEW,
+        
+        /** used when we couldn't reach the repository **/
+        REPOSITORY_EXCEPTION
+    }
+
+    private final Reason reason;
+    
+    public UndefinedClusterViewException(Reason reason) {
         super();
+        this.reason = reason;
     }
 
-    public UndefinedClusterViewException(String msg) {
+    public UndefinedClusterViewException(Reason reason, String msg) {
         super(msg);
+        this.reason = reason;
+    }
+    
+    public Reason getReason() {
+        return reason;
     }
 }

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
 Thu Sep 24 10:09:22 2015
@@ -80,6 +80,11 @@ public class VotingHandler implements Ev
     /** the sling id of the local instance **/
     private String slingId;
 
+    /** the HeartbeatHandler sets the leaderElectionid - this is subsequently 
used
+     * to ensure the leaderElectionId is correctly set upon voting
+     */
+    private volatile String leaderElectionId;
+
     protected void activate(final ComponentContext context) {
         slingId = slingSettingsService.getSlingId();
         logger = LoggerFactory.getLogger(this.getClass().getCanonicalName()
@@ -171,13 +176,13 @@ public class VotingHandler implements Ev
                 // ongoingVotingRes, and I have not voted on
                 // ongoingVotingRes yet.
                 // so I vote no there now
-                ongoingVotingRes.vote(slingId, false);
+                ongoingVotingRes.vote(slingId, false, null);
                 it.remove();
             } else if (!ongoingVotingRes.matchesLiveView(clusterNodesRes,
                     config)) {
                 logger.warn("analyzeVotings: encountered a voting which does 
not match mine. Voting no: "
                         + ongoingVotingRes);
-                ongoingVotingRes.vote(slingId, false);
+                ongoingVotingRes.vote(slingId, false, null);
                 it.remove();
             } else if (ongoingVotingRes.isInitiatedBy(slingId)
                     && ongoingVotingRes.hasNoVotes()) {
@@ -214,7 +219,7 @@ public class VotingHandler implements Ev
                    logger.debug("analyzeVotings: only one voting found for 
which I did not yet vote - and it is not mine. I'll vote yes then: "
                            + votingResource);
                }
-            votingResource.vote(slingId, true);
+            votingResource.vote(slingId, true, leaderElectionId);
         }
 
         // otherwise there is more than one voting going on, all matching my
@@ -255,15 +260,15 @@ public class VotingHandler implements Ev
                    logger.debug("analyzeVotings: I apparently have not yet 
voted. So I shall vote now for the lowest id which is: "
                            + lowestVoting);
                }
-            lowestVoting.vote(slingId, true);
+            lowestVoting.vote(slingId, true, leaderElectionId);
         } else {
             // otherwise I've already voted, but not for the lowest. which
             // is a shame.
             // I shall change my mind!
             logger.warn("analyzeVotings: I've already voted - but it so 
happened that there was a lower voting created after I voted... so I shall 
change my vote from "
                     + myYesVoteResource + " to " + lowestVoting);
-            myYesVoteResource.vote(slingId, null);
-            lowestVoting.vote(slingId, true);
+            myYesVoteResource.vote(slingId, null, null);
+            lowestVoting.vote(slingId, true, leaderElectionId);
         }
        if (logger.isDebugEnabled()) {
                logger.debug("analyzeVotings: all done now. I've voted yes for "
@@ -430,4 +435,9 @@ public class VotingHandler implements Ev
         logger.debug("promote: done with promotiong. saving.");
         resourceResolver.commit();
     }
+
+    public void setLeaderElectionId(String leaderElectionId) {
+        logger.info("setLeaderElectionId: leaderElectionId="+leaderElectionId);
+        this.leaderElectionId = leaderElectionId;
+    }
 }
\ No newline at end of file

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
 Thu Sep 24 10:09:22 2015
@@ -286,7 +286,8 @@ public class VotingView extends View {
      * @param slingId the slingId which is voting
      * @param vote true for a yes-vote, false for a no-vote
      */
-    public void vote(final String slingId, final Boolean vote) {
+    public void vote(final String slingId, final Boolean vote,
+                     final String leaderElectionId) {
        if (logger.isDebugEnabled()) {
                logger.debug("vote: slingId=" + slingId + ", vote=" + vote);
        }
@@ -314,8 +315,21 @@ public class VotingView extends View {
                 logger.warn("vote: got a RepositoryException: "+e, e);
             }
             if (shouldVote) {
+                logger.info("vote: slingId=" + slingId + " is voting vote=" + 
vote+" on "+getResource());
                 memberMap.put("vote", vote);
                 memberMap.put("votedAt", Calendar.getInstance());
+                String currentLeaderElectionId = 
memberMap.get("leaderElectionId", String.class);
+                if (leaderElectionId!=null &&
+                        (currentLeaderElectionId == null || 
!currentLeaderElectionId.equals(leaderElectionId))) {
+                    // SLING-5030 : to ensure leader-step-down after being
+                    // isolated from the cluster, the leaderElectionId must
+                    // be explicitly set upon voting.
+                    // for 99% of the cases not be necessary,
+                    // for the rejoin-after-isolation case however it is
+                    logger.info("vote: changing leaderElectionId on vote to 
"+leaderElectionId);
+                    memberMap.put("leaderElectionId", leaderElectionId);
+                    memberMap.put("leaderElectionIdCreatedAt", new Date());
+                }
             }
         }
         try {

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
 Thu Sep 24 10:09:22 2015
@@ -126,8 +126,15 @@ public class HeartbeatHandler implements
     private String nextVotingId = UUID.randomUUID().toString();
 
     /** whether or not to reset the leaderElectionId at next heartbeat time **/
-    private boolean resetLeaderElectionId = false;
+    private volatile boolean resetLeaderElectionId = false;
 
+    /** SLING-5030 : upon resetLeaderElectionId() a newLeaderElectionId is 
calculated 
+     * and passed on to the VotingHandler - but the actual storing under 
./clusterInstances
+     * is only done on heartbeats - this field is used to temporarily store 
the new
+     * leaderElectionId that the next heartbeat then stores
+     */
+    private volatile String newLeaderElectionId;
+    
     /** lock object for synchronizing the run method **/
     private final Object lock = new Object();
 
@@ -278,6 +285,44 @@ public class HeartbeatHandler implements
             logger.info("triggerHeartbeat: Could not trigger heartbeat: " + e);
         }
     }
+    
+    /**
+     * Hook that will cause a reset of the leaderElectionId 
+     * on next invocation of issueClusterLocalHeartbeat.
+     * @return true if the leaderElectionId was reset - false if that was not
+     * necessary as that happened earlier already and it has not propagated
+     * yet to the ./clusterInstances in the meantime
+     */
+    public boolean resetLeaderElectionId() {
+        if (resetLeaderElectionId) {
+            // then we already have a reset pending
+            // resetting twice doesn't work
+            return false;
+        }
+        resetLeaderElectionId = true;
+        ResourceResolver resourceResolver = null;
+        try{
+            resourceResolver = getResourceResolver();
+            if (resourceResolver!=null) {
+                newLeaderElectionId = newLeaderElectionId(resourceResolver);
+                if (votingHandler!=null) {
+                    logger.info("resetLeaderElectionId: set new 
leaderElectionId with votingHandler to: "+newLeaderElectionId);
+                    votingHandler.setLeaderElectionId(newLeaderElectionId);
+                } else {
+                    logger.info("resetLeaderElectionId: no votingHandler, new 
leaderElectionId would be: "+newLeaderElectionId);
+                }
+            } else {
+                logger.warn("resetLeaderElectionId: could not login, new 
leaderElectionId will be calculated upon next heartbeat only!");
+            }
+        } catch (LoginException e) {
+            logger.error("resetLeaderElectionid: could not login: "+e, e);
+        } finally {
+            if (resourceResolver!=null) {
+                resourceResolver.close();
+            }
+        }
+        return true;
+    }
 
     /**
      * Issue a heartbeat.
@@ -409,38 +454,16 @@ public class HeartbeatHandler implements
                        new Object[]{runtimeId, endpointsAsString, 
slingHomePath});
             }
             if (resetLeaderElectionId || 
!resourceMap.containsKey("leaderElectionId")) {
-                int maxLongLength = String.valueOf(Long.MAX_VALUE).length();
-                String currentTimeMillisStr = String.format("%0"
-                        + maxLongLength + "d", System.currentTimeMillis());
-
-                final boolean shouldInvertRepositoryDescriptor = 
config.shouldInvertRepositoryDescriptor();
-                String prefix = (shouldInvertRepositoryDescriptor ? "1" : "0");
-
-                String leaderElectionRepositoryDescriptor = 
config.getLeaderElectionRepositoryDescriptor();
-                if (leaderElectionRepositoryDescriptor!=null && 
leaderElectionRepositoryDescriptor.length()!=0) {
-                    // when this property is configured, check the value of 
the repository descriptor
-                    // and if that value is set, include it in the leader 
election id
-
-                    final Session session = 
resourceResolver.adaptTo(Session.class);
-                    if ( session != null ) {
-                        String value = session.getRepository()
-                                
.getDescriptor(leaderElectionRepositoryDescriptor);
-                        if (value != null) {
-                            if (value.equalsIgnoreCase("true")) {
-                                if (!shouldInvertRepositoryDescriptor) {
-                                    prefix = "1";
-                                } else {
-                                    prefix = "0";
-                                }
-                            }
-                        }
-                    }
-                }
-                final String newLeaderElectionId = prefix + "_"
-                        + currentTimeMillisStr + "_" + slingId;
+                // the new leaderElectionId might have been 'pre set' in the 
field 'newLeaderElectionId'
+                // if that's the case, use that one, otherwise calculate a new 
one now
+                final String newLeaderElectionId = 
this.newLeaderElectionId!=null ? this.newLeaderElectionId : 
newLeaderElectionId(resourceResolver);
+                this.newLeaderElectionId = null;
                 resourceMap.put("leaderElectionId", newLeaderElectionId);
                 resourceMap.put("leaderElectionIdCreatedAt", new Date());
-                logger.debug("issueClusterLocalHeartbeat: set leaderElectionId 
to "+newLeaderElectionId);
+                logger.info("issueClusterLocalHeartbeat: set leaderElectionId 
to "+newLeaderElectionId);
+                if (votingHandler!=null) {
+                    votingHandler.setLeaderElectionId(newLeaderElectionId);
+                }
                 resetLeaderElectionId = false;
             }
             resourceResolver.commit();
@@ -465,6 +488,42 @@ public class HeartbeatHandler implements
         }
     }
 
+    /**
+     * Calculate a new leaderElectionId based on the current config and system 
time
+     */
+    private String newLeaderElectionId(ResourceResolver resourceResolver) {
+        int maxLongLength = String.valueOf(Long.MAX_VALUE).length();
+        String currentTimeMillisStr = String.format("%0"
+                + maxLongLength + "d", System.currentTimeMillis());
+
+        final boolean shouldInvertRepositoryDescriptor = 
config.shouldInvertRepositoryDescriptor();
+        String prefix = (shouldInvertRepositoryDescriptor ? "1" : "0");
+
+        String leaderElectionRepositoryDescriptor = 
config.getLeaderElectionRepositoryDescriptor();
+        if (leaderElectionRepositoryDescriptor!=null && 
leaderElectionRepositoryDescriptor.length()!=0) {
+            // when this property is configured, check the value of the 
repository descriptor
+            // and if that value is set, include it in the leader election id
+
+            final Session session = resourceResolver.adaptTo(Session.class);
+            if ( session != null ) {
+                String value = session.getRepository()
+                        .getDescriptor(leaderElectionRepositoryDescriptor);
+                if (value != null) {
+                    if (value.equalsIgnoreCase("true")) {
+                        if (!shouldInvertRepositoryDescriptor) {
+                            prefix = "1";
+                        } else {
+                            prefix = "0";
+                        }
+                    }
+                }
+            }
+        }
+        final String newLeaderElectionId = prefix + "_"
+                + currentTimeMillisStr + "_" + slingId;
+        return newLeaderElectionId;
+    }
+
     /** Check whether the established view matches the reality, ie matches the
      * heartbeats
      */

Modified: 
sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatTest.java?rev=1705024&r1=1705023&r2=1705024&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatTest.java
 (original)
+++ 
sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatTest.java
 Thu Sep 24 10:09:22 2015
@@ -138,12 +138,14 @@ public class HeartbeatTest {
         assertEquals(1, 
slowMachine.getDiscoveryService().getTopology().getInstances().size());
         assertEquals(slowMachine.getSlingId(), 
slowMachine.getDiscoveryService().getTopology().getInstances().iterator().next().getSlingId());
         instances.add(slowMachine);
+        Thread.sleep(10); // wait 10ms to ensure 'slowMachine' has the lowerst 
leaderElectionId (to become leader)
         SimpleTopologyEventListener slowListener = new 
SimpleTopologyEventListener("slow");
         slowMachine.bindTopologyEventListener(slowListener);
         Instance fastMachine1 = 
Instance.newClusterInstance("/var/discovery/impl/", "fast1", slowMachine, 
false, 5, 1, 0);
         assertEquals(1, 
fastMachine1.getDiscoveryService().getTopology().getInstances().size());
         assertEquals(fastMachine1.getSlingId(), 
fastMachine1.getDiscoveryService().getTopology().getInstances().iterator().next().getSlingId());
         instances.add(fastMachine1);
+        Thread.sleep(10); // wait 10ms to ensure 'fastMachine1' has the 2nd 
lowerst leaderElectionId (to become leader during partitioning)
         SimpleTopologyEventListener fastListener1 = new 
SimpleTopologyEventListener("fast1");
         fastMachine1.bindTopologyEventListener(fastListener1);
         Instance fastMachine2 = 
Instance.newClusterInstance("/var/discovery/impl/", "fast2", slowMachine, 
false, 5, 1, 0);
@@ -186,18 +188,23 @@ public class HeartbeatTest {
         assertNotNull(fastListener1.getLastEvent());
         assertEquals(TopologyEvent.Type.TOPOLOGY_INIT, 
fastListener1.getLastEvent().getType());
         assertEquals(5, 
fastListener1.getLastEvent().getNewView().getInstances().size());
+        
assertFalse(fastListener1.getLastEvent().getNewView().getLocalInstance().isLeader());
         assertNotNull(fastListener2.getLastEvent());
         assertEquals(TopologyEvent.Type.TOPOLOGY_INIT, 
fastListener2.getLastEvent().getType());
         assertEquals(5, 
fastListener2.getLastEvent().getNewView().getInstances().size());
+        
assertFalse(fastListener2.getLastEvent().getNewView().getLocalInstance().isLeader());
         assertNotNull(fastListener3.getLastEvent());
         assertEquals(TopologyEvent.Type.TOPOLOGY_INIT, 
fastListener3.getLastEvent().getType());
         assertEquals(5, 
fastListener3.getLastEvent().getNewView().getInstances().size());
+        
assertFalse(fastListener3.getLastEvent().getNewView().getLocalInstance().isLeader());
         assertNotNull(fastListener4.getLastEvent());
         assertEquals(TopologyEvent.Type.TOPOLOGY_INIT, 
fastListener4.getLastEvent().getType());
         assertEquals(5, 
fastListener4.getLastEvent().getNewView().getInstances().size());
+        
assertFalse(fastListener4.getLastEvent().getNewView().getLocalInstance().isLeader());
         assertNotNull(slowListener.getLastEvent());
         assertEquals(TopologyEvent.Type.TOPOLOGY_INIT, 
slowListener.getLastEvent().getType());
         assertEquals(5, 
slowListener.getLastEvent().getNewView().getInstances().size());
+        
assertTrue(slowListener.getLastEvent().getNewView().getLocalInstance().isLeader());
         
         // after 7sec the slow instance' heartbeat should have timed out
         for(int i=0; i<7; i++) {
@@ -225,6 +232,11 @@ public class HeartbeatTest {
             assertEquals(4, 
fastListener3.getLastEvent().getNewView().getInstances().size());
             assertEquals(TopologyEvent.Type.TOPOLOGY_CHANGED, 
fastListener4.getLastEvent().getType());
             assertEquals(4, 
fastListener4.getLastEvent().getNewView().getInstances().size());
+            
+            
assertTrue(fastListener1.getLastEvent().getNewView().getLocalInstance().isLeader());
+            
assertFalse(fastListener2.getLastEvent().getNewView().getLocalInstance().isLeader());
+            
assertFalse(fastListener3.getLastEvent().getNewView().getLocalInstance().isLeader());
+            
assertFalse(fastListener4.getLastEvent().getNewView().getLocalInstance().isLeader());
 
             // and the slow instance should be isolated
             
assertFalse(slowMachine.getDiscoveryService().getTopology().isCurrent());
@@ -276,6 +288,14 @@ public class HeartbeatTest {
         assertEquals(5, 
fastListener4.getLastEvent().getNewView().getInstances().size());
         assertEquals(TopologyEvent.Type.TOPOLOGY_CHANGED, 
slowListener.getLastEvent().getType());
         assertEquals(5, 
slowListener.getLastEvent().getNewView().getInstances().size());
+        
+        // SLING-5030 part 2 : after rejoin-after-partitioning the 
slowMachine1 should again be leader
+        slowMachine.dumpRepo();
+        
assertFalse(slowListener.getLastEvent().getNewView().getLocalInstance().isLeader());
+        
assertTrue(fastListener1.getLastEvent().getNewView().getLocalInstance().isLeader());
+        
assertFalse(fastListener2.getLastEvent().getNewView().getLocalInstance().isLeader());
+        
assertFalse(fastListener3.getLastEvent().getNewView().getLocalInstance().isLeader());
+        
assertFalse(fastListener4.getLastEvent().getNewView().getLocalInstance().isLeader());
     }
     
     /**


Reply via email to