You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by st...@apache.org on 2013/06/06 16:37:32 UTC

svn commit: r1490301 - in /sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl: cluster/ClusterLoadTest.java setup/Instance.java setup/MockFactory.java setup/MockedResource.java setup/MockedResourceResolver.java

Author: stefanegli
Date: Thu Jun  6 14:37:32 2013
New Revision: 1490301

URL: http://svn.apache.org/r1490301
Log:
SLING-2914 : adding a load test to discovery.impl - takes 140sec to run, so including in normal tests so far

Added:
    sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterLoadTest.java
Modified:
    sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java
    sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java
    sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java
    sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java

Added: sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterLoadTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterLoadTest.java?rev=1490301&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterLoadTest.java (added)
+++ sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterLoadTest.java Thu Jun  6 14:37:32 2013
@@ -0,0 +1,164 @@
+package org.apache.sling.discovery.impl.cluster;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.sling.discovery.impl.common.resource.EstablishedInstanceDescription;
+import org.apache.sling.discovery.impl.common.resource.IsolatedInstanceDescription;
+import org.apache.sling.discovery.impl.setup.Instance;
+import org.junit.After;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClusterLoadTest {
+	
+	private final Random random = new Random();
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    List<Instance> instances = new LinkedList<Instance>();
+    
+    @After
+    public void tearDown() throws Exception {
+    	if (instances==null || instances.size()==0) {
+    		return;
+    	}
+    	for (Iterator<Instance> it = instances.iterator(); it.hasNext();) {
+			Instance i = it.next();
+			i.stop();
+			it.remove();
+		}
+    }
+    
+    @Test
+    public void testFramework() throws Exception {
+		Instance firstInstance = Instance.newStandaloneInstance("firstInstance", true, 2, 0);
+		instances.add(firstInstance);
+    	Thread.sleep(2000);
+    	// without any heartbeat action, the discovery service reports its local instance
+    	// in so called 'isolated' mode - lets test for that
+        assertEquals(IsolatedInstanceDescription.class, firstInstance
+                .getClusterViewService().getClusterView().getInstances().get(0)
+                .getClass());
+        firstInstance.startHeartbeats(1);
+        Thread.sleep(2000);
+        // after a heartbeat and letting it settle, the discovery service must have
+        // established a view - test for that
+        assertEquals(EstablishedInstanceDescription.class, firstInstance
+                .getClusterViewService().getClusterView().getInstances().get(0)
+                .getClass());
+        
+        Instance secondInstance = Instance.newClusterInstance("secondInstance", firstInstance, false, 2, 0);
+        instances.add(secondInstance);
+        secondInstance.startHeartbeats(1);
+        Thread.sleep(2000);
+        assertEquals(firstInstance.getClusterViewService().getClusterView().getInstances().size(), 2);
+        assertEquals(secondInstance.getClusterViewService().getClusterView().getInstances().size(), 2);
+    }
+
+    @Test
+    public void testTwoInstances() throws Throwable {
+    	doTest(2, 5);
+    }
+
+    @Test
+    public void testThreeInstances() throws Throwable {
+    	doTest(3, 6);
+    }
+
+    @Test
+    public void testFourInstances() throws Throwable {
+    	doTest(4, 7);
+    }
+    
+    @Test
+    public void testFiveInstances() throws Throwable {
+    	doTest(5, 8);
+    }
+
+    @Test
+    public void testSixInstances() throws Throwable {
+    	doTest(6, 9);
+    }
+
+    @Test
+    public void testSevenInstances() throws Throwable {
+    	doTest(7, 10);
+    }
+
+	private void doTest(final int size, final int loopCnt) throws Throwable {
+		if (size<2) {
+			fail("can only test 2 or more instances");
+		}
+		Instance firstInstance = Instance.newStandaloneInstance("firstInstance", true, 2, 0);
+		firstInstance.startHeartbeats(1);
+		instances.add(firstInstance);
+		for(int i=1; i<size; i++) {
+			Instance subsequentInstance = Instance.newClusterInstance("subsequentInstance-"+i, firstInstance, false, 2, 0);
+			instances.add(subsequentInstance);
+			subsequentInstance.startHeartbeats(1);
+		}
+		
+		for(int i=0; i<loopCnt; i++) {
+			logger.info("=====================");
+			logger.info(" START of LOOP "+i);
+			logger.info("=====================");
+			// wait 2 heartbeat intervals to let things settle
+			Thread.sleep(3000);
+			
+			// count how many instances had heartbeats running in the first place
+			int aliveCnt = 0;
+			for (Iterator<Instance> it = instances.iterator(); it.hasNext();) {
+				Instance instance = it.next();
+				if (instance.isHeartbeatRunning()) {
+					aliveCnt++;
+				}
+			}
+			logger.info("=====================");
+			logger.info(" original aliveCnt "+aliveCnt);
+			logger.info("=====================");
+			if (aliveCnt==0) {
+				// if no one is sending heartbeats, all instances go back to isolated mode
+				aliveCnt=1;
+			}
+			
+			for (Iterator<Instance> it = instances.iterator(); it.hasNext();) {
+				Instance instance = it.next();
+				instance.dumpRepo();
+			}
+
+			// then verify that each instance sees that many instances
+			for (Iterator<Instance> it = instances.iterator(); it.hasNext();) {
+				Instance instance = it.next();
+				int actualCount = instance.getClusterViewService().getClusterView().getInstances().size();
+				if (!instance.isHeartbeatRunning()) {
+					// if the heartbeat is not running, this instance is considered dead
+					// hence we're not doing any assert here (as the count is only
+					// valid if heartbeat/checkView is running and that would void the test)
+				} else {
+					assertEquals(actualCount, aliveCnt);
+				}
+			}
+			
+			// start/stop heartbeats accordingly
+			for (Iterator<Instance> it = instances.iterator(); it.hasNext();) {
+				Instance instance = it.next();
+				if (random.nextBoolean()) {
+					logger.info("Starting heartbeats with "+instance.slingId);
+					instance.startHeartbeats(1);
+				} else {
+					logger.info("Stopping heartbeats with "+instance.slingId);
+					instance.stopHeartbeats();
+				}
+			}
+			
+		}
+	}
+
+}

