HBASE-16194 Should count in MSLAB chunk allocation into heap size change when adding duplicate cells

This commit is contained in:
Yu Li 2016-07-10 13:45:17 +08:00
parent 632969787a
commit 9cf012cd00
6 changed files with 73 additions and 11 deletions

View File

@ -109,7 +109,8 @@ public abstract class AbstractMemStore implements MemStore {
@Override @Override
public long add(Cell cell) { public long add(Cell cell) {
Cell toAdd = maybeCloneWithAllocator(cell); Cell toAdd = maybeCloneWithAllocator(cell);
return internalAdd(toAdd); boolean useMSLAB = (toAdd != cell);
return internalAdd(toAdd, useMSLAB);
} }
/** /**
@ -156,7 +157,8 @@ public abstract class AbstractMemStore implements MemStore {
@Override @Override
public long delete(Cell deleteCell) { public long delete(Cell deleteCell) {
Cell toAdd = maybeCloneWithAllocator(deleteCell); Cell toAdd = maybeCloneWithAllocator(deleteCell);
long s = internalAdd(toAdd); boolean useMSLAB = (toAdd != deleteCell);
long s = internalAdd(toAdd, useMSLAB);
return s; return s;
} }
@ -243,7 +245,7 @@ public abstract class AbstractMemStore implements MemStore {
// hitting OOME - see TestMemStore.testUpsertMSLAB for a // hitting OOME - see TestMemStore.testUpsertMSLAB for a
// test that triggers the pathological case if we don't avoid MSLAB // test that triggers the pathological case if we don't avoid MSLAB
// here. // here.
long addedSize = internalAdd(cell); long addedSize = internalAdd(cell, false);
// Get the Cells for the row/family/qualifier regardless of timestamp. // Get the Cells for the row/family/qualifier regardless of timestamp.
// For this case we want to clean up any other puts // For this case we want to clean up any other puts
@ -387,9 +389,12 @@ public abstract class AbstractMemStore implements MemStore {
* allocator, and doesn't take the lock. * allocator, and doesn't take the lock.
* *
* Callers should ensure they already have the read lock taken * Callers should ensure they already have the read lock taken
* @param toAdd the cell to add
* @param useMSLAB whether using MSLAB
* @return the heap size change in bytes
*/ */
private long internalAdd(final Cell toAdd) { private long internalAdd(final Cell toAdd, final boolean useMSLAB) {
long s = active.add(toAdd); long s = active.add(toAdd, useMSLAB);
setOldestEditTimeToNow(); setOldestEditTimeToNow();
checkActiveSize(); checkActiveSize();
return s; return s;

View File

@ -29,6 +29,7 @@ import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.ByteRange;
import org.apache.hadoop.hbase.util.SimpleMutableByteRange; import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
/** /**
@ -206,6 +207,11 @@ public class HeapMemStoreLAB implements MemStoreLAB {
} }
} }
@VisibleForTesting
Chunk getCurrentChunk() {
return this.curChunk.get();
}
/** /**
* A chunk of memory out of which allocations are sliced. * A chunk of memory out of which allocations are sliced.
*/ */
@ -311,5 +317,10 @@ public class HeapMemStoreLAB implements MemStoreLAB {
" allocs=" + allocCount.get() + "waste=" + " allocs=" + allocCount.get() + "waste=" +
(data.length - nextFreeOffset.get()); (data.length - nextFreeOffset.get());
} }
@VisibleForTesting
int getNextFreeOffset() {
return this.nextFreeOffset.get();
}
} }
} }

View File

@ -197,7 +197,8 @@ class MemStoreCompactor {
// The scanner is doing all the elimination logic // The scanner is doing all the elimination logic
// now we just copy it to the new segment // now we just copy it to the new segment
Cell newKV = result.maybeCloneWithAllocator(c); Cell newKV = result.maybeCloneWithAllocator(c);
result.internalAdd(newKV); boolean useMSLAB = (newKV != c);
result.internalAdd(newKV, useMSLAB);
} }
kvs.clear(); kvs.clear();

View File

@ -35,10 +35,12 @@ public class MutableSegment extends Segment {
/** /**
* Adds the given cell into the segment * Adds the given cell into the segment
* @param cell the cell to add
* @param useMSLAB whether using MSLAB
* @return the change in the heap size * @return the change in the heap size
*/ */
public long add(Cell cell) { public long add(Cell cell, boolean useMSLAB) {
return internalAdd(cell); return internalAdd(cell, useMSLAB);
} }
/** /**

View File

@ -31,6 +31,8 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.ByteRange;
import com.google.common.annotations.VisibleForTesting;
/** /**
* This is an abstraction of a segment maintained in a memstore, e.g., the active * This is an abstraction of a segment maintained in a memstore, e.g., the active
* cell set or its snapshot. * cell set or its snapshot.
@ -136,7 +138,7 @@ public abstract class Segment {
return cell; return cell;
} }
int len = KeyValueUtil.length(cell); int len = getCellLength(cell);
ByteRange alloc = getMemStoreLAB().allocateBytes(len); ByteRange alloc = getMemStoreLAB().allocateBytes(len);
if (alloc == null) { if (alloc == null) {
// The allocation was too large, allocator decided // The allocation was too large, allocator decided
@ -150,6 +152,14 @@ public abstract class Segment {
return newKv; return newKv;
} }
/**
* Get cell length after serialized in {@link KeyValue}
*/
@VisibleForTesting
int getCellLength(Cell cell) {
return KeyValueUtil.length(cell);
}
public abstract boolean shouldSeek(Scan scan, long oldestUnexpiredTS); public abstract boolean shouldSeek(Scan scan, long oldestUnexpiredTS);
public abstract long getMinTimestamp(); public abstract long getMinTimestamp();
@ -240,9 +250,15 @@ public abstract class Segment {
return comparator; return comparator;
} }
protected long internalAdd(Cell cell) { protected long internalAdd(Cell cell, boolean useMSLAB) {
boolean succ = getCellSet().add(cell); boolean succ = getCellSet().add(cell);
long s = AbstractMemStore.heapSizeChange(cell, succ); long s = AbstractMemStore.heapSizeChange(cell, succ);
// If there's already a same cell in the CellSet and we are using MSLAB, we must count in the
// MSLAB allocation size as well, or else there will be memory leak (occupied heap size larger
// than the counted number)
if (!succ && useMSLAB) {
s += getCellLength(cell);
}
updateMetaInfo(cell, s); updateMetaInfo(cell, s);
return s; return s;
} }
@ -269,7 +285,8 @@ public abstract class Segment {
return getCellSet().tailSet(firstCell); return getCellSet().tailSet(firstCell);
} }
private MemStoreLAB getMemStoreLAB() { @VisibleForTesting
public MemStoreLAB getMemStoreLAB() {
return memStoreLAB; return memStoreLAB;
} }

View File

@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.regionserver;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
@ -56,6 +57,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import org.junit.experimental.categories.Category; import org.junit.experimental.categories.Category;
import org.junit.rules.TestName; import org.junit.rules.TestName;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
@ -110,6 +112,30 @@ public class TestDefaultMemStore {
assertTrue(Bytes.toString(found.getValueArray()), CellUtil.matchingValue(samekey, found)); assertTrue(Bytes.toString(found.getValueArray()), CellUtil.matchingValue(samekey, found));
} }
@Test
public void testPutSameCell() {
byte[] bytes = Bytes.toBytes(getName());
KeyValue kv = new KeyValue(bytes, bytes, bytes, bytes);
long sizeChangeForFirstCell = this.memstore.add(kv);
long sizeChangeForSecondCell = this.memstore.add(kv);
// make sure memstore size increase won't double-count MSLAB chunk size
assertEquals(AbstractMemStore.heapSizeChange(kv, true), sizeChangeForFirstCell);
Segment segment = this.memstore.getActive();
MemStoreLAB msLab = segment.getMemStoreLAB();
if (msLab != null) {
// make sure memstore size increased even when writing the same cell, if using MSLAB
assertEquals(segment.getCellLength(kv), sizeChangeForSecondCell);
// make sure chunk size increased even when writing the same cell, if using MSLAB
if (msLab instanceof HeapMemStoreLAB) {
assertEquals(2 * segment.getCellLength(kv),
((HeapMemStoreLAB) msLab).getCurrentChunk().getNextFreeOffset());
}
} else {
// make sure no memstore size change w/o MSLAB
assertEquals(0, sizeChangeForSecondCell);
}
}
/** /**
* Test memstore snapshot happening while scanning. * Test memstore snapshot happening while scanning.
* @throws IOException * @throws IOException