1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.regionserver;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.when;
27
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.HBaseTestingUtility;
35 import org.apache.hadoop.hbase.HColumnDescriptor;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.HRegionInfo;
38 import org.apache.hadoop.hbase.HTableDescriptor;
39 import org.apache.hadoop.hbase.KeyValue;
40 import org.apache.hadoop.hbase.Server;
41 import org.apache.hadoop.hbase.SmallTests;
42 import org.apache.hadoop.hbase.client.Scan;
43 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
44 import org.apache.hadoop.hbase.io.hfile.HFile;
45 import org.apache.hadoop.hbase.io.hfile.LruBlockCache;
46 import org.apache.hadoop.hbase.regionserver.wal.HLog;
47 import org.apache.hadoop.hbase.util.Bytes;
48 import org.apache.hadoop.hbase.util.PairOfSameType;
49 import org.apache.zookeeper.KeeperException;
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.experimental.categories.Category;
54 import org.mockito.Mockito;
55
56 import com.google.common.collect.ImmutableList;
57
58
59
60
61
62 @Category(SmallTests.class)
63 public class TestSplitTransaction {
64 private final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
65 private final Path testdir =
66 TEST_UTIL.getDataTestDir(this.getClass().getName());
67 private HRegion parent;
68 private HLog wal;
69 private FileSystem fs;
70 private static final byte [] STARTROW = new byte [] {'a', 'a', 'a'};
71
72 private static final byte [] ENDROW = new byte [] {'{', '{', '{'};
73 private static final byte [] GOOD_SPLIT_ROW = new byte [] {'d', 'd', 'd'};
74 private static final byte [] CF = HConstants.CATALOG_FAMILY;
75
76 @Before public void setup() throws IOException {
77 this.fs = FileSystem.get(TEST_UTIL.getConfiguration());
78 this.fs.delete(this.testdir, true);
79 this.wal = new HLog(fs, new Path(this.testdir, "logs"),
80 new Path(this.testdir, "archive"),
81 TEST_UTIL.getConfiguration());
82 this.parent = createRegion(this.testdir, this.wal);
83 TEST_UTIL.getConfiguration().setBoolean("hbase.testing.nocluster", true);
84 }
85
86 @After public void teardown() throws IOException {
87 if (this.parent != null && !this.parent.isClosed()) this.parent.close();
88 if (this.fs.exists(this.parent.getRegionDir()) &&
89 !this.fs.delete(this.parent.getRegionDir(), true)) {
90 throw new IOException("Failed delete of " + this.parent.getRegionDir());
91 }
92 if (this.wal != null) this.wal.closeAndDelete();
93 this.fs.delete(this.testdir, true);
94 }
95
96 @Test public void testFailAfterPONR() throws IOException, KeeperException {
97 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
98 assertTrue(rowcount > 0);
99 int parentRowCount = countRows(this.parent);
100 assertEquals(rowcount, parentRowCount);
101
102
103 SplitTransaction st = prepareGOOD_SPLIT_ROW();
104 SplitTransaction spiedUponSt = spy(st);
105 Mockito
106 .doThrow(new MockedFailedDaughterOpen())
107 .when(spiedUponSt)
108 .openDaughterRegion((Server) Mockito.anyObject(),
109 (HRegion) Mockito.anyObject());
110
111
112 boolean expectedException = false;
113 Server mockServer = Mockito.mock(Server.class);
114 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
115 try {
116 spiedUponSt.execute(mockServer, null);
117 } catch (IOException e) {
118 if (e.getCause() != null &&
119 e.getCause() instanceof MockedFailedDaughterOpen) {
120 expectedException = true;
121 }
122 }
123 assertTrue(expectedException);
124
125 assertFalse(spiedUponSt.rollback(null, null));
126
127
128
129 Path tableDir = this.parent.getRegionDir().getParent();
130 Path daughterADir =
131 new Path(tableDir, spiedUponSt.getFirstDaughter().getEncodedName());
132 Path daughterBDir =
133 new Path(tableDir, spiedUponSt.getSecondDaughter().getEncodedName());
134 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterADir));
135 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterBDir));
136 }
137
138
139
140
141
142 @Test public void testPrepare() throws IOException {
143 prepareGOOD_SPLIT_ROW();
144 }
145
146 private SplitTransaction prepareGOOD_SPLIT_ROW() {
147 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
148 assertTrue(st.prepare());
149 return st;
150 }
151
152
153
154
155 @Test public void testPrepareWithRegionsWithReference() throws IOException {
156
157 StoreFile storeFileMock = Mockito.mock(StoreFile.class);
158 when(storeFileMock.isReference()).thenReturn(true);
159
160
161 Store storeMock = Mockito.mock(Store.class);
162 List<StoreFile> storeFileList = new ArrayList<StoreFile>(1);
163 storeFileList.add(storeFileMock);
164 when(storeMock.getStorefiles()).thenReturn(storeFileList);
165 when(storeMock.close()).thenReturn(ImmutableList.copyOf(storeFileList));
166 this.parent.stores.put(Bytes.toBytes(""), storeMock);
167
168 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
169
170 assertFalse("a region should not be splittable if it has instances of store file references",
171 st.prepare());
172 }
173
174
175
176
177 @Test public void testPrepareWithBadSplitRow() throws IOException {
178
179 SplitTransaction st = new SplitTransaction(this.parent, STARTROW);
180 assertFalse(st.prepare());
181 st = new SplitTransaction(this.parent, HConstants.EMPTY_BYTE_ARRAY);
182 assertFalse(st.prepare());
183 st = new SplitTransaction(this.parent, new byte [] {'A', 'A', 'A'});
184 assertFalse(st.prepare());
185 st = new SplitTransaction(this.parent, ENDROW);
186 assertFalse(st.prepare());
187 }
188
189 @Test public void testPrepareWithClosedRegion() throws IOException {
190 this.parent.close();
191 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
192 assertFalse(st.prepare());
193 }
194
195 @Test public void testWholesomeSplitWithHFileV1() throws IOException {
196 int defaultVersion = TEST_UTIL.getConfiguration().getInt(
197 HFile.FORMAT_VERSION_KEY, 2);
198 TEST_UTIL.getConfiguration().setInt(HFile.FORMAT_VERSION_KEY, 1);
199 try {
200 for (Store store : this.parent.stores.values()) {
201 store.getFamily().setBloomFilterType(StoreFile.BloomType.ROW);
202 }
203 testWholesomeSplit();
204 } finally {
205 TEST_UTIL.getConfiguration().setInt(HFile.FORMAT_VERSION_KEY,
206 defaultVersion);
207 }
208 }
209
210 @Test public void testWholesomeSplit() throws IOException {
211 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF, true);
212 assertTrue(rowcount > 0);
213 int parentRowCount = countRows(this.parent);
214 assertEquals(rowcount, parentRowCount);
215
216
217
218 CacheConfig cacheConf = new CacheConfig(TEST_UTIL.getConfiguration());
219 ((LruBlockCache) cacheConf.getBlockCache()).clearCache();
220
221
222 SplitTransaction st = prepareGOOD_SPLIT_ROW();
223
224
225 Server mockServer = Mockito.mock(Server.class);
226 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
227 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
228
229 assertTrue(this.fs.exists(st.getSplitDir()));
230
231 assertTrue(this.parent.isClosed());
232
233
234
235 assertEquals(0, this.fs.listStatus(st.getSplitDir()).length);
236
237 assertTrue(Bytes.equals(this.parent.getStartKey(),
238 daughters.getFirst().getStartKey()));
239 assertTrue(Bytes.equals(GOOD_SPLIT_ROW,
240 daughters.getFirst().getEndKey()));
241 assertTrue(Bytes.equals(daughters.getSecond().getStartKey(),
242 GOOD_SPLIT_ROW));
243 assertTrue(Bytes.equals(this.parent.getEndKey(),
244 daughters.getSecond().getEndKey()));
245
246 int daughtersRowCount = 0;
247 for (HRegion r: daughters) {
248
249 HRegion openRegion = HRegion.openHRegion(this.testdir, r.getRegionInfo(),
250 r.getTableDesc(), r.getLog(), TEST_UTIL.getConfiguration());
251 try {
252 int count = countRows(openRegion);
253 assertTrue(count > 0 && count != rowcount);
254 daughtersRowCount += count;
255 } finally {
256 openRegion.close();
257 openRegion.getLog().closeAndDelete();
258 }
259 }
260 assertEquals(rowcount, daughtersRowCount);
261
262 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
263 }
264
265 @Test public void testRollback() throws IOException {
266 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
267 assertTrue(rowcount > 0);
268 int parentRowCount = countRows(this.parent);
269 assertEquals(rowcount, parentRowCount);
270
271
272 SplitTransaction st = prepareGOOD_SPLIT_ROW();
273 SplitTransaction spiedUponSt = spy(st);
274 when(spiedUponSt.createDaughterRegion(spiedUponSt.getSecondDaughter(), null)).
275 thenThrow(new MockedFailedDaughterCreation());
276
277 boolean expectedException = false;
278 Server mockServer = Mockito.mock(Server.class);
279 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
280 try {
281 spiedUponSt.execute(mockServer, null);
282 } catch (MockedFailedDaughterCreation e) {
283 expectedException = true;
284 }
285 assertTrue(expectedException);
286
287 assertTrue(spiedUponSt.rollback(null, null));
288
289
290 int parentRowCount2 = countRows(this.parent);
291 assertEquals(parentRowCount, parentRowCount2);
292
293
294 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getFirstDaughter())));
295 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getSecondDaughter())));
296 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
297
298
299 assertTrue(st.prepare());
300 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
301
302 int daughtersRowCount = 0;
303 for (HRegion r: daughters) {
304
305 HRegion openRegion = HRegion.openHRegion(this.testdir, r.getRegionInfo(),
306 r.getTableDesc(), r.getLog(), TEST_UTIL.getConfiguration());
307 try {
308 int count = countRows(openRegion);
309 assertTrue(count > 0 && count != rowcount);
310 daughtersRowCount += count;
311 } finally {
312 openRegion.close();
313 openRegion.getLog().closeAndDelete();
314 }
315 }
316 assertEquals(rowcount, daughtersRowCount);
317
318 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
319 }
320
321
322
323
324 @SuppressWarnings("serial")
325 private class MockedFailedDaughterCreation extends IOException {}
326 private class MockedFailedDaughterOpen extends IOException {}
327
328 private int countRows(final HRegion r) throws IOException {
329 int rowcount = 0;
330 InternalScanner scanner = r.getScanner(new Scan());
331 try {
332 List<KeyValue> kvs = new ArrayList<KeyValue>();
333 boolean hasNext = true;
334 while (hasNext) {
335 hasNext = scanner.next(kvs);
336 if (!kvs.isEmpty()) rowcount++;
337 }
338 } finally {
339 scanner.close();
340 }
341 return rowcount;
342 }
343
344 HRegion createRegion(final Path testdir, final HLog wal)
345 throws IOException {
346
347
348 HTableDescriptor htd = new HTableDescriptor("table");
349 HColumnDescriptor hcd = new HColumnDescriptor(CF);
350 htd.addFamily(hcd);
351 HRegionInfo hri = new HRegionInfo(htd.getName(), STARTROW, ENDROW);
352 HRegion r = HRegion.createHRegion(hri, testdir, TEST_UTIL.getConfiguration(), htd);
353 r.close();
354 r.getLog().closeAndDelete();
355 return HRegion.openHRegion(testdir, hri, htd, wal,
356 TEST_UTIL.getConfiguration());
357 }
358
359 @org.junit.Rule
360 public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
361 new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
362 }
363