SLIDER-587 test for dynamic role replacement passing
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/0b06d631 Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/0b06d631 Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/0b06d631 Branch: refs/heads/feature/SLIDER-531-registry-enhancements Commit: 0b06d631598097ea4d867252a2864d3ce374b768 Parents: 34f78ad Author: Steve Loughran <ste...@apache.org> Authored: Fri Oct 31 21:24:24 2014 +0000 Committer: Steve Loughran <ste...@apache.org> Committed: Fri Oct 31 21:24:24 2014 +0000 ---------------------------------------------------------------------- .../slider/server/appmaster/state/AppState.java | 2 - .../server/appmaster/state/NodeEntry.java | 12 ++--- .../server/appmaster/state/NodeInstance.java | 4 +- .../server/appmaster/state/RoleHistory.java | 44 +++++++++------ .../TestMockAppStateDynamicHistory.groovy | 57 ++++++++++++++------ .../appmaster/model/mock/MockAppState.groovy | 11 ++++ 6 files changed, 86 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/main/java/org/apache/slider/server/appmaster/state/AppState.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/AppState.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/AppState.java index 2b5d0ee..0848173 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/AppState.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/AppState.java @@ -708,8 +708,6 @@ public class AppState { log.info("New role {}", roleStatus); roleHistory.addNewProviderRole(dynamicRole); newRoles.add(dynamicRole); - } else { - log.debug("known role: {}", name); } } return newRoles; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeEntry.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeEntry.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeEntry.java index 83c590b..ebddaf9 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeEntry.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeEntry.java @@ -37,10 +37,10 @@ package org.apache.slider.server.appmaster.state; */ public class NodeEntry { - public final int index; + public final int rolePriority; - public NodeEntry(int index) { - this.index = index; + public NodeEntry(int rolePriority) { + this.rolePriority = rolePriority; } /** @@ -132,8 +132,7 @@ public class NodeEntry { public synchronized boolean onStartFailed() { decStarting(); ++startFailed; - ++failed; - return isAvailable(); + return containerCompleted(false); } /** @@ -211,7 +210,8 @@ public class NodeEntry { @Override public String toString() { final StringBuilder sb = new StringBuilder("NodeEntry{"); - sb.append("requested=").append(requested); + sb.append("priority=").append(rolePriority); + sb.append(", requested=").append(requested); sb.append(", starting=").append(starting); sb.append(", live=").append(live); sb.append(", failed=").append(failed); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeInstance.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeInstance.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeInstance.java index 1ba2282..bc79b71 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeInstance.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/NodeInstance.java @@ -52,7 +52,7 @@ public class NodeInstance { */ public synchronized NodeEntry get(int role) { for (NodeEntry nodeEntry : nodeEntries) { - if (nodeEntry.index == role) { + if (nodeEntry.rolePriority == role) { return nodeEntry; } } @@ -146,7 +146,7 @@ public class NodeInstance { new StringBuilder(toString()); int i = 0; for (NodeEntry entry : nodeEntries) { - sb.append(String.format("\n [%02d] ", i++)); + sb.append(String.format("\n [%02d] ", entry.rolePriority)); sb.append(entry.toString()); } return sb.toString(); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/main/java/org/apache/slider/server/appmaster/state/RoleHistory.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/RoleHistory.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/RoleHistory.java index 33c3442..f1c0af5 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/RoleHistory.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/RoleHistory.java @@ -63,8 +63,6 @@ public class RoleHistory { protected static final Logger log = LoggerFactory.getLogger(RoleHistory.class); private final List<ProviderRole> providerRoles; - private final Map<String, ProviderRole> providerRoleMap = - new HashMap<String, ProviderRole>(); private long startTime; /** * Time when saved @@ -97,6 +95,7 @@ public class RoleHistory { * ask with/without locality. Has other potential uses as well. */ private Set<String> failedNodes = new HashSet<String>(); + // dummy to be used in maps for faster lookup where we don't care about values private final Object DUMMY_VALUE = new Object(); @@ -104,9 +103,6 @@ public class RoleHistory { BadConfigException { this.providerRoles = providerRoles; roleSize = providerRoles.size(); - for (ProviderRole providerRole : providerRoles) { - providerRoleMap.put(providerRole.name, providerRole); - } reset(); } @@ -120,18 +116,24 @@ public class RoleHistory { resetAvailableNodeLists(); outstandingRequests = new OutstandingRequestTracker(); + Map<Integer, RoleStatus> roleStats = new HashMap<Integer, RoleStatus>(); - - for (ProviderRole providerRole : providerRoles) { - addProviderRole(roleStats, providerRole); + checkProviderRole(roleStats, providerRole); } } - - private void addProviderRole(Map<Integer, RoleStatus> roleStats, - ProviderRole providerRole) - throws ArrayIndexOutOfBoundsException, BadConfigException { + /** + * safety check: make sure the provider role is unique amongst + * the role stats...which is extended with the new role + * @param roleStats role stats + * @param providerRole role + * @throws ArrayIndexOutOfBoundsException + * @throws BadConfigException + */ + private void checkProviderRole(Map<Integer, RoleStatus> roleStats, + ProviderRole providerRole) + throws BadConfigException { int index = providerRole.id; if (index < 0) { throw new BadConfigException("Provider " + providerRole @@ -154,12 +156,12 @@ public class RoleHistory { throws BadConfigException { Map<Integer, RoleStatus> roleStats = new HashMap<Integer, RoleStatus>(); - for (ProviderRole role : providerRoles) { roleStats.put(role.id, new RoleStatus(role)); } - addProviderRole(roleStats, providerRole); + checkProviderRole(roleStats, providerRole); + this.providerRoles.add(providerRole); } /** @@ -432,7 +434,8 @@ public class RoleHistory { * @param id role ID * @return potenially null list */ - private List<NodeInstance> getNodesForRoleId(int id) { + @VisibleForTesting + public List<NodeInstance> getNodesForRoleId(int id) { return availableNodes.get(id); } @@ -610,6 +613,8 @@ public class RoleHistory { public synchronized boolean onContainerAllocated(Container container, int desiredCount, int actualCount) { int role = ContainerPriority.extractRole(container); String hostname = RoleHistoryUtils.hostnameOf(container); + LinkedList<NodeInstance> nodeInstances = + getOrCreateNodesForRoleId(role); boolean requestFound = outstandingRequests.onContainerAllocated(role, hostname); if (desiredCount <= actualCount) { @@ -619,7 +624,7 @@ public class RoleHistory { if (!hosts.isEmpty()) { //add the list log.debug("Adding {} hosts for role {}", hosts.size(), role); - getOrCreateNodesForRoleId(role).addAll(hosts); + nodeInstances.addAll(hosts); sortAvailableNodeList(role); } } @@ -734,6 +739,8 @@ public class RoleHistory { boolean wasReleased, boolean shortLived) { NodeEntry nodeEntry = getOrCreateNodeEntry(container); + log.debug("Finished container for node {}, released={}, shortlived={}", + nodeEntry.rolePriority, wasReleased, shortLived); boolean available; if (shortLived) { nodeEntry.onStartFailed(); @@ -781,13 +788,16 @@ public class RoleHistory { List<NodeInstance> instances = getOrCreateNodesForRoleId(role.id); log.info(" available: " + instances.size() - + " " + SliderUtils.joinWithInnerSeparator(", ", instances)); + + " " + SliderUtils.joinWithInnerSeparator(" ", instances)); } log.info("Nodes in Cluster: {}", getClusterSize()); for (NodeInstance node : nodemap.values()) { log.info(node.toFullString()); } + + log.info("Failed nodes: {}", + SliderUtils.joinWithInnerSeparator(" ", failedNodes)); } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.groovy b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.groovy index 9a9ad23..7d41012 100644 --- a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.groovy @@ -24,7 +24,6 @@ import org.apache.hadoop.conf.Configuration import org.apache.hadoop.yarn.api.records.ContainerId import org.apache.slider.api.ResourceKeys import org.apache.slider.core.conf.ConfTreeOperations -import org.apache.slider.providers.PlacementPolicy import org.apache.slider.providers.ProviderRole import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest import org.apache.slider.server.appmaster.model.mock.MockAppState @@ -84,7 +83,7 @@ class TestMockAppStateDynamicHistory extends BaseMockAppStateTest public void testDynamicRoleHistory() throws Throwable { def dynamic = "dynamicRole" - int priority_num_8 = 8 + int role_priority_8 = 8 int desired = 1 int placementPolicy = 0 // snapshot and patch existing spec @@ -92,7 +91,7 @@ class TestMockAppStateDynamicHistory extends BaseMockAppStateTest appState.resourcesSnapshot.confTree) def opts = [ (ResourceKeys.COMPONENT_INSTANCES): ""+desired, - (ResourceKeys.COMPONENT_PRIORITY) : "" +priority_num_8, + (ResourceKeys.COMPONENT_PRIORITY) : "" +role_priority_8, (ResourceKeys.COMPONENT_PLACEMENT_POLICY): "" + placementPolicy ] @@ -109,21 +108,21 @@ class TestMockAppStateDynamicHistory extends BaseMockAppStateTest def snapshotDefinition = appState.resourcesSnapshot.getMandatoryComponent( dynamic) assert snapshotDefinition.getMandatoryOptionInt( - ResourceKeys.COMPONENT_PRIORITY) == priority_num_8 + ResourceKeys.COMPONENT_PRIORITY) == role_priority_8 // now look at the role map assert appState.roleMap[dynamic] != null def mappedRole = appState.roleMap[dynamic] - assert mappedRole.id == priority_num_8 + assert mappedRole.id == role_priority_8 def priorityMap = appState.rolePriorityMap assert priorityMap.size() == 4 ProviderRole dynamicProviderRole - assert (dynamicProviderRole = priorityMap[priority_num_8]) != null - assert dynamicProviderRole.id == priority_num_8 + assert (dynamicProviderRole = priorityMap[role_priority_8]) != null + assert dynamicProviderRole.id == role_priority_8 - assert null != appState.roleStatusMap[priority_num_8] - def dynamicRoleStatus = appState.roleStatusMap[priority_num_8] + assert null != appState.roleStatusMap[role_priority_8] + def dynamicRoleStatus = appState.roleStatusMap[role_priority_8] assert dynamicRoleStatus.desired == desired @@ -135,6 +134,9 @@ class TestMockAppStateDynamicHistory extends BaseMockAppStateTest assert targetNode == engine.allocator.nextIndex() def targetHostname = engine.cluster.nodeAt(targetNode).hostname + // clock is set to a small value + appState.time = 100000 + // allocate the nodes def actions = appState.reviewRequestAndReleaseNodes() assert actions.size() == 1 @@ -150,31 +152,52 @@ class TestMockAppStateDynamicHistory extends BaseMockAppStateTest RoleInstance ri = allocations[0] assert ri.role == dynamic - assert ri.roleId == priority_num_8 + assert ri.roleId == role_priority_8 assert ri.host.host == targetHostname // now look at the role history def roleHistory = appState.roleHistory - def activeNodes = roleHistory.listActiveNodes(priority_num_8) + def activeNodes = roleHistory.listActiveNodes(role_priority_8) assert activeNodes.size() == 1 NodeInstance activeNode = activeNodes[0] + assert activeNode.get(role_priority_8) + def entry8 = activeNode.get(role_priority_8) + assert entry8.active == 1 assert activeNode.hostname == targetHostname - - // now trigger a termination event on that role + + def activeNodeInstance = roleHistory.getOrCreateNodeInstance(ri.container) + + assert activeNode == activeNodeInstance + def entry + assert (entry = activeNodeInstance.get(role_priority_8)) != null + assert entry.active + assert entry.live + // now trigger a termination event on that role + + // increment time for a long-lived failure event + appState.time = appState.time + 100000 + + log.debug("Triggering failure") def cid = ri.id - // failure AppState.NodeCompletionResult result = appState.onCompletedNode( containerStatus(cid, 1)) assert result.roleInstance == ri assert result.containerFailed + + roleHistory.dump(); + // values should have changed + assert entry.failed == 1 + assert !entry.startFailed + assert !entry.active + assert !entry.live + - def nodeForNewInstance = roleHistory.findNodeForNewInstance( - dynamicRoleStatus) - assert nodeForNewInstance + def nodesForRoleId = roleHistory.getNodesForRoleId(role_priority_8) + assert nodesForRoleId // make sure new nodes will default to a different host in the engine assert targetNode < engine.allocator.nextIndex() http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/0b06d631/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/mock/MockAppState.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/mock/MockAppState.groovy b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/mock/MockAppState.groovy index ad85b89..e683587 100644 --- a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/mock/MockAppState.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/mock/MockAppState.groovy @@ -30,6 +30,8 @@ class MockAppState extends AppState { super(recordFactory); } + long time = 0; + /** * Instance with a mock record factory */ @@ -40,4 +42,13 @@ class MockAppState extends AppState { public Map<String, ProviderRole> getRoleMap() { return super.roleMap; } + + /** + * Current time. if the <code>time</code> field + * is set, that value is returned + * @return the current time. + */ + protected long now() { + return time ?: System.currentTimeMillis(); + } }