diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerAttribute.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerAttribute.java
index 593b7e42f557..4f4ad53ace53 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerAttribute.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerAttribute.java
@@ -19,13 +19,13 @@
import static org.apache.hadoop.hdds.scm.exceptions.SCMException.ResultCodes.FAILED_TO_CHANGE_CONTAINER_STATE;
-import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
-import java.util.Collections;
-import java.util.Map;
+import com.google.common.collect.Maps;
+import java.util.EnumMap;
import java.util.NavigableSet;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.Objects;
+import java.util.TreeSet;
import org.apache.hadoop.hdds.scm.container.ContainerID;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.slf4j.Logger;
@@ -38,8 +38,6 @@
*
* 1. StateMap - LifeCycleState -> Set of ContainerIDs
* 2. TypeMap - ReplicationType -> Set of ContainerIDs
- * 3. OwnerMap - OwnerNames -> Set of ContainerIDs
- * 4. FactorMap - ReplicationFactor -> Set of ContainerIDs
*
* This means that for a cluster size of 750 PB -- we will have around 150
* Million containers, if we assume 5GB average container size.
@@ -57,29 +55,27 @@
* are going to rely on ContainerStateMap locks to maintain consistency of
* data in these classes too, since ContainerAttribute is only used by
* ContainerStateMap class.
+ *
+ * @param Attribute type
*/
-public class ContainerAttribute {
+public class ContainerAttribute> {
private static final Logger LOG =
LoggerFactory.getLogger(ContainerAttribute.class);
- private final Map> attributeMap;
- private static final NavigableSet EMPTY_SET = Collections
- .unmodifiableNavigableSet(new ConcurrentSkipListSet<>());
-
- /**
- * Creates a Container Attribute map from an existing Map.
- *
- * @param attributeMap - AttributeMap
- */
- public ContainerAttribute(Map> attributeMap) {
- this.attributeMap = attributeMap;
- }
+ private final Class attributeClass;
+ private final ImmutableMap> attributeMap;
/**
* Create an empty Container Attribute map.
*/
- public ContainerAttribute() {
- this.attributeMap = new ConcurrentHashMap<>();
+ public ContainerAttribute(Class attributeClass) {
+ this.attributeClass = attributeClass;
+
+ final EnumMap> map = new EnumMap<>(attributeClass);
+ for (T t : attributeClass.getEnumConstants()) {
+ map.put(t, new TreeSet<>());
+ }
+ this.attributeMap = Maps.immutableEnumMap(map);
}
/**
@@ -88,51 +84,12 @@ public ContainerAttribute() {
*
* @param key - The key to the set where the ContainerID should exist.
* @param value - Actual Container ID.
- * @throws SCMException - on Error
- */
- public boolean insert(T key, ContainerID value) throws SCMException {
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(value);
- attributeMap.computeIfAbsent(key, any ->
- new ConcurrentSkipListSet<>()).add(value);
- return true;
- }
-
- /**
- * Returns true if have this bucket in the attribute map.
- *
- * @param key - Key to lookup
- * @return true if we have the key
- */
- public boolean hasKey(T key) {
- Preconditions.checkNotNull(key);
- return this.attributeMap.containsKey(key);
- }
-
- /**
- * Returns true if we have the key and the containerID in the bucket.
- *
- * @param key - Key to the bucket
- * @param id - container ID that we want to lookup
- * @return true or false
+ * @return true if the value is added;
+ * otherwise, the value already exists, return false.
*/
- public boolean hasContainerID(T key, ContainerID id) {
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(id);
-
- return this.attributeMap.containsKey(key) &&
- this.attributeMap.get(key).contains(id);
- }
-
- /**
- * Returns true if we have the key and the containerID in the bucket.
- *
- * @param key - Key to the bucket
- * @param id - container ID that we want to lookup
- * @return true or false
- */
- public boolean hasContainerID(T key, int id) {
- return hasContainerID(key, ContainerID.valueOf(id));
+ public boolean insert(T key, ContainerID value) {
+ Objects.requireNonNull(value, "value == null");
+ return get(key).add(value);
}
/**
@@ -141,15 +98,7 @@ public boolean hasContainerID(T key, int id) {
* @param key - Key that identifies the Set.
*/
public void clearSet(T key) {
- Preconditions.checkNotNull(key);
-
- if (attributeMap.containsKey(key)) {
- attributeMap.get(key).clear();
- } else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("key: {} does not exist in the attributeMap", key);
- }
- }
+ get(key).clear();
}
/**
@@ -159,24 +108,24 @@ public void clearSet(T key) {
* @param value - Container ID
*/
public boolean remove(T key, ContainerID value) {
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(value);
-
- if (attributeMap.containsKey(key)) {
- if (!attributeMap.get(key).remove(value)) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("ContainerID: {} does not exist in the set pointed by " +
- "key:{}", value, key);
- }
- return false;
- }
- return true;
- } else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("key: {} does not exist in the attributeMap", key);
- }
+ Objects.requireNonNull(value, "value == null");
+
+ if (!get(key).remove(value)) {
+ LOG.debug("Container {} not found in {} attribute", value, key);
return false;
}
+ return true;
+ }
+
+ NavigableSet get(T attribute) {
+ Objects.requireNonNull(attribute, "attribute == null");
+
+ final NavigableSet set = attributeMap.get(attribute);
+ if (set == null) {
+ throw new IllegalStateException("Attribute not found: " + attribute
+ + " (" + attributeClass.getSimpleName() + ")");
+ }
+ return set;
}
/**
@@ -186,15 +135,7 @@ public boolean remove(T key, ContainerID value) {
* @return Underlying Set in immutable form.
*/
public NavigableSet getCollection(T key) {
- Preconditions.checkNotNull(key);
-
- if (this.attributeMap.containsKey(key)) {
- return ImmutableSortedSet.copyOf(this.attributeMap.get(key));
- }
- if (LOG.isDebugEnabled()) {
- LOG.debug("No such Key. Key {}", key);
- }
- return EMPTY_SET;
+ return ImmutableSortedSet.copyOf(get(key));
}
/**
@@ -207,31 +148,22 @@ public NavigableSet getCollection(T key) {
*/
public void update(T currentKey, T newKey, ContainerID value)
throws SCMException {
- Preconditions.checkNotNull(currentKey);
- Preconditions.checkNotNull(newKey);
- // Return if container attribute not changed
- if (currentKey == newKey) {
+ if (currentKey == newKey) { // use == for enum
return;
}
- boolean removed = false;
- try {
- removed = remove(currentKey, value);
- if (!removed) {
- throw new SCMException("Unable to find key in the current key bucket",
- FAILED_TO_CHANGE_CONTAINER_STATE);
- }
- insert(newKey, value);
- } catch (SCMException ex) {
- // if we removed the key, insert it back to original bucket, since the
- // next insert failed.
- LOG.error("error in update.", ex);
- if (removed) {
- insert(currentKey, value);
- if (LOG.isTraceEnabled()) {
- LOG.trace("reinserted the removed key. {}", currentKey);
- }
- }
- throw ex;
+
+ Objects.requireNonNull(newKey, "newKey == null");
+ final boolean removed = remove(currentKey, value);
+ if (!removed) {
+ throw new SCMException("Failed to update Container " + value + " from " + currentKey + " to " + newKey
+ + ": Container " + value + " not found in attribute " + currentKey,
+ FAILED_TO_CHANGE_CONTAINER_STATE);
+ }
+
+ final boolean inserted = insert(newKey, value);
+ if (!inserted) {
+ LOG.warn("Update Container {} from {} to {}: Container {} already exists in {}",
+ value, currentKey, newKey, value, newKey);
}
}
}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerStateMap.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerStateMap.java
index 561104b810d7..40b7d51c8c75 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerStateMap.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerStateMap.java
@@ -17,8 +17,6 @@
package org.apache.hadoop.hdds.scm.container.states;
-import static org.apache.hadoop.hdds.scm.exceptions.SCMException.ResultCodes.FAILED_TO_CHANGE_CONTAINER_STATE;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
@@ -28,7 +26,6 @@
import java.util.NavigableSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.hadoop.hdds.client.ReplicationConfig;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType;
import org.apache.hadoop.hdds.scm.container.ContainerID;
@@ -75,10 +72,8 @@ public class ContainerStateMap {
private static final Logger LOG =
LoggerFactory.getLogger(ContainerStateMap.class);
- private final ContainerAttribute lifeCycleStateMap;
- private final ContainerAttribute ownerMap;
- private final ContainerAttribute repConfigMap;
- private final ContainerAttribute typeMap;
+ private final ContainerAttribute lifeCycleStateMap = new ContainerAttribute<>(LifeCycleState.class);
+ private final ContainerAttribute typeMap = new ContainerAttribute<>(ReplicationType.class);
private final Map containerMap;
private final Map> replicaMap;
@@ -86,10 +81,6 @@ public class ContainerStateMap {
* Create a ContainerStateMap.
*/
public ContainerStateMap() {
- this.lifeCycleStateMap = new ContainerAttribute<>();
- this.ownerMap = new ContainerAttribute<>();
- this.repConfigMap = new ContainerAttribute<>();
- this.typeMap = new ContainerAttribute<>();
this.containerMap = new ConcurrentHashMap<>();
this.replicaMap = new ConcurrentHashMap<>();
}
@@ -112,8 +103,6 @@ public void addContainer(final ContainerInfo info)
if (!contains(id)) {
containerMap.put(id, info);
lifeCycleStateMap.insert(info.getState(), id);
- ownerMap.insert(info.getOwner(), id);
- repConfigMap.insert(info.getReplicationConfig(), id);
typeMap.insert(info.getReplicationType(), id);
replicaMap.put(id, Collections.emptySet());
@@ -137,8 +126,6 @@ public void removeContainer(final ContainerID id) {
// remove operation fails?
final ContainerInfo info = containerMap.remove(id);
lifeCycleStateMap.remove(info.getState(), id);
- ownerMap.remove(info.getOwner(), id);
- repConfigMap.remove(info.getReplicationConfig(), id);
typeMap.remove(info.getReplicationType(), id);
replicaMap.remove(id);
LOG.trace("Container {} removed from ContainerStateMap.", id);
@@ -215,56 +202,16 @@ private void replaceReplicaSet(ContainerID containerID,
*/
public void updateState(ContainerID containerID, LifeCycleState currentState,
LifeCycleState newState) throws SCMException {
- Preconditions.checkNotNull(currentState);
- Preconditions.checkNotNull(newState);
- if (!contains(containerID)) {
- return;
- }
- // Return if updating state not changed
- if (currentState == newState) {
- LOG.debug("CurrentState and NewState are the same, return from " +
- "updateState directly.");
+ if (currentState == newState) { // state not changed
return;
}
- // TODO: Simplify this logic.
final ContainerInfo currentInfo = containerMap.get(containerID);
- try {
- currentInfo.setState(newState);
-
- // We are updating two places before this update is done, these can
- // fail independently, since the code needs to handle it.
-
- // We update the attribute map, if that fails it will throw an
- // exception, so no issues, if we are successful, we keep track of the
- // fact that we have updated the lifecycle state in the map, and update
- // the container state. If this second update fails, we will attempt to
- // roll back the earlier change we did. If the rollback fails, we can
- // be in an inconsistent state,
-
- lifeCycleStateMap.update(currentState, newState, containerID);
- if (LOG.isTraceEnabled()) {
- LOG.trace("Updated the container {} to new state. Old = {}, new = " +
- "{}", containerID, currentState, newState);
- }
- } catch (SCMException ex) {
- LOG.error("Unable to update the container state.", ex);
- // we need to revert the change in this attribute since we are not
- // able to update the hash table.
- LOG.info("Reverting the update to lifecycle state. Moving back to " +
- "old state. Old = {}, Attempted state = {}", currentState,
- newState);
-
- currentInfo.revertState();
-
- // if this line throws, the state map can be in an inconsistent
- // state, since we will have modified the attribute by the
- // container state will not in sync since we were not able to put
- // that into the hash table.
- lifeCycleStateMap.update(newState, currentState, containerID);
-
- throw new SCMException("Updating the container map failed.", ex,
- FAILED_TO_CHANGE_CONTAINER_STATE);
+ if (currentInfo == null) { // container not found
+ return;
}
+ lifeCycleStateMap.update(currentState, newState, containerID);
+ LOG.trace("Updated the container {} from {} to {}", containerID, currentState, newState);
+ currentInfo.setState(newState);
}
public Set getAllContainerIDs() {
diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/states/TestContainerAttribute.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/states/TestContainerAttribute.java
index 9038a5f1d42e..acf58ded0be7 100644
--- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/states/TestContainerAttribute.java
+++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/states/TestContainerAttribute.java
@@ -23,8 +23,7 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.util.Arrays;
-import java.util.List;
+import java.util.NavigableSet;
import org.apache.hadoop.hdds.scm.container.ContainerID;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.junit.jupiter.api.Test;
@@ -33,99 +32,90 @@
* Test ContainerAttribute management.
*/
public class TestContainerAttribute {
+ enum Key { K1, K2, K3 }
+
+ private final Key key1 = Key.K1;
+ private final Key key2 = Key.K2;
+ private final Key key3 = Key.K3;
+
+ static > boolean hasContainerID(ContainerAttribute attribute, T key, int id) {
+ return hasContainerID(attribute, key, ContainerID.valueOf(id));
+ }
+
+ static > boolean hasContainerID(ContainerAttribute attribute, T key, ContainerID id) {
+ final NavigableSet set = attribute.get(key);
+ return set != null && set.contains(id);
+ }
@Test
- public void testInsert() throws SCMException {
- ContainerAttribute containerAttribute = new ContainerAttribute<>();
+ public void testInsert() {
+ ContainerAttribute containerAttribute = new ContainerAttribute<>(Key.class);
ContainerID id = ContainerID.valueOf(42);
- containerAttribute.insert(1, id);
- assertEquals(1, containerAttribute.getCollection(1).size());
- assertThat(containerAttribute.getCollection(1)).contains(id);
+ containerAttribute.insert(key1, id);
+ assertEquals(1, containerAttribute.getCollection(key1).size());
+ assertThat(containerAttribute.getCollection(key1)).contains(id);
// Insert again and verify that the new ContainerId is inserted.
ContainerID newId =
ContainerID.valueOf(42);
- containerAttribute.insert(1, newId);
- assertEquals(1, containerAttribute.getCollection(1).size());
- assertThat(containerAttribute.getCollection(1)).contains(newId);
- }
-
- @Test
- public void testHasKey() throws SCMException {
- ContainerAttribute containerAttribute = new ContainerAttribute<>();
-
- for (int x = 1; x < 42; x++) {
- containerAttribute.insert(1, ContainerID.valueOf(x));
- }
- assertTrue(containerAttribute.hasKey(1));
- for (int x = 1; x < 42; x++) {
- assertTrue(containerAttribute.hasContainerID(1, x));
- }
-
- assertFalse(containerAttribute.hasContainerID(1,
- ContainerID.valueOf(42)));
+ containerAttribute.insert(key1, newId);
+ assertEquals(1, containerAttribute.getCollection(key1).size());
+ assertThat(containerAttribute.getCollection(key1)).contains(newId);
}
@Test
- public void testClearSet() throws SCMException {
- List keyslist = Arrays.asList("Key1", "Key2", "Key3");
- ContainerAttribute containerAttribute = new ContainerAttribute<>();
- for (String k : keyslist) {
+ public void testClearSet() {
+ ContainerAttribute containerAttribute = new ContainerAttribute<>(Key.class);
+ for (Key k : Key.values()) {
for (int x = 1; x < 101; x++) {
containerAttribute.insert(k, ContainerID.valueOf(x));
}
}
- for (String k : keyslist) {
+ for (Key k : Key.values()) {
assertEquals(100, containerAttribute.getCollection(k).size());
}
- containerAttribute.clearSet("Key1");
- assertEquals(0, containerAttribute.getCollection("Key1").size());
+ containerAttribute.clearSet(key1);
+ assertEquals(0, containerAttribute.getCollection(key1).size());
}
@Test
- public void testRemove() throws SCMException {
+ public void testRemove() {
- List keyslist = Arrays.asList("Key1", "Key2", "Key3");
- ContainerAttribute containerAttribute = new ContainerAttribute<>();
+ ContainerAttribute containerAttribute = new ContainerAttribute<>(Key.class);
- for (String k : keyslist) {
+ for (Key k : Key.values()) {
for (int x = 1; x < 101; x++) {
containerAttribute.insert(k, ContainerID.valueOf(x));
}
}
for (int x = 1; x < 101; x += 2) {
- containerAttribute.remove("Key1", ContainerID.valueOf(x));
+ containerAttribute.remove(key1, ContainerID.valueOf(x));
}
for (int x = 1; x < 101; x += 2) {
- assertFalse(containerAttribute.hasContainerID("Key1",
- ContainerID.valueOf(x)));
+ assertFalse(hasContainerID(containerAttribute, key1, x));
}
- assertEquals(100, containerAttribute.getCollection("Key2").size());
+ assertEquals(100, containerAttribute.getCollection(key2).size());
- assertEquals(100, containerAttribute.getCollection("Key3").size());
+ assertEquals(100, containerAttribute.getCollection(key3).size());
- assertEquals(50, containerAttribute.getCollection("Key1").size());
+ assertEquals(50, containerAttribute.getCollection(key1).size());
}
@Test
public void tesUpdate() throws SCMException {
- String key1 = "Key1";
- String key2 = "Key2";
- String key3 = "Key3";
-
- ContainerAttribute containerAttribute = new ContainerAttribute<>();
+ ContainerAttribute containerAttribute = new ContainerAttribute<>(Key.class);
ContainerID id = ContainerID.valueOf(42);
containerAttribute.insert(key1, id);
- assertTrue(containerAttribute.hasContainerID(key1, id));
- assertFalse(containerAttribute.hasContainerID(key2, id));
+ assertTrue(hasContainerID(containerAttribute, key1, id));
+ assertFalse(hasContainerID(containerAttribute, key2, id));
// This should move the id from key1 bucket to key2 bucket.
containerAttribute.update(key1, key2, id);
- assertFalse(containerAttribute.hasContainerID(key1, id));
- assertTrue(containerAttribute.hasContainerID(key2, id));
+ assertFalse(hasContainerID(containerAttribute, key1, id));
+ assertTrue(hasContainerID(containerAttribute, key2, id));
// This should fail since we cannot find this id in the key3 bucket.
assertThrows(SCMException.class,