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