AMBARI-21427. Assigning hosts concurrently to same config group may fail with 'org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException: Config group already exist'. (stoader)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/3c9f125c Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/3c9f125c Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/3c9f125c Branch: refs/heads/branch-feature-logsearch-ui Commit: 3c9f125cc08269558f35a971c321777d331de1ca Parents: 7f3d3b2 Author: Toader, Sebastian <stoa...@hortonworks.com> Authored: Mon Jul 10 13:02:20 2017 +0200 Committer: Toader, Sebastian <stoa...@hortonworks.com> Committed: Mon Jul 10 13:02:45 2017 +0200 ---------------------------------------------------------------------- .../ambari/server/topology/AmbariContext.java | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/3c9f125c/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java index 9b64edc..dee0e6c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java @@ -30,6 +30,7 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; import javax.annotation.Nullable; import javax.inject.Inject; @@ -81,6 +82,7 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Striped; import com.google.inject.Provider; @@ -121,6 +123,16 @@ public class AmbariContext { private final static Logger LOG = LoggerFactory.getLogger(AmbariContext.class); + + /** + * When config groups are created using Blueprints these are created when + * hosts join a hostgroup and are added to the corresponding config group. + * Since hosts join in parallel there might be a race condition in creating + * the config group a host is to be added to. Thus we need to synchronize + * the creation of config groups with the same name. + */ + private Striped<Lock> configGroupCreateLock = Striped.lazyWeakLock(1); + public boolean isClusterKerberosEnabled(long clusterId) { Cluster cluster; try { @@ -341,11 +353,17 @@ public class AmbariContext { } public void registerHostWithConfigGroup(final String hostName, final ClusterTopology topology, final String groupName) { + String qualifiedGroupName = getConfigurationGroupName(topology.getBlueprint().getName(), groupName); + + Lock configGroupLock = configGroupCreateLock.get(qualifiedGroupName); + try { + configGroupLock.lock(); + boolean hostAdded = RetryHelper.executeWithRetry(new Callable<Boolean>() { @Override public Boolean call() throws Exception { - return addHostToExistingConfigGroups(hostName, topology, groupName); + return addHostToExistingConfigGroups(hostName, topology, qualifiedGroupName); } }); if (!hostAdded) { @@ -355,6 +373,9 @@ public class AmbariContext { LOG.error("Unable to register config group for host: ", e); throw new RuntimeException("Unable to register config group for host: " + hostName); } + finally { + configGroupLock.unlock(); + } } public RequestStatusResponse installHost(String hostName, String clusterName, Collection<String> skipInstallForComponents, Collection<String> dontSkipInstallForComponents, boolean skipFailure) { @@ -562,7 +583,7 @@ public class AmbariContext { /** * Add the new host to an existing config group. */ - private boolean addHostToExistingConfigGroups(String hostName, ClusterTopology topology, String groupName) { + private boolean addHostToExistingConfigGroups(String hostName, ClusterTopology topology, String configGroupName) { boolean addedHost = false; Clusters clusters; Cluster cluster; @@ -576,9 +597,8 @@ public class AmbariContext { // I don't know of a method to get config group by name //todo: add a method to get config group by name Map<Long, ConfigGroup> configGroups = cluster.getConfigGroups(); - String qualifiedGroupName = getConfigurationGroupName(topology.getBlueprint().getName(), groupName); for (ConfigGroup group : configGroups.values()) { - if (group.getName().equals(qualifiedGroupName)) { + if (group.getName().equals(configGroupName)) { try { Host host = clusters.getHost(hostName); addedHost = true;