HDFS-12042. Lazy initialize AbstractINodeDiffList#diffs for snapshots to reduce memory consumption. Contributed by Misha Dmitriev.
(cherry picked from commit bcba844d11
)
This commit is contained in:
parent
2d69952925
commit
94bc5cdbb3
|
@ -65,8 +65,11 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
||||||
return inode.asDirectory();
|
return inode.asDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final int DEFAULT_FILES_PER_DIRECTORY = 5;
|
// Profiling shows that most of the file lists are between 1 and 4 elements.
|
||||||
final static byte[] ROOT_NAME = DFSUtil.string2Bytes("");
|
// Thus allocate the corresponding ArrayLists with a small initial capacity.
|
||||||
|
public static final int DEFAULT_FILES_PER_DIRECTORY = 2;
|
||||||
|
|
||||||
|
static final byte[] ROOT_NAME = DFSUtil.string2Bytes("");
|
||||||
|
|
||||||
private List<INode> children = null;
|
private List<INode> children = null;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.hdfs.server.namenode.INode;
|
import org.apache.hadoop.hdfs.server.namenode.INode;
|
||||||
import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
|
import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
|
||||||
|
import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of snapshot diffs for storing snapshot data.
|
* A list of snapshot diffs for storing snapshot data.
|
||||||
|
@ -35,17 +36,19 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
A extends INodeAttributes,
|
A extends INodeAttributes,
|
||||||
D extends AbstractINodeDiff<N, A, D>>
|
D extends AbstractINodeDiff<N, A, D>>
|
||||||
implements Iterable<D> {
|
implements Iterable<D> {
|
||||||
/** Diff list sorted by snapshot IDs, i.e. in chronological order. */
|
/** Diff list sorted by snapshot IDs, i.e. in chronological order.
|
||||||
private final List<D> diffs = new ArrayList<D>();
|
* Created lazily to avoid wasting memory by empty lists. */
|
||||||
|
private List<D> diffs;
|
||||||
|
|
||||||
/** @return this list as a unmodifiable {@link List}. */
|
/** @return this list as a unmodifiable {@link List}. */
|
||||||
public final List<D> asList() {
|
public final List<D> asList() {
|
||||||
return Collections.unmodifiableList(diffs);
|
return diffs != null ?
|
||||||
|
Collections.unmodifiableList(diffs) : Collections.<D>emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the size of the list and then clear it. */
|
/** Clear the list. */
|
||||||
public void clear() {
|
public void clear() {
|
||||||
diffs.clear();
|
diffs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return an {@link AbstractINodeDiff}. */
|
/** @return an {@link AbstractINodeDiff}. */
|
||||||
|
@ -66,6 +69,9 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
*/
|
*/
|
||||||
public final void deleteSnapshotDiff(INode.ReclaimContext reclaimContext,
|
public final void deleteSnapshotDiff(INode.ReclaimContext reclaimContext,
|
||||||
final int snapshot, final int prior, final N currentINode) {
|
final int snapshot, final int prior, final N currentINode) {
|
||||||
|
if (diffs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int snapshotIndex = Collections.binarySearch(diffs, snapshot);
|
int snapshotIndex = Collections.binarySearch(diffs, snapshot);
|
||||||
|
|
||||||
D removed;
|
D removed;
|
||||||
|
@ -75,6 +81,9 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
diffs.get(snapshotIndex).setSnapshotId(prior);
|
diffs.get(snapshotIndex).setSnapshotId(prior);
|
||||||
} else { // there is no snapshot before
|
} else { // there is no snapshot before
|
||||||
removed = diffs.remove(0);
|
removed = diffs.remove(0);
|
||||||
|
if (diffs.isEmpty()) {
|
||||||
|
diffs = null;
|
||||||
|
}
|
||||||
removed.destroyDiffAndCollectBlocks(reclaimContext, currentINode);
|
removed.destroyDiffAndCollectBlocks(reclaimContext, currentINode);
|
||||||
}
|
}
|
||||||
} else if (snapshotIndex > 0) {
|
} else if (snapshotIndex > 0) {
|
||||||
|
@ -103,6 +112,7 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
|
|
||||||
/** Append the diff at the end of the list. */
|
/** Append the diff at the end of the list. */
|
||||||
private D addLast(D diff) {
|
private D addLast(D diff) {
|
||||||
|
createDiffsIfNeeded();
|
||||||
final D last = getLast();
|
final D last = getLast();
|
||||||
diffs.add(diff);
|
diffs.add(diff);
|
||||||
if (last != null) {
|
if (last != null) {
|
||||||
|
@ -113,15 +123,25 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
|
|
||||||
/** Add the diff to the beginning of the list. */
|
/** Add the diff to the beginning of the list. */
|
||||||
final void addFirst(D diff) {
|
final void addFirst(D diff) {
|
||||||
final D first = diffs.isEmpty()? null: diffs.get(0);
|
createDiffsIfNeeded();
|
||||||
|
final D first = diffs.isEmpty()? null : diffs.get(0);
|
||||||
diffs.add(0, diff);
|
diffs.add(0, diff);
|
||||||
diff.setPosterior(first);
|
diff.setPosterior(first);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the last diff. */
|
/** @return the last diff. */
|
||||||
public final D getLast() {
|
public final D getLast() {
|
||||||
final int n = diffs.size();
|
if (diffs == null) {
|
||||||
return n == 0? null: diffs.get(n - 1);
|
return null;
|
||||||
|
}
|
||||||
|
int n = diffs.size();
|
||||||
|
return n == 0 ? null : diffs.get(n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createDiffsIfNeeded() {
|
||||||
|
if (diffs == null) {
|
||||||
|
diffs = new ArrayList<>(INodeDirectory.DEFAULT_FILES_PER_DIRECTORY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the id of the last snapshot. */
|
/** @return the id of the last snapshot. */
|
||||||
|
@ -139,10 +159,14 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
* @return The id of the latest snapshot before the given snapshot.
|
* @return The id of the latest snapshot before the given snapshot.
|
||||||
*/
|
*/
|
||||||
public final int getPrior(int anchorId, boolean exclusive) {
|
public final int getPrior(int anchorId, boolean exclusive) {
|
||||||
|
if (diffs == null) {
|
||||||
|
return Snapshot.NO_SNAPSHOT_ID;
|
||||||
|
}
|
||||||
if (anchorId == Snapshot.CURRENT_STATE_ID) {
|
if (anchorId == Snapshot.CURRENT_STATE_ID) {
|
||||||
int last = getLastSnapshotId();
|
int last = getLastSnapshotId();
|
||||||
if(exclusive && last == anchorId)
|
if (exclusive && last == anchorId) {
|
||||||
return Snapshot.NO_SNAPSHOT_ID;
|
return Snapshot.NO_SNAPSHOT_ID;
|
||||||
|
}
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
final int i = Collections.binarySearch(diffs, anchorId);
|
final int i = Collections.binarySearch(diffs, anchorId);
|
||||||
|
@ -181,7 +205,7 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
}
|
}
|
||||||
|
|
||||||
public final D getDiffById(final int snapshotId) {
|
public final D getDiffById(final int snapshotId) {
|
||||||
if (snapshotId == Snapshot.CURRENT_STATE_ID) {
|
if (snapshotId == Snapshot.CURRENT_STATE_ID || diffs == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final int i = Collections.binarySearch(diffs, snapshotId);
|
final int i = Collections.binarySearch(diffs, snapshotId);
|
||||||
|
@ -193,7 +217,7 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
// given snapshot and the next state so that the diff for the given
|
// given snapshot and the next state so that the diff for the given
|
||||||
// snapshot was not recorded. Thus, return the next state.
|
// snapshot was not recorded. Thus, return the next state.
|
||||||
final int j = -i - 1;
|
final int j = -i - 1;
|
||||||
return j < diffs.size()? diffs.get(j): null;
|
return j < diffs.size() ? diffs.get(j) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +231,9 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
}
|
}
|
||||||
|
|
||||||
final int[] changedBetweenSnapshots(Snapshot from, Snapshot to) {
|
final int[] changedBetweenSnapshots(Snapshot from, Snapshot to) {
|
||||||
|
if (diffs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Snapshot earlier = from;
|
Snapshot earlier = from;
|
||||||
Snapshot later = to;
|
Snapshot later = to;
|
||||||
if (Snapshot.ID_COMPARATOR.compare(from, to) > 0) {
|
if (Snapshot.ID_COMPARATOR.compare(from, to) > 0) {
|
||||||
|
@ -275,11 +302,11 @@ abstract class AbstractINodeDiffList<N extends INode,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<D> iterator() {
|
public Iterator<D> iterator() {
|
||||||
return diffs.iterator();
|
return diffs != null ? diffs.iterator() : Collections.<D>emptyIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + ": " + diffs;
|
return getClass().getSimpleName() + ": " + (diffs != null ? diffs : "[]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,7 @@ public class TestTruncateQuotaUpdate {
|
||||||
FileDiff diff = mock(FileDiff.class);
|
FileDiff diff = mock(FileDiff.class);
|
||||||
when(diff.getBlocks()).thenReturn(blocks);
|
when(diff.getBlocks()).thenReturn(blocks);
|
||||||
FileDiffList diffList = new FileDiffList();
|
FileDiffList diffList = new FileDiffList();
|
||||||
|
Whitebox.setInternalState(diffList, "diffs", new ArrayList<FileDiff>());
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ArrayList<FileDiff> diffs = ((ArrayList<FileDiff>)Whitebox.getInternalState
|
ArrayList<FileDiff> diffs = ((ArrayList<FileDiff>)Whitebox.getInternalState
|
||||||
(diffList, "diffs"));
|
(diffList, "diffs"));
|
||||||
|
|
Loading…
Reference in New Issue