1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.junit.Assert.*;
22
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Random;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.hbase.HBaseConfiguration;
31 import org.apache.hadoop.hbase.MultithreadedTestUtil;
32 import org.apache.hadoop.hbase.MultithreadedTestUtil.TestThread;
33 import org.apache.hadoop.hbase.testclassification.SmallTests;
34 import org.apache.hadoop.hbase.util.ByteRange;
35 import org.junit.Test;
36
37 import com.google.common.collect.Iterables;
38 import com.google.common.collect.Lists;
39 import com.google.common.collect.Maps;
40 import com.google.common.primitives.Ints;
41
42 import org.junit.experimental.categories.Category;
43
44 @Category(SmallTests.class)
45 public class TestMemStoreLAB {
46
47
48
49
50 @Test
51 public void testLABRandomAllocation() {
52 Random rand = new Random();
53 MemStoreLAB mslab = new HeapMemStoreLAB();
54 int expectedOff = 0;
55 byte[] lastBuffer = null;
56
57
58
59 for (int i = 0; i < 100000; i++) {
60 int size = rand.nextInt(1000);
61 ByteRange alloc = mslab.allocateBytes(size);
62
63 if (alloc.getBytes() != lastBuffer) {
64 expectedOff = 0;
65 lastBuffer = alloc.getBytes();
66 }
67 assertEquals(expectedOff, alloc.getOffset());
68 assertTrue("Allocation overruns buffer",
69 alloc.getOffset() + size <= alloc.getBytes().length);
70 expectedOff += size;
71 }
72 }
73
74 @Test
75 public void testLABLargeAllocation() {
76 MemStoreLAB mslab = new HeapMemStoreLAB();
77 ByteRange alloc = mslab.allocateBytes(2*1024*1024);
78 assertNull("2MB allocation shouldn't be satisfied by LAB.",
79 alloc);
80 }
81
82
83
84
85
86 @Test
87 public void testLABThreading() throws Exception {
88 Configuration conf = new Configuration();
89 MultithreadedTestUtil.TestContext ctx =
90 new MultithreadedTestUtil.TestContext(conf);
91
92 final AtomicInteger totalAllocated = new AtomicInteger();
93
94 final MemStoreLAB mslab = new HeapMemStoreLAB();
95 List<List<AllocRecord>> allocations = Lists.newArrayList();
96
97 for (int i = 0; i < 10; i++) {
98 final List<AllocRecord> allocsByThisThread = Lists.newLinkedList();
99 allocations.add(allocsByThisThread);
100
101 TestThread t = new MultithreadedTestUtil.RepeatingTestThread(ctx) {
102 private Random r = new Random();
103 @Override
104 public void doAnAction() throws Exception {
105 int size = r.nextInt(1000);
106 ByteRange alloc = mslab.allocateBytes(size);
107 totalAllocated.addAndGet(size);
108 allocsByThisThread.add(new AllocRecord(alloc, size));
109 }
110 };
111 ctx.addThread(t);
112 }
113
114 ctx.startThreads();
115 while (totalAllocated.get() < 50*1024*1024 && ctx.shouldRun()) {
116 Thread.sleep(10);
117 }
118 ctx.stop();
119
120
121
122 Map<byte[], Map<Integer, AllocRecord>> mapsByChunk =
123 Maps.newHashMap();
124
125 int sizeCounted = 0;
126 for (AllocRecord rec : Iterables.concat(allocations)) {
127 sizeCounted += rec.size;
128 if (rec.size == 0) continue;
129
130 Map<Integer, AllocRecord> mapForThisByteArray =
131 mapsByChunk.get(rec.alloc.getBytes());
132 if (mapForThisByteArray == null) {
133 mapForThisByteArray = Maps.newTreeMap();
134 mapsByChunk.put(rec.alloc.getBytes(), mapForThisByteArray);
135 }
136 AllocRecord oldVal = mapForThisByteArray.put(rec.alloc.getOffset(), rec);
137 assertNull("Already had an entry " + oldVal + " for allocation " + rec,
138 oldVal);
139 }
140 assertEquals("Sanity check test", sizeCounted, totalAllocated.get());
141
142
143 for (Map<Integer, AllocRecord> allocsInChunk : mapsByChunk.values()) {
144 int expectedOff = 0;
145 for (AllocRecord alloc : allocsInChunk.values()) {
146 assertEquals(expectedOff, alloc.alloc.getOffset());
147 assertTrue("Allocation overruns buffer",
148 alloc.alloc.getOffset() + alloc.size <= alloc.alloc.getBytes().length);
149 expectedOff += alloc.size;
150 }
151 }
152
153 }
154
155
156
157
158
159
160 @Test
161 public void testLABChunkQueue() throws Exception {
162 HeapMemStoreLAB mslab = new HeapMemStoreLAB();
163
164 assertNull(mslab.getChunkQueue());
165
166 Configuration conf = HBaseConfiguration.create();
167 conf.setDouble(MemStoreChunkPool.CHUNK_POOL_MAXSIZE_KEY, 0.1);
168
169 conf.setLong(HeapMemStoreLAB.CHUNK_SIZE_KEY, HeapMemStoreLAB.MAX_ALLOC_DEFAULT);
170
171 MemStoreChunkPool.clearDisableFlag();
172 mslab = new HeapMemStoreLAB(conf);
173
174 List<Thread> threads = new ArrayList<Thread>();
175 for (int i = 0; i < 10; i++) {
176 threads.add(getChunkQueueTestThread(mslab, "testLABChunkQueue-" + i));
177 }
178 for (Thread thread : threads) {
179 thread.start();
180 }
181
182 Thread.sleep(1000);
183 for (Thread thread : threads) {
184 thread.interrupt();
185 }
186 boolean threadsRunning = true;
187 while (threadsRunning) {
188 for (Thread thread : threads) {
189 if (thread.isAlive()) {
190 threadsRunning = true;
191 break;
192 }
193 }
194 threadsRunning = false;
195 }
196
197 mslab.close();
198
199 int queueLength = mslab.getChunkQueue().size();
200 assertTrue("All chunks in chunk queue should be reclaimed or removed"
201 + " after mslab closed but actually: " + queueLength, queueLength == 0);
202 }
203
204 private Thread getChunkQueueTestThread(final HeapMemStoreLAB mslab, String threadName) {
205 Thread thread = new Thread() {
206 boolean stopped = false;
207
208 @Override
209 public void run() {
210 while (!stopped) {
211
212 mslab.allocateBytes(HeapMemStoreLAB.MAX_ALLOC_DEFAULT - 1);
213 }
214 }
215
216 @Override
217 public void interrupt() {
218 this.stopped = true;
219 }
220 };
221 thread.setName(threadName);
222 thread.setDaemon(true);
223 return thread;
224 }
225
226 private static class AllocRecord implements Comparable<AllocRecord>{
227 private final ByteRange alloc;
228 private final int size;
229 public AllocRecord(ByteRange alloc, int size) {
230 super();
231 this.alloc = alloc;
232 this.size = size;
233 }
234
235 @Override
236 public int compareTo(AllocRecord e) {
237 if (alloc.getBytes() != e.alloc.getBytes()) {
238 throw new RuntimeException("Can only compare within a particular array");
239 }
240 return Ints.compare(alloc.getOffset(), e.alloc.getOffset());
241 }
242
243 @Override
244 public String toString() {
245 return "AllocRecord(offset=" + alloc.getOffset() + ", size=" + size + ")";
246 }
247
248 }
249
250 }
251