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
}
+
}