You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/04/26 18:01:10 UTC
[04/34] ambari git commit: AMBARI-20819. LogSearch Integration should
limit requests to portal for missing components. (rnettleton)
AMBARI-20819. LogSearch Integration should limit requests to portal for missing components. (rnettleton)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/8c039bbc
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/8c039bbc
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/8c039bbc
Branch: refs/heads/branch-feature-AMBARI-12556
Commit: 8c039bbc20d7a66fc46f62e5c1d2d690171b667e
Parents: e8fd10c
Author: Bob Nettleton <rn...@hortonworks.com>
Authored: Mon Apr 24 10:55:15 2017 -0400
Committer: Bob Nettleton <rn...@hortonworks.com>
Committed: Mon Apr 24 10:55:15 2017 -0400
----------------------------------------------------------------------
.../logging/LogSearchDataRetrievalService.java | 75 ++++--
.../LogSearchDataRetrievalServiceTest.java | 249 ++++++++++++++++++-
2 files changed, 304 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/8c039bbc/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java
index 6b484a4..487182e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java
@@ -17,10 +17,12 @@
*/
package org.apache.ambari.server.controller.logging;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.ambari.server.AmbariService;
import org.apache.ambari.server.configuration.Configuration;
@@ -30,9 +32,9 @@ import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.AbstractService;
import com.google.inject.Inject;
@@ -69,6 +71,13 @@ public class LogSearchDataRetrievalService extends AbstractService {
private static Logger LOG = LoggerFactory.getLogger(LogSearchDataRetrievalService.class);
/**
+ * Maximum number of failed attempts that the LogSearch integration code will attempt for
+ * a given component before treating the component as failed and skipping the request.
+ *
+ */
+ private static int MAX_RETRIES_FOR_FAILED_METADATA_REQUEST = 10;
+
+ /**
* Factory instance used to handle URL string generation requests on the
* main request thread.
*/
@@ -109,6 +118,19 @@ public class LogSearchDataRetrievalService extends AbstractService {
*/
private final Set<String> currentRequests = Sets.newConcurrentHashSet();
+ /**
+ * A map that maintains the set of failure counts for logging
+ * metadata requests on a per-component basis. This map should
+ * be consulted prior to making a metadata request to the LogSearch
+ * service. If LogSearch has already returned an empty list for the given
+ * component, or any other error has occurred for a certain number of attempts,
+ * the request should not be attempted further.
+ *
+ */
+ private final Map<String, AtomicInteger> componentRequestFailureCounts =
+ Maps.newConcurrentMap();
+
+
/**
* Executor instance to be used to run REST queries against
@@ -172,18 +194,20 @@ public class LogSearchDataRetrievalService extends AbstractService {
LOG.debug("LogFileNames result for key = {} found in cache", key);
return cacheResult;
} else {
- // queue up a thread to create the LogSearch REST request to obtain this information
- if (currentRequests.contains(key)) {
- LOG.debug("LogFileNames request has been made for key = {}, but not completed yet", key);
+ if (!componentRequestFailureCounts.containsKey(component) || componentRequestFailureCounts.get(component).get() < MAX_RETRIES_FOR_FAILED_METADATA_REQUEST) {
+ // queue up a thread to create the LogSearch REST request to obtain this information
+ if (currentRequests.contains(key)) {
+ LOG.debug("LogFileNames request has been made for key = {}, but not completed yet", key);
+ } else {
+ LOG.debug("LogFileNames result for key = {} not in cache, queueing up remote request", key);
+ // add request key to queue, to keep multiple copies of the same request from
+ // being submitted
+ currentRequests.add(key);
+ startLogSearchFileNameRequest(host, component, cluster);
+ }
} else {
- LOG.debug("LogFileNames result for key = {} not in cache, queueing up remote request", key);
- // add request key to queue, to keep multiple copies of the same request from
- // being submitted
- currentRequests.add(key);
- startLogSearchFileNameRequest(host, component, cluster);
+ LOG.debug("Too many failures occurred while attempting to obtain log file metadata for component = {}, Ambari will ignore this component for LogSearch Integration", component);
}
-
-
}
return null;
@@ -260,6 +284,15 @@ public class LogSearchDataRetrievalService extends AbstractService {
return currentRequests;
}
+ /**
+ * This protected method allows for simpler unit tests.
+ *
+ * @return the Map of failure counts on a per-component basis
+ */
+ protected Map<String, AtomicInteger> getComponentRequestFailureCounts() {
+ return componentRequestFailureCounts;
+ }
+
private void startLogSearchFileNameRequest(String host, String component, String cluster) {
// Create a separate instance of LoggingRequestHelperFactory for
// each task launched, since these tasks will occur on a separate thread
@@ -268,7 +301,7 @@ public class LogSearchDataRetrievalService extends AbstractService {
// TODO: the LoggingRequestHelperFactory implementation thread-safe, so that
// TODO: a single factory instance can be shared across multiple threads safely
executor.execute(new LogSearchFileNameRequestRunnable(host, component, cluster, logFileNameCache, currentRequests,
- injector.getInstance(LoggingRequestHelperFactory.class)));
+ injector.getInstance(LoggingRequestHelperFactory.class), componentRequestFailureCounts));
}
private AmbariManagementController getController() {
@@ -304,20 +337,24 @@ public class LogSearchDataRetrievalService extends AbstractService {
private LoggingRequestHelperFactory loggingRequestHelperFactory;
+ private final Map<String, AtomicInteger> componentRequestFailureCounts;
+
private AmbariManagementController controller;
- LogSearchFileNameRequestRunnable(String host, String component, String cluster, Cache<String, Set<String>> logFileNameCache, Set<String> currentRequests, LoggingRequestHelperFactory loggingRequestHelperFactory) {
- this(host, component, cluster, logFileNameCache, currentRequests, loggingRequestHelperFactory, AmbariServer.getController());
+ LogSearchFileNameRequestRunnable(String host, String component, String cluster, Cache<String, Set<String>> logFileNameCache, Set<String> currentRequests, LoggingRequestHelperFactory loggingRequestHelperFactory,
+ Map<String, AtomicInteger> componentRequestFailureCounts) {
+ this(host, component, cluster, logFileNameCache, currentRequests, loggingRequestHelperFactory, componentRequestFailureCounts, AmbariServer.getController());
}
LogSearchFileNameRequestRunnable(String host, String component, String cluster, Cache<String, Set<String>> logFileNameCache, Set<String> currentRequests,
- LoggingRequestHelperFactory loggingRequestHelperFactory, AmbariManagementController controller) {
+ LoggingRequestHelperFactory loggingRequestHelperFactory, Map<String, AtomicInteger> componentRequestFailureCounts, AmbariManagementController controller) {
this.host = host;
this.component = component;
this.cluster = cluster;
this.logFileNameCache = logFileNameCache;
this.currentRequests = currentRequests;
this.loggingRequestHelperFactory = loggingRequestHelperFactory;
+ this.componentRequestFailureCounts = componentRequestFailureCounts;
this.controller = controller;
}
@@ -340,7 +377,13 @@ public class LogSearchDataRetrievalService extends AbstractService {
// update cache with returned result
logFileNameCache.put(key, logFileNamesResult);
} else {
- LOG.debug("LogSearchFileNameRequestRunnable: remote request was not successful");
+ LOG.debug("LogSearchFileNameRequestRunnable: remote request was not successful for component = {} on host ={}", component, host);
+ if (!componentRequestFailureCounts.containsKey(component)) {
+ componentRequestFailureCounts.put(component, new AtomicInteger());
+ }
+
+ // increment the failure count for this component
+ componentRequestFailureCounts.get(component).incrementAndGet();
}
} else {
LOG.debug("LogSearchFileNameRequestRunnable: request helper was null. This may mean that LogSearch is not available, or could be a potential connection problem.");
http://git-wip-us.apache.org/repos/asf/ambari/blob/8c039bbc/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java
index d60596b..1bf0204 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java
@@ -17,19 +17,26 @@
*/
package org.apache.ambari.server.controller.logging;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isA;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.AmbariManagementController;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
import org.easymock.EasyMockSupport;
import org.junit.Test;
@@ -166,6 +173,161 @@ public class LogSearchDataRetrievalServiceTest {
assertTrue("Incorrect HostComponent set on request set",
retrievalService.getCurrentRequests().contains(expectedComponentName + "+" + expectedHostName));
+ assertEquals("Incorrect size for failure counts for components, should be 0",
+ 0, retrievalService.getComponentRequestFailureCounts().size());
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ public void testGetLogFileNamesExistingFailuresLessThanThreshold() throws Exception {
+ final String expectedHostName = "c6401.ambari.apache.org";
+ final String expectedComponentName = "DATANODE";
+ final String expectedClusterName = "clusterone";
+
+ EasyMockSupport mockSupport = new EasyMockSupport();
+
+ LoggingRequestHelperFactory helperFactoryMock = mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+ Executor executorMock = mockSupport.createMock(Executor.class);
+
+ Injector injectorMock =
+ mockSupport.createMock(Injector.class);
+
+ Configuration configurationMock =
+ mockSupport.createMock(Configuration.class);
+
+ // expect the executor to be called to execute the LogSearch request
+ executorMock.execute(isA(LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable.class));
+ // executor should only be called once
+ expectLastCall().once();
+
+ expect(injectorMock.getInstance(LoggingRequestHelperFactory.class)).andReturn(helperFactoryMock);
+
+ expect(configurationMock.getLogSearchMetadataCacheExpireTimeout()).andReturn(1).atLeastOnce();
+
+ mockSupport.replayAll();
+
+ LogSearchDataRetrievalService retrievalService = new LogSearchDataRetrievalService();
+ retrievalService.setLoggingRequestHelperFactory(helperFactoryMock);
+ retrievalService.setInjector(injectorMock);
+ retrievalService.setConfiguration(configurationMock);
+ // call the initialization routine called by the Google framework
+ retrievalService.doStart();
+ retrievalService.setExecutor(executorMock);
+ // initialize the comopnent-based failure count to have a count > 0, but less than threshold (10)
+ retrievalService.getComponentRequestFailureCounts().put(expectedComponentName, new AtomicInteger(5));
+
+
+ assertEquals("Default request set should be empty", 0, retrievalService.getCurrentRequests().size());
+
+ Set<String> resultSet = retrievalService.getLogFileNames(expectedComponentName, expectedHostName, expectedClusterName);
+
+ assertNull("Inital query on the retrieval service should be null, since cache is empty by default", resultSet);
+ assertEquals("Incorrect number of entries in the current request set", 1, retrievalService.getCurrentRequests().size());
+
+ assertTrue("Incorrect HostComponent set on request set",
+ retrievalService.getCurrentRequests().contains(expectedComponentName + "+" + expectedHostName));
+ assertEquals("Incorrect size for failure counts for components, should be 0",
+ 1, retrievalService.getComponentRequestFailureCounts().size());
+ assertEquals("Incorrect failure count for component",
+ 5, retrievalService.getComponentRequestFailureCounts().get(expectedComponentName).get());
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ public void testGetLogFileNamesExistingFailuresAtThreshold() throws Exception {
+ final String expectedHostName = "c6401.ambari.apache.org";
+ final String expectedComponentName = "DATANODE";
+ final String expectedClusterName = "clusterone";
+
+ EasyMockSupport mockSupport = new EasyMockSupport();
+
+ LoggingRequestHelperFactory helperFactoryMock = mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+ Executor executorMock = mockSupport.createMock(Executor.class);
+
+ Injector injectorMock =
+ mockSupport.createMock(Injector.class);
+
+ Configuration configurationMock =
+ mockSupport.createMock(Configuration.class);
+
+ expect(configurationMock.getLogSearchMetadataCacheExpireTimeout()).andReturn(1).atLeastOnce();
+
+ mockSupport.replayAll();
+
+ LogSearchDataRetrievalService retrievalService = new LogSearchDataRetrievalService();
+ retrievalService.setLoggingRequestHelperFactory(helperFactoryMock);
+ retrievalService.setInjector(injectorMock);
+ retrievalService.setConfiguration(configurationMock);
+ // call the initialization routine called by the Google framework
+ retrievalService.doStart();
+ retrievalService.setExecutor(executorMock);
+ // initialize the comopnent-based failure count to have a count at the threshold
+ retrievalService.getComponentRequestFailureCounts().put(expectedComponentName, new AtomicInteger(10));
+
+ assertEquals("Default request set should be empty", 0, retrievalService.getCurrentRequests().size());
+
+ Set<String> resultSet =
+ retrievalService.getLogFileNames(expectedComponentName, expectedHostName, expectedClusterName);
+
+ assertNull("Inital query on the retrieval service should be null, since cache is empty by default", resultSet);
+ assertEquals("Incorrect number of entries in the current request set", 0, retrievalService.getCurrentRequests().size());
+
+ assertEquals("Incorrect size for failure counts for components, should be 0",
+ 1, retrievalService.getComponentRequestFailureCounts().size());
+ assertEquals("Incorrect failure count for component",
+ 10, retrievalService.getComponentRequestFailureCounts().get(expectedComponentName).get());
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ public void testGetLogFileNamesExistingFailuresOverThreshold() throws Exception {
+ final String expectedHostName = "c6401.ambari.apache.org";
+ final String expectedComponentName = "DATANODE";
+ final String expectedClusterName = "clusterone";
+
+ EasyMockSupport mockSupport = new EasyMockSupport();
+
+ LoggingRequestHelperFactory helperFactoryMock = mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+ Executor executorMock = mockSupport.createMock(Executor.class);
+
+ Injector injectorMock =
+ mockSupport.createMock(Injector.class);
+
+ Configuration configurationMock =
+ mockSupport.createMock(Configuration.class);
+
+ expect(configurationMock.getLogSearchMetadataCacheExpireTimeout()).andReturn(1).atLeastOnce();
+
+ mockSupport.replayAll();
+
+ LogSearchDataRetrievalService retrievalService = new LogSearchDataRetrievalService();
+ retrievalService.setLoggingRequestHelperFactory(helperFactoryMock);
+ retrievalService.setInjector(injectorMock);
+ retrievalService.setConfiguration(configurationMock);
+ // call the initialization routine called by the Google framework
+ retrievalService.doStart();
+ retrievalService.setExecutor(executorMock);
+ // initialize the comopnent-based failure count to have a count over the threshold
+ retrievalService.getComponentRequestFailureCounts().put(expectedComponentName, new AtomicInteger(20));
+
+ assertEquals("Default request set should be empty", 0, retrievalService.getCurrentRequests().size());
+
+ Set<String> resultSet =
+ retrievalService.getLogFileNames(expectedComponentName, expectedHostName, expectedClusterName);
+
+ assertNull("Inital query on the retrieval service should be null, since cache is empty by default", resultSet);
+ assertEquals("Incorrect number of entries in the current request set", 0, retrievalService.getCurrentRequests().size());
+
+ assertEquals("Incorrect size for failure counts for components, should be 0",
+ 1, retrievalService.getComponentRequestFailureCounts().size());
+ assertEquals("Incorrect failure count for component",
+ 20, retrievalService.getComponentRequestFailureCounts().get(expectedComponentName).get());
mockSupport.verifyAll();
}
@@ -225,6 +387,7 @@ public class LogSearchDataRetrievalServiceTest {
Cache<String, Set<String>> cacheMock = mockSupport.createMock(Cache.class);
Set<String> currentRequestsMock = mockSupport.createMock(Set.class);
+ Map<String, AtomicInteger> componentFailureCounts = mockSupport.createMock(Map.class);
expect(helperFactoryMock.getHelper(controllerMock, expectedClusterName)).andReturn(helperMock);
expect(helperMock.sendGetLogFileNamesRequest(expectedComponentName, expectedHostName)).andReturn(Collections.singleton("/this/is/just/a/test/directory"));
@@ -237,7 +400,7 @@ public class LogSearchDataRetrievalServiceTest {
LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable loggingRunnable =
new LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable(expectedHostName, expectedComponentName, expectedClusterName,
- cacheMock, currentRequestsMock, helperFactoryMock, controllerMock);
+ cacheMock, currentRequestsMock, helperFactoryMock, componentFailureCounts, controllerMock);
loggingRunnable.run();
mockSupport.verifyAll();
@@ -258,6 +421,7 @@ public class LogSearchDataRetrievalServiceTest {
Cache<String, Set<String>> cacheMock = mockSupport.createMock(Cache.class);
Set<String> currentRequestsMock = mockSupport.createMock(Set.class);
+ Map<String, AtomicInteger> componentFailureCounts = mockSupport.createMock(Map.class);
// return null to simulate an error during helper instance creation
expect(helperFactoryMock.getHelper(controllerMock, expectedClusterName)).andReturn(null);
@@ -269,7 +433,7 @@ public class LogSearchDataRetrievalServiceTest {
LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable loggingRunnable =
new LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable(expectedHostName, expectedComponentName, expectedClusterName,
- cacheMock, currentRequestsMock, helperFactoryMock, controllerMock);
+ cacheMock, currentRequestsMock, helperFactoryMock, componentFailureCounts, controllerMock);
loggingRunnable.run();
mockSupport.verifyAll();
@@ -283,6 +447,7 @@ public class LogSearchDataRetrievalServiceTest {
final String expectedComponentName = "DATANODE";
final String expectedClusterName = "clusterone";
final String expectedComponentAndHostName = expectedComponentName + "+" + expectedHostName;
+ final AtomicInteger testInteger = new AtomicInteger(0);
EasyMockSupport mockSupport = new EasyMockSupport();
@@ -292,6 +457,9 @@ public class LogSearchDataRetrievalServiceTest {
Cache<String, Set<String>> cacheMock = mockSupport.createMock(Cache.class);
Set<String> currentRequestsMock = mockSupport.createMock(Set.class);
+ Map<String, AtomicInteger> componentFailureCounts = mockSupport.createMock(Map.class);
+
+ Capture<AtomicInteger> captureFailureCount = EasyMock.newCapture();
expect(helperFactoryMock.getHelper(controllerMock, expectedClusterName)).andReturn(helperMock);
// return null to simulate an error occurring during the LogSearch data request
@@ -299,14 +467,72 @@ public class LogSearchDataRetrievalServiceTest {
// expect that the completed request is removed from the current request set,
// even in the event of a failure to obtain the LogSearch data
expect(currentRequestsMock.remove(expectedComponentAndHostName)).andReturn(true).once();
+ // expect that the component failure map is initially empty
+ expect(componentFailureCounts.containsKey(expectedComponentName)).andReturn(false);
+ // expect that the component map is updated with a new count
+ expect(componentFailureCounts.put(eq(expectedComponentName), capture(captureFailureCount))).andReturn(new AtomicInteger(0));
+ // expect that the runnable will obtain an increment the failure count
+ expect(componentFailureCounts.get(expectedComponentName)).andReturn(testInteger);
+
mockSupport.replayAll();
LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable loggingRunnable =
new LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable(expectedHostName, expectedComponentName, expectedClusterName,
- cacheMock, currentRequestsMock, helperFactoryMock, controllerMock);
+ cacheMock, currentRequestsMock, helperFactoryMock, componentFailureCounts, controllerMock);
loggingRunnable.run();
+ assertEquals("Initial count set by Runnable should be 0",
+ 0, captureFailureCount.getValue().get());
+ assertEquals("Failure count should have been incremented",
+ 1, testInteger.get());
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testRunnableWithFailedCallNullResultExistingFailureCount() throws Exception {
+ final String expectedHostName = "c6401.ambari.apache.org";
+ final String expectedComponentName = "DATANODE";
+ final String expectedClusterName = "clusterone";
+ final String expectedComponentAndHostName = expectedComponentName + "+" + expectedHostName;
+ final AtomicInteger testFailureCount = new AtomicInteger(2);
+
+ EasyMockSupport mockSupport = new EasyMockSupport();
+
+ LoggingRequestHelperFactory helperFactoryMock = mockSupport.createMock(LoggingRequestHelperFactory.class);
+ AmbariManagementController controllerMock = mockSupport.createMock(AmbariManagementController.class);
+ LoggingRequestHelper helperMock = mockSupport.createMock(LoggingRequestHelper.class);
+
+ Cache<String, Set<String>> cacheMock = mockSupport.createMock(Cache.class);
+ Set<String> currentRequestsMock = mockSupport.createMock(Set.class);
+ Map<String, AtomicInteger> componentFailureCounts = mockSupport.createMock(Map.class);
+
+ expect(helperFactoryMock.getHelper(controllerMock, expectedClusterName)).andReturn(helperMock);
+ // return null to simulate an error occurring during the LogSearch data request
+ expect(helperMock.sendGetLogFileNamesRequest(expectedComponentName, expectedHostName)).andReturn(null);
+ // expect that the completed request is removed from the current request set,
+ // even in the event of a failure to obtain the LogSearch data
+ expect(currentRequestsMock.remove(expectedComponentAndHostName)).andReturn(true).once();
+ // expect that the component failure map is initially empty
+ expect(componentFailureCounts.containsKey(expectedComponentName)).andReturn(true);
+ // expect that the runnable will obtain an increment the existing failure count
+ expect(componentFailureCounts.get(expectedComponentName)).andReturn(testFailureCount);
+
+ mockSupport.replayAll();
+
+ assertEquals("Initial count should be 2",
+ 2, testFailureCount.get());
+
+ LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable loggingRunnable =
+ new LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable(expectedHostName, expectedComponentName, expectedClusterName,
+ cacheMock, currentRequestsMock, helperFactoryMock, componentFailureCounts, controllerMock);
+ loggingRunnable.run();
+
+ assertEquals("Failure count should have been incremented",
+ 3, testFailureCount.get());
+
mockSupport.verifyAll();
}
@@ -317,6 +543,7 @@ public class LogSearchDataRetrievalServiceTest {
final String expectedComponentName = "DATANODE";
final String expectedClusterName = "clusterone";
final String expectedComponentAndHostName = expectedComponentName + "+" + expectedHostName;
+ final AtomicInteger testInteger = new AtomicInteger(0);
EasyMockSupport mockSupport = new EasyMockSupport();
@@ -326,6 +553,9 @@ public class LogSearchDataRetrievalServiceTest {
Cache<String, Set<String>> cacheMock = mockSupport.createMock(Cache.class);
Set<String> currentRequestsMock = mockSupport.createMock(Set.class);
+ Map<String, AtomicInteger> componentFailureCounts = mockSupport.createMock(Map.class);
+
+ Capture<AtomicInteger> captureFailureCount = EasyMock.newCapture();
expect(helperFactoryMock.getHelper(controllerMock, expectedClusterName)).andReturn(helperMock);
// return null to simulate an error occurring during the LogSearch data request
@@ -333,14 +563,25 @@ public class LogSearchDataRetrievalServiceTest {
// expect that the completed request is removed from the current request set,
// even in the event of a failure to obtain the LogSearch data
expect(currentRequestsMock.remove(expectedComponentAndHostName)).andReturn(true).once();
+ // expect that the component failure map is initially empty
+ expect(componentFailureCounts.containsKey(expectedComponentName)).andReturn(false);
+ // expect that the component map is updated with a new count
+ expect(componentFailureCounts.put(eq(expectedComponentName), capture(captureFailureCount))).andReturn(new AtomicInteger(0));
+ // expect that the runnable will obtain an increment the failure count
+ expect(componentFailureCounts.get(expectedComponentName)).andReturn(testInteger);
mockSupport.replayAll();
LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable loggingRunnable =
new LogSearchDataRetrievalService.LogSearchFileNameRequestRunnable(expectedHostName, expectedComponentName, expectedClusterName,
- cacheMock, currentRequestsMock, helperFactoryMock, controllerMock);
+ cacheMock, currentRequestsMock, helperFactoryMock, componentFailureCounts, controllerMock);
loggingRunnable.run();
+ assertEquals("Initial count set by Runnable should be 0",
+ 0, captureFailureCount.getValue().get());
+ assertEquals("Failure count should have been incremented",
+ 1, testInteger.get());
+
mockSupport.verifyAll();
}
}