You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mp...@apache.org on 2018/07/03 15:05:46 UTC

[ambari] branch branch-2.7 updated: AMBARI-24147. Make STOMP updates immutable. (#1670)

This is an automated email from the ASF dual-hosted git repository.

mpapirkovskyy pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new 6dda637  AMBARI-24147. Make STOMP updates immutable. (#1670)
6dda637 is described below

commit 6dda63723b2b3771c54dfcd1ec8d39a25a1fd28a
Author: Myroslav Papirkovskyi <mp...@apache.org>
AuthorDate: Tue Jul 3 18:05:44 2018 +0300

    AMBARI-24147. Make STOMP updates immutable. (#1670)
    
    * AMBARI-24147. Make STOMP updates immutable. (mpapirkovskyy)
    
    * AMBARI-24147. Make STOMP updates immutable. (mpapirkovskyy)
---
 ...ctionEvent.java => AmbariRuntimeException.java} |  39 +------
 .../server/agent/stomp/AgentConfigsHolder.java     |   7 +-
 .../server/agent/stomp/AgentHostDataHolder.java    |  74 ++++++++-----
 .../server/agent/stomp/AlertDefinitionsHolder.java |  40 +++++--
 .../server/agent/stomp/HostLevelParamsHolder.java  |  75 +++++++++----
 .../server/agent/stomp/dto/AlertCluster.java       |  45 +++++---
 .../server/agent/stomp/dto/ClusterConfigs.java     |   7 --
 .../agent/stomp/dto/HostLevelParamsCluster.java    |   8 --
 .../server/agent/stomp/dto/HostRepositories.java   |   8 --
 .../server/controller/KerberosHelperImpl.java      |   5 +
 .../controller/internal/HostResourceProvider.java  |   5 +-
 .../ambari/server/events/AgentActionEvent.java     |   6 +-
 .../server/events/AgentConfigsUpdateEvent.java     |  11 +-
 .../server/events/ExecutionCommandEvent.java       |  19 +---
 .../server/events/HostLevelParamsUpdateEvent.java  |  17 ++-
 .../events/publishers/AgentCommandsPublisher.java  |  14 ++-
 .../kerberos/CreateKeytabFilesServerAction.java    |   4 +-
 .../apache/ambari/server/state/ConfigHelper.java   |   3 +-
 .../ambari/server/agent/HeartbeatTestHelper.java   |   5 +-
 .../server/agent/stomp/AgentDataHolderTest.java    |  10 +-
 .../agent/stomp/AlertDefinitionsHolderTest.java    | 121 +++++++++++++++++++++
 .../agent/stomp/HostLevelParamsHolderTest.java     | 118 ++++++++++++++++++++
 .../AmbariCustomCommandExecutionHelperTest.java    |   4 +-
 .../ambari/server/state/UpgradeHelperTest.java     |   2 +-
 24 files changed, 458 insertions(+), 189 deletions(-)

diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/AmbariRuntimeException.java
similarity index 50%
copy from ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java
copy to ambari-server/src/main/java/org/apache/ambari/server/AmbariRuntimeException.java
index e8352cf..c6a20eb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/AmbariRuntimeException.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -15,41 +15,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ambari.server.events;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
+package org.apache.ambari.server;
 
 /**
- * Event to send action commands to agent.
+ * Ambari unchecked exception.
  */
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public class AgentActionEvent extends STOMPHostEvent {
-
-  /**
-   * Host id with agent action commands will be send to.
-   */
-  private Long hostId;
-
-  @JsonProperty("actionName")
-  private AgentAction agentAction;
-
-  public AgentActionEvent(AgentAction agentAction, Long hostId) {
-    super(Type.AGENT_ACTIONS);
-    this.agentAction = agentAction;
-    this.hostId = hostId;
-  }
-
-  public void setHostId(Long hostId) {
-    this.hostId = hostId;
-  }
-
-  @Override
-  public Long getHostId() {
-    return hostId;
-  }
-
-  public enum AgentAction {
-    RESTART_AGENT
+public class AmbariRuntimeException extends RuntimeException {
+  public AmbariRuntimeException(String message, Throwable cause) {
+    super(message, cause);
   }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
index 44a6e7f..958f36d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
@@ -58,9 +58,9 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
     return configHelper.getHostActualConfigsExcludeCluster(hostId, clusterId);
   }
 
-  protected boolean handleUpdate(AgentConfigsUpdateEvent update) throws AmbariException {
-    setData(update, update.getHostId());
-    return true;
+  @Override
+  protected AgentConfigsUpdateEvent handleUpdate(AgentConfigsUpdateEvent current, AgentConfigsUpdateEvent update) throws AmbariException {
+    return update;
   }
 
   public void updateData(Long clusterId, List<Long> hostIds) throws AmbariException {
@@ -75,7 +75,6 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
 
     for (Long hostId : hostIds) {
       AgentConfigsUpdateEvent agentConfigsUpdateEvent = configHelper.getHostActualConfigs(hostId);
-      agentConfigsUpdateEvent.setHostId(hostId);
       updateData(agentConfigsUpdateEvent);
     }
   }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentHostDataHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentHostDataHolder.java
index cf43f24..af4ebee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentHostDataHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentHostDataHolder.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import javax.inject.Inject;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
 import org.apache.ambari.server.agent.stomp.dto.Hashable;
 import org.apache.ambari.server.events.STOMPEvent;
 import org.apache.ambari.server.events.STOMPHostEvent;
@@ -42,10 +43,10 @@ public abstract class AgentHostDataHolder<T extends STOMPHostEvent & Hashable> e
   @Inject
   private STOMPUpdatePublisher STOMPUpdatePublisher;
 
-  private final Map<Long, T> data = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<Long, T> data = new ConcurrentHashMap<>();
 
   protected abstract T getCurrentData(Long hostId) throws AmbariException;
-  protected abstract boolean handleUpdate(T update) throws AmbariException;
+  protected abstract T handleUpdate(T current, T update) throws AmbariException;
 
   public T getUpdateIfChanged(String agentHash, Long hostId) throws AmbariException {
     T hostData = initializeDataIfNeeded(hostId, true);
@@ -53,23 +54,25 @@ public abstract class AgentHostDataHolder<T extends STOMPHostEvent & Hashable> e
   }
 
   public T initializeDataIfNeeded(Long hostId, boolean regenerateHash) throws AmbariException {
-    updateLock.lock();
     try {
-      T hostData = data.get(hostId);
-      if (hostData == null) {
-        hostData = data.get(hostId);
-        if (hostData == null) {
-          hostData = getCurrentData(hostId);
-          if (regenerateHash) {
-            regenerateDataIdentifiers(hostData);
-          }
-          data.put(hostId, hostData);
-        }
-      }
-      return hostData;
-    } finally {
-      updateLock.unlock();
+      return data.computeIfAbsent(hostId, id -> initializeData(hostId, regenerateHash));
+    } catch (AmbariRuntimeException e) {
+      throw new AmbariException(e.getMessage(), e);
+    }
+  }
+
+  private T initializeData(Long hostId, boolean regenerateHash) {
+    T hostData;
+    try {
+      hostData = getCurrentData(hostId);
+    } catch (AmbariException e) {
+      LOG.error("Error during retrieving initial value for host: {} and class {}", hostId, getClass().getName(), e);
+      throw new AmbariRuntimeException("Error during retrieving initial value for host: " + hostId + " and class: " + getClass().getName(), e);
+    }
+    if (regenerateHash) {
+      regenerateDataIdentifiers(hostData);
     }
+    return hostData;
   }
 
   /**
@@ -77,21 +80,34 @@ public abstract class AgentHostDataHolder<T extends STOMPHostEvent & Hashable> e
    * event to listeners.
    */
   public void updateData(T update) throws AmbariException {
-    //TODO need optimization for perf cluster
-    updateLock.lock();
     try {
-      initializeDataIfNeeded(update.getHostId(), true);
-      if (handleUpdate(update)) {
-        T hostData = getData(update.getHostId());
-        regenerateDataIdentifiers(hostData);
-        setIdentifiersToEventUpdate(update, hostData);
-        if (update.getType().equals(STOMPEvent.Type.AGENT_CONFIGS)) {
-          LOG.info("Configs update with hash {} will be sent to host {}", update.getHash(), hostData.getHostId());
+      data.compute(update.getHostId(), (id, current) -> {
+        if (current == null) {
+          current = initializeData(id, true);
+        }
+        T updated;
+        try {
+          updated = handleUpdate(current, update);
+        } catch (AmbariException e) {
+          LOG.error("Error during handling update for host: {} and class {}", id, getClass().getName(), e);
+          throw new AmbariRuntimeException("Error during handling update for host: " + id + " and class: " + getClass().getName(), e);
         }
-        STOMPUpdatePublisher.publish(update);
+        if (updated == null) {
+          return current;
+        } else {
+          regenerateDataIdentifiers(updated);
+          setIdentifiersToEventUpdate(update, updated);
+          return updated;
+        }
+      });
+    } catch(AmbariRuntimeException e) {
+      throw new AmbariException(e.getMessage(), e);
+    }
+    if (isIdentifierValid(update)) {
+      if (update.getType().equals(STOMPEvent.Type.AGENT_CONFIGS)) {
+        LOG.info("Configs update with hash {} will be sent to host {}", update.getHash(), update.getHostId());
       }
-    } finally {
-      updateLock.unlock();
+      STOMPUpdatePublisher.publish(update);
     }
   }
 
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
index 1080905..308ec84 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolder.java
@@ -108,15 +108,17 @@ public class AlertDefinitionsHolder extends AgentHostDataHolder<AlertDefinitions
   }
 
   @Override
-  protected boolean handleUpdate(AlertDefinitionsAgentUpdateEvent update) throws AmbariException {
+  protected AlertDefinitionsAgentUpdateEvent handleUpdate(AlertDefinitionsAgentUpdateEvent current, AlertDefinitionsAgentUpdateEvent update) throws AmbariException {
     Map<Long, AlertCluster> updateClusters = update.getClusters();
     if (updateClusters.isEmpty()) {
-      return false;
+      return null;
     }
+    AlertDefinitionsAgentUpdateEvent result = null;
 
     Long hostId = update.getHostId();
     boolean changed = false;
-    Map<Long, AlertCluster> existingClusters = getData(hostId).getClusters();
+    Map<Long, AlertCluster> existingClusters = current.getClusters();
+    Map<Long, AlertCluster> mergedClusters = new HashMap<>();
 
     switch (update.getEventType()) {
       case UPDATE:
@@ -124,12 +126,27 @@ public class AlertDefinitionsHolder extends AgentHostDataHolder<AlertDefinitions
         if (!existingClusters.keySet().containsAll(updateClusters.keySet())) {
           LOG.info("Unknown clusters in update, perhaps cluster was removed previously");
         }
+        for (Map.Entry<Long, AlertCluster> e : existingClusters.entrySet()) {
+          Long clusterId = e.getKey();
+          if (!updateClusters.containsKey(clusterId)) {
+            mergedClusters.put(clusterId, e.getValue());
+          }
+        }
         for (Map.Entry<Long, AlertCluster> e : updateClusters.entrySet()) {
-          if (update.getEventType().equals(DELETE) && CollectionUtils.isEmpty(e.getValue().getAlertDefinitions())) {
-            existingClusters.remove(e.getKey());
-            changed = true;
+          Long clusterId = e.getKey();
+          if (existingClusters.containsKey(clusterId)) {
+            if (update.getEventType().equals(DELETE) && CollectionUtils.isEmpty(e.getValue().getAlertDefinitions())) {
+              changed = true;
+            } else {
+              AlertCluster mergedCluster = existingClusters.get(e.getKey()).handleUpdate(update.getEventType(), e.getValue());
+              if (mergedCluster != null) {
+                mergedClusters.put(clusterId, mergedCluster);
+                changed = true;
+              }
+            }
           } else {
-            changed |= existingClusters.get(e.getKey()).handleUpdate(update.getEventType(), e.getValue());
+            mergedClusters.put(clusterId, e.getValue());
+            changed = true;
           }
         }
         LOG.debug("Handled {} of alerts for {} cluster(s) on host with id {}, changed = {}", update.getEventType(), updateClusters.size(), hostId, changed);
@@ -139,7 +156,8 @@ public class AlertDefinitionsHolder extends AgentHostDataHolder<AlertDefinitions
           if (!Sets.intersection(existingClusters.keySet(), updateClusters.keySet()).isEmpty()) {
             throw new AmbariException("Existing clusters in create");
           }
-          existingClusters.putAll(updateClusters);
+          mergedClusters.putAll(existingClusters);
+          mergedClusters.putAll(updateClusters);
           LOG.debug("Handled {} of alerts for {} cluster(s)", update.getEventType(), updateClusters.size());
           changed = true;
         }
@@ -148,8 +166,10 @@ public class AlertDefinitionsHolder extends AgentHostDataHolder<AlertDefinitions
         LOG.warn("Unhandled event type {}", update.getEventType());
         break;
     }
-
-    return changed;
+    if (changed) {
+      result = new AlertDefinitionsAgentUpdateEvent(CREATE, mergedClusters, current.getHostName(), hostId);
+    }
+    return result;
   }
 
   @Subscribe
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolder.java
index 3c44f57..b309bbb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolder.java
@@ -17,12 +17,17 @@
  */
 package org.apache.ambari.server.agent.stomp;
 
+import java.util.HashMap;
 import java.util.Map;
+import java.util.SortedMap;
 import java.util.TreeMap;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.agent.CommandRepository;
+import org.apache.ambari.server.agent.RecoveryConfig;
 import org.apache.ambari.server.agent.RecoveryConfigHelper;
 import org.apache.ambari.server.agent.stomp.dto.HostLevelParamsCluster;
+import org.apache.ambari.server.agent.stomp.dto.HostRepositories;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.events.ClusterComponentsRepoChangedEvent;
 import org.apache.ambari.server.events.HostLevelParamsUpdateEvent;
@@ -76,42 +81,72 @@ public class HostLevelParamsHolder extends AgentHostDataHolder<HostLevelParamsUp
       hostLevelParamsClusters.put(Long.toString(cl.getClusterId()),
           hostLevelParamsCluster);
     }
-    HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(hostLevelParamsClusters);
-    hostLevelParamsUpdateEvent.setHostId(hostId);
+    HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(hostId, hostLevelParamsClusters);
     return hostLevelParamsUpdateEvent;
   }
 
-  protected boolean handleUpdate(HostLevelParamsUpdateEvent update) {
+  @Override
+  protected HostLevelParamsUpdateEvent handleUpdate(HostLevelParamsUpdateEvent current, HostLevelParamsUpdateEvent update) {
+    HostLevelParamsUpdateEvent result = null;
     boolean changed = false;
+    Map<String, HostLevelParamsCluster> mergedClusters = new HashMap<>();
     if (MapUtils.isNotEmpty(update.getHostLevelParamsClusters())) {
-      Long hostId = update.getHostId();
+      // put from current all clusters absent in update
+      for (Map.Entry<String, HostLevelParamsCluster> hostLevelParamsClusterEntry : current.getHostLevelParamsClusters().entrySet()) {
+        String clusterId = hostLevelParamsClusterEntry.getKey();
+        if (!update.getHostLevelParamsClusters().containsKey(clusterId)) {
+          mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
+        }
+      }
+      // process clusters from update
       for (Map.Entry<String, HostLevelParamsCluster> hostLevelParamsClusterEntry : update.getHostLevelParamsClusters().entrySet()) {
-        HostLevelParamsCluster updatedCluster = hostLevelParamsClusterEntry.getValue();
         String clusterId = hostLevelParamsClusterEntry.getKey();
-        Map<String, HostLevelParamsCluster> clusters = getData().get(hostId).getHostLevelParamsClusters();
-        if (clusters.containsKey(clusterId)) {
-          HostLevelParamsCluster cluster = clusters.get(clusterId);
-          if (!cluster.getRecoveryConfig().equals(updatedCluster.getRecoveryConfig())) {
-            cluster.setRecoveryConfig(updatedCluster.getRecoveryConfig());
-            changed = true;
+        if (current.getHostLevelParamsClusters().containsKey(clusterId)) {
+          boolean clusterChanged = false;
+          HostLevelParamsCluster updatedCluster = hostLevelParamsClusterEntry.getValue();
+          HostLevelParamsCluster currentCluster = current.getHostLevelParamsClusters().get(clusterId);
+          RecoveryConfig mergedRecoveryConfig;
+          SortedMap<Long, CommandRepository> mergedRepositories;
+          SortedMap<String, Long> mergedComponentRepos;
+          if (!currentCluster.getRecoveryConfig().equals(updatedCluster.getRecoveryConfig())) {
+            mergedRecoveryConfig = updatedCluster.getRecoveryConfig();
+            clusterChanged = true;
+          } else {
+            mergedRecoveryConfig = currentCluster.getRecoveryConfig();
           }
-          if (!cluster.getHostRepositories().getRepositories()
+          if (!currentCluster.getHostRepositories().getRepositories()
               .equals(updatedCluster.getHostRepositories().getRepositories())) {
-            cluster.getHostRepositories().setRepositories(updatedCluster.getHostRepositories().getRepositories());
-            changed = true;
+            mergedRepositories = updatedCluster.getHostRepositories().getRepositories();
+            clusterChanged = true;
+          } else {
+            mergedRepositories = currentCluster.getHostRepositories().getRepositories();
           }
-          if (!cluster.getHostRepositories().getComponentRepos()
+          if (!currentCluster.getHostRepositories().getComponentRepos()
               .equals(updatedCluster.getHostRepositories().getComponentRepos())) {
-            cluster.getHostRepositories().setComponentRepos(updatedCluster.getHostRepositories().getComponentRepos());
+            mergedComponentRepos = updatedCluster.getHostRepositories().getComponentRepos();
+            clusterChanged = true;
+          } else {
+            mergedComponentRepos = currentCluster.getHostRepositories().getComponentRepos();
+          }
+          if (clusterChanged) {
+            HostLevelParamsCluster mergedCluster = new HostLevelParamsCluster(
+                new HostRepositories(mergedRepositories, mergedComponentRepos),
+                mergedRecoveryConfig);
+            mergedClusters.put(clusterId, mergedCluster);
             changed = true;
+          } else {
+            mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
           }
         } else {
-          clusters.put(clusterId, updatedCluster);
+          mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
           changed = true;
         }
       }
     }
-    return changed;
+    if (changed) {
+      result = new HostLevelParamsUpdateEvent(current.getHostId(), mergedClusters);
+    }
+    return result;
   }
 
   @Override
@@ -137,11 +172,11 @@ public class HostLevelParamsHolder extends AgentHostDataHolder<HostLevelParamsUp
   }
 
   private void updateDataOfHost(long clusterId, Cluster cluster, Host host) throws AmbariException {
-    HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(Long.toString(clusterId),
+    HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(host.getHostId(),
+        Long.toString(clusterId),
             new HostLevelParamsCluster(
                     m_ambariManagementController.get().retrieveHostRepositories(cluster, host),
                     recoveryConfigHelper.getRecoveryConfig(cluster.getClusterName(), host.getHostName())));
-    hostLevelParamsUpdateEvent.setHostId(host.getHostId());
     updateData(hostLevelParamsUpdateEvent);
   }
 
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
index ac34d4a..e4a4234 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/AlertCluster.java
@@ -79,41 +79,58 @@ public class AlertCluster {
     return hostName;
   }
 
-  public boolean handleUpdate(AlertDefinitionEventType eventType, AlertCluster update) {
+  public AlertCluster handleUpdate(AlertDefinitionEventType eventType, AlertCluster update) {
     boolean changed = false;
 
+    AlertCluster mergedCluster = null;
+    Map<Long, AlertDefinition> mergedDefinitions = new HashMap<>();
+    Integer mergedStaleIntervalMultiplier = null;
     switch (eventType) {
       case CREATE:
         // FIXME should clear map first?
       case UPDATE:
-        changed = !alertDefinitions.keySet().containsAll(update.alertDefinitions.keySet());
-        if (changed) {
-          alertDefinitions.putAll(update.alertDefinitions);
-        } else {
-          for (Map.Entry<Long, AlertDefinition> e : update.alertDefinitions.entrySet()) {
-            Long definitionId = e.getKey();
-            AlertDefinition newDefinition = e.getValue();
-            AlertDefinition oldDefinition = alertDefinitions.put(definitionId, newDefinition);
-            changed = changed || !oldDefinition.deeplyEquals(newDefinition);
+        for (Map.Entry<Long, AlertDefinition> alertDefinitionEntry : alertDefinitions.entrySet()) {
+          Long definitionId = alertDefinitionEntry.getKey();
+          if (!update.alertDefinitions.containsKey(definitionId)) {
+            mergedDefinitions.put(definitionId, alertDefinitionEntry.getValue());
+          } else {
+            AlertDefinition newDefinition = update.alertDefinitions.get(definitionId);
+            AlertDefinition oldDefinition = alertDefinitionEntry.getValue();
+            if (!oldDefinition.deeplyEquals(newDefinition)) {
+              changed = true;
+            }
+            mergedDefinitions.put(definitionId, oldDefinition);
           }
         }
         if (update.getStaleIntervalMultiplier() != null
             && !update.getStaleIntervalMultiplier().equals(staleIntervalMultiplier)) {
-          staleIntervalMultiplier = update.getStaleIntervalMultiplier();
+          mergedStaleIntervalMultiplier = update.getStaleIntervalMultiplier();
           changed = true;
+        } else {
+          mergedStaleIntervalMultiplier = staleIntervalMultiplier;
         }
         LOG.debug("Handled {} of {} alerts, changed = {}", eventType, update.alertDefinitions.size(), changed);
         break;
       case DELETE:
-        changed = alertDefinitions.keySet().removeAll(update.alertDefinitions.keySet());
+        for (Map.Entry<Long, AlertDefinition> alertDefinitionEntry : alertDefinitions.entrySet()) {
+          Long definitionId = alertDefinitionEntry.getKey();
+          if (!update.alertDefinitions.containsKey(definitionId)) {
+            mergedDefinitions.put(definitionId, alertDefinitionEntry.getValue());
+          } else {
+            changed = true;
+          }
+        }
+        mergedStaleIntervalMultiplier = staleIntervalMultiplier;
         LOG.debug("Handled {} of {} alerts", eventType, update.alertDefinitions.size());
         break;
       default:
         LOG.warn("Unhandled event type {}", eventType);
         break;
     }
-
-    return changed;
+    if (changed) {
+      mergedCluster = new AlertCluster(mergedDefinitions, hostName, mergedStaleIntervalMultiplier);
+    }
+    return mergedCluster;
   }
 
   public static AlertCluster emptyAlertCluster() {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/ClusterConfigs.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/ClusterConfigs.java
index 13c1f55..296fb2f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/ClusterConfigs.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/ClusterConfigs.java
@@ -36,18 +36,11 @@ public class ClusterConfigs {
     return configurations;
   }
 
-  public void setConfigurations(SortedMap<String, SortedMap<String, String>> configurations) {
-    this.configurations = configurations;
-  }
 
   public SortedMap<String, SortedMap<String, SortedMap<String, String>>> getConfigurationAttributes() {
     return configurationAttributes;
   }
 
-  public void setConfigurationAttributes(SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurationAttributes) {
-    this.configurationAttributes = configurationAttributes;
-  }
-
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostLevelParamsCluster.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostLevelParamsCluster.java
index 5ca72e0..dfbc6f7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostLevelParamsCluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostLevelParamsCluster.java
@@ -41,15 +41,7 @@ public class HostLevelParamsCluster {
     return hostRepositories;
   }
 
-  public void setHostRepositories(HostRepositories hostRepositories) {
-    this.hostRepositories = hostRepositories;
-  }
-
   public RecoveryConfig getRecoveryConfig() {
     return recoveryConfig;
   }
-
-  public void setRecoveryConfig(RecoveryConfig recoveryConfig) {
-    this.recoveryConfig = recoveryConfig;
-  }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostRepositories.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostRepositories.java
index 1e63812..bc3018e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostRepositories.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/HostRepositories.java
@@ -44,15 +44,7 @@ public class HostRepositories {
     return repositories;
   }
 
-  public void setRepositories(SortedMap<Long, CommandRepository> repositories) {
-    this.repositories = repositories;
-  }
-
   public SortedMap<String, Long> getComponentRepos() {
     return componentRepos;
   }
-
-  public void setComponentRepos(SortedMap<String, Long> componentRepos) {
-    this.componentRepos = componentRepos;
-  }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index ba9ed59..ad4d411 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -2747,6 +2747,9 @@ public class KerberosHelperImpl implements KerberosHelper {
 
             if (allowedStates.contains(host.getState())) {
               hostNames.add(hostname);
+            } else {
+              LOG.warn("Host {} was excluded due {} state is not allowed. Allowed states: {}", hostname, host.getState(),
+                  allowedStates);
             }
           }
 
@@ -4277,6 +4280,8 @@ public class KerberosHelperImpl implements KerberosHelper {
         for (Host host : clusterHosts) {
           if (host.getState() == HostState.HEALTHY) {
             hosts.add(host.getHostName());
+          } else {
+            LOG.warn("Host {} was excluded due {} state", host.getHostName(), host.getState());
           }
         }
       }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 8d787f5..3045bfa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -565,12 +565,13 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
             addedHost.getHostName(),
             addedHost.getRackInfo(),
             addedHost.getIPv4()));
-        HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(clusterId, new HostLevelParamsCluster(
+        HostLevelParamsUpdateEvent hostLevelParamsUpdateEvent = new HostLevelParamsUpdateEvent(addedHost.getHostId(),
+            clusterId,
+            new HostLevelParamsCluster(
             getManagementController().retrieveHostRepositories(cl, addedHost),
             recoveryConfigHelper.getRecoveryConfig(cl.getClusterName(),
                 addedHost.getHostName())
         ));
-        hostLevelParamsUpdateEvent.setHostId(addedHost.getHostId());
         hostLevelParamsUpdateEvents.add(hostLevelParamsUpdateEvent);
       }
     }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java
index e8352cf..ea86efd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/AgentActionEvent.java
@@ -29,7 +29,7 @@ public class AgentActionEvent extends STOMPHostEvent {
   /**
    * Host id with agent action commands will be send to.
    */
-  private Long hostId;
+  private final Long hostId;
 
   @JsonProperty("actionName")
   private AgentAction agentAction;
@@ -40,10 +40,6 @@ public class AgentActionEvent extends STOMPHostEvent {
     this.hostId = hostId;
   }
 
-  public void setHostId(Long hostId) {
-    this.hostId = hostId;
-  }
-
   @Override
   public Long getHostId() {
     return hostId;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentConfigsUpdateEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/AgentConfigsUpdateEvent.java
index 797b739..6ded338 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/AgentConfigsUpdateEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/AgentConfigsUpdateEvent.java
@@ -44,7 +44,7 @@ public class AgentConfigsUpdateEvent extends STOMPHostEvent implements Hashable
   /**
    * Host identifier.
    */
-  private Long hostId;
+  private final Long hostId;
 
   /**
    * Configs grouped by cluster id as keys.
@@ -52,8 +52,9 @@ public class AgentConfigsUpdateEvent extends STOMPHostEvent implements Hashable
   @JsonProperty("clusters")
   private final SortedMap<String, ClusterConfigs> clustersConfigs;
 
-  public AgentConfigsUpdateEvent(SortedMap<String, ClusterConfigs> clustersConfigs) {
+  public AgentConfigsUpdateEvent(Long hostId, SortedMap<String, ClusterConfigs> clustersConfigs) {
     super(Type.AGENT_CONFIGS);
+    this.hostId = hostId;
     this.clustersConfigs = clustersConfigs;
     this.timestamp = System.currentTimeMillis();
   }
@@ -75,10 +76,6 @@ public class AgentConfigsUpdateEvent extends STOMPHostEvent implements Hashable
     this.timestamp = timestamp;
   }
 
-  public void setHostId(Long hostId) {
-    this.hostId = hostId;
-  }
-
   @Override
   public Long getHostId() {
     return hostId;
@@ -89,7 +86,7 @@ public class AgentConfigsUpdateEvent extends STOMPHostEvent implements Hashable
   }
 
   public static AgentConfigsUpdateEvent emptyUpdate() {
-    return new AgentConfigsUpdateEvent(null);
+    return new AgentConfigsUpdateEvent(null, null);
   }
 
   @Override
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ExecutionCommandEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ExecutionCommandEvent.java
index 1c0238b..80439f3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/ExecutionCommandEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ExecutionCommandEvent.java
@@ -33,7 +33,7 @@ public class ExecutionCommandEvent extends STOMPHostEvent {
   /**
    * Host id with agent execution commands will be send to.
    */
-  private Long hostId;
+  private final Long hostId;
 
   /**
    *
@@ -47,8 +47,11 @@ public class ExecutionCommandEvent extends STOMPHostEvent {
   @JsonProperty("clusters")
   private TreeMap<String, ExecutionCommandsCluster> clusters;
 
-  public ExecutionCommandEvent(TreeMap<String, ExecutionCommandsCluster> clusters) {
+  public ExecutionCommandEvent(Long hostId, Long requiredConfigTimestamp,
+                               TreeMap<String, ExecutionCommandsCluster> clusters) {
     super(Type.COMMAND);
+    this.hostId = hostId;
+    this.requiredConfigTimestamp = requiredConfigTimestamp;
     this.clusters = clusters;
   }
 
@@ -56,10 +59,6 @@ public class ExecutionCommandEvent extends STOMPHostEvent {
     return clusters;
   }
 
-  public void setClusters(TreeMap<String, ExecutionCommandsCluster> clusters) {
-    this.clusters = clusters;
-  }
-
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -78,10 +77,6 @@ public class ExecutionCommandEvent extends STOMPHostEvent {
     return result;
   }
 
-  public void setHostId(Long hostId) {
-    this.hostId = hostId;
-  }
-
   @Override
   public Long getHostId() {
     return hostId;
@@ -90,8 +85,4 @@ public class ExecutionCommandEvent extends STOMPHostEvent {
   public Long getRequiredConfigTimestamp() {
     return requiredConfigTimestamp;
   }
-
-  public void setRequiredConfigTimestamp(Long requiredConfigTimestamp) {
-    this.requiredConfigTimestamp = requiredConfigTimestamp;
-  }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/HostLevelParamsUpdateEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/HostLevelParamsUpdateEvent.java
index facce46..1381ee6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/HostLevelParamsUpdateEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/HostLevelParamsUpdateEvent.java
@@ -42,7 +42,7 @@ public class HostLevelParamsUpdateEvent extends STOMPHostEvent implements Hashab
   /**
    * Host identifier.
    */
-  private Long hostId;
+  private final Long hostId;
 
   /**
    * Host level parameters by clusters.
@@ -50,13 +50,14 @@ public class HostLevelParamsUpdateEvent extends STOMPHostEvent implements Hashab
   @JsonProperty("clusters")
   private final Map<String, HostLevelParamsCluster> hostLevelParamsClusters;
 
-  public HostLevelParamsUpdateEvent(Map<String, HostLevelParamsCluster> hostLevelParamsClusters) {
+  public HostLevelParamsUpdateEvent(Long hostId, Map<String, HostLevelParamsCluster> hostLevelParamsClusters) {
     super(Type.HOSTLEVELPARAMS);
+    this.hostId = hostId;
     this.hostLevelParamsClusters = hostLevelParamsClusters;
   }
 
-  public HostLevelParamsUpdateEvent(String clusterId, HostLevelParamsCluster hostLevelParamsCluster) {
-    this(Collections.singletonMap(clusterId, hostLevelParamsCluster));
+  public HostLevelParamsUpdateEvent(Long hostId, String clusterId, HostLevelParamsCluster hostLevelParamsCluster) {
+    this(hostId, Collections.singletonMap(clusterId, hostLevelParamsCluster));
   }
 
   @Override
@@ -70,11 +71,7 @@ public class HostLevelParamsUpdateEvent extends STOMPHostEvent implements Hashab
   }
 
   public static HostLevelParamsUpdateEvent emptyUpdate() {
-    return new HostLevelParamsUpdateEvent(null);
-  }
-
-  public void setHostId(Long hostId) {
-    this.hostId = hostId;
+    return new HostLevelParamsUpdateEvent(null, null);
   }
 
   @Override
@@ -83,7 +80,7 @@ public class HostLevelParamsUpdateEvent extends STOMPHostEvent implements Hashab
   }
 
   public Map<String, HostLevelParamsCluster> getHostLevelParamsClusters() {
-    return hostLevelParamsClusters;
+    return hostLevelParamsClusters == null ? null : Collections.unmodifiableMap(hostLevelParamsClusters);
   }
 
   @Override
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/AgentCommandsPublisher.java b/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/AgentCommandsPublisher.java
index c4fb497..68ad652 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/AgentCommandsPublisher.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/AgentCommandsPublisher.java
@@ -89,10 +89,10 @@ public class AgentCommandsPublisher {
       }
       for (Map.Entry<Long, TreeMap<String, ExecutionCommandsCluster>> hostEntry : executionCommandsClusters.entrySet()) {
         Long hostId = hostEntry.getKey();
-        ExecutionCommandEvent executionCommandEvent = new ExecutionCommandEvent(hostEntry.getValue());
-        executionCommandEvent.setHostId(hostId);
-        executionCommandEvent.setRequiredConfigTimestamp(agentConfigsHolder
-            .initializeDataIfNeeded(hostId, true).getTimestamp());
+        ExecutionCommandEvent executionCommandEvent = new ExecutionCommandEvent(hostId,
+            agentConfigsHolder
+                .initializeDataIfNeeded(hostId, true).getTimestamp(),
+            hostEntry.getValue());
         STOMPUpdatePublisher.publish(executionCommandEvent);
       }
     }
@@ -189,7 +189,9 @@ public class AgentCommandsPublisher {
             if (targetHost.equalsIgnoreCase(hostName)) {
 
               if (SET_KEYTAB.equalsIgnoreCase(command)) {
+                String principal = resolvedPrincipal.getPrincipal();
                 String keytabFilePath = resolvedKeytab.getFile();
+                LOG.info("Processing principal {} for host {} and keytab file path {}", principal, hostName, keytabFilePath);
 
                 if (keytabFilePath != null) {
 
@@ -198,7 +200,6 @@ public class AgentCommandsPublisher {
 
                   if (keytabFile.canRead()) {
                     Map<String, String> keytabMap = new HashMap<>();
-                    String principal = resolvedPrincipal.getPrincipal();
 
                     keytabMap.put(KerberosIdentityDataFileReader.HOSTNAME, hostName);
                     keytabMap.put(KerberosIdentityDataFileReader.PRINCIPAL, principal);
@@ -219,6 +220,9 @@ public class AgentCommandsPublisher {
                     keytabMap.put(KerberosServerAction.KEYTAB_CONTENT_BASE64, keytabContentBase64);
 
                     kcp.add(keytabMap);
+                  } else {
+                    LOG.warn("Keytab file for principal {} and host {} can not to be read at path {}",
+                        principal, hostName, keytabFile.getAbsolutePath());
                   }
                 }
               } else if (REMOVE_KEYTAB.equalsIgnoreCase(command) || CHECK_KEYTABS.equalsIgnoreCase(command)) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
index a803dcf..0e3ad5f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
@@ -214,7 +214,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
                     // There is nothing to do for this since it must already exist and we don't want to
                     // regenerate the keytab
                     message = String.format("Skipping keytab file for %s, missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
-                    LOG.debug(message);
+                    LOG.info(message);
                   } else {
                     if (cachedKeytabPath == null) {
                       message = String.format("Failed to create keytab for %s, missing cached file", resolvedPrincipal.getPrincipal());
@@ -241,7 +241,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
                         ensureAmbariOnlyAccess(destinationKeytabFile);
 
                         message = String.format("Successfully created keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
-                        LOG.debug(message);
+                        LOG.info(message);
                         auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
                       } else {
                         message = String.format("Failed to create keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
index 5d30436..ba8c3d6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
@@ -2067,8 +2067,7 @@ public class ConfigHelper {
           new ClusterConfigs(configurationsTreeMap, configurationAttributesTreeMap));
     }
 
-    AgentConfigsUpdateEvent agentConfigsUpdateEvent = new AgentConfigsUpdateEvent(clustersConfigs);
-    agentConfigsUpdateEvent.setHostId(hostId);
+    AgentConfigsUpdateEvent agentConfigsUpdateEvent = new AgentConfigsUpdateEvent(hostId, clustersConfigs);
     return agentConfigsUpdateEvent;
   }
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
index 193f024..fd6fc02 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
@@ -44,6 +44,7 @@ import org.apache.ambari.server.actionmanager.Request;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.events.publishers.STOMPUpdatePublisher;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
@@ -65,6 +66,7 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.cluster.ClustersImpl;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent;
+import org.easymock.EasyMock;
 
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -117,6 +119,7 @@ public class HeartbeatTestHelper {
       @Override
       protected void configure() {
         super.configure();
+        binder().bind(STOMPUpdatePublisher.class).toInstance(EasyMock.createNiceMock(STOMPUpdatePublisher.class));
       }
     };
   }
@@ -184,7 +187,7 @@ public class HeartbeatTestHelper {
     // forcefully will refresh the internal state so that any tests which
     // incorrect use Clusters after calling this won't be affected
     Clusters clusters = injector.getInstance(Clusters.class);
-    Method method = ClustersImpl.class.getDeclaredMethod("loadClustersAndHosts");
+    Method method = ClustersImpl.class.getDeclaredMethod("safelyLoadClustersAndHosts");
     method.setAccessible(true);
     method.invoke(clusters);
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
index bb5c988..d5ba86f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
@@ -35,31 +35,31 @@ public class AgentDataHolderTest {
     AmbariEventPublisher ambariEventPublisher = createNiceMock(AmbariEventPublisher.class);
     AgentConfigsHolder agentConfigsHolder = new AgentConfigsHolder(ambariEventPublisher);
 
-    AgentConfigsUpdateEvent event1 = new AgentConfigsUpdateEvent(null);
+    AgentConfigsUpdateEvent event1 = new AgentConfigsUpdateEvent(null, null);
     event1.setHash("01");
     event1.setTimestamp(1L);
     String eventHash1 = agentConfigsHolder.getHash(event1);
 
     // difference in hash only
-    AgentConfigsUpdateEvent event2 = new AgentConfigsUpdateEvent(null);
+    AgentConfigsUpdateEvent event2 = new AgentConfigsUpdateEvent(null, null);
     event2.setHash("02");
     event2.setTimestamp(1L);
     String eventHash2 = agentConfigsHolder.getHash(event2);
 
     // difference in timestamp only
-    AgentConfigsUpdateEvent event3 = new AgentConfigsUpdateEvent(null);
+    AgentConfigsUpdateEvent event3 = new AgentConfigsUpdateEvent(null, null);
     event3.setHash("01");
     event3.setTimestamp(2L);
     String eventHash3 = agentConfigsHolder.getHash(event3);
 
     // difference in both hash and timestamp
-    AgentConfigsUpdateEvent event4 = new AgentConfigsUpdateEvent(null);
+    AgentConfigsUpdateEvent event4 = new AgentConfigsUpdateEvent(null, null);
     event4.setHash("02");
     event4.setTimestamp(2L);
     String eventHash4 = agentConfigsHolder.getHash(event4);
 
     // hash and timestamp are the same, changes in body
-    AgentConfigsUpdateEvent event5 = new AgentConfigsUpdateEvent(MapUtils.EMPTY_SORTED_MAP);
+    AgentConfigsUpdateEvent event5 = new AgentConfigsUpdateEvent(null, MapUtils.EMPTY_SORTED_MAP);
     event5.setHash("01");
     event5.setTimestamp(1L);
     String eventHash5 = agentConfigsHolder.getHash(event5);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolderTest.java
new file mode 100644
index 0000000..67fbbf3
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AlertDefinitionsHolderTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.ambari.server.agent.stomp;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.agent.stomp.dto.AlertCluster;
+import org.apache.ambari.server.events.AlertDefinitionEventType;
+import org.apache.ambari.server.events.AlertDefinitionsAgentUpdateEvent;
+import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.junit.Test;
+
+public class AlertDefinitionsHolderTest {
+  private final Long HOST_ID = 1L;
+
+  @Test
+  public void testHandleUpdateEmptyCurrent() throws AmbariException {
+    AlertDefinitionsAgentUpdateEvent current = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        Collections.emptyMap(), "host1", HOST_ID);
+    Map<Long, AlertCluster> clusters = new HashMap<>();
+    AlertCluster cluster = AlertCluster.emptyAlertCluster();
+    clusters.put(1L, cluster);
+    AlertDefinitionsAgentUpdateEvent update = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.UPDATE,
+        clusters, "host1", HOST_ID);
+
+    AlertDefinitionsHolder alertDefinitionsHolder = new AlertDefinitionsHolder(createNiceMock(AmbariEventPublisher.class));
+    AlertDefinitionsAgentUpdateEvent result = alertDefinitionsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(AlertDefinitionEventType.CREATE, result.getEventType());
+    assertEquals(result.getClusters(), update.getClusters());
+  }
+
+  @Test
+  public void testHandleUpdateEmptyUpdate() throws AmbariException {
+    Map<Long, AlertCluster> clusters = new HashMap<>();
+    AlertCluster cluster = AlertCluster.emptyAlertCluster();
+    clusters.put(1L, cluster);
+    AlertDefinitionsAgentUpdateEvent current = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        clusters, "host1", HOST_ID);
+    AlertDefinitionsAgentUpdateEvent update = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.UPDATE,
+        Collections.emptyMap(), "host1", HOST_ID);
+
+    AlertDefinitionsHolder alertDefinitionsHolder = new AlertDefinitionsHolder(createNiceMock(AmbariEventPublisher.class));
+    AlertDefinitionsAgentUpdateEvent result = alertDefinitionsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(result, null);
+  }
+
+  @Test
+  public void testHandleUpdateNoChanges() throws AmbariException {
+    Map<Long, AlertCluster> currentClusters = new HashMap<>();
+    AlertCluster currentCluster = new AlertCluster(Collections.emptyMap(), "host1");
+    currentClusters.put(1L, currentCluster);
+    AlertDefinitionsAgentUpdateEvent current = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        currentClusters, "host1", HOST_ID);
+
+    Map<Long, AlertCluster> updateClusters = new HashMap<>();
+    AlertCluster updateCluster = new AlertCluster(Collections.emptyMap(), "host1");
+    updateClusters.put(1L, updateCluster);
+    AlertDefinitionsAgentUpdateEvent update = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.UPDATE,
+        updateClusters, "host1", HOST_ID);
+
+    AlertDefinitionsHolder alertDefinitionsHolder = new AlertDefinitionsHolder(createNiceMock(AmbariEventPublisher.class));
+    AlertDefinitionsAgentUpdateEvent result = alertDefinitionsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(result, null);
+  }
+
+  @Test
+  public void testHandleUpdateOnChanges() throws AmbariException {
+    Map<Long, AlertCluster> currentClusters = new HashMap<>();
+    AlertCluster currentCluster = new AlertCluster(Collections.emptyMap(), "host1");
+    currentClusters.put(1L, currentCluster);
+    AlertDefinitionsAgentUpdateEvent current = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.CREATE,
+        currentClusters, "host1", HOST_ID);
+
+    Map<Long, AlertCluster> updateClusters = new HashMap<>();
+    AlertCluster updateCluster = new AlertCluster(Collections.emptyMap(), "host1");
+    updateClusters.put(2L, updateCluster);
+    AlertDefinitionsAgentUpdateEvent update = new AlertDefinitionsAgentUpdateEvent(AlertDefinitionEventType.UPDATE,
+        updateClusters, "host1", HOST_ID);
+
+    AlertDefinitionsHolder alertDefinitionsHolder = new AlertDefinitionsHolder(createNiceMock(AmbariEventPublisher.class));
+    AlertDefinitionsAgentUpdateEvent result = alertDefinitionsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(2, result.getClusters().size());
+    assertTrue(result.getClusters().containsKey(1L));
+    assertTrue(result.getClusters().containsKey(2L));
+  }
+}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolderTest.java
new file mode 100644
index 0000000..4fef7fe
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/HostLevelParamsHolderTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.ambari.server.agent.stomp;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ambari.server.agent.RecoveryConfig;
+import org.apache.ambari.server.agent.stomp.dto.HostLevelParamsCluster;
+import org.apache.ambari.server.agent.stomp.dto.HostRepositories;
+import org.apache.ambari.server.events.HostLevelParamsUpdateEvent;
+import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.junit.Test;
+
+public class HostLevelParamsHolderTest {
+  private final Long HOST_ID = 1L;
+
+  @Test
+  public void testHandleUpdateEmptyCurrent() {
+    HostLevelParamsUpdateEvent current = new HostLevelParamsUpdateEvent(HOST_ID, Collections.emptyMap());
+    Map<String, HostLevelParamsCluster> clusters = new HashMap<>();
+    HostRepositories hostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster cluster = new HostLevelParamsCluster(hostRepositories, new RecoveryConfig(null));
+    clusters.put("1", cluster);
+    HostLevelParamsUpdateEvent update = new HostLevelParamsUpdateEvent(HOST_ID, clusters);
+
+    HostLevelParamsHolder levelParamsHolder = new HostLevelParamsHolder(createNiceMock(AmbariEventPublisher.class));
+    HostLevelParamsUpdateEvent result = levelParamsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(result, update);
+  }
+
+  @Test
+  public void testHandleUpdateEmptyUpdate() {
+    Map<String, HostLevelParamsCluster> clusters = new HashMap<>();
+    HostRepositories hostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster cluster = new HostLevelParamsCluster(hostRepositories, new RecoveryConfig(null));
+    clusters.put("1", cluster);
+    HostLevelParamsUpdateEvent current = new HostLevelParamsUpdateEvent(HOST_ID, clusters);
+    HostLevelParamsUpdateEvent update = new HostLevelParamsUpdateEvent(HOST_ID, Collections.emptyMap());
+
+    HostLevelParamsHolder levelParamsHolder = new HostLevelParamsHolder(createNiceMock(AmbariEventPublisher.class));
+    HostLevelParamsUpdateEvent result = levelParamsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(result, null);
+  }
+
+  @Test
+  public void testHandleUpdateNoChanges() {
+    Map<String, HostLevelParamsCluster> currentClusters = new HashMap<>();
+    HostRepositories currentHostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster currentCluster = new HostLevelParamsCluster(currentHostRepositories, new RecoveryConfig(null));
+    currentClusters.put("1", currentCluster);
+    HostLevelParamsUpdateEvent current = new HostLevelParamsUpdateEvent(HOST_ID, currentClusters);
+
+    Map<String, HostLevelParamsCluster> updateClusters = new HashMap<>();
+    HostRepositories updateHostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster updateCluster = new HostLevelParamsCluster(updateHostRepositories, new RecoveryConfig(null));
+    updateClusters.put("1", updateCluster);
+    HostLevelParamsUpdateEvent update = new HostLevelParamsUpdateEvent(HOST_ID, updateClusters);
+
+    HostLevelParamsHolder levelParamsHolder = new HostLevelParamsHolder(createNiceMock(AmbariEventPublisher.class));
+    HostLevelParamsUpdateEvent result = levelParamsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(result, null);
+  }
+
+  @Test
+  public void testHandleUpdateOnChanges() {
+    Map<String, HostLevelParamsCluster> currentClusters = new HashMap<>();
+    HostRepositories currentHostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster currentCluster = new HostLevelParamsCluster(currentHostRepositories, new RecoveryConfig(null));
+    currentClusters.put("1", currentCluster);
+    HostLevelParamsUpdateEvent current = new HostLevelParamsUpdateEvent(HOST_ID, currentClusters);
+
+    Map<String, HostLevelParamsCluster> updateClusters = new HashMap<>();
+    HostRepositories updateHostRepositories = new HostRepositories(Collections.emptySortedMap(), Collections.emptySortedMap());
+    HostLevelParamsCluster updateCluster = new HostLevelParamsCluster(updateHostRepositories, new RecoveryConfig(null));
+    updateClusters.put("2", updateCluster);
+    HostLevelParamsUpdateEvent update = new HostLevelParamsUpdateEvent(HOST_ID, updateClusters);
+
+    HostLevelParamsHolder levelParamsHolder = new HostLevelParamsHolder(createNiceMock(AmbariEventPublisher.class));
+    HostLevelParamsUpdateEvent result = levelParamsHolder.handleUpdate(current, update);
+
+    assertFalse(result == update);
+    assertFalse(result == current);
+    assertEquals(2, result.getHostLevelParamsClusters().size());
+    assertTrue(result.getHostLevelParamsClusters().containsKey("1"));
+    assertTrue(result.getHostLevelParamsClusters().containsKey("2"));
+  }
+}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
index 3533a7d..528e589 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
@@ -147,7 +147,7 @@ public class AmbariCustomCommandExecutionHelperTest {
         EasyMock.anyObject(Map.class))).andReturn(Collections.EMPTY_SET);
 
     EasyMock.expect(configHelper.getHostActualConfigs(EasyMock.anyLong())).andReturn(
-        new AgentConfigsUpdateEvent(new TreeMap<>())).anyTimes();
+        new AgentConfigsUpdateEvent(null, new TreeMap<>())).anyTimes();
 
     EasyMock.replay(configHelper);
 
@@ -544,7 +544,7 @@ public class AmbariCustomCommandExecutionHelperTest {
     AmbariCustomCommandExecutionHelper helper = injector.getInstance(AmbariCustomCommandExecutionHelper.class);
 
     EasyMock.expect(configHelper.getHostActualConfigs(EasyMock.anyLong())).andReturn(
-        new AgentConfigsUpdateEvent(new TreeMap<>())).anyTimes();
+        new AgentConfigsUpdateEvent(null, new TreeMap<>())).anyTimes();
 
     EasyMock.replay(configHelper);
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
index e1b93de..0a7beea 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
@@ -156,7 +156,7 @@ public class UpgradeHelperTest extends EasyMockSupport {
     expect(m_configHelper.getEffectiveDesiredTags(
         EasyMock.anyObject(Cluster.class), EasyMock.anyObject(String.class))).andReturn(new HashMap<>()).anyTimes();
     expect(m_configHelper.getHostActualConfigs(
-        EasyMock.anyLong())).andReturn(new AgentConfigsUpdateEvent(Collections.emptySortedMap())).anyTimes();
+        EasyMock.anyLong())).andReturn(new AgentConfigsUpdateEvent(null, Collections.emptySortedMap())).anyTimes();
     expect(m_configHelper.getChangedConfigTypes(anyObject(Cluster.class), anyObject(ServiceConfigEntity.class),
         anyLong(), anyLong(), anyString())).andReturn(Collections.emptyMap()).anyTimes();
   }