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/05/31 20:12:43 UTC

[12/50] [abbrv] ambari git commit: AMBARI-20958 - Host Version on Finalization Must Be Scoped Correctly Based on Upgrade Type (jonathanhurley)

AMBARI-20958 - Host Version on Finalization Must Be Scoped Correctly Based on Upgrade Type (jonathanhurley)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a2632675
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a2632675
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a2632675

Branch: refs/heads/trunk
Commit: a2632675a37223d66dc2cb2edb3138467d74fb5b
Parents: aaa821c
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Mon May 8 14:46:53 2017 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Tue May 9 16:49:23 2017 -0400

----------------------------------------------------------------------
 .../ClusterStackVersionResourceProvider.java    |  15 --
 .../VersionDefinitionResourceProvider.java      |   4 +-
 .../upgrade/HostVersionOutOfSyncListener.java   |   6 +-
 .../listeners/upgrade/StackVersionListener.java | 154 +++++++++----------
 .../server/orm/dao/HostComponentStateDAO.java   |  31 ----
 .../ambari/server/orm/dao/HostVersionDAO.java   | 147 +++---------------
 .../server/orm/dao/RepositoryVersionDAO.java    |   9 +-
 .../server/orm/entities/HostVersionEntity.java  |  35 +++--
 .../orm/entities/RepositoryVersionEntity.java   |  72 +++++----
 .../serveraction/ServerActionExecutor.java      |   3 +-
 .../upgrades/FinalizeUpgradeAction.java         | 106 ++++---------
 .../upgrades/UpdateDesiredStackAction.java      |  32 +++-
 .../org/apache/ambari/server/state/Cluster.java |  16 --
 .../server/state/ServiceComponentHost.java      |  18 ++-
 .../ambari/server/state/UpgradeContext.java     |  37 ++++-
 .../ambari/server/state/UpgradeHelper.java      |   2 +-
 .../server/state/cluster/ClusterImpl.java       |  63 --------
 .../svccomphost/ServiceComponentHostImpl.java   |  82 +++++-----
 .../ServiceComponentHostSummary.java            | 104 ++++++-------
 .../main/resources/Ambari-DDL-Derby-CREATE.sql  |   2 +-
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   2 +-
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   2 +-
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   2 +-
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |   2 +-
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |   2 +-
 .../internal/UpgradeResourceProviderTest.java   |  19 +--
 .../upgrade/StackVersionListenerTest.java       |  37 +++--
 .../server/state/cluster/ClusterTest.java       | 112 +++-----------
 28 files changed, 436 insertions(+), 680 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
index 774ba0c..1e49eb2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
@@ -681,21 +681,6 @@ public class ClusterStackVersionResourceProvider extends AbstractControllerResou
   }
 
   /**
-   * Updates the version states.  Transactional to ensure only one transaction for all updates
-   * @param clusterId the cluster
-   * @param current   the repository that is current for the cluster
-   * @param target    the target repository
-   */
-  @Transactional
-  protected void updateVersionStates(Long clusterId, RepositoryVersionEntity current,
-      RepositoryVersionEntity target) {
-
-    hostComponentStateDAO.updateVersions(target.getVersion());
-    hostVersionDAO.updateVersions(target, current);
-//    clusterVersionDAO.updateVersions(clusterId, target, current);
-  }
-
-  /**
    * Additional check over {@link VersionUtils#compareVersions(String, String)} that
    * compares build numbers
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
index 5f12e52..e41e3da 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
@@ -355,7 +355,7 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
     Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
 
     if (propertyMaps.isEmpty()) {
-      List<RepositoryVersionEntity> versions = s_repoVersionDAO.findAllDefinitions();
+      List<RepositoryVersionEntity> versions = s_repoVersionDAO.findRepositoriesWithVersionDefinitions();
 
       for (RepositoryVersionEntity entity : versions) {
         results.add(toResource(entity, requestPropertyIds));
@@ -393,7 +393,7 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
               results.add(res);
             }
           } else {
-            List<RepositoryVersionEntity> versions = s_repoVersionDAO.findAllDefinitions();
+            List<RepositoryVersionEntity> versions = s_repoVersionDAO.findRepositoriesWithVersionDefinitions();
 
             for (RepositoryVersionEntity entity : versions) {
               results.add(toResource(entity, requestPropertyIds));

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
index c0a074f..aa665a7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
@@ -24,6 +24,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.locks.Lock;
 
+import org.apache.ambari.annotations.Experimental;
+import org.apache.ambari.annotations.ExperimentalFeature;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -283,7 +285,9 @@ public class HostVersionOutOfSyncListener {
       LOG.debug(event.toString());
     }
 
-    List<RepositoryVersionEntity> repos = repositoryVersionDAO.get().findAllDefinitions();
+    // create host version entries for every repository
+    @Experimental(feature=ExperimentalFeature.PATCH_UPGRADES, comment="Eventually take into account deleted repositories")
+    List<RepositoryVersionEntity> repos = repositoryVersionDAO.get().findAll();
 
     for (String hostName : event.getHostNames()) {
       HostEntity hostEntity = hostDAO.get().findByName(hostName);

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
index 1cedea8..1326154 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
@@ -19,13 +19,11 @@ package org.apache.ambari.server.events.listeners.upgrade;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
-import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.events.HostComponentVersionAdvertisedEvent;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.State;
@@ -36,14 +34,13 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
 /**
  * The {@link StackVersionListener} class handles the propagation of versions
  * advertised by the {@link org.apache.ambari.server.state.ServiceComponentHost}
  * that bubble up to the
- * {@link org.apache.ambari.server.orm.entities.HostVersionEntity}
+ * {@link org.apache.ambari.server.orm.entities.HostVersionEntity}.
  */
 @Singleton
 @EagerSingleton
@@ -57,9 +54,6 @@ public class StackVersionListener {
   @Inject
   private RepositoryVersionDAO repositoryVersionDAO;
 
-  @Inject
-  Provider<AmbariMetaInfo> ambariMetaInfo;
-
   /**
    * Constructor.
    *
@@ -101,27 +95,27 @@ public class StackVersionListener {
 
     // Update host component version value if needed
     try {
-      AmbariMetaInfo metaInfo = ambariMetaInfo.get();
-      ComponentInfo componentInfo = metaInfo.getComponent(cluster.getDesiredStackVersion().getStackName(),
-      cluster.getDesiredStackVersion().getStackVersion(), sch.getServiceName(), sch.getServiceComponentName());
-      ServiceComponent sc = cluster.getService(sch.getServiceName()).getServiceComponent(sch.getServiceComponentName());
-
-      if (componentInfo.isVersionAdvertised() && StringUtils.isNotBlank(newVersion)
-          && !UNKNOWN_VERSION.equalsIgnoreCase(newVersion)) {
-        processComponentAdvertisedVersion(cluster, sch, newVersion, sc);
-      } else if(!sc.isVersionAdvertised() && StringUtils.isNotBlank(newVersion)
-          && !UNKNOWN_VERSION.equalsIgnoreCase(newVersion)) {
-        LOG.debug("ServiceComponent {} doesn't advertise version, " +
-                "however ServiceHostComponent {} on host {} advertised version as {}. Skipping version update",
-            sc.getName(), sch.getServiceComponentName(), sch.getHostName(), newVersion);
-      } else {
-        if (UNKNOWN_VERSION.equals(sc.getDesiredVersion())) {
-          processUnknownDesiredVersion(cluster, sc, sch, newVersion);
-        } else {
-          processComponentAdvertisedVersion(cluster, sch, newVersion, sc);
+      ServiceComponent sc = cluster.getService(sch.getServiceName()).getServiceComponent(
+          sch.getServiceComponentName());
+
+      // not advertising a version, do nothing
+      if (!sc.isVersionAdvertised()) {
+        // that's odd; a version came back - log it and still do nothing
+        if (!StringUtils.equalsIgnoreCase(UNKNOWN_VERSION, newVersion)) {
+          LOG.debug(
+              "ServiceComponent {} doesn't advertise version, however ServiceHostComponent {} on host {} advertised version as {}. Skipping version update",
+              sc.getName(), sch.getServiceComponentName(), sch.getHostName(), newVersion);
         }
+        return;
       }
 
+      // proces the UNKNOWN version
+      if (StringUtils.equalsIgnoreCase(UNKNOWN_VERSION, newVersion)) {
+        processUnknownDesiredVersion(cluster, sc, sch, newVersion);
+        return;
+      }
+
+      processComponentAdvertisedVersion(cluster, sc, sch, newVersion);
     } catch (Exception e) {
       LOG.error(
           "Unable to propagate version for ServiceHostComponent on component: {}, host: {}. Error: {}",
@@ -129,46 +123,67 @@ public class StackVersionListener {
     }
   }
 
+
   /**
-   * Update host component version
-   * or
-   * Bootstrap cluster/repo version when version is reported for the first time
-   * @param cluster target cluster
-   * @param sch target host component
-   * @param newVersion advertised version
-   * @param sc target service component
+   * Updates the version and {@link UpgradeState} for the specified
+   * {@link ServiceComponentHost} if necessary. If the version or the upgrade
+   * state changes, then this method will call
+   * {@link ServiceComponentHost#recalculateHostVersionState()} in order to
+   * ensure that the host version state is properly updated.
+   * <p/>
+   *
+   *
+   * @param cluster
+   * @param sc
+   * @param sch
+   * @param newVersion
    * @throws AmbariException
    */
-  private void processComponentAdvertisedVersion(Cluster cluster, ServiceComponentHost sch, String newVersion, ServiceComponent sc) throws AmbariException {
+  private void processComponentAdvertisedVersion(Cluster cluster, ServiceComponent sc,
+      ServiceComponentHost sch, String newVersion) throws AmbariException {
     if (StringUtils.isBlank(newVersion)) {
       return;
     }
 
     String previousVersion = sch.getVersion();
-    if (previousVersion == null || UNKNOWN_VERSION.equalsIgnoreCase(previousVersion)) {
+    String desiredVersion = sc.getDesiredVersion();
+    UpgradeState upgradeState = sch.getUpgradeState();
+
+    boolean versionIsCorrect = StringUtils.equals(desiredVersion, newVersion);
+
+    // update the SCH to the new version reported only if it changed
+    if (!StringUtils.equals(previousVersion, newVersion)) {
+      sch.setVersion(newVersion);
+    }
+
+    if (previousVersion == null || StringUtils.equalsIgnoreCase(UNKNOWN_VERSION, previousVersion)) {
       // value may be "UNKNOWN" when upgrading from older Ambari versions
       // or if host component reports it's version for the first time
       sch.setUpgradeState(UpgradeState.NONE);
-      sch.setVersion(newVersion);
-      bootstrapVersion(cluster, sch);
-    } else if (!StringUtils.equals(previousVersion, newVersion)) {
-      processComponentVersionChange(cluster, sc, sch, newVersion);
+      sch.recalculateHostVersionState();
+    } else {
+      if (versionIsCorrect) {
+        boolean isUpgradeInProgressForThisComponent = null != cluster.getUpgradeInProgress()
+            && upgradeState != UpgradeState.NONE;
+
+        if (isUpgradeInProgressForThisComponent) {
+          setUpgradeStateAndRecalculateHostVersions(sch, UpgradeState.COMPLETE);
+        } else {
+          // no upgrade in progress for this component, then this should always
+          // be NONE
+          setUpgradeStateAndRecalculateHostVersions(sch, UpgradeState.NONE);
+        }
+      } else {
+        // if the versions don't match for any reason, regardless of upgrade
+        // state, then VERSION_MISMATCH it
+        setUpgradeStateAndRecalculateHostVersions(sch, UpgradeState.VERSION_MISMATCH);
+      }
     }
 
     sc.updateRepositoryState(newVersion);
   }
 
   /**
-   * Bootstrap cluster/repo version when version is reported for the first time
-   * @param cluster target cluster
-   * @param sch target host component
-   * @throws AmbariException
-   */
-  private void bootstrapVersion(Cluster cluster, ServiceComponentHost sch) throws AmbariException {
-    sch.recalculateHostVersionState();
-  }
-
-  /**
    * Possible situation after upgrade from older Ambari version. Just use
    * reported component version as desired version
    * @param cluster target cluster
@@ -181,40 +196,23 @@ public class StackVersionListener {
                                             String newVersion) throws AmbariException {
     sch.setUpgradeState(UpgradeState.NONE);
     sch.setVersion(newVersion);
-    bootstrapVersion(cluster, sch);
+    sch.recalculateHostVersionState();
   }
 
   /**
-   * Focuses on cases when host component version really changed
-   * @param cluster target cluster
-   * @param sc target service component
-   * @param sch target host component
-   * @param newVersion advertised version
+   * @param sch
+   * @param upgradeState
+   * @throws AmbariException
    */
-  private void processComponentVersionChange(Cluster cluster, ServiceComponent sc,
-                                             ServiceComponentHost sch,
-                                             String newVersion) {
-    String desiredVersion = sc.getDesiredVersion();
-    UpgradeState upgradeState = sch.getUpgradeState();
-    if (upgradeState == UpgradeState.IN_PROGRESS) {
-      // Component status update is received during upgrade process
-      if (desiredVersion.equals(newVersion)) {
-        // Component upgrade confirmed
-        sch.setUpgradeState(UpgradeState.COMPLETE);
-      } else { // Unexpected (wrong) version received
-        // Even during failed upgrade, we should not receive wrong version
-        // That's why mark as VERSION_MISMATCH
-        sch.setUpgradeState(UpgradeState.VERSION_MISMATCH);
-      }
-    } else if (upgradeState == UpgradeState.VERSION_MISMATCH && desiredVersion.equals(newVersion)) {
-      if (cluster.getUpgradeInProgress() != null) {
-        sch.setUpgradeState(UpgradeState.COMPLETE);
-      } else {
-        sch.setUpgradeState(UpgradeState.NONE);
-      }
-    } else { // No upgrade in progress, unexpected version change
-      sch.setUpgradeState(UpgradeState.VERSION_MISMATCH);
+  private void setUpgradeStateAndRecalculateHostVersions(ServiceComponentHost sch,
+      UpgradeState upgradeState) throws AmbariException {
+
+    if (sch.getUpgradeState() == upgradeState) {
+      return;
     }
-    sch.setVersion(newVersion);
+
+    // if the upgrade state changes, then also recalculate host versions
+    sch.setUpgradeState(upgradeState);
+    sch.recalculateHostVersionState();
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
index 6174912..935db28 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.orm.dao;
 
-import java.util.Arrays;
 import java.util.List;
 
 import javax.persistence.EntityManager;
@@ -27,7 +26,6 @@ import javax.persistence.TypedQuery;
 
 import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
-import org.apache.ambari.server.state.UpgradeState;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -180,35 +178,6 @@ public class HostComponentStateDAO {
   }
 
   /**
-   * Marks hosts components to the specified version that are NOT already set or "UNKNOWN".
-   * Also marks all host components as not being in an upgrade state.  This method
-   * invokes {@code clear()} on the entity manager to force entities to be refreshed.
-   *
-   * @param version the version
-   */
-  @Transactional
-  public void updateVersions(String version) {
-    EntityManager em = entityManagerProvider.get();
-
-    // !!! first the version
-    StringBuilder sb = new StringBuilder("UPDATE HostComponentStateEntity hostComponent");
-    sb.append(" SET hostComponent.version = ?1 ");
-    sb.append(" WHERE hostComponent.version NOT IN ?2");
-
-    TypedQuery<Long> query = em.createQuery(sb.toString(), Long.class);
-    daoUtils.executeUpdate(query, version, Arrays.asList(version, "UNKNOWN"));
-
-    // !!! now the upgrade state
-    sb = new StringBuilder("UPDATE HostComponentStateEntity hostComponent");
-    sb.append(" SET hostComponent.upgradeState = ?1");
-
-    query = em.createQuery(sb.toString(), Long.class);
-    daoUtils.executeUpdate(query, UpgradeState.NONE);
-
-    em.clear();
-  }
-
-  /**
    * @param serviceName
    * @param componentName
    * @param version

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
index d367aa0..cffb599 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
@@ -22,11 +22,10 @@ import java.util.Collection;
 import java.util.List;
 
 import javax.persistence.EntityManager;
-import javax.persistence.NoResultException;
-import javax.persistence.NonUniqueResultException;
 import javax.persistence.TypedQuery;
 
 import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.RepositoryVersionState;
@@ -132,7 +131,6 @@ public class HostVersionDAO extends CrudDAO<HostVersionEntity, Long> {
 
   /**
    * Retrieve all of the host versions for the given cluster name, host name, and state. <br/>
-   * Consider using faster method: {@link HostVersionDAO#findByClusterHostAndState(long, long, org.apache.ambari.server.state.RepositoryVersionState)}
    * @param clusterName Cluster name
    * @param hostName FQDN of host
    * @param state repository version state
@@ -150,77 +148,6 @@ public class HostVersionDAO extends CrudDAO<HostVersionEntity, Long> {
   }
 
   /**
-   * Faster version of {@link HostVersionDAO#findByClusterHostAndState(java.lang.String, java.lang.String, org.apache.ambari.server.state.RepositoryVersionState)}
-   *
-   * @param clusterId Cluster ID
-   * @param hostId Host ID
-   * @param state repository version state
-   * @return Return all of the host versions that match the criteria.
-   */
-  @RequiresSession
-  public List<HostVersionEntity> findByClusterHostAndState(long clusterId, long hostId, RepositoryVersionState state) {
-    TypedQuery<HostVersionEntity> query =
-        entityManagerProvider.get().createNamedQuery("hostVersionByClusterHostIdAndState", HostVersionEntity.class);
-
-    query.setParameter("clusterId", clusterId);
-    query.setParameter("hostId", hostId);
-    query.setParameter("state", state);
-
-    return daoUtils.selectList(query);
-  }
-
-  /**
-   * Retrieve the single host version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
-   * for the given host.
-   * Consider using faster method {@link HostVersionDAO#findByHostAndStateCurrent(long, long)}
-   *
-   * @param clusterName Cluster name
-   * @param hostName Host name
-   * @return Returns the single host version for this host whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, or {@code null} otherwise.
-   */
-  @RequiresSession
-  public HostVersionEntity findByHostAndStateCurrent(String clusterName, String hostName) {
-    try {
-      List<?> results = findByClusterHostAndState(clusterName, hostName, RepositoryVersionState.CURRENT);
-      if (results.isEmpty()) {
-        return null;
-      } else {
-        if (results.size() == 1) {
-          return (HostVersionEntity) results.get(0);
-        }
-      }
-      throw new NonUniqueResultException();
-    } catch (NoResultException ignored) {
-      return null;
-    }
-  }
-
-  /**
-   * Retrieve the single host version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
-   * for the given host.
-   * Faster version of {@link HostVersionDAO#findByHostAndStateCurrent(java.lang.String, java.lang.String)}
-   * @param clusterId Cluster ID
-   * @param hostId host ID
-   * @return Returns the single host version for this host whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, or {@code null} otherwise.
-   */
-  @RequiresSession
-  public HostVersionEntity findByHostAndStateCurrent(long clusterId, long hostId) {
-    try {
-      List<?> results = findByClusterHostAndState(clusterId, hostId, RepositoryVersionState.CURRENT);
-      if (results.isEmpty()) {
-        return null;
-      } else {
-        if (results.size() == 1) {
-          return (HostVersionEntity) results.get(0);
-        }
-      }
-      throw new NonUniqueResultException();
-    } catch (NoResultException ignored) {
-      return null;
-    }
-  }
-
-  /**
    * Retrieve the single host version for the given cluster, stack name, stack
    * version, and host name. <br/>
    * This query is slow and not suitable for frequent use. <br/>
@@ -253,29 +180,6 @@ public class HostVersionDAO extends CrudDAO<HostVersionEntity, Long> {
   }
 
   /**
-   * Optimized version of {@link HostVersionDAO#findByClusterStackVersionAndHost(java.lang.String, org.apache.ambari.server.state.StackId, java.lang.String, java.lang.String)}
-   * @param clusterId Id of cluster
-   * @param stackId Stack ID (e.g., HDP-2.2)
-   * @param version Stack version (e.g., 2.2.0.1-995)
-   * @param hostId Host Id
-   * @return Returns the single host version that matches the criteria.
-   */
-  @RequiresSession
-  public HostVersionEntity findByClusterStackVersionAndHost(long clusterId, StackId stackId, String version,
-                                                            long hostId) {
-    TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
-        .createNamedQuery("hostVersionByClusterStackVersionAndHostId", HostVersionEntity.class);
-
-    query.setParameter("clusterId", clusterId);
-    query.setParameter("stackName", stackId.getStackName());
-    query.setParameter("stackVersion", stackId.getStackVersion());
-    query.setParameter("version", version);
-    query.setParameter("hostId", hostId);
-
-    return daoUtils.selectSingle(query);
-  }
-
-  /**
    * Gets all host version entities assocaited with the specified cluster and
    * repository.
    *
@@ -319,41 +223,30 @@ public class HostVersionDAO extends CrudDAO<HostVersionEntity, Long> {
     return daoUtils.selectList(query);
   }
 
-  @Transactional
-  public void removeByHostName(String hostName) {
-    Collection<HostVersionEntity> hostVersions = findByHost(hostName);
-    this.remove(hostVersions);
-  }
-
   /**
-   * Updates the host versions existing CURRENT record to the INSTALLED, and the target
-   * becomes CURRENT.  This method invokes {@code clear()} on the entity manager to force entities to be refreshed.
-   * @param target    the repo version that all hosts to mark as CURRENT
-   * @param current   the repo version that all hosts marked as INSTALLED
+   * Gets the {@link HostVersionEntity} associted with the specified host and
+   * repository.
+   *
+   * @param host
+   * @param repositoryVersion
+   * @return
    */
-  @Transactional
-  public void updateVersions(RepositoryVersionEntity target, RepositoryVersionEntity current) {
-    // !!! first update target to be current
-    StringBuilder sb = new StringBuilder("UPDATE HostVersionEntity hve");
-    sb.append(" SET hve.state = ?1 ");
-    sb.append(" WHERE hve.repositoryVersion = ?2");
-
-    EntityManager em = entityManagerProvider.get();
-
-    TypedQuery<Long> query = em.createQuery(sb.toString(), Long.class);
-    daoUtils.executeUpdate(query, RepositoryVersionState.CURRENT, target);
+  @RequiresSession
+  public HostVersionEntity findHostVersionByHostAndRepository(HostEntity host,
+      RepositoryVersionEntity repositoryVersion) {
+    TypedQuery<HostVersionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "findByHostAndRepository", HostVersionEntity.class);
 
-    // !!! then move existing current to installed
-    sb = new StringBuilder("UPDATE HostVersionEntity hve");
-    sb.append(" SET hve.state = ?1 ");
-    sb.append(" WHERE hve.repositoryVersion = ?2");
-    sb.append(" AND hve.state = ?3");
+    query.setParameter("host", host);
+    query.setParameter("repositoryVersion", repositoryVersion);
 
-    query = em.createQuery(sb.toString(), Long.class);
-    daoUtils.executeUpdate(query, RepositoryVersionState.INSTALLED, current,
-        RepositoryVersionState.CURRENT);
+    return daoUtils.selectOne(query);
+  }
 
-    em.clear();
+  @Transactional
+  public void removeByHostName(String hostName) {
+    Collection<HostVersionEntity> hostVersions = findByHost(hostName);
+    this.remove(hostVersions);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RepositoryVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RepositoryVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RepositoryVersionDAO.java
index 26f96e8..a2472b6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RepositoryVersionDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/RepositoryVersionDAO.java
@@ -198,12 +198,15 @@ public class RepositoryVersionDAO extends CrudDAO<RepositoryVersionEntity, Long>
   }
 
   /**
-   * Retrieves repository version when they are loaded by a version definition file
+   * Retrieves repository version when they are loaded by a version definition
+   * file. This will not return all repositories - it will only return those
+   * which have a non-NULL VDF.
    *
-   * @return a list of entities, or an empty list when there are none
+   * @return a list of repositories created by VDF, or an empty list when there
+   *         are none.
    */
   @RequiresSession
-  public List<RepositoryVersionEntity> findAllDefinitions() {
+  public List<RepositoryVersionEntity> findRepositoriesWithVersionDefinitions() {
     final TypedQuery<RepositoryVersionEntity> query = entityManagerProvider.get().createNamedQuery(
         "repositoryVersionsFromDefinition", RepositoryVersionEntity.class);
     return daoUtils.selectList(query);

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
index 4bd6e9d..ee5f296 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
@@ -35,13 +35,19 @@ import javax.persistence.UniqueConstraint;
 
 import org.apache.ambari.server.state.RepositoryVersionState;
 
-@Table(name = "host_version", uniqueConstraints = @UniqueConstraint(name = "UQ_host_repo", columnNames = { "repo_version_id", "host_id" }))
 @Entity
-@TableGenerator(name = "host_version_id_generator",
-    table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
-    , pkColumnValue = "host_version_id_seq"
-    , initialValue = 0
-)
+@Table(
+    name = "host_version",
+    uniqueConstraints = @UniqueConstraint(
+        name = "UQ_host_repo",
+        columnNames = { "host_id", "repo_version_id" }))
+@TableGenerator(
+    name = "host_version_id_generator",
+    table = "ambari_sequences",
+    pkColumnName = "sequence_name",
+    valueColumnName = "sequence_value",
+    pkColumnValue = "host_version_id_seq",
+    initialValue = 0)
 @NamedQueries({
     @NamedQuery(name = "hostVersionByClusterAndStackAndVersion", query =
         "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN host.clusterEntities clusters " +
@@ -64,22 +70,19 @@ import org.apache.ambari.server.state.RepositoryVersionState;
             "WHERE clusters.clusterName=:clusterName AND hostVersion.repositoryVersion.stack.stackName=:stackName AND hostVersion.repositoryVersion.stack.stackVersion=:stackVersion AND hostVersion.repositoryVersion.version=:version AND " +
             "hostVersion.hostEntity.hostName=:hostName"),
 
-    @NamedQuery(name = "hostVersionByClusterHostIdAndState", query =
-        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN host.clusterEntities clusters " +
-            "WHERE clusters.clusterId=:clusterId AND hostVersion.hostId=:hostId AND hostVersion.state=:state"),
-
-    @NamedQuery(name = "hostVersionByClusterStackVersionAndHostId", query =
-        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN host.clusterEntities clusters " +
-        "WHERE hostVersion.hostId=:hostId AND clusters.clusterId=:clusterId AND hostVersion.repositoryVersion.stack.stackName=:stackName " +
-        "AND hostVersion.repositoryVersion.stack.stackVersion=:stackVersion AND hostVersion.repositoryVersion.version=:version"),
-
     @NamedQuery(
         name = "findHostVersionByClusterAndRepository",
         query = "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN host.clusterEntities clusters "
             + "WHERE clusters.clusterId = :clusterId AND hostVersion.repositoryVersion = :repositoryVersion"),
+
     @NamedQuery(
         name = "hostVersionByRepositoryAndStates",
-        query = "SELECT hostVersion FROM HostVersionEntity hostVersion WHERE hostVersion.repositoryVersion = :repositoryVersion AND hostVersion.state IN :states")
+        query = "SELECT hostVersion FROM HostVersionEntity hostVersion WHERE hostVersion.repositoryVersion = :repositoryVersion AND hostVersion.state IN :states"),
+
+    @NamedQuery(
+        name = "findByHostAndRepository",
+        query = "SELECT hostVersion FROM HostVersionEntity hostVersion WHERE hostVersion.hostEntity = :host AND hostVersion.repositoryVersion = :repositoryVersion")
+
 })
 public class HostVersionEntity {
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
index 7d6db2c..47abde4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
@@ -52,9 +52,11 @@ import org.apache.ambari.server.state.repository.Release;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Objects;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -275,37 +277,6 @@ public class RepositoryVersionEntity {
     this.type = type;
   }
 
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-
-    RepositoryVersionEntity that = (RepositoryVersionEntity) o;
-
-    if (id != null ? !id.equals(that.id) : that.id != null) {
-      return false;
-    }
-    if (stack != null ? !stack.equals(that.stack) : that.stack != null) {
-      return false;
-    }
-    if (version != null ? !version.equals(that.version) : that.version != null) {
-      return false;
-    }
-    if (displayName != null ? !displayName.equals(that.displayName) : that.displayName != null) {
-      return false;
-    }
-
-    if (operatingSystems != null ? !operatingSystems.equals(that.operatingSystems) : that.operatingSystems != null) {
-      return false;
-    }
-
-    return true;
-  }
-
   /**
    * @return the XML that is the basis for the version
    */
@@ -366,14 +337,41 @@ public class RepositoryVersionEntity {
     return versionDefinition;
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    RepositoryVersionEntity that = (RepositoryVersionEntity) o;
+    return new EqualsBuilder().append(id, that.id).append(stack, that.stack).append(version,
+        that.version).append(displayName, that.displayName).isEquals();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public int hashCode() {
-    int result = id != null ? id.hashCode() : 0;
-    result = 31 * result + (stack != null ? stack.hashCode() : 0);
-    result = 31 * result + (version != null ? version.hashCode() : 0);
-    result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
-    result = 31 * result + (operatingSystems != null ? operatingSystems.hashCode() : 0);
-    return result;
+    return Objects.hashCode(id, stack, version, displayName);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString(){
+    return Objects.toStringHelper(this)
+        .add("id", id)
+        .add("stack", stack)
+        .add("version", version).toString();
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/serveraction/ServerActionExecutor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/ServerActionExecutor.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/ServerActionExecutor.java
index 68124fc..50e3cfe 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/ServerActionExecutor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/ServerActionExecutor.java
@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.serveraction;
 
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -84,7 +83,7 @@ public class ServerActionExecutor {
    * requestSharedDataMap object
    */
   private final Map<Long, ConcurrentMap<String, Object>> requestSharedDataMap =
-    new HashMap<>();
+      new ConcurrentHashMap<>();
 
   /**
    * Database accessor to query and update the database of action commands.

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
index 7a39dcd..1b9fb23 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
@@ -38,7 +38,6 @@ import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
-import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
@@ -55,7 +54,6 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.UpgradeContext;
 import org.apache.ambari.server.state.UpgradeState;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostSummary;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.text.StrBuilder;
 
@@ -136,66 +134,49 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
       outSB.append(message).append(System.lineSeparator());
 
       Cluster cluster = upgradeContext.getCluster();
-      StackId clusterDesiredStackId = cluster.getDesiredStackVersion();
-      StackId clusterCurrentStackId = cluster.getCurrentStackVersion();
       String version = upgradeContext.getVersion();
       RepositoryVersionEntity repositoryVersion = upgradeContext.getTargetRepositoryVersion();
 
-      // for all hosts participating in this upgrade, validate their repo
-      // versions
+      // iterate through all host components and make sure that they are on the
+      // correct version; if they are not, then this will throw an exception
+      List<InfoTuple> errors = getHostComponentsWhichDidNotUpgrade(upgradeContext);
+      if (!errors.isEmpty()) {
+        StrBuilder messageBuff = new StrBuilder(String.format(
+            "The following %d host component(s) "
+                + "have not been upgraded to version %s. Please install and upgrade "
+                + "the Stack Version on those hosts and try again.\nHost components:",
+            errors.size(), version)).append(System.lineSeparator());
+
+        for (InfoTuple error : errors) {
+          messageBuff.append(String.format("%s on host %s\n", error.componentName, error.hostName));
+        }
+
+        throw new AmbariException(messageBuff.toString());
+      }
+
+      // for all hosts participating in this upgrade, update thei repository
+      // versions and upgrade state
       List<HostVersionEntity> hostVersions = hostVersionDAO.findHostVersionByClusterAndRepository(
           cluster.getClusterId(), repositoryVersion);
 
-      // Will include hosts whose state is INSTALLED
       Set<HostVersionEntity> hostVersionsAllowed = new HashSet<>();
       Set<String> hostsWithoutCorrectVersionState = new HashSet<>();
-      Set<String> hostsToUpdate = new HashSet<>();
 
-      // It is important to only iterate over the hosts with a version, as
-      // opposed to all hosts, since some hosts may only have components that do
-      // not advertise a version, such as AMBARI_METRICS.
+      // for every host version for this repository, determine if any didn't
+      // transition correctly
       for (HostVersionEntity hostVersion : hostVersions) {
-        boolean hostHasCorrectVersionState = false;
         RepositoryVersionState hostVersionState = hostVersion.getState();
         switch( hostVersionState ){
-          case CURRENT:{
-            // if the state is correct, then do nothing
-            hostHasCorrectVersionState = true;
-            break;
-          }
-          case NOT_REQUIRED:
-          case INSTALLED:{
-            // It is possible that the host version has a state of INSTALLED and it
-            // never changed if the host only has components that do not advertise a
-            // version.
-            HostEntity host = hostVersion.getHostEntity();
-
-            ServiceComponentHostSummary hostSummary = new ServiceComponentHostSummary(ambariMetaInfo,
-                host, clusterDesiredStackId);
-
-            // if all components have finished advertising their version, then
-            // this host can be considered upgraded
-            if (hostSummary.haveAllComponentsFinishedAdvertisingVersion()) {
-              // mark this as upgraded
-              hostHasCorrectVersionState = true;
-            } else {
-              hostsWithoutCorrectVersionState.add(hostVersion.getHostName());
-            }
-
+          case CURRENT:
+          case NOT_REQUIRED: {
+            hostVersionsAllowed.add(hostVersion);
             break;
           }
           default: {
-            // all other states are not allowed
             hostsWithoutCorrectVersionState.add(hostVersion.getHostName());
             break;
           }
         }
-
-        // keep track of this host version in order to transition it correctly
-        if (hostHasCorrectVersionState) {
-          hostVersionsAllowed.add(hostVersion);
-          hostsToUpdate.add(hostVersion.getHostName());
-        }
       }
 
       // throw an exception if there are hosts which are not not fully upgraded
@@ -210,24 +191,6 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
         throw new AmbariException(message);
       }
 
-      // iterate through all host components and make sure that they are on the
-      // correct version; if they are not, then this will throw an exception
-      List<InfoTuple> errors = getHostComponentsWhichDidNotUpgrade(upgradeContext);
-      if (! errors.isEmpty()) {
-        StrBuilder messageBuff = new StrBuilder(
-            String.format(
-                "The following %d host component(s) "
-                    + "have not been upgraded to version %s. Please install and upgrade "
-                    + "the Stack Version on those hosts and try again.\nHost components:",
-                errors.size(), version)).append(System.lineSeparator());
-
-        for (InfoTuple error : errors) {
-          messageBuff.append(String.format("%s on host %s\n", error.componentName, error.hostName));
-        }
-
-        throw new AmbariException(messageBuff.toString());
-      }
-
       outSB.append(
           String.format("Finalizing the upgrade state of %d host(s).",
               hostVersionsAllowed.size())).append(System.lineSeparator());
@@ -246,15 +209,8 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
           String.format("Finalizing the version for %d host(s).",
               hostVersionsAllowed.size())).append(System.lineSeparator());
 
-
       versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));
 
-      // transitioning the cluster into CURRENT will update the current/desired
-      // stack values
-      outSB.append(
-          String.format("Finalizing the version for cluster %s.", cluster.getClusterName())).append(
-              System.lineSeparator());
-
       outSB.append("Creating upgrade history...").append(System.lineSeparator());
       writeComponentHistory(upgradeContext);
 
@@ -285,7 +241,6 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
 
     try {
       Cluster cluster = upgradeContext.getCluster();
-      StackId currentClusterStackId = cluster.getCurrentStackVersion();
       RepositoryVersionEntity repositoryVersion = upgradeContext.getTargetRepositoryVersion();
 
       String message;
@@ -322,9 +277,14 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
       List<HostVersionEntity> hostVersions = hostVersionDAO.findHostVersionByClusterAndRepository(
           cluster.getClusterId(), repositoryVersion);
 
+      outSB.append(
+          String.format("Finalizing the downgrade state of %d host(s).",
+              hostVersions.size())).append(
+              System.lineSeparator());
+
       for( HostVersionEntity hostVersion : hostVersions ){
-        if( hostVersion.getState() != RepositoryVersionState.INSTALLED ){
-          hostVersion.setState(RepositoryVersionState.INSTALLED);
+        if (hostVersion.getState() != RepositoryVersionState.CURRENT) {
+          hostVersion.setState(RepositoryVersionState.CURRENT);
           hostVersionDAO.merge(hostVersion);
         }
 
@@ -339,12 +299,14 @@ public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
 
       // ensure that when downgrading, we set the desired back to the
       // original value
-      cluster.setDesiredStackVersion(currentClusterStackId);
       versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));
 
       // Reset upgrade state
       cluster.setUpgradeEntity(null);
 
+      message = String.format("The downgrade to %s has completed.", upgradeContext.getVersion());
+      outSB.append(message).append(System.lineSeparator());
+
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", outSB.toString(), errSB.toString());
     } catch (Exception e) {
       StringWriter sw = new StringWriter();

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
index 22f2e73..e6336c8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
@@ -22,6 +22,7 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.VERSION;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.text.MessageFormat;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
@@ -33,10 +34,14 @@ import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.AmbariServer;
 import org.apache.ambari.server.controller.internal.UpgradeResourceProvider;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.serveraction.ServerAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.UpgradeContext;
 import org.apache.ambari.server.state.stack.UpgradePack;
@@ -46,6 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.google.inject.persist.Transactional;
 
 /**
  * Action that represents updating the Desired Stack Id during the middle of a stack upgrade (typically NonRolling).
@@ -95,6 +101,12 @@ public class UpdateDesiredStackAction extends AbstractUpgradeServerAction {
   private Configuration m_configuration;
 
   /**
+   * Used for restting host version states on downgrade.
+   */
+  @Inject
+  private HostVersionDAO m_hostVersionDAO;
+
+  /**
    * {@inheritDoc}
    */
   @Override
@@ -152,7 +164,8 @@ public class UpdateDesiredStackAction extends AbstractUpgradeServerAction {
    *          username performing the action
    * @return the command report to return
    */
-  private CommandReport updateDesiredRepositoryVersion(
+  @Transactional
+  CommandReport updateDesiredRepositoryVersion(
       Cluster cluster, StackId originalStackId, StackId targetStackId,
       UpgradeContext upgradeContext, UpgradePack upgradePack, String userName)
       throws AmbariException, InterruptedException {
@@ -177,7 +190,22 @@ public class UpdateDesiredStackAction extends AbstractUpgradeServerAction {
             upgradeContext.getVersion(), StringUtils.join(servicesInUpgrade, ','));
       }
 
-      out.append(message);
+      out.append(message).append(System.lineSeparator());
+
+      // a downgrade must force host versions back to INSTALLED
+      if (upgradeContext.getDirection() == Direction.DOWNGRADE) {
+        RepositoryVersionEntity downgradeFromRepositoryVersion = upgradeContext.getDowngradeFromRepositoryVersion();
+        out.append(String.format("Setting all host versions back to %s for repository version %s",
+            RepositoryVersionState.INSTALLED, downgradeFromRepositoryVersion.getVersion()));
+
+        List<HostVersionEntity> hostVersionsToReset = m_hostVersionDAO.findHostVersionByClusterAndRepository(
+            cluster.getClusterId(), downgradeFromRepositoryVersion);
+
+        for (HostVersionEntity hostVersion : hostVersionsToReset) {
+          hostVersion.setState(RepositoryVersionState.INSTALLED);
+        }
+      }
+
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", out.toString(), err.toString());
     } catch (Exception e) {
       StringWriter sw = new StringWriter();

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 6cefd42..9098cf1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -28,8 +28,6 @@ import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
-import org.apache.ambari.server.orm.entities.HostEntity;
-import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.UpgradeEntity;
@@ -201,21 +199,7 @@ public interface Cluster {
       VersionDefinitionXml versionDefinitionXml, boolean forceInstalled) throws AmbariException;
 
   /**
-   * For a given host, will either either update an existing Host Version Entity for the given version, or create
-   * one if it doesn't exist
-   *
-   * @param host Host Entity object
-   * @param repositoryVersion Repository Version that the host is transitioning to
-   * @param stack Stack information with the version
-   * @return Returns either the newly created or the updated Host Version Entity.
-   * @throws AmbariException
-   */
-  HostVersionEntity transitionHostVersionState(HostEntity host,
-      final RepositoryVersionEntity repositoryVersion, final StackId stack)
-      throws AmbariException;
-
 
-  /**
    * Update state of a cluster stack version for cluster based on states of host versions and stackids.
    * @param repositoryVersion the repository version entity whose version is a value like 2.2.1.0-100)
    * @throws AmbariException

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
index b7f8d29..9a35bcc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
@@ -24,7 +24,7 @@ import java.util.Map;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ServiceComponentHostResponse;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
-import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 
 
@@ -234,12 +234,6 @@ public interface ServiceComponentHost {
    */
   void setRestartRequired(boolean restartRequired);
 
-  /**
-   * Changes host version state according to state of the components installed on the host.
-   * @return The Repository Version Entity with that component in the host
-   * @throws AmbariException if host is detached from the cluster
-   */
-  RepositoryVersionEntity recalculateHostVersionState() throws AmbariException;
 
   HostComponentDesiredStateEntity getDesiredStateEntity();
 
@@ -250,4 +244,14 @@ public interface ServiceComponentHost {
    */
   ServiceComponent getServiceComponent();
 
+  /**
+   * Updates an existing {@link HostVersionEntity} for the desired repository of
+   * this component, or create one if it doesn't exist.
+   *
+   * @return Returns either the newly created or the updated Host Version
+   *         Entity.
+   * @throws AmbariException
+   */
+  HostVersionEntity recalculateHostVersionState() throws AmbariException;
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
index f1bd900..a68a2e1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
@@ -43,6 +43,7 @@ import org.apache.ambari.server.state.stack.upgrade.Grouping;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeScope;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 
+import com.google.common.base.Objects;
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.inject.Inject;
@@ -138,6 +139,11 @@ public class UpgradeContext {
    */
   private RepositoryVersionEntity m_targetRepositoryVersion;
 
+  /**
+   * Optionally set if {@link #setDowngradeFromVersion(String)} is called.
+   */
+  private RepositoryVersionEntity m_downgradeFromRepositoryVersion;
+
   private MasterHostResolver m_resolver;
   private AmbariMetaInfo m_metaInfo;
   private List<ServiceComponentHost> m_unhealthy = new ArrayList<>();
@@ -249,7 +255,7 @@ public class UpgradeContext {
     setSourceAndTargetVersions();
 
     if (m_direction == Direction.DOWNGRADE) {
-      m_downgradeFromVersion = upgradeEntity.getFromVersion();
+      setDowngradeFromVersion(upgradeEntity.getFromVersion());
     }
 
     // since this constructor is initialized from an entity, then this map is
@@ -309,7 +315,7 @@ public class UpgradeContext {
         break;
     }
 
-    m_targetStackId = targetStackId;
+    m_targetStackId = m_targetRepositoryVersion.getStackId();
   }
 
   /**
@@ -499,7 +505,8 @@ public class UpgradeContext {
   }
 
   /**
-   * This method returns the non-finalized version we are downgrading from.
+   * Optionally set if doing a downgrade. Represents the non-finalized version
+   * being downgraded from.
    *
    * @return version cluster is downgrading from
    */
@@ -508,12 +515,25 @@ public class UpgradeContext {
   }
 
   /**
+   * Optionally set if doing a downgrade. Represents the non-finalized version
+   * being downgraded from.
+   *
+   * @return
+   */
+  public RepositoryVersionEntity getDowngradeFromRepositoryVersion() {
+    return m_downgradeFromRepositoryVersion;
+  }
+
+  /**
    * Set the HDP stack version we are downgrading from.
    *
    * @param downgradeFromVersion
    */
   public void setDowngradeFromVersion(String downgradeFromVersion) {
     m_downgradeFromVersion = downgradeFromVersion;
+
+    m_downgradeFromRepositoryVersion = m_repoVersionDAO.findByStackAndVersion(m_targetStackId,
+        downgradeFromVersion);
   }
 
   /**
@@ -683,4 +703,15 @@ public class UpgradeContext {
     parameters.put(KeyNames.REFRESH_CONFIG_TAGS_BEFORE_EXECUTION, "true");
     return parameters;
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return Objects.toStringHelper(this)
+        .add("direction", m_direction)
+        .add("type", m_type)
+        .add("target",m_targetRepositoryVersion).toString();
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
index bb84fb7..3ec907f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
@@ -779,7 +779,7 @@ public class UpgradeHelper {
           }
 
           // !!! if we aren't version advertised, but there IS a version, set it.
-          if (!versionAdvertised && StringUtils.equals(StackVersionListener.UNKNOWN_VERSION,
+          if (!versionAdvertised && !StringUtils.equals(StackVersionListener.UNKNOWN_VERSION,
               serviceComponentHost.getVersion())) {
             serviceComponentHost.setVersion(StackVersionListener.UNKNOWN_VERSION);
           }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 7e162d7..281523a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -131,7 +131,6 @@ import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.scheduler.RequestExecution;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostSummary;
 import org.apache.ambari.server.topology.TopologyRequest;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
@@ -1080,68 +1079,6 @@ public class ClusterImpl implements Cluster {
     return hostsRequiringInstallation;
   }
 
-  /**
-   * Transition the Host Version across states.
-   * @param host Host object
-   * @param repositoryVersion Repository Version with stack and version information
-   * @param stack Stack information
-   * @throws AmbariException
-   */
-  @Override
-  @Transactional
-  public HostVersionEntity transitionHostVersionState(HostEntity host, final RepositoryVersionEntity repositoryVersion, final StackId stack) throws AmbariException {
-    StackEntity repoVersionStackEntity = repositoryVersion.getStack();
-    StackId repoVersionStackId = new StackId(repoVersionStackEntity);
-
-    HostVersionEntity hostVersionEntity = hostVersionDAO.findByClusterStackVersionAndHost(
-      getClusterId(), repoVersionStackId, repositoryVersion.getVersion(),
-      host.getHostId());
-
-    hostTransitionStateWriteLock.lock();
-    try {
-      // Create one if it doesn't already exist. It will be possible to make further transitions below.
-      boolean performingInitialBootstrap = false;
-      if (hostVersionEntity == null) {
-        if (hostVersionDAO.findByClusterAndHost(getClusterName(), host.getHostName()).isEmpty()) {
-          // That is an initial bootstrap
-          performingInitialBootstrap = true;
-        }
-        hostVersionEntity = new HostVersionEntity(host, repositoryVersion, RepositoryVersionState.INSTALLING);
-
-        LOG.info("Creating host version for {}, state={}, repo={} (repo_id={})",
-            hostVersionEntity.getHostName(), hostVersionEntity.getState(),
-            hostVersionEntity.getRepositoryVersion().getVersion(), hostVersionEntity.getRepositoryVersion().getId());
-
-        hostVersionDAO.create(hostVersionEntity);
-      }
-
-      HostVersionEntity currentVersionEntity = hostVersionDAO.findByHostAndStateCurrent(getClusterId(), host.getHostId());
-      boolean isCurrentPresent = (currentVersionEntity != null);
-      final ServiceComponentHostSummary hostSummary = new ServiceComponentHostSummary(ambariMetaInfo, host, stack);
-
-      if (!isCurrentPresent) {
-        // Transition from UPGRADING -> CURRENT. This is allowed because Host Version Entity is bootstrapped in an UPGRADING state.
-        // Alternatively, transition to CURRENT during initial bootstrap if at least one host component advertised a version
-        if (hostSummary.isUpgradeFinished() || performingInitialBootstrap) {
-          hostVersionEntity.setState(RepositoryVersionState.CURRENT);
-          hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
-        }
-      } else {
-        // Handle transitions during a Stack Upgrade
-        if (hostSummary.isUpgradeFinished() && hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED)) {
-          currentVersionEntity.setState(RepositoryVersionState.INSTALLED);
-          hostVersionEntity.setState(RepositoryVersionState.CURRENT);
-
-          hostVersionDAO.merge(currentVersionEntity);
-          hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
-        }
-      }
-    } finally {
-      hostTransitionStateWriteLock.unlock();
-    }
-    return hostVersionEntity;
-  }
-
   @Override
   @Transactional
   public void setCurrentStackVersion(StackId stackId) throws AmbariException {

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
index 056959e..e08b1f9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
@@ -24,7 +24,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
@@ -42,12 +41,14 @@ import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.HostComponentStateDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
@@ -61,6 +62,7 @@ import org.apache.ambari.server.state.HostComponentAdminState;
 import org.apache.ambari.server.state.HostConfig;
 import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.SecurityState;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
@@ -82,6 +84,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Striped;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -108,6 +111,9 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
   @Inject
   private RepositoryVersionDAO repositoryVersionDAO;
 
+  @Inject
+  private HostVersionDAO hostVersionDAO;
+
   private final ServiceComponentDesiredStateDAO serviceComponentDesiredStateDAO;
 
   private final Clusters clusters;
@@ -156,6 +162,12 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
   private ImmutableList<Map<String, String>> processes = ImmutableList.of();
 
   /**
+   * Used for preventing multiple components on the same host from trying to
+   * recalculate versions concurrently.
+   */
+  private static final Striped<Lock> HOST_VERSION_LOCK = Striped.lazyWeakLock(20);
+
+  /**
    * The name of the host (which should never, ever change)
    */
   private final String hostName;
@@ -1486,46 +1498,46 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
   }
 
   /**
-   * Bootstrap any Repo Version, and potentially transition the Host Version across states.
-   * If a Host Component has a valid version, then create a Host Version if it does not already exist.
-   * If a Host Component does not have a version, return right away because no information is known.
-   * @return Return the Repository Version object
-   * @throws AmbariException
+   * {@inheritDoc}
    */
   @Override
-  public RepositoryVersionEntity recalculateHostVersionState() throws AmbariException {
-    RepositoryVersionEntity repositoryVersion = null;
-    String version = getVersion();
-    if (getUpgradeState().equals(UpgradeState.IN_PROGRESS) ||
-      getUpgradeState().equals(UpgradeState.VERSION_MISMATCH) ||
-        State.UNKNOWN.toString().equals(version)) {
-      // TODO: we still recalculate host version if upgrading component failed. It seems to be ok
-      // Recalculate only if no upgrade in progress/no version mismatch
-      return null;
-    }
+  @Transactional
+  public HostVersionEntity recalculateHostVersionState() throws AmbariException {
+    RepositoryVersionEntity repositoryVersion = serviceComponent.getDesiredRepositoryVersion();
+    HostEntity hostEntity = host.getHostEntity();
+    HostVersionEntity hostVersionEntity = hostVersionDAO.findHostVersionByHostAndRepository(
+        hostEntity, repositoryVersion);
 
-    final String hostName = getHostName();
-    final long hostId = getHost().getHostId();
-    final Set<Cluster> clustersForHost = clusters.getClustersForHost(hostName);
-    if (clustersForHost.size() != 1) {
-      throw new AmbariException("Host " + hostName + " should be assigned only to one cluster");
-    }
-    final Cluster cluster = clustersForHost.iterator().next();
-    final StackId stackId = cluster.getDesiredStackVersion();
-    final StackInfo stackInfo = ambariMetaInfo.getStack(stackId.getStackName(), stackId.getStackVersion());
+    Lock lock = HOST_VERSION_LOCK.get(host.getHostName());
+    lock.lock();
+    try {
+      // Create one if it doesn't already exist. It will be possible to make
+      // further transitions below.
+      if (hostVersionEntity == null) {
+        hostVersionEntity = new HostVersionEntity(hostEntity, repositoryVersion,
+            RepositoryVersionState.INSTALLING);
+
+        LOG.info("Creating host version for {}, state={}, repo={} (repo_id={})",
+            hostVersionEntity.getHostName(), hostVersionEntity.getState(),
+            hostVersionEntity.getRepositoryVersion().getVersion(),
+            hostVersionEntity.getRepositoryVersion().getId());
+
+        hostVersionDAO.create(hostVersionEntity);
+      }
 
-    // Check if there is a Repo Version already for the version.
-    // If it doesn't exist, will have to create it.
-    repositoryVersion = repositoryVersionDAO.findByStackNameAndVersion(stackId.getStackName(), version);
+      final ServiceComponentHostSummary hostSummary = new ServiceComponentHostSummary(
+          ambariMetaInfo, hostEntity, repositoryVersion);
 
-    if (null == repositoryVersion) {
-      repositoryVersion = createRepositoryVersion(version, stackId, stackInfo);
+      if (hostSummary.isVersionCorrectForAllHosts(repositoryVersion)) {
+        if (hostVersionEntity.getState() != RepositoryVersionState.CURRENT) {
+          hostVersionEntity.setState(RepositoryVersionState.CURRENT);
+          hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
+        }
+      }
+    } finally {
+      lock.unlock();
     }
-
-    final HostEntity host = hostDAO.findById(hostId);
-    cluster.transitionHostVersionState(host, repositoryVersion, stackId);
-
-    return repositoryVersion;
+    return hostVersionEntity;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
index 75d5fa6..bc26edb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
@@ -21,17 +21,17 @@ package org.apache.ambari.server.state.svccomphost;
 
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
-import org.apache.ambari.server.orm.entities.UpgradeEntity;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
 import org.apache.ambari.server.state.ComponentInfo;
-import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.UpgradeState;
+import org.apache.commons.lang.StringUtils;
 
 
 /**
@@ -43,14 +43,28 @@ public class ServiceComponentHostSummary {
   private Collection<HostComponentStateEntity> haveAdvertisedVersion;
   private Collection<HostComponentStateEntity> waitingToAdvertiseVersion;
   private Collection<HostComponentStateEntity> noVersionToAdvertise;
-  private Set<String> versions;
 
-  public ServiceComponentHostSummary(AmbariMetaInfo ambariMetaInfo, HostEntity host, String stackName, String stackVersion) throws AmbariException {
+  /**
+   * Constructor.
+   *
+   * @param ambariMetaInfo
+   *          used to lookup whether a component advertises a version (not
+   *          {@code null}).
+   * @param host
+   *          the host to generate a component summary for (not {@code null}).
+   * @param repositoryVersion
+   *          the repository to generate a summary for (not {@code null}).
+   * @throws AmbariException
+   */
+  public ServiceComponentHostSummary(AmbariMetaInfo ambariMetaInfo, HostEntity host,
+      RepositoryVersionEntity repositoryVersion) throws AmbariException {
     allHostComponents = host.getHostComponentStateEntities();
     haveAdvertisedVersion = new HashSet<>();
     waitingToAdvertiseVersion = new HashSet<>();
     noVersionToAdvertise = new HashSet<>();
-    versions = new HashSet<>();
+
+    String stackName = repositoryVersion.getStackName();
+    String stackVersion = repositoryVersion.getStackVersion();
 
     for (HostComponentStateEntity hostComponentStateEntity : allHostComponents) {
       ComponentInfo compInfo = ambariMetaInfo.getComponent(
@@ -60,64 +74,50 @@ public class ServiceComponentHostSummary {
       if (!compInfo.isVersionAdvertised()) {
         // Some Components cannot advertise a version. E.g., ZKF, AMBARI_METRICS, Kerberos
         noVersionToAdvertise.add(hostComponentStateEntity);
-      } else {
-        if (hostComponentStateEntity.getUpgradeState().equals(UpgradeState.IN_PROGRESS) ||
-            hostComponentStateEntity.getVersion().equalsIgnoreCase(State.UNKNOWN.toString())) {
-          waitingToAdvertiseVersion.add(hostComponentStateEntity);
-        } else {
-          haveAdvertisedVersion.add(hostComponentStateEntity);
-          versions.add(hostComponentStateEntity.getVersion());
-        } // TODO: what if component reported wrong version?
+        continue;
       }
-    }
-  }
-
-  public ServiceComponentHostSummary(AmbariMetaInfo ambariMetaInfo, HostEntity host, StackId stackId) throws AmbariException {
-    this(ambariMetaInfo, host, stackId.getStackName(), stackId.getStackVersion());
-  }
-
-  public Collection<HostComponentStateEntity> getHaveAdvertisedVersion() {
-    return haveAdvertisedVersion;
-  }
 
-  public boolean isUpgradeFinished() {
-    return haveAllComponentsFinishedAdvertisingVersion() && noComponentVersionMismatches(getHaveAdvertisedVersion());
-  }
-
-  /**
-   * @param upgradeEntity Upgrade info about update on given host
-   * @return Return true if multiple component versions are found for this host, or if it does not coincide with the
-   * CURRENT repo version.
-   */
-  public boolean isUpgradeInProgress(UpgradeEntity upgradeEntity) {
-    // Exactly one CURRENT version must exist
-    // We can only detect an upgrade if the Host has at least one component that advertises a version and has done so already
-    // If distinct versions have been advertises, then an upgrade is in progress.
-    // If exactly one version has been advertises, but it doesn't coincide with the CURRENT HostVersion, then an upgrade is in progress.
-    return upgradeEntity != null;
-  }
+      String versionAdvertised = hostComponentStateEntity.getVersion();
+      if (hostComponentStateEntity.getUpgradeState() == UpgradeState.IN_PROGRESS
+          || StringUtils.equals(versionAdvertised, State.UNKNOWN.name())) {
+        waitingToAdvertiseVersion.add(hostComponentStateEntity);
+        continue;
+      }
 
-  /**
-   * Determine if all of the components on that need to advertise a version have finished doing so.
-   * @return Return a bool indicating if all components that can report a version have done so.
-   */
-  public boolean haveAllComponentsFinishedAdvertisingVersion() {
-    return waitingToAdvertiseVersion.isEmpty();
+      haveAdvertisedVersion.add(hostComponentStateEntity);
+    }
   }
 
   /**
-   * Checks that every component has really advertised version (in other words, we are not waiting
-   * for version advertising), and that no version mismatch occurred
+   * Gets whether all hosts for a service component have reported the correct
+   * version.
    *
-   * @param hostComponents host components
-   * @return true if components have advertised the same version, or collection is empty, false otherwise.
+   * @param repositoryVersion
+   *          the version to report (not {@code null}).
+   * @return {@code true} if all hosts for this service component have reported
+   *         the correct version, {@code false} othwerise.
    */
-  public static boolean noComponentVersionMismatches(Collection<HostComponentStateEntity> hostComponents) {
-    for (HostComponentStateEntity hostComponent : hostComponents) {
+  public boolean isVersionCorrectForAllHosts(RepositoryVersionEntity repositoryVersion) {
+    if (!waitingToAdvertiseVersion.isEmpty()) {
+      return false;
+    }
+
+    for (HostComponentStateEntity hostComponent : haveAdvertisedVersion) {
       if (UpgradeState.VERSION_NON_ADVERTISED_STATES.contains(hostComponent.getUpgradeState())) {
         return false;
       }
+
+      ServiceComponentDesiredStateEntity desiredState = hostComponent.getServiceComponentDesiredStateEntity();
+      RepositoryVersionEntity desiredRepositoryVersion = desiredState.getDesiredRepositoryVersion();
+      if (!desiredRepositoryVersion.equals(repositoryVersion)) {
+        continue;
+      }
+
+      if (!StringUtils.equals(hostComponent.getVersion(), desiredRepositoryVersion.getVersion())) {
+        return false;
+      }
     }
+
     return true;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
index 359d446..a2a1ea9 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -230,7 +230,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id, state));
 
 CREATE TABLE servicedesiredstate (
   cluster_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index d5221dc..6dcbf3d 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -250,7 +250,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id));
 
 CREATE TABLE servicedesiredstate (
   cluster_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index d49bd95..15de29c 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -230,7 +230,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id));
 
 CREATE TABLE servicedesiredstate (
   cluster_id NUMBER(19) NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 2bd5a9d..9e2f2a7 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -228,7 +228,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id));
 
 CREATE TABLE servicedesiredstate (
   cluster_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index 72ae04b..473e8ca 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -227,7 +227,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id));
 
 CREATE TABLE servicedesiredstate (
   cluster_id NUMERIC(19) NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/a2632675/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index 676fde2..72189aa 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -683,7 +683,7 @@ CREATE TABLE host_version (
   CONSTRAINT PK_host_version PRIMARY KEY CLUSTERED (id),
   CONSTRAINT FK_host_version_host_id FOREIGN KEY (host_id) REFERENCES hosts (host_id),
   CONSTRAINT FK_host_version_repovers_id FOREIGN KEY (repo_version_id) REFERENCES repo_version (repo_version_id),
-  CONSTRAINT UQ_host_repo UNIQUE(repo_version_id, host_id));
+  CONSTRAINT UQ_host_repo UNIQUE(host_id, repo_version_id));
 
 CREATE TABLE artifact (
   artifact_name VARCHAR(255) NOT NULL,