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/515a641c Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/515a641c Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/515a641c Branch: refs/heads/branch-feature-AMBARI-21348 Commit: 515a641cda7045ddd7c01c58e294c2d1853b4cec Parents: e92b503 Author: Toader, Sebastian <stoa...@hortonworks.com> Authored: Mon Jul 10 13:02:20 2017 +0200 Committer: Toader, Sebastian <stoa...@hortonworks.com> Committed: Tue Jul 11 11:19:59 2017 +0200 ---------------------------------------------------------------------- .../ambari/server/topology/AmbariContext.java | 30 ++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/515a641c/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 106d7c8..f5b674e 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 @@ -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 @@ -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; @@ -79,6 +80,7 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Striped; /** @@ -112,6 +114,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 { @@ -328,11 +340,17 @@ public class AmbariContext { } public void registerHostWithConfigGroup(final String hostName, final ClusterTopology topology, final String groupName) { + final 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) { @@ -342,6 +360,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) { @@ -549,7 +570,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; @@ -563,9 +584,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;