Modified: sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java?rev=1490301&r1=1490300&r2=1490301&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java (original)
+++ sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java Thu Jun  6 14:37:32 2013
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.discovery.impl.setup;
 
+import java.lang.reflect.InvocationTargetException;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
@@ -87,9 +89,9 @@ public class Instance {
     private ResourceResolver resourceResolver;
 
     private int serviceId = 999;
-
+    
     private static Scheduler singletonScheduler = null;
-
+    
     private static Scheduler getSingletonScheduler() throws Exception {
     	if (singletonScheduler!=null) {
     		return singletonScheduler;
@@ -107,10 +109,54 @@ public class Instance {
         singletonScheduler = newscheduler;
         return singletonScheduler;
     }
+    
+    private HeartbeatRunner heartbeatRunner = null;
+    
+    private class HeartbeatRunner implements Runnable {
+    	
+    	private final int intervalInSeconds;
+
+    	private boolean stopped_ = false;
+    	
+		public HeartbeatRunner(int intervalInSeconds) {
+    		this.intervalInSeconds = intervalInSeconds;
+    	}
+		
+		public synchronized void stop() {
+			System.err.println("Stopping Instance ["+slingId+"]");
+			stopped_ = true;
+		}
+
+		public void run() {
+			while(true) {
+				synchronized(this) {
+					if (stopped_) {
+						System.err.println("Instance ["+slingId+"] stopps.");
+						return;
+					}
+				}
+				runHeartbeatOnce();
+				try {
+					Thread.sleep(intervalInSeconds*1000);
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+					return;
+				}
+			}
+		}
+    	
+    }
 
     private Instance(String debugName,
             ResourceResolverFactory resourceResolverFactory, boolean resetRepo)
             throws Exception {
+    	this(debugName, resourceResolverFactory, resetRepo, 20, 1);
+    }
+    
+    private Instance(String debugName,
+            ResourceResolverFactory resourceResolverFactory, boolean resetRepo,
+            final int heartbeatTimeout, final int minEventDelay)
+            throws Exception {
         this.debugName = debugName;
 
         osgiMock = new OSGiMock();
@@ -120,15 +166,15 @@ public class Instance {
         Config config = new Config() {
             @Override
             public long getHeartbeatTimeout() {
-                return 20;
+                return heartbeatTimeout;
             }
 
             @Override
             public int getMinEventDelay() {
-            	return 1;
+            	return minEventDelay;
             }
         };
-
+        
         clusterViewService = OSGiFactory.createClusterViewServiceImpl(slingId,
                 resourceResolverFactory, config);
         announcementRegistry = OSGiFactory.createITopologyAnnouncementRegistry(
@@ -140,7 +186,7 @@ public class Instance {
                 connectorRegistry, config,
                 resourceResolverFactory.getAdministrativeResourceResolver(null)
                         .adaptTo(Repository.class), getSingletonScheduler());
-
+        
 		discoveryService = OSGiFactory.createDiscoverService(slingId,
                 heartbeatHandler, clusterViewService, announcementRegistry,
                 resourceResolverFactory, config, connectorRegistry, getSingletonScheduler());
@@ -209,6 +255,20 @@ public class Instance {
     }
 
     public static Instance newStandaloneInstance(String debugName,
+            Repository repository) throws Exception {
+        ResourceResolverFactory resourceResolverFactory = MockFactory
+                .mockResourceResolverFactory(repository);
+        return new Instance(debugName, resourceResolverFactory, false);
+    }
+
+    public static Instance newStandaloneInstance(String debugName,
+            boolean resetRepo, int heartbeatTimeout, int minEventDelay) throws Exception {
+        ResourceResolverFactory resourceResolverFactory = MockFactory
+                .mockResourceResolverFactory();
+        return new Instance(debugName, resourceResolverFactory, resetRepo, heartbeatTimeout, minEventDelay);
+    }
+    
+    public static Instance newStandaloneInstance(String debugName,
             boolean resetRepo) throws Exception {
         ResourceResolverFactory resourceResolverFactory = MockFactory
                 .mockResourceResolverFactory();
@@ -216,6 +276,11 @@ public class Instance {
     }
 
     public static Instance newClusterInstance(String debugName, Instance other,
+            boolean resetRepo, int heartbeatTimeout, int minEventDelay) throws Exception {
+        return new Instance(debugName, other.resourceResolverFactory, resetRepo, heartbeatTimeout, minEventDelay);
+    }
+
+    public static Instance newClusterInstance(String debugName, Instance other,
             boolean resetRepo) throws Exception {
         return new Instance(debugName, other.resourceResolverFactory, resetRepo);
     }
@@ -240,8 +305,32 @@ public class Instance {
     }
 
     public void runHeartbeatOnce() {
+    	System.err.println("Instance ["+slingId+"] issues a heartbeat now "+new Date());
         heartbeatHandler.run();
     }
+    
+    public void startHeartbeats(int intervalInSeconds) throws IllegalAccessException, InvocationTargetException {
+    	if (heartbeatRunner!=null) {
+    		heartbeatRunner.stop();
+    	}
+    	OSGiMock.activate(heartbeatHandler);
+    	heartbeatRunner = new HeartbeatRunner(intervalInSeconds);
+    	Thread th = new Thread(heartbeatRunner, "Test-Heartbeat-Runner");
+    	th.setDaemon(true);
+    	th.start();
+    }
+    
+	public boolean isHeartbeatRunning() {
+		return (heartbeatRunner!=null);
+	}
+
+    public void stopHeartbeats() throws Throwable {
+    	if (heartbeatRunner!=null) {
+    		heartbeatRunner.stop();
+    		heartbeatRunner = null;
+    	}
+    	PrivateAccessor.invoke(heartbeatHandler, "deactivate", null, null);
+    }
 
     public void dumpRepo() throws Exception {
         Session session = resourceResolverFactory
@@ -301,6 +390,10 @@ public class Instance {
     }
 
     public void stop() throws Exception {
+    	if (heartbeatRunner!=null) {
+    		heartbeatRunner.stop();
+    		heartbeatRunner = null;
+    	}
         if (resourceResolver != null) {
             resourceResolver.close();
         }

Modified: sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java?rev=1490301&r1=1490300&r2=1490301&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java (original)
+++ sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java Thu Jun  6 14:37:32 2013
@@ -21,6 +21,8 @@ package org.apache.sling.discovery.impl.
 import java.util.Dictionary;
 import java.util.Properties;
 
+import javax.jcr.Repository;
+
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.commons.scheduler.Scheduler;
 import org.apache.sling.settings.SlingSettingsService;
@@ -40,6 +42,11 @@ public class MockFactory {
 
     public static ResourceResolverFactory mockResourceResolverFactory()
             throws Exception {
+    	return mockResourceResolverFactory(null);
+    }
+    
+    public static ResourceResolverFactory mockResourceResolverFactory(final Repository repositoryOrNull)
+            throws Exception {
         Mockery context = new JUnit4Mockery();
 
         final ResourceResolverFactory resourceResolverFactory = context
@@ -56,7 +63,7 @@ public class MockFactory {
 
                     public Object invoke(Invocation invocation)
                             throws Throwable {
-                        return new MockedResourceResolver();
+                    	return new MockedResourceResolver(repositoryOrNull);
                     }
 
                     public void describeTo(Description arg0) {

Modified: sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java?rev=1490301&r1=1490300&r2=1490301&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java (original)
+++ sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java Thu Jun  6 14:37:32 2013
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
@@ -54,7 +55,7 @@ public class MockedResource extends Synt
         synchronized (this) {
             if (session == null) {
                 try {
-                    session = mockedResourceResolver.createSession();
+                    session = mockedResourceResolver.getSession();
                 } catch (RepositoryException e) {
                     throw new RuntimeException("RepositoryException: " + e, e);
                 }
@@ -129,7 +130,22 @@ public class MockedResource extends Synt
                 }
                 
                 public Object remove(Object arg0) {
-                    throw new UnsupportedOperationException();
+                    Session session = getSession();
+                    try{
+                        final Node node = session.getNode(getPath());
+                        final Property p = node.getProperty(String.valueOf(arg0));
+                        if (p!=null) {
+                        	p.remove();
+                        }
+                        // this is not according to the spec - but OK for tests since
+                        // the return value is never used
+                        return null;
+                    } catch(PathNotFoundException pnfe) {
+                    	// perfectly fine
+                    	return null;
+                    } catch(RepositoryException e) {
+                        throw new RuntimeException(e);
+                    }
                 }
                 
                 public void putAll(Map<? extends String, ? extends Object> arg0) {

Modified: sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java?rev=1490301&r1=1490300&r2=1490301&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java (original)
+++ sling/trunk/bundles/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java Thu Jun  6 14:37:32 2013
@@ -24,12 +24,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import javax.jcr.Credentials;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.sling.api.resource.LoginException;
@@ -40,39 +42,55 @@ import org.apache.sling.commons.testing.
 
 public class MockedResourceResolver implements ResourceResolver {
 
-    public final RepositoryProvider repoProvider;
+	private final Repository repository;
+	
+	private Session session;
+	
     private List<MockedResource> resources = new LinkedList<MockedResource>();
 
-    private Session session;
-
-    public MockedResourceResolver() throws Exception {
-        this.repoProvider = RepositoryProvider.instance();
+    public MockedResourceResolver() throws RepositoryException {
+    	this(null);
     }
 
-    public Session createSession() throws RepositoryException {
+    public MockedResourceResolver(Repository repositoryOrNull) throws RepositoryException {
+    	if (repositoryOrNull==null) {
+    		this.repository = RepositoryProvider.instance().getRepository();
+    	} else {
+    		this.repository = repositoryOrNull;
+    	}
+    }
+    
+    public Session getSession() throws RepositoryException {
         synchronized (this) {
             if (session != null) {
                 return session;
             }
-            session = repoProvider.getRepository().loginAdministrative(null);
+            session = createSession();
             return session;
         }
     }
 
+    private Repository getRepository() {
+    	return repository;
+    }
+    
+    private Session createSession() throws RepositoryException {
+        final Credentials credentials = new SimpleCredentials("admin",
+                "admin".toCharArray());
+        return repository.login(credentials, "default");
+    }
+	
+	
     @SuppressWarnings("unchecked")
     public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
         if (type.equals(Session.class)) {
             try {
-                return (AdapterType) createSession();
+                return (AdapterType) getSession();
             } catch (RepositoryException e) {
                 throw new RuntimeException("RepositoryException: " + e, e);
             }
         } else if (type.equals(Repository.class)) {
-            try {
-                return (AdapterType) repoProvider.getRepository();
-            } catch (RepositoryException e) {
-                throw new RuntimeException("RepositoryException: " + e, e);
-            }
+        	return (AdapterType) getRepository();
         }
         throw new UnsupportedOperationException("Not implemented");
     }
@@ -101,7 +119,7 @@ public class MockedResourceResolver impl
     public Resource getResource(String path) {
         Session session;
         try {
-            session = createSession();
+            session = getSession();
             session.getNode(path);
         } catch (PathNotFoundException e) {
             return null;
@@ -290,4 +308,5 @@ public class MockedResourceResolver impl
         // TODO Auto-generated method stub
 
     }
+
 }