diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java index d392410..6e5333a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java @@ -593,6 +593,8 @@ protected void serviceStart() throws Exception { } } + rmStore.initAttemptsInStateStore(rmContext.getRMApps().values()); + super.serviceStart(); } @@ -817,6 +819,33 @@ public void handle(RMAppAttemptEvent event) { LOG.error("Error in handling event type " + event.getType() + " for applicationAttempt " + appAttemptId, t); } + } else if (rmApp.getApplicationSubmissionContext() != null + && rmApp.getApplicationSubmissionContext() + .getKeepContainersAcrossApplicationAttempts() + && event.getType() == RMAppAttemptEventType.CONTAINER_FINISHED) { + // For work-preserving AM restart, failed attempts are still + // capturing CONTAINER_FINISHED events and record the finished + // containers which will be used by current attempt. + // We just keep 'yarn.resourcemanager.am.max-attempts' in + // RMStateStore. If the finished container's attempt is deleted, we + // use the first attempt in app.attempts to deal with these events. + + RMAppAttempt previousFailedAttempt = + rmApp.getAppAttempts().values().iterator().next(); + if (previousFailedAttempt != null) { + try { + LOG.debug("Event " + event.getType() + " handled by " + + previousFailedAttempt); + previousFailedAttempt.handle(event); + } catch (Throwable t) { + LOG.error("Error in handling event type " + event.getType() + + " for applicationAttempt " + appAttemptId + + " with " + previousFailedAttempt, t); + } + } else { + LOG.error("Event " + event.getType() + + " not handled, because previousFailedAttempt is null"); + } } } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceTrackerService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceTrackerService.java index bd24b25..902244b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceTrackerService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceTrackerService.java @@ -230,6 +230,11 @@ void handleNMContainerStatus(NMContainerStatus containerStatus, NodeId nodeId) { } RMAppAttempt rmAppAttempt = rmApp.getRMAppAttempt(appAttemptId); + if (rmAppAttempt == null) { + LOG.info("Ignoring not found attempt " + appAttemptId); + return; + } + Container masterContainer = rmAppAttempt.getMasterContainer(); if (masterContainer.getId().equals(containerStatus.getContainerId()) && containerStatus.getContainerState() == ContainerState.COMPLETE) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java index a1cebf5..021ca36 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java @@ -482,6 +482,18 @@ public synchronized void updateApplicationAttemptStateInternal( } @Override + public synchronized void removeApplicationAttemptInternal( + ApplicationAttemptId appAttemptId) + throws Exception { + Path appDirPath = + getAppDir(rmAppRoot, appAttemptId.getApplicationId()); + Path nodeRemovePath = getNodePath(appDirPath, appAttemptId.toString()); + LOG.info("Removing info for attempt: " + appAttemptId + " at: " + + nodeRemovePath); + deleteFileWithRetries(nodeRemovePath); + } + + @Override public synchronized void removeApplicationStateInternal( ApplicationStateData appState) throws Exception { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java index afc6721..7ec94fc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java @@ -500,6 +500,22 @@ ApplicationStateData loadRMAppState(ApplicationId appId) throws IOException { return createApplicationState(appId.toString(), data); } + @VisibleForTesting + ApplicationAttemptStateData loadRMAppAttemptState( + ApplicationAttemptId attemptId) throws IOException { + String attemptKey = getApplicationAttemptNodeKey(attemptId); + byte[] data = null; + try { + data = db.get(bytes(attemptKey)); + } catch (DBException e) { + throw new IOException(e); + } + if (data == null) { + return null; + } + return createAttemptState(attemptId.toString(), data); + } + private ApplicationAttemptStateData createAttemptState(String itemName, byte[] data) throws IOException { ApplicationAttemptId attemptId = @@ -575,6 +591,22 @@ protected void updateApplicationAttemptStateInternal( } @Override + public synchronized void removeApplicationAttemptInternal( + ApplicationAttemptId attemptId) + throws IOException { + String attemptKey = getApplicationAttemptNodeKey(attemptId); + if (LOG.isDebugEnabled()) { + LOG.debug("Removing state for attempt " + attemptId + " at " + + attemptKey); + } + try { + db.delete(bytes(attemptKey)); + } catch (DBException e) { + throw new IOException(e); + } + } + + @Override protected void removeApplicationStateInternal(ApplicationStateData appState) throws IOException { ApplicationId appId = diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java index ce6addb..caaea7e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java @@ -142,6 +142,19 @@ public synchronized void updateApplicationAttemptStateInternal( } @Override + public synchronized void removeApplicationAttemptInternal( + ApplicationAttemptId appAttemptId) throws Exception { + ApplicationStateData appState = + state.getApplicationState().get(appAttemptId.getApplicationId()); + ApplicationAttemptStateData attemptState = + appState.attempts.remove(appAttemptId); + LOG.info("Removing state for attempt: " + appAttemptId); + if (attemptState == null) { + throw new YarnRuntimeException("Application doesn't exist"); + } + } + + @Override public synchronized void removeApplicationStateInternal( ApplicationStateData appState) throws Exception { ApplicationId appId = diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java index 96f77f5..f6fd6fe 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java @@ -132,6 +132,12 @@ protected void updateApplicationAttemptStateInternal(ApplicationAttemptId attemp } @Override + public synchronized void removeApplicationAttemptInternal( + ApplicationAttemptId attemptId) throws Exception { + // Do nothing + } + + @Override public void checkVersion() throws Exception { // Do nothing } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java index ec42cbe..e20ab8a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java @@ -18,9 +18,13 @@ package org.apache.hadoop.yarn.server.resourcemanager.recovery; +import java.util.ArrayList; +import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -98,6 +102,8 @@ private ResourceManager resourceManager; private final ReadLock readLock; private final WriteLock writeLock; + private Map> attemptsInStateStore + = new HashMap<>(); public static final Log LOG = LogFactory.getLog(RMStateStore.class); @@ -137,6 +143,10 @@ new UpdateAppAttemptTransition()) .addTransition(RMStateStoreState.ACTIVE, EnumSet.of(RMStateStoreState.ACTIVE, RMStateStoreState.FENCED), + RMStateStoreEventType.REMOVE_APP_ATTEMPT, + new RemoveAppAttemptTransition()) + .addTransition(RMStateStoreState.ACTIVE, + EnumSet.of(RMStateStoreState.ACTIVE, RMStateStoreState.FENCED), RMStateStoreEventType.STORE_MASTERKEY, new StoreRMDTMasterKeyTransition()) .addTransition(RMStateStoreState.ACTIVE, @@ -211,6 +221,8 @@ public RMStateStoreState transition(RMStateStore store, store.storeApplicationStateInternal(appId, appState); store.notifyApplication(new RMAppEvent(appId, RMAppEventType.APP_NEW_SAVED)); + store.attemptsInStateStore.put(appId, + new ArrayList()); } catch (Exception e) { LOG.error("Error storing app: " + appId, e); isFenced = store.notifyStoreOperationFailedInternal(e); @@ -269,6 +281,7 @@ public RMStateStoreState transition(RMStateStore store, LOG.info("Removing info for app: " + appId); try { store.removeApplicationStateInternal(appState); + store.attemptsInStateStore.remove(appId); } catch (Exception e) { LOG.error("Error removing app: " + appId, e); isFenced = store.notifyStoreOperationFailedInternal(e); @@ -300,6 +313,7 @@ public RMStateStoreState transition(RMStateStore store, store.notifyApplicationAttempt(new RMAppAttemptEvent (attemptState.getAttemptId(), RMAppAttemptEventType.ATTEMPT_NEW_SAVED)); + store.addToAttemptsInStateStore(attemptState.getAttemptId()); } catch (Exception e) { LOG.error("Error storing appAttempt: " + attemptState.getAttemptId(), e); isFenced = store.notifyStoreOperationFailedInternal(e); @@ -552,6 +566,45 @@ private static RMStateStoreState finalState(boolean isFenced) { return isFenced ? RMStateStoreState.FENCED : RMStateStoreState.ACTIVE; } + private static class RemoveAppAttemptTransition implements + MultipleArcTransition { + @Override + public RMStateStoreState transition(RMStateStore store, + RMStateStoreEvent event) { + if (!(event instanceof RMStateStoreRemoveAppAttemptEvent)) { + // should never happen + LOG.error("Illegal event type: " + event.getClass()); + return RMStateStoreState.ACTIVE; + } + boolean isFenced = false; + ApplicationId appId = + ((RMStateStoreRemoveAppAttemptEvent) event).getApplicationId(); + int maxAttemptsToKeep = + ((RMStateStoreRemoveAppAttemptEvent) event).getMaxAttemptsToKeep(); + List attemptIds = + store.attemptsInStateStore.get(appId); + if (attemptIds == null || attemptIds.size() <= maxAttemptsToKeep) { + return RMStateStoreState.ACTIVE; + } + ApplicationAttemptId attemptId = null; + try { + Iterator attemptIdIterator = + attemptIds.iterator(); + while(attemptIds.size() > maxAttemptsToKeep) { + attemptId = attemptIdIterator.next(); + LOG.info("Removing attempt " + attemptId + " from app " + appId); + store.removeApplicationAttemptInternal(attemptId); + attemptIdIterator.remove(); + } + } catch (Exception e) { + LOG.error("Error removing attempt: " + attemptId, e); + isFenced = store.notifyStoreOperationFailedInternal(e); + } + return finalState(isFenced); + } + } + public RMStateStore() { super(RMStateStore.class.getName()); ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); @@ -983,6 +1036,30 @@ public void removeApplication(RMApp app) { protected abstract void removeApplicationStateInternal( ApplicationStateData appState) throws Exception; + /** + * Non-blocking API + * ResourceManager services call this to remove an attempt from the state + * store + * This does not block the dispatcher threads + * There is no notification of completion for this operation. + */ + @SuppressWarnings("unchecked") + public synchronized void removeApplicationAttempt( + ApplicationId applicationId, int maxAttemptsToKeep) { + dispatcher.getEventHandler().handle( + new RMStateStoreRemoveAppAttemptEvent(applicationId, + maxAttemptsToKeep)); + } + + /** + * Blocking API + * Derived classes must implement this method to remove the state of specified + * attempt. + */ + protected abstract void removeApplicationAttemptInternal( + ApplicationAttemptId attemptId) throws Exception; + + // TODO: This should eventually become cluster-Id + "AM_RM_TOKEN_SERVICE". See // YARN-1779 public static final Text AM_RM_TOKEN_SERVICE = new Text( @@ -1123,6 +1200,41 @@ public void setResourceManager(ResourceManager rm) { this.resourceManager = rm; } + @VisibleForTesting + public Map> + getAttemptsInStateStore() { + return attemptsInStateStore; + } + + public void initAttemptsInStateStore(Collection rmApps) { + for (RMApp rmApp : rmApps) { + List attemptIds = new ArrayList<>(); + attemptIds.addAll(rmApp.getAppAttempts().keySet()); + attemptsInStateStore.put(rmApp.getApplicationId(), attemptIds); + } + } + + public void addToAttemptsInStateStore(ApplicationAttemptId attemptId) { + ApplicationId applicationId = attemptId.getApplicationId(); + List attemptIds = + attemptsInStateStore.get(applicationId); + if (attemptIds == null) { + LOG.error(applicationId + " is not found in StateStore"); + return; + } + if (attemptIds.isEmpty()) { + attemptIds.add(attemptId); + return; + } + int i; + for(i = attemptIds.size(); i > 0; i--) { + if (attemptIds.get(i - 1).getAttemptId() < attemptId.getAttemptId()) { + break; + } + } + attemptIds.add(i, attemptId); + } + private class StandByTransitionThread implements Runnable { @Override public void run() { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java index 492826d..b34634d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java @@ -24,6 +24,7 @@ UPDATE_APP, UPDATE_APP_ATTEMPT, REMOVE_APP, + REMOVE_APP_ATTEMPT, FENCED, // Below events should be called synchronously diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreRemoveAppAttemptEvent.java similarity index 57% copy from hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java copy to hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreRemoveAppAttemptEvent.java index 492826d..ad3c9c1 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreRemoveAppAttemptEvent.java @@ -18,21 +18,27 @@ package org.apache.hadoop.yarn.server.resourcemanager.recovery; -public enum RMStateStoreEventType { - STORE_APP_ATTEMPT, - STORE_APP, - UPDATE_APP, - UPDATE_APP_ATTEMPT, - REMOVE_APP, - FENCED, +import org.apache.hadoop.yarn.api.records.ApplicationId; - // Below events should be called synchronously - STORE_MASTERKEY, - REMOVE_MASTERKEY, - STORE_DELEGATION_TOKEN, - REMOVE_DELEGATION_TOKEN, - UPDATE_DELEGATION_TOKEN, - UPDATE_AMRM_TOKEN, - STORE_RESERVATION, - REMOVE_RESERVATION, +/** + * A event used to remove an attempt. + */ +public class RMStateStoreRemoveAppAttemptEvent extends RMStateStoreEvent { + private ApplicationId applicationId; + private int maxAttemptsToKeep; + + RMStateStoreRemoveAppAttemptEvent(ApplicationId applicationId, + int maxAttemptsToKeep) { + super(RMStateStoreEventType.REMOVE_APP_ATTEMPT); + this.applicationId = applicationId; + this.maxAttemptsToKeep = maxAttemptsToKeep; + } + + public ApplicationId getApplicationId() { + return applicationId; + } + + public int getMaxAttemptsToKeep() { + return maxAttemptsToKeep; + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java index ca0f4ac..ddb8a0b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java @@ -659,6 +659,22 @@ public synchronized void updateApplicationAttemptStateInternal( } @Override + public synchronized void removeApplicationAttemptInternal( + ApplicationAttemptId appAttemptId) + throws Exception { + String appId = appAttemptId.getApplicationId().toString(); + String appIdRemovePath = getNodePath(rmAppRoot, appId); + String attemptIdRemovePath = getNodePath(appIdRemovePath, + appAttemptId.toString()); + + if (LOG.isDebugEnabled()) { + LOG.debug("Removing info for attempt: " + appAttemptId + " at: " + + attemptIdRemovePath); + } + safeDelete(attemptIdRemovePath); + } + + @Override public synchronized void removeApplicationStateInternal( ApplicationStateData appState) throws Exception { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/records/ApplicationStateData.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/records/ApplicationStateData.java index 1d199ed..2348380 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/records/ApplicationStateData.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/records/ApplicationStateData.java @@ -80,6 +80,16 @@ public ApplicationAttemptStateData getAttempt( return attempts.get(attemptId); } + public int getFirstAttemptId() { + int min = Integer.MAX_VALUE; + for(ApplicationAttemptId attemptId : attempts.keySet()) { + if (attemptId.getAttemptId() < min) { + min = attemptId.getAttemptId(); + } + } + return min == Integer.MAX_VALUE ? 1 : min; + } + public abstract ApplicationStateDataProto getProto(); /** diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java index c4c8d2e..544e7fc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java @@ -149,6 +149,7 @@ private long startTime; private long finishTime = 0; private long storedFinishTime = 0; + private int nextAttemptId = 1; // This field isn't protected by readlock now. private volatile RMAppAttempt currentAttempt; private String queue; @@ -809,6 +810,7 @@ public void recover(RMState state) { this.storedFinishTime = appState.getFinishTime(); this.startTime = appState.getStartTime(); this.callerContext = appState.getCallerContext(); + this.nextAttemptId = appState.getFirstAttemptId(); // send the ATS create Event sendATSCreateEvent(this, this.startTime); @@ -822,7 +824,7 @@ public void recover(RMState state) { private void createNewAttempt() { ApplicationAttemptId appAttemptId = - ApplicationAttemptId.newInstance(applicationId, attempts.size() + 1); + ApplicationAttemptId.newInstance(applicationId, nextAttemptId++); BlacklistManager currentAMBlacklist; if (currentAttempt != null) { @@ -1304,6 +1306,9 @@ public RMAppState transition(RMAppImpl app, RMAppEvent event) { + app.attemptFailuresValidityInterval + " milliseconds " : " ") + "is " + numberOfFailure + ". The max attempts is " + app.maxAppAttempts); + + removeExcessAttemptsInStateStore(app); + if (!app.submissionContext.getUnmanagedAM() && numberOfFailure < app.maxAppAttempts) { if (initialState.equals(RMAppState.KILLING)) { @@ -1340,6 +1345,17 @@ public RMAppState transition(RMAppImpl app, RMAppEvent event) { return RMAppState.FINAL_SAVING; } } + + private void removeExcessAttemptsInStateStore(RMAppImpl app) { + // The number of attempts in memory is greater than attempts in + // RMStateStore. If the former is not greater than app.maxAppAttempts, + // the later must be not greater than app.maxAppAttempts. + if (app.attempts.size() <= app.maxAppAttempts) { + return; + } + app.rmContext.getStateStore().removeApplicationAttempt( + app.getApplicationId(), app.maxAppAttempts); + } } @Override diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/applicationsmanager/TestAMRestart.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/applicationsmanager/TestAMRestart.java index acacc40..4de15b6 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/applicationsmanager/TestAMRestart.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/applicationsmanager/TestAMRestart.java @@ -857,6 +857,17 @@ public void testRMAppAttemptFailuresValidityInterval() throws Exception { @SuppressWarnings("resource") MockRM rm2 = new MockRM(conf, memStore); rm2.start(); + ApplicationStateData app1State = + memStore.getState().getApplicationState().get(app1.getApplicationId()); + Assert.assertEquals(1, app1State.getFirstAttemptId()); + + Map> attemptsInStateStore = + memStore.getAttemptsInStateStore(); + Assert.assertEquals(2, attemptsInStateStore.size()); + Assert.assertEquals(2, + attemptsInStateStore.get(app.getApplicationId()).size()); + Assert.assertEquals(3, + attemptsInStateStore.get(app1.getApplicationId()).size()); // re-register the NM nm1.setResourceTrackerService(rm2.getResourceTrackerService()); @@ -869,6 +880,7 @@ public void testRMAppAttemptFailuresValidityInterval() throws Exception { nm1.registerNode(Collections.singletonList(status), null); rm2.waitForState(attempt3.getAppAttemptId(), RMAppAttemptState.FAILED); + Assert.assertEquals(2, app1State.getAttemptCount()); rm2.waitForState(app1.getApplicationId(), RMAppState.ACCEPTED); @@ -882,6 +894,7 @@ public void testRMAppAttemptFailuresValidityInterval() throws Exception { nm1 .nodeHeartbeat(am4.getApplicationAttemptId(), 1, ContainerState.COMPLETE); am4.waitForState(RMAppAttemptState.FAILED); + Assert.assertEquals(2, app1State.getAttemptCount()); // can launch the 5th attempt successfully rm2.waitForState(app1.getApplicationId(), RMAppState.ACCEPTED); @@ -895,6 +908,7 @@ public void testRMAppAttemptFailuresValidityInterval() throws Exception { nm1 .nodeHeartbeat(am5.getApplicationAttemptId(), 1, ContainerState.COMPLETE); am5.waitForState(RMAppAttemptState.FAILED); + Assert.assertEquals(2, app1State.getAttemptCount()); rm2.waitForState(app1.getApplicationId(), RMAppState.FAILED); rm1.stop(); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java index 32824ef..2ddaec2 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -134,6 +135,7 @@ void afterStoreAppAttempt(RMStateStore store, ApplicationAttemptId void writeVersion(Version version) throws Exception; Version getCurrentVersion() throws Exception; boolean appExists(RMApp app) throws Exception; + boolean attemptExists(RMAppAttempt attempt) throws Exception; } void waitNotify(TestDispatcher dispatcher) { @@ -172,7 +174,7 @@ protected RMApp storeApp(RMStateStore store, ApplicationId appId, return mockApp; } - protected ContainerId storeAttempt(RMStateStore store, + protected RMAppAttempt storeAttempt(RMStateStore store, ApplicationAttemptId attemptId, String containerIdStr, Token appToken, SecretKey clientTokenMasterKey, TestDispatcher dispatcher) @@ -195,7 +197,7 @@ protected ContainerId storeAttempt(RMStateStore store, dispatcher.attemptId = attemptId; store.storeNewApplicationAttempt(mockAttempt); waitNotify(dispatcher); - return container.getId(); + return mockAttempt; } void testRMAppStateStore(RMStateStoreHelper stateStoreHelper) @@ -238,8 +240,9 @@ void testRMAppStateStore(RMStateStoreHelper stateStoreHelper, clientToAMTokenMgr.createMasterKey(attemptId1); ContainerId containerId1 = storeAttempt(store, attemptId1, - "container_1352994193343_0001_01_000001", - appAttemptToken1, clientTokenKey1, dispatcher); + "container_1352994193343_0001_01_000001", + appAttemptToken1, clientTokenKey1, dispatcher) + .getMasterContainer().getId(); String appAttemptIdStr2 = "appattempt_1352994193343_0001_000002"; ApplicationAttemptId attemptId2 = @@ -252,8 +255,9 @@ void testRMAppStateStore(RMStateStoreHelper stateStoreHelper, clientToAMTokenMgr.createMasterKey(attemptId2); ContainerId containerId2 = storeAttempt(store, attemptId2, - "container_1352994193343_0001_02_000001", - appAttemptToken2, clientTokenKey2, dispatcher); + "container_1352994193343_0001_02_000001", + appAttemptToken2, clientTokenKey2, dispatcher) + .getMasterContainer().getId(); ApplicationAttemptId attemptIdRemoved = ConverterUtils .toApplicationAttemptId("appattempt_1352994193343_0002_000001"); @@ -633,6 +637,47 @@ public void testRemoveApplication(RMStateStoreHelper stateStoreHelper) Assert.assertTrue(stateStoreHelper.appExists(rmApp2)); } + public void testRemoveAttempt(RMStateStoreHelper stateStoreHelper) + throws Exception { + RMStateStore store = stateStoreHelper.getRMStateStore(); + TestDispatcher dispatcher = new TestDispatcher(); + store.setRMDispatcher(dispatcher); + + ApplicationId appId = ApplicationId.newInstance(1383183339, 6); + storeApp(store, appId, 123456, 564321); + + ApplicationAttemptId attemptId1 = + ApplicationAttemptId.newInstance(appId, 1); + RMAppAttempt attempt1 = storeAttempt(store, attemptId1, + ContainerId.newContainerId(attemptId1, 1).toString(), + null, null, dispatcher); + ApplicationAttemptId attemptId2 = + ApplicationAttemptId.newInstance(appId, 2); + RMAppAttempt attempt2 = storeAttempt(store, attemptId2, + ContainerId.newContainerId(attemptId2, 1).toString(), + null, null, dispatcher); + store.removeApplicationAttemptInternal(attemptId1); + Assert.assertFalse(stateStoreHelper.attemptExists(attempt1)); + Assert.assertTrue(stateStoreHelper.attemptExists(attempt2)); + + // let things settle down + Thread.sleep(1000); + store.close(); + + // load state + store = stateStoreHelper.getRMStateStore(); + RMState state = store.loadState(); + Map rmAppState = + state.getApplicationState(); + + ApplicationStateData appState = rmAppState.get(appId); + // app is loaded + assertNotNull(appState); + assertEquals(2, appState.getFirstAttemptId()); + assertNull(appState.getAttempt(attemptId1)); + assertNotNull(appState.getAttempt(attemptId2)); + } + protected void modifyAppState() throws Exception { } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java index a2ff4b3..a51ccb5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java @@ -47,6 +47,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.recovery.records.ApplicationStateData; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState; +import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt; import org.apache.hadoop.yarn.util.ConverterUtils; import org.junit.Assert; import org.junit.Test; @@ -88,6 +89,12 @@ public Path getAppDir(String appId) { Path appDir = new Path(appRootDir, appId); return appDir; } + + public Path getAttemptDir(String appId, String attemptId) { + Path appDir = getAppDir(appId); + Path attemptDir = new Path(appDir, attemptId); + return attemptDir; + } } public TestFSRMStateStoreTester(MiniDFSCluster cluster, boolean adminCheckEnable) throws Exception { @@ -151,6 +158,15 @@ public boolean appExists(RMApp app) throws IOException { store.getAppDir(app.getApplicationId().toString()); return fs.exists(nodePath); } + + public boolean attemptExists(RMAppAttempt attempt) throws IOException { + FileSystem fs = cluster.getFileSystem(); + ApplicationAttemptId attemptId = attempt.getAppAttemptId(); + Path nodePath = + store.getAttemptDir(attemptId.getApplicationId().toString(), + attemptId.toString()); + return fs.exists(nodePath); + } } @Test(timeout = 60000) @@ -185,6 +201,7 @@ public void testFSRMStateStore() throws Exception { testAppDeletion(fsTester); testDeleteStore(fsTester); testRemoveApplication(fsTester); + testRemoveAttempt(fsTester); testAMRMTokenSecretManagerStateStore(fsTester); testReservationStateStore(fsTester); } finally { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java index 4666142..ce186e6 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java @@ -25,6 +25,7 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.server.records.Version; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp; +import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -97,6 +98,12 @@ public void testRemoveApplication() throws Exception { } @Test(timeout = 60000) + public void testRemoveAttempt() throws Exception { + LeveldbStateStoreTester tester = new LeveldbStateStoreTester(); + testRemoveAttempt(tester); + } + + @Test(timeout = 60000) public void testAMTokens() throws Exception { LeveldbStateStoreTester tester = new LeveldbStateStoreTester(); testAMRMTokenSecretManagerStateStore(tester); @@ -147,5 +154,14 @@ public boolean appExists(RMApp app) throws Exception { } return stateStore.loadRMAppState(app.getApplicationId()) != null; } + + @Override + public boolean attemptExists(RMAppAttempt attempt) throws Exception { + if (stateStore.isClosed()) { + getRMStateStore(); + } + return stateStore.loadRMAppAttemptState(attempt.getAppAttemptId()) + != null; + } } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java index 66b023c..406fcf6 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java @@ -123,6 +123,10 @@ public String getAppNode(String appId) { + appId; } + public String getAttemptNode(String appId, String attemptId) { + return getAppNode(appId) + "/" + attemptId; + } + /** * Emulating retrying createRootDir not to raise NodeExist exception * @throws Exception @@ -165,6 +169,13 @@ public boolean appExists(RMApp app) throws Exception { return null != curatorFramework.checkExists() .forPath(store.getAppNode(app.getApplicationId().toString())); } + + public boolean attemptExists(RMAppAttempt attempt) throws Exception { + ApplicationAttemptId attemptId = attempt.getAppAttemptId(); + return null != curatorFramework.checkExists() + .forPath(store.getAttemptNode( + attemptId.getApplicationId().toString(), attemptId.toString())); + } } @Test (timeout = 60000) @@ -177,6 +188,7 @@ public void testZKRMStateStoreRealZK() throws Exception { testAppDeletion(zkTester); testDeleteStore(zkTester); testRemoveApplication(zkTester); + testRemoveAttempt(zkTester); testAMRMTokenSecretManagerStateStore(zkTester); testReservationStateStore(zkTester); ((TestZKRMStateStoreTester.TestZKRMStateStoreInternal)