Skip to content

Commit 03c80f8

Browse files
authored
HDDS-12591. Include ContainerInfo in ContainerAttribute. (#8083)
1 parent 49a2c85 commit 03c80f8

File tree

3 files changed

+81
-80
lines changed

3 files changed

+81
-80
lines changed

hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerAttribute.java

+38-48
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@
2020
import static org.apache.hadoop.hdds.scm.exceptions.SCMException.ResultCodes.FAILED_TO_CHANGE_CONTAINER_STATE;
2121

2222
import com.google.common.collect.ImmutableMap;
23-
import com.google.common.collect.ImmutableSortedSet;
2423
import com.google.common.collect.Maps;
24+
import java.util.ArrayList;
2525
import java.util.EnumMap;
26-
import java.util.NavigableSet;
26+
import java.util.List;
27+
import java.util.NavigableMap;
2728
import java.util.Objects;
28-
import java.util.SortedSet;
29-
import java.util.TreeSet;
29+
import java.util.SortedMap;
30+
import java.util.TreeMap;
3031
import org.apache.hadoop.hdds.scm.container.ContainerID;
32+
import org.apache.hadoop.hdds.scm.container.ContainerInfo;
3133
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
32-
import org.slf4j.Logger;
33-
import org.slf4j.LoggerFactory;
34+
import org.apache.ratis.util.Preconditions;
3435

3536
/**
3637
* Each Attribute that we manage for a container is maintained as a map.
@@ -60,37 +61,30 @@
6061
* @param <T> Attribute type
6162
*/
6263
public class ContainerAttribute<T extends Enum<T>> {
63-
private static final Logger LOG =
64-
LoggerFactory.getLogger(ContainerAttribute.class);
65-
6664
private final Class<T> attributeClass;
67-
private final ImmutableMap<T, NavigableSet<ContainerID>> attributeMap;
65+
private final ImmutableMap<T, NavigableMap<ContainerID, ContainerInfo>> attributeMap;
6866

6967
/**
7068
* Create an empty Container Attribute map.
7169
*/
7270
public ContainerAttribute(Class<T> attributeClass) {
7371
this.attributeClass = attributeClass;
7472

75-
final EnumMap<T, NavigableSet<ContainerID>> map = new EnumMap<>(attributeClass);
73+
final EnumMap<T, NavigableMap<ContainerID, ContainerInfo>> map = new EnumMap<>(attributeClass);
7674
for (T t : attributeClass.getEnumConstants()) {
77-
map.put(t, new TreeSet<>());
75+
map.put(t, new TreeMap<>());
7876
}
7977
this.attributeMap = Maps.immutableEnumMap(map);
8078
}
8179

8280
/**
83-
* Insert the value in the Attribute map, keep the original value if it exists
84-
* already.
85-
*
86-
* @param key - The key to the set where the ContainerID should exist.
87-
* @param value - Actual Container ID.
88-
* @return true if the value is added;
89-
* otherwise, the value already exists, return false.
81+
* Add the given non-existing {@link ContainerInfo} to this attribute.
82+
* @throws IllegalStateException if it already exists.
9083
*/
91-
public boolean insert(T key, ContainerID value) {
92-
Objects.requireNonNull(value, "value == null");
93-
return get(key).add(value);
84+
public void addNonExisting(T key, ContainerInfo info) {
85+
Objects.requireNonNull(info, "value == null");
86+
final ContainerInfo previous = get(key).put(info.containerID(), info);
87+
Preconditions.assertNull(previous, "previous");
9488
}
9589

9690
/**
@@ -103,30 +97,30 @@ public void clearSet(T key) {
10397
}
10498

10599
/**
106-
* Removes a container ID from the set pointed by the key.
107-
*
108-
* @param key - key to identify the set.
109-
* @param value - Container ID
100+
* Remove a container for the given id.
101+
* @return the info if there was a mapping for the id; otherwise, return null
110102
*/
111-
public boolean remove(T key, ContainerID value) {
112-
Objects.requireNonNull(value, "value == null");
103+
public ContainerInfo remove(T key, ContainerID id) {
104+
Objects.requireNonNull(id, "id == null");
105+
return get(key).remove(id);
106+
}
113107

114-
if (!get(key).remove(value)) {
115-
LOG.debug("Container {} not found in {} attribute", value, key);
116-
return false;
117-
}
118-
return true;
108+
/** Remove an existing {@link ContainerInfo}. */
109+
public void removeExisting(T key, ContainerInfo existing) {
110+
Objects.requireNonNull(existing, "existing == null");
111+
final ContainerInfo removed = remove(key, existing.containerID());
112+
Preconditions.assertSame(existing, removed, "removed");
119113
}
120114

121-
NavigableSet<ContainerID> get(T attribute) {
115+
NavigableMap<ContainerID, ContainerInfo> get(T attribute) {
122116
Objects.requireNonNull(attribute, "attribute == null");
123117

124-
final NavigableSet<ContainerID> set = attributeMap.get(attribute);
125-
if (set == null) {
118+
final NavigableMap<ContainerID, ContainerInfo> map = attributeMap.get(attribute);
119+
if (map == null) {
126120
throw new IllegalStateException("Attribute not found: " + attribute
127121
+ " (" + attributeClass.getSimpleName() + ")");
128122
}
129-
return set;
123+
return map;
130124
}
131125

132126
/**
@@ -135,13 +129,13 @@ NavigableSet<ContainerID> get(T attribute) {
135129
* @param key - Key to the bucket.
136130
* @return Underlying Set in immutable form.
137131
*/
138-
public NavigableSet<ContainerID> getCollection(T key) {
139-
return ImmutableSortedSet.copyOf(get(key));
132+
public List<ContainerInfo> getCollection(T key) {
133+
return new ArrayList<>(get(key).values());
140134
}
141135

142-
public SortedSet<ContainerID> tailSet(T key, ContainerID start) {
136+
public SortedMap<ContainerID, ContainerInfo> tailMap(T key, ContainerID start) {
143137
Objects.requireNonNull(start, "start == null");
144-
return get(key).tailSet(start);
138+
return get(key).tailMap(start);
145139
}
146140

147141
public int count(T key) {
@@ -163,17 +157,13 @@ public void update(T currentKey, T newKey, ContainerID value)
163157
}
164158

165159
Objects.requireNonNull(newKey, "newKey == null");
166-
final boolean removed = remove(currentKey, value);
167-
if (!removed) {
160+
final ContainerInfo removed = remove(currentKey, value);
161+
if (removed == null) {
168162
throw new SCMException("Failed to update Container " + value + " from " + currentKey + " to " + newKey
169163
+ ": Container " + value + " not found in attribute " + currentKey,
170164
FAILED_TO_CHANGE_CONTAINER_STATE);
171165
}
172166

173-
final boolean inserted = insert(newKey, value);
174-
if (!inserted) {
175-
LOG.warn("Update Container {} from {} to {}: Container {} already exists in {}",
176-
value, currentKey, newKey, value, newKey);
177-
}
167+
addNonExisting(newKey, removed);
178168
}
179169
}

hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/states/ContainerStateMap.java

+25-15
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@
6868
* select a container that belongs to user1, with Ratis replication which can
6969
* make 3 copies of data. The fact that we will look for open containers by
7070
* default and if we cannot find them we will add new containers.
71-
*
71+
* <p>
7272
* All the calls are idempotent.
73+
* <p>
74+
* This class is NOT thread-safe.
7375
*/
7476
public class ContainerStateMap {
7577
private static final Logger LOG =
@@ -167,9 +169,23 @@ ContainerReplica removeReplica(ContainerID containerID, DatanodeID datanodeID) {
167169
return entry == null ? null : entry.removeReplica(datanodeID);
168170
}
169171
}
170-
172+
/**
173+
* Map {@link LifeCycleState} to {@link ContainerInfo}.
174+
* Note that a {@link ContainerInfo} can only exists in at most one of the {@link LifeCycleState}s.
175+
*/
171176
private final ContainerAttribute<LifeCycleState> lifeCycleStateMap = new ContainerAttribute<>(LifeCycleState.class);
177+
/**
178+
* Map {@link ReplicationType} to {@link ContainerInfo}.
179+
* Note that a {@link ContainerInfo} can only exists in at most one of the {@link ReplicationType}s.
180+
*/
172181
private final ContainerAttribute<ReplicationType> typeMap = new ContainerAttribute<>(ReplicationType.class);
182+
/**
183+
* Map {@link ContainerID} to ({@link ContainerInfo} and {@link ContainerReplica}).
184+
* Note that the following sets are exactly the same
185+
* 1. The {@link ContainerInfo} in this map.
186+
* 2. The {@link ContainerInfo} in the union of all the states in {@link #lifeCycleStateMap}.
187+
* 2. The {@link ContainerInfo} in the union of all the types in {@link #typeMap}.
188+
*/
173189
private final ContainerMap containerMap = new ContainerMap();
174190

175191
/**
@@ -191,9 +207,8 @@ public static Logger getLogger() {
191207
public void addContainer(final ContainerInfo info) {
192208
Objects.requireNonNull(info, "info == null");
193209
if (containerMap.addIfAbsent(info)) {
194-
final ContainerID id = info.containerID();
195-
lifeCycleStateMap.insert(info.getState(), id);
196-
typeMap.insert(info.getReplicationType(), id);
210+
lifeCycleStateMap.addNonExisting(info.getState(), info);
211+
typeMap.addNonExisting(info.getReplicationType(), info);
197212
LOG.trace("Added {}", info);
198213
}
199214
}
@@ -211,8 +226,8 @@ public void removeContainer(final ContainerID id) {
211226
Objects.requireNonNull(id, "id == null");
212227
final ContainerInfo info = containerMap.remove(id);
213228
if (info != null) {
214-
lifeCycleStateMap.remove(info.getState(), id);
215-
typeMap.remove(info.getReplicationType(), id);
229+
lifeCycleStateMap.removeExisting(info.getState(), info);
230+
typeMap.removeExisting(info.getReplicationType(), info);
216231
LOG.trace("Removed {}", info);
217232
}
218233
}
@@ -291,22 +306,17 @@ public List<ContainerInfo> getContainerInfos(ContainerID start, int count) {
291306
*/
292307
public List<ContainerInfo> getContainerInfos(LifeCycleState state, ContainerID start, int count) {
293308
Preconditions.assertTrue(count >= 0, "count < 0");
294-
return lifeCycleStateMap.tailSet(state, start).stream()
295-
.map(this::getContainerInfo)
309+
return lifeCycleStateMap.tailMap(state, start).values().stream()
296310
.limit(count)
297311
.collect(Collectors.toList());
298312
}
299313

300314
public List<ContainerInfo> getContainerInfos(LifeCycleState state) {
301-
return lifeCycleStateMap.getCollection(state).stream()
302-
.map(this::getContainerInfo)
303-
.collect(Collectors.toList());
315+
return lifeCycleStateMap.getCollection(state);
304316
}
305317

306318
public List<ContainerInfo> getContainerInfos(ReplicationType type) {
307-
return typeMap.getCollection(type).stream()
308-
.map(this::getContainerInfo)
309-
.collect(Collectors.toList());
319+
return typeMap.getCollection(type);
310320
}
311321

312322
/** @return the number of containers for the given state. */

hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/states/TestContainerAttribute.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
import static org.junit.jupiter.api.Assertions.assertThrows;
2424
import static org.junit.jupiter.api.Assertions.assertTrue;
2525

26-
import java.util.NavigableSet;
26+
import java.util.NavigableMap;
2727
import org.apache.hadoop.hdds.scm.container.ContainerID;
28+
import org.apache.hadoop.hdds.scm.container.ContainerInfo;
2829
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
2930
import org.junit.jupiter.api.Test;
3031

@@ -43,32 +44,30 @@ static <T extends Enum<T>> boolean hasContainerID(ContainerAttribute<T> attribut
4344
}
4445

4546
static <T extends Enum<T>> boolean hasContainerID(ContainerAttribute<T> attribute, T key, ContainerID id) {
46-
final NavigableSet<ContainerID> set = attribute.get(key);
47-
return set != null && set.contains(id);
47+
final NavigableMap<ContainerID, ContainerInfo> map = attribute.get(key);
48+
return map != null && map.containsKey(id);
4849
}
4950

5051
@Test
51-
public void testInsert() {
52+
public void testAddNonExisting() {
5253
ContainerAttribute<Key> containerAttribute = new ContainerAttribute<>(Key.class);
53-
ContainerID id = ContainerID.valueOf(42);
54-
containerAttribute.insert(key1, id);
54+
ContainerInfo info = new ContainerInfo.Builder().setContainerID(42).build();
55+
ContainerID id = info.containerID();
56+
containerAttribute.addNonExisting(key1, info);
5557
assertEquals(1, containerAttribute.getCollection(key1).size());
56-
assertThat(containerAttribute.getCollection(key1)).contains(id);
58+
assertThat(containerAttribute.get(key1)).containsKey(id);
5759

58-
// Insert again and verify that the new ContainerId is inserted.
59-
ContainerID newId =
60-
ContainerID.valueOf(42);
61-
containerAttribute.insert(key1, newId);
62-
assertEquals(1, containerAttribute.getCollection(key1).size());
63-
assertThat(containerAttribute.getCollection(key1)).contains(newId);
60+
// Adding it again should fail.
61+
assertThrows(IllegalStateException.class, () -> containerAttribute.addNonExisting(key1, info));
6462
}
6563

6664
@Test
6765
public void testClearSet() {
6866
ContainerAttribute<Key> containerAttribute = new ContainerAttribute<>(Key.class);
6967
for (Key k : Key.values()) {
7068
for (int x = 1; x < 101; x++) {
71-
containerAttribute.insert(k, ContainerID.valueOf(x));
69+
ContainerInfo info = new ContainerInfo.Builder().setContainerID(x).build();
70+
containerAttribute.addNonExisting(k, info);
7271
}
7372
}
7473
for (Key k : Key.values()) {
@@ -85,7 +84,8 @@ public void testRemove() {
8584

8685
for (Key k : Key.values()) {
8786
for (int x = 1; x < 101; x++) {
88-
containerAttribute.insert(k, ContainerID.valueOf(x));
87+
ContainerInfo info = new ContainerInfo.Builder().setContainerID(x).build();
88+
containerAttribute.addNonExisting(k, info);
8989
}
9090
}
9191
for (int x = 1; x < 101; x += 2) {
@@ -106,9 +106,10 @@ public void testRemove() {
106106
@Test
107107
public void tesUpdate() throws SCMException {
108108
ContainerAttribute<Key> containerAttribute = new ContainerAttribute<>(Key.class);
109-
ContainerID id = ContainerID.valueOf(42);
109+
ContainerInfo info = new ContainerInfo.Builder().setContainerID(42).build();
110+
ContainerID id = info.containerID();
110111

111-
containerAttribute.insert(key1, id);
112+
containerAttribute.addNonExisting(key1, info);
112113
assertTrue(hasContainerID(containerAttribute, key1, id));
113114
assertFalse(hasContainerID(containerAttribute, key2, id));
114115

0 commit comments

Comments
 (0)