org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainerImpl.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.yarn.server.resourcemanager.rmcontainer;

import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.ContainerExitStatus;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.ContainerReport;
import org.apache.hadoop.yarn.api.records.ContainerState;
import org.apache.hadoop.yarn.api.records.ContainerStatus;
import org.apache.hadoop.yarn.api.records.ContainerUpdateType;
import org.apache.hadoop.yarn.api.records.ExecutionType;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.api.records.Priority;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.event.EventHandler;
import org.apache.hadoop.yarn.server.api.protocolrecords.NMContainerStatus;
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
import org.apache.hadoop.yarn.server.resourcemanager.nodelabels.RMNodeLabelsManager;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppRunningOnNodeEvent;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEvent;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEventType;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.event.RMAppAttemptContainerFinishedEvent;
import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeCleanContainerEvent;
import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeUpdateContainerEvent;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.common.ContainerRequest;
import org.apache.hadoop.yarn.server.scheduler.SchedulerRequestKey;
import org.apache.hadoop.yarn.state.InvalidStateTransitionException;
import org.apache.hadoop.yarn.state.MultipleArcTransition;
import org.apache.hadoop.yarn.state.SingleArcTransition;
import org.apache.hadoop.yarn.state.StateMachine;
import org.apache.hadoop.yarn.state.StateMachineFactory;
import org.apache.hadoop.yarn.util.resource.Resources;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class RMContainerImpl implements RMContainer {

    private static final Log LOG = LogFactory.getLog(RMContainerImpl.class);

    private static final StateMachineFactory<RMContainerImpl, RMContainerState, RMContainerEventType, RMContainerEvent> stateMachineFactory = new StateMachineFactory<RMContainerImpl, RMContainerState, RMContainerEventType, RMContainerEvent>(
            RMContainerState.NEW)

                    // Transitions from NEW state
                    .addTransition(RMContainerState.NEW, RMContainerState.ALLOCATED, RMContainerEventType.START,
                            new ContainerStartedTransition())
                    .addTransition(RMContainerState.NEW, RMContainerState.KILLED, RMContainerEventType.KILL)
                    .addTransition(RMContainerState.NEW, RMContainerState.RESERVED, RMContainerEventType.RESERVED,
                            new ContainerReservedTransition())
                    .addTransition(RMContainerState.NEW, RMContainerState.ACQUIRED, RMContainerEventType.ACQUIRED,
                            new AcquiredTransition())
                    .addTransition(RMContainerState.NEW,
                            EnumSet.of(RMContainerState.RUNNING, RMContainerState.COMPLETED),
                            RMContainerEventType.RECOVER, new ContainerRecoveredTransition())

                    // Transitions from RESERVED state
                    .addTransition(RMContainerState.RESERVED, RMContainerState.RESERVED,
                            RMContainerEventType.RESERVED, new ContainerReservedTransition())
                    .addTransition(RMContainerState.RESERVED, RMContainerState.ALLOCATED,
                            RMContainerEventType.START, new ContainerStartedTransition())
                    .addTransition(RMContainerState.RESERVED, RMContainerState.KILLED, RMContainerEventType.KILL) // nothing to do
                    .addTransition(RMContainerState.RESERVED, RMContainerState.RELEASED,
                            RMContainerEventType.RELEASED) // nothing to do

                    // Transitions from ALLOCATED state
                    .addTransition(RMContainerState.ALLOCATED, RMContainerState.ACQUIRED,
                            RMContainerEventType.ACQUIRED, new AcquiredTransition())
                    .addTransition(RMContainerState.ALLOCATED, RMContainerState.EXPIRED,
                            RMContainerEventType.EXPIRE, new FinishedTransition())
                    .addTransition(RMContainerState.ALLOCATED, RMContainerState.KILLED, RMContainerEventType.KILL,
                            new FinishedTransition())

                    // Transitions from ACQUIRED state
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.RUNNING,
                            RMContainerEventType.LAUNCHED)
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.ACQUIRED,
                            RMContainerEventType.ACQUIRED)
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.COMPLETED,
                            RMContainerEventType.FINISHED, new FinishedTransition())
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.RELEASED,
                            RMContainerEventType.RELEASED, new KillTransition())
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.EXPIRED, RMContainerEventType.EXPIRE,
                            new KillTransition())
                    .addTransition(RMContainerState.ACQUIRED, RMContainerState.KILLED, RMContainerEventType.KILL,
                            new KillTransition())

                    // Transitions from RUNNING state
                    .addTransition(RMContainerState.RUNNING, RMContainerState.COMPLETED,
                            RMContainerEventType.FINISHED, new FinishedTransition())
                    .addTransition(RMContainerState.RUNNING, RMContainerState.KILLED, RMContainerEventType.KILL,
                            new KillTransition())
                    .addTransition(RMContainerState.RUNNING, RMContainerState.RELEASED,
                            RMContainerEventType.RELEASED, new KillTransition())
                    .addTransition(RMContainerState.RUNNING, RMContainerState.RUNNING,
                            RMContainerEventType.ACQUIRED)
                    .addTransition(RMContainerState.RUNNING, RMContainerState.RUNNING,
                            RMContainerEventType.RESERVED, new ContainerReservedTransition())
                    .addTransition(RMContainerState.RUNNING, RMContainerState.RUNNING,
                            RMContainerEventType.ACQUIRE_UPDATED_CONTAINER,
                            new ContainerAcquiredWhileRunningTransition())
                    .addTransition(RMContainerState.RUNNING, RMContainerState.RUNNING,
                            RMContainerEventType.NM_DONE_CHANGE_RESOURCE,
                            new NMReportedContainerChangeIsDoneTransition())

                    // Transitions from COMPLETED state
                    .addTransition(RMContainerState.COMPLETED, RMContainerState.COMPLETED,
                            EnumSet.of(RMContainerEventType.EXPIRE, RMContainerEventType.RELEASED,
                                    RMContainerEventType.KILL))

                    // Transitions from EXPIRED state
                    .addTransition(RMContainerState.EXPIRED, RMContainerState.EXPIRED,
                            EnumSet.of(RMContainerEventType.RELEASED, RMContainerEventType.KILL))

                    // Transitions from RELEASED state
                    .addTransition(RMContainerState.RELEASED, RMContainerState.RELEASED,
                            EnumSet.of(RMContainerEventType.EXPIRE, RMContainerEventType.RELEASED,
                                    RMContainerEventType.KILL, RMContainerEventType.FINISHED))

                    // Transitions from KILLED state
                    .addTransition(RMContainerState.KILLED, RMContainerState.KILLED,
                            EnumSet.of(RMContainerEventType.EXPIRE, RMContainerEventType.RELEASED,
                                    RMContainerEventType.KILL, RMContainerEventType.FINISHED))

                    // create the topology tables
                    .installTopology();

    private final StateMachine<RMContainerState, RMContainerEventType, RMContainerEvent> stateMachine;
    private final ReadLock readLock;
    private final WriteLock writeLock;
    private final ApplicationAttemptId appAttemptId;
    private final NodeId nodeId;
    private final RMContext rmContext;
    private final EventHandler eventHandler;
    private final ContainerAllocationExpirer containerAllocationExpirer;
    private final String user;
    private final String nodeLabelExpression;

    private volatile Container container;
    private Resource reservedResource;
    private NodeId reservedNode;
    private SchedulerRequestKey reservedSchedulerKey;
    private long creationTime;
    private long finishTime;
    private ContainerStatus finishedStatus;
    private boolean isAMContainer;
    private ContainerRequest containerRequestForRecovery;

    // Only used for container resource increase and decrease. This is the
    // resource to rollback to should container resource increase token expires.
    private Resource lastConfirmedResource;
    private volatile String queueName;

    private boolean isExternallyAllocated;
    private SchedulerRequestKey allocatedSchedulerKey;

    private volatile Set<String> allocationTags = null;

    public RMContainerImpl(Container container, SchedulerRequestKey schedulerKey, ApplicationAttemptId appAttemptId,
            NodeId nodeId, String user, RMContext rmContext) {
        this(container, schedulerKey, appAttemptId, nodeId, user, rmContext, System.currentTimeMillis(), "");
    }

    public RMContainerImpl(Container container, SchedulerRequestKey schedulerKey, ApplicationAttemptId appAttemptId,
            NodeId nodeId, String user, RMContext rmContext, boolean isExternallyAllocated) {
        this(container, schedulerKey, appAttemptId, nodeId, user, rmContext, System.currentTimeMillis(), "",
                isExternallyAllocated);
    }

    private boolean saveNonAMContainerMetaInfo;

    public RMContainerImpl(Container container, SchedulerRequestKey schedulerKey, ApplicationAttemptId appAttemptId,
            NodeId nodeId, String user, RMContext rmContext, String nodeLabelExpression) {
        this(container, schedulerKey, appAttemptId, nodeId, user, rmContext, System.currentTimeMillis(),
                nodeLabelExpression);
    }

    public RMContainerImpl(Container container, SchedulerRequestKey schedulerKey, ApplicationAttemptId appAttemptId,
            NodeId nodeId, String user, RMContext rmContext, long creationTime, String nodeLabelExpression) {
        this(container, schedulerKey, appAttemptId, nodeId, user, rmContext, creationTime, nodeLabelExpression,
                false);
    }

    public RMContainerImpl(Container container, SchedulerRequestKey schedulerKey, ApplicationAttemptId appAttemptId,
            NodeId nodeId, String user, RMContext rmContext, long creationTime, String nodeLabelExpression,
            boolean isExternallyAllocated) {
        this.stateMachine = stateMachineFactory.make(this);
        this.nodeId = nodeId;
        this.container = container;
        this.allocatedSchedulerKey = schedulerKey;
        this.appAttemptId = appAttemptId;
        this.user = user;
        this.creationTime = creationTime;
        this.rmContext = rmContext;
        this.eventHandler = rmContext.getDispatcher().getEventHandler();
        this.containerAllocationExpirer = rmContext.getContainerAllocationExpirer();
        this.isAMContainer = false;
        this.nodeLabelExpression = nodeLabelExpression;
        this.lastConfirmedResource = container.getResource();
        this.isExternallyAllocated = isExternallyAllocated;

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        this.readLock = lock.readLock();
        this.writeLock = lock.writeLock();

        saveNonAMContainerMetaInfo = shouldPublishNonAMContainerEventstoATS(rmContext);

        if (container.getId() != null) {
            rmContext.getRMApplicationHistoryWriter().containerStarted(this);
        }

        if (this.container != null) {
            this.allocationTags = this.container.getAllocationTags();
        }
    }

    @Override
    public ContainerId getContainerId() {
        return this.container.getId();
    }

    @Override
    public ApplicationAttemptId getApplicationAttemptId() {
        return this.appAttemptId;
    }

    @Override
    public Container getContainer() {
        return this.container;
    }

    public void setContainer(Container container) {
        this.container = container;
    }

    @Override
    public RMContainerState getState() {
        this.readLock.lock();
        try {
            return this.stateMachine.getCurrentState();
        } finally {
            this.readLock.unlock();
        }
    }

    @Override
    public Resource getReservedResource() {
        return reservedResource;
    }

    @Override
    public NodeId getReservedNode() {
        return reservedNode;
    }

    @Override
    public SchedulerRequestKey getReservedSchedulerKey() {
        return reservedSchedulerKey;
    }

    @Override
    public Resource getAllocatedResource() {
        try {
            readLock.lock();
            return container.getResource();
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Resource getLastConfirmedResource() {
        try {
            readLock.lock();
            return this.lastConfirmedResource;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public NodeId getAllocatedNode() {
        return container.getNodeId();
    }

    @Override
    public SchedulerRequestKey getAllocatedSchedulerKey() {
        return allocatedSchedulerKey;
    }

    @Override
    public Priority getAllocatedPriority() {
        return container.getPriority();
    }

    @Override
    public long getCreationTime() {
        return creationTime;
    }

    @Override
    public long getFinishTime() {
        try {
            readLock.lock();
            return finishTime;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public String getDiagnosticsInfo() {
        try {
            readLock.lock();
            if (finishedStatus != null) {
                return finishedStatus.getDiagnostics();
            } else {
                return null;
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public String getLogURL() {
        try {
            readLock.lock();
            StringBuilder logURL = new StringBuilder();
            logURL.append(WebAppUtils.getHttpSchemePrefix(rmContext.getYarnConfiguration()));
            logURL.append(WebAppUtils.getRunningLogURL(container.getNodeHttpAddress(), getContainerId().toString(),
                    user));
            return logURL.toString();
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public int getContainerExitStatus() {
        try {
            readLock.lock();
            if (finishedStatus != null) {
                return finishedStatus.getExitStatus();
            } else {
                return 0;
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public ContainerState getContainerState() {
        try {
            readLock.lock();
            if (finishedStatus != null) {
                return finishedStatus.getState();
            } else {
                return ContainerState.RUNNING;
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public ContainerRequest getContainerRequest() {
        try {
            readLock.lock();
            return containerRequestForRecovery;
        } finally {
            readLock.unlock();
        }
    }

    public void setContainerRequest(ContainerRequest request) {
        writeLock.lock();
        try {
            this.containerRequestForRecovery = request;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public String toString() {
        return getContainerId().toString();
    }

    @Override
    public boolean isAMContainer() {
        try {
            readLock.lock();
            return isAMContainer;
        } finally {
            readLock.unlock();
        }
    }

    public void setAMContainer(boolean isAMContainer) {
        try {
            writeLock.lock();
            this.isAMContainer = isAMContainer;
        } finally {
            writeLock.unlock();
        }

        // Even if saveNonAMContainerMetaInfo is not true, the AM container's system
        // metrics still need to be saved so that the AM's logs can be accessed.
        // This call to getSystemMetricsPublisher().containerCreated() is mutually
        // exclusive with the one in the RMContainerImpl constructor.
        if (!saveNonAMContainerMetaInfo && this.isAMContainer) {
            rmContext.getSystemMetricsPublisher().containerCreated(this, this.creationTime);
        }
    }

    @Override
    public void handle(RMContainerEvent event) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Processing " + event.getContainerId() + " of type " + event.getType());
        }
        try {
            writeLock.lock();
            RMContainerState oldState = getState();
            try {
                stateMachine.doTransition(event.getType(), event);
            } catch (InvalidStateTransitionException e) {
                LOG.error("Can't handle this event at current state", e);
                LOG.error("Invalid event " + event.getType() + " on container " + this.getContainerId());
            }
            if (oldState != getState()) {
                LOG.info(event.getContainerId() + " Container Transitioned from " + oldState + " to " + getState());
            }
        }

        finally {
            writeLock.unlock();
        }
    }

    public boolean completed() {
        return finishedStatus != null;
    }

    @Override
    public NodeId getNodeId() {
        return nodeId;
    }

    @Override
    public Set<String> getAllocationTags() {
        return allocationTags;
    }

    public void setAllocationTags(Set<String> tags) {
        this.allocationTags = tags;
    }

    private static class BaseTransition implements SingleArcTransition<RMContainerImpl, RMContainerEvent> {

        @Override
        public void transition(RMContainerImpl cont, RMContainerEvent event) {

        }
    }

    private static final class ContainerRecoveredTransition
            implements MultipleArcTransition<RMContainerImpl, RMContainerEvent, RMContainerState> {
        @Override
        public RMContainerState transition(RMContainerImpl container, RMContainerEvent event) {
            NMContainerStatus report = ((RMContainerRecoverEvent) event).getContainerReport();
            // Set the allocation tags from the NMContainerStatus
            container.setAllocationTags(report.getAllocationTags());
            // Notify AllocationTagsManager
            container.rmContext.getAllocationTagsManager().addContainer(container.getNodeId(),
                    container.getContainerId(), container.getAllocationTags());

            if (report.getContainerState().equals(ContainerState.COMPLETE)) {
                ContainerStatus status = ContainerStatus.newInstance(report.getContainerId(),
                        report.getContainerState(), report.getDiagnostics(), report.getContainerExitStatus());
                new FinishedTransition().transition(container, new RMContainerFinishedEvent(
                        container.getContainerId(), status, RMContainerEventType.FINISHED));
                return RMContainerState.COMPLETED;
            } else if (report.getContainerState().equals(ContainerState.RUNNING)) {
                // Tell the app
                container.eventHandler.handle(new RMAppRunningOnNodeEvent(
                        container.getApplicationAttemptId().getApplicationId(), container.nodeId));
                return RMContainerState.RUNNING;
            } else {
                // This can never happen.
                LOG.warn("RMContainer received unexpected recover event with container" + " state "
                        + report.getContainerState() + " while recovering.");
                return RMContainerState.RUNNING;
            }
        }
    }

    private static final class ContainerReservedTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            RMContainerReservedEvent e = (RMContainerReservedEvent) event;
            container.reservedResource = e.getReservedResource();
            container.reservedNode = e.getReservedNode();
            container.reservedSchedulerKey = e.getReservedSchedulerKey();

            Container c = container.getContainer();
            if (c != null) {
                c.setNodeId(container.reservedNode);
            }
        }
    }

    private static final class ContainerStartedTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            // Notify AllocationTagsManager
            container.rmContext.getAllocationTagsManager().addContainer(container.getNodeId(),
                    container.getContainerId(), container.getAllocationTags());

            container.eventHandler.handle(
                    new RMAppAttemptEvent(container.appAttemptId, RMAppAttemptEventType.CONTAINER_ALLOCATED));

            publishNonAMContainerEventstoATS(container);

        }
    }

    private static final class AcquiredTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            // Clear ResourceRequest stored in RMContainer, we don't need to remember
            // this anymore.
            container.setContainerRequest(null);

            // Register with containerAllocationExpirer.
            container.containerAllocationExpirer.register(new AllocationExpirationInfo(container.getContainerId()));

            // Tell the app
            container.eventHandler.handle(new RMAppRunningOnNodeEvent(
                    container.getApplicationAttemptId().getApplicationId(), container.nodeId));

            publishNonAMContainerEventstoATS(container);
        }
    }

    private static final class ContainerAcquiredWhileRunningTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            RMContainerUpdatesAcquiredEvent acquiredEvent = (RMContainerUpdatesAcquiredEvent) event;
            if (acquiredEvent.isIncreasedContainer()) {
                // If container is increased but not started by AM, we will start
                // containerAllocationExpirer for this container in this transition. 
                container.containerAllocationExpirer
                        .register(new AllocationExpirationInfo(event.getContainerId(), true));
            }
        }
    }

    private static final class NMReportedContainerChangeIsDoneTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            RMContainerNMDoneChangeResourceEvent nmDoneChangeResourceEvent = (RMContainerNMDoneChangeResourceEvent) event;
            Resource rmContainerResource = container.getAllocatedResource();
            Resource nmContainerResource = nmDoneChangeResourceEvent.getNMContainerResource();

            if (Resources.equals(rmContainerResource, nmContainerResource)) {
                // If rmContainerResource == nmContainerResource, the resource
                // increase is confirmed.
                // In this case:
                //    - Set the lastConfirmedResource as nmContainerResource
                //    - Unregister the allocation expirer
                container.lastConfirmedResource = nmContainerResource;
                container.containerAllocationExpirer
                        .unregister(new AllocationExpirationInfo(event.getContainerId()));
            } else if (Resources.fitsIn(rmContainerResource, nmContainerResource)) {
                // If rmContainerResource < nmContainerResource, this is caused by the
                // following sequence:
                //   1. AM asks for increase from 1G to 5G, and RM approves it
                //   2. AM acquires the increase token and increases on NM
                //   3. Before NM reports 5G to RM to confirm the increase, AM sends
                //      a decrease request to 4G, and RM approves it
                //   4. When NM reports 5G to RM, RM now sees its own allocation as 4G
                // In this cases:
                //    - Set the lastConfirmedResource as rmContainerResource
                //    - Unregister the allocation expirer
                //    - Notify NM to reduce its resource to rmContainerResource
                container.lastConfirmedResource = rmContainerResource;
                container.containerAllocationExpirer
                        .unregister(new AllocationExpirationInfo(event.getContainerId()));
                container.eventHandler.handle(new RMNodeUpdateContainerEvent(container.nodeId,
                        Collections.singletonMap(container.getContainer(), ContainerUpdateType.DECREASE_RESOURCE)));
            } else if (Resources.fitsIn(nmContainerResource, rmContainerResource)) {
                // If nmContainerResource < rmContainerResource, this is caused by the
                // following sequence:
                //    1. AM asks for increase from 1G to 2G, and RM approves it
                //    2. AM asks for increase from 2G to 4G, and RM approves it
                //    3. AM only uses the 2G token to increase on NM, but never uses the
                //       4G token
                //    4. NM reports 2G to RM, but RM sees its own allocation as 4G
                // In this case:
                //    - Set the lastConfirmedResource as the maximum of
                //      nmContainerResource and lastConfirmedResource
                //    - Do NOT unregister the allocation expirer
                // When the increase allocation expires, resource will be rolled back to
                // the last confirmed resource.
                container.lastConfirmedResource = Resources.componentwiseMax(nmContainerResource,
                        container.lastConfirmedResource);
            } else {
                // Something wrong happened, kill the container
                LOG.warn("Something wrong happened, container size reported by NM"
                        + " is not expected, ContainerID=" + container.getContainerId() + " rm-size-resource:"
                        + rmContainerResource + " nm-size-resource:" + nmContainerResource);
                container.eventHandler
                        .handle(new RMNodeCleanContainerEvent(container.nodeId, container.getContainerId()));

            }
        }
    }

    private static class FinishedTransition extends BaseTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {
            RMContainerFinishedEvent finishedEvent = (RMContainerFinishedEvent) event;

            container.finishTime = System.currentTimeMillis();
            container.finishedStatus = finishedEvent.getRemoteContainerStatus();
            // Inform AppAttempt
            // container.getContainer() can return null when a RMContainer is a
            // reserved container
            updateAttemptMetrics(container);

            container.eventHandler.handle(new RMAppAttemptContainerFinishedEvent(container.appAttemptId,
                    finishedEvent.getRemoteContainerStatus(), container.getAllocatedNode()));

            container.rmContext.getRMApplicationHistoryWriter().containerFinished(container);

            boolean saveNonAMContainerMetaInfo = shouldPublishNonAMContainerEventstoATS(container.rmContext);

            if (saveNonAMContainerMetaInfo || container.isAMContainer()) {
                container.rmContext.getSystemMetricsPublisher().containerFinished(container, container.finishTime);
            }
        }

        private static void updateAttemptMetrics(RMContainerImpl container) {
            Resource resource = container.getContainer().getResource();
            RMApp app = container.rmContext.getRMApps().get(container.getApplicationAttemptId().getApplicationId());
            if (app != null) {
                RMAppAttempt rmAttempt = app.getCurrentAppAttempt();
                if (rmAttempt != null) {
                    long usedMillis = container.finishTime - container.creationTime;
                    rmAttempt.getRMAppAttemptMetrics().updateAggregateAppResourceUsage(resource, usedMillis);
                    // If this is a preempted container, update preemption metrics
                    if (ContainerExitStatus.PREEMPTED == container.finishedStatus.getExitStatus()) {
                        rmAttempt.getRMAppAttemptMetrics().updatePreemptionInfo(resource, container);
                        rmAttempt.getRMAppAttemptMetrics().updateAggregatePreemptedAppResourceUsage(resource,
                                usedMillis);
                    }
                }
            }
        }
    }

    private static boolean shouldPublishNonAMContainerEventstoATS(RMContext rmContext) {
        return rmContext.getYarnConfiguration().getBoolean(
                YarnConfiguration.APPLICATION_HISTORY_SAVE_NON_AM_CONTAINER_META_INFO,
                YarnConfiguration.DEFAULT_APPLICATION_HISTORY_SAVE_NON_AM_CONTAINER_META_INFO);
    }

    private static void publishNonAMContainerEventstoATS(RMContainerImpl rmContainer) {
        boolean saveNonAMContainerMetaInfo = shouldPublishNonAMContainerEventstoATS(rmContainer.rmContext);

        // If saveNonAMContainerMetaInfo is true, store system metrics for all
        // containers. If false, and if this container is marked as the AM, metrics
        // will still be published for this container, but that calculation happens
        // later.
        if (saveNonAMContainerMetaInfo && null != rmContainer.container.getId()) {
            rmContainer.rmContext.getSystemMetricsPublisher().containerCreated(rmContainer,
                    rmContainer.creationTime);
        }
    }

    private static final class KillTransition extends FinishedTransition {

        @Override
        public void transition(RMContainerImpl container, RMContainerEvent event) {

            // Unregister from containerAllocationExpirer.
            container.containerAllocationExpirer
                    .unregister(new AllocationExpirationInfo(container.getContainerId()));

            // Inform node
            container.eventHandler
                    .handle(new RMNodeCleanContainerEvent(container.nodeId, container.getContainerId()));

            // Inform appAttempt
            super.transition(container, event);
        }
    }

    @Override
    public ContainerReport createContainerReport() {
        this.readLock.lock();
        ContainerReport containerReport = null;
        try {
            containerReport = ContainerReport.newInstance(this.getContainerId(), this.getAllocatedResource(),
                    this.getAllocatedNode(), this.getAllocatedSchedulerKey().getPriority(), this.getCreationTime(),
                    this.getFinishTime(), this.getDiagnosticsInfo(), this.getLogURL(),
                    this.getContainerExitStatus(), this.getContainerState(), this.getNodeHttpAddress(),
                    this.getExecutionType());
        } finally {
            this.readLock.unlock();
        }
        return containerReport;
    }

    @Override
    public String getNodeHttpAddress() {
        try {
            readLock.lock();
            if (container.getNodeHttpAddress() != null) {
                StringBuilder httpAddress = new StringBuilder();
                httpAddress.append(WebAppUtils.getHttpSchemePrefix(rmContext.getYarnConfiguration()));
                httpAddress.append(container.getNodeHttpAddress());
                return httpAddress.toString();
            } else {
                return null;
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public String getNodeLabelExpression() {
        if (nodeLabelExpression == null) {
            return RMNodeLabelsManager.NO_LABEL;
        }
        return nodeLabelExpression;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof RMContainer) {
            if (null != getContainerId()) {
                return getContainerId().equals(((RMContainer) obj).getContainerId());
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        if (null != getContainerId()) {
            return getContainerId().hashCode();
        }
        return super.hashCode();
    }

    @Override
    public int compareTo(RMContainer o) {
        if (getContainerId() != null && o.getContainerId() != null) {
            return getContainerId().compareTo(o.getContainerId());
        }
        return -1;
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    @Override
    public String getQueueName() {
        return queueName;
    }

    @Override
    public ExecutionType getExecutionType() {
        return container.getExecutionType();
    }

    @Override
    public boolean isRemotelyAllocated() {
        return isExternallyAllocated;
    }

    @Override
    public Resource getAllocatedOrReservedResource() {
        try {
            readLock.lock();
            if (getState().equals(RMContainerState.RESERVED)) {
                return getReservedResource();
            } else {
                return getAllocatedResource();
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void setContainerId(ContainerId containerId) {
        // In some cases, for example, global scheduling. It is possible that
        // container created without container-id assigned, so we will publish
        // container creation event to timeline service when id assigned.
        container.setId(containerId);

        if (containerId != null) {
            rmContext.getRMApplicationHistoryWriter().containerStarted(this);
        }
    }
}