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