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.client.Scan;
42 import org.apache.hadoop.hbase.regionserver.wal.HLog;
43 import org.apache.hadoop.hbase.util.Bytes;
44 import org.apache.hadoop.hbase.util.PairOfSameType;
45 import org.apache.zookeeper.KeeperException;
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.mockito.Mockito;
50
51
52
53
54
55 public class TestSplitTransaction {
56 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
57 private final Path testdir =
58 HBaseTestingUtility.getTestDir(this.getClass().getName());
59 private HRegion parent;
60 private HLog wal;
61 private FileSystem fs;
62 private static final byte [] STARTROW = new byte [] {'a', 'a', 'a'};
63
64 private static final byte [] ENDROW = new byte [] {'{', '{', '{'};
65 private static final byte [] GOOD_SPLIT_ROW = new byte [] {'d', 'd', 'd'};
66 private static final byte [] CF = HConstants.CATALOG_FAMILY;
67
68 @Before public void setup() throws IOException {
69 this.fs = FileSystem.get(TEST_UTIL.getConfiguration());
70 this.fs.delete(this.testdir, true);
71 this.wal = new HLog(fs, new Path(this.testdir, "logs"),
72 new Path(this.testdir, "archive"),
73 TEST_UTIL.getConfiguration());
74 this.parent = createRegion(this.testdir, this.wal);
75 TEST_UTIL.getConfiguration().setBoolean("hbase.testing.nocluster", true);
76 }
77
78 @After public void teardown() throws IOException {
79 if (this.parent != null && !this.parent.isClosed()) this.parent.close();
80 if (this.fs.exists(this.parent.getRegionDir()) &&
81 !this.fs.delete(this.parent.getRegionDir(), true)) {
82 throw new IOException("Failed delete of " + this.parent.getRegionDir());
83 }
84 if (this.wal != null) this.wal.closeAndDelete();
85 this.fs.delete(this.testdir, true);
86 }
87
88 @Test public void testFailAfterPONR() throws IOException, KeeperException {
89 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
90 assertTrue(rowcount > 0);
91 int parentRowCount = countRows(this.parent);
92 assertEquals(rowcount, parentRowCount);
93
94
95 SplitTransaction st = prepareGOOD_SPLIT_ROW();
96 SplitTransaction spiedUponSt = spy(st);
97 Mockito.doThrow(new MockedFailedDaughterOpen()).
98 when(spiedUponSt).openDaughterRegion((Server)Mockito.anyObject(),
99 (RegionServerServices)Mockito.anyObject(), (HRegion)Mockito.anyObject());
100
101
102 boolean expectedException = false;
103 Server mockServer = Mockito.mock(Server.class);
104 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
105 try {
106 spiedUponSt.execute(mockServer, null);
107 } catch (IOException e) {
108 if (e.getCause() != null &&
109 e.getCause() instanceof MockedFailedDaughterOpen) {
110 expectedException = true;
111 }
112 }
113 assertTrue(expectedException);
114
115 assertFalse(spiedUponSt.rollback(null, null));
116
117
118
119 Path tableDir = this.parent.getRegionDir().getParent();
120 Path daughterADir =
121 new Path(tableDir, spiedUponSt.getFirstDaughter().getEncodedName());
122 Path daughterBDir =
123 new Path(tableDir, spiedUponSt.getSecondDaughter().getEncodedName());
124 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterADir));
125 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterBDir));
126 }
127
128
129
130
131
132 @Test public void testPrepare() throws IOException {
133 prepareGOOD_SPLIT_ROW();
134 }
135
136 private SplitTransaction prepareGOOD_SPLIT_ROW() {
137 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
138 assertTrue(st.prepare());
139 return st;
140 }
141
142
143
144
145 @Test public void testPrepareWithBadSplitRow() throws IOException {
146
147 SplitTransaction st = new SplitTransaction(this.parent, STARTROW);
148 assertFalse(st.prepare());
149 st = new SplitTransaction(this.parent, HConstants.EMPTY_BYTE_ARRAY);
150 assertFalse(st.prepare());
151 st = new SplitTransaction(this.parent, new byte [] {'A', 'A', 'A'});
152 assertFalse(st.prepare());
153 st = new SplitTransaction(this.parent, ENDROW);
154 assertFalse(st.prepare());
155 }
156
157 @Test public void testPrepareWithClosedRegion() throws IOException {
158 this.parent.close();
159 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
160 assertFalse(st.prepare());
161 }
162
163 @Test public void testWholesomeSplit() throws IOException {
164 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
165 assertTrue(rowcount > 0);
166 int parentRowCount = countRows(this.parent);
167 assertEquals(rowcount, parentRowCount);
168
169
170 SplitTransaction st = prepareGOOD_SPLIT_ROW();
171
172
173 Server mockServer = Mockito.mock(Server.class);
174 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
175 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
176
177 assertTrue(this.fs.exists(st.getSplitDir()));
178
179 assertTrue(this.parent.isClosed());
180
181
182
183 assertEquals(0, this.fs.listStatus(st.getSplitDir()).length);
184
185 assertTrue(Bytes.equals(this.parent.getStartKey(),
186 daughters.getFirst().getStartKey()));
187 assertTrue(Bytes.equals(GOOD_SPLIT_ROW,
188 daughters.getFirst().getEndKey()));
189 assertTrue(Bytes.equals(daughters.getSecond().getStartKey(),
190 GOOD_SPLIT_ROW));
191 assertTrue(Bytes.equals(this.parent.getEndKey(),
192 daughters.getSecond().getEndKey()));
193
194 int daughtersRowCount = 0;
195 for (HRegion r: daughters) {
196
197 HRegion openRegion = HRegion.openHRegion(r.getRegionInfo(),
198 r.getLog(), r.getConf());
199 try {
200 int count = countRows(openRegion);
201 assertTrue(count > 0 && count != rowcount);
202 daughtersRowCount += count;
203 } finally {
204 openRegion.close();
205 }
206 }
207 assertEquals(rowcount, daughtersRowCount);
208
209 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
210 }
211
212 @Test public void testRollback() throws IOException {
213 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
214 assertTrue(rowcount > 0);
215 int parentRowCount = countRows(this.parent);
216 assertEquals(rowcount, parentRowCount);
217
218
219 SplitTransaction st = prepareGOOD_SPLIT_ROW();
220 SplitTransaction spiedUponSt = spy(st);
221 when(spiedUponSt.createDaughterRegion(spiedUponSt.getSecondDaughter(), null)).
222 thenThrow(new MockedFailedDaughterCreation());
223
224 boolean expectedException = false;
225 Server mockServer = Mockito.mock(Server.class);
226 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
227 try {
228 spiedUponSt.execute(mockServer, null);
229 } catch (MockedFailedDaughterCreation e) {
230 expectedException = true;
231 }
232 assertTrue(expectedException);
233
234 assertTrue(spiedUponSt.rollback(null, null));
235
236
237 int parentRowCount2 = countRows(this.parent);
238 assertEquals(parentRowCount, parentRowCount2);
239
240
241 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getFirstDaughter())));
242 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getSecondDaughter())));
243 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
244
245
246 assertTrue(st.prepare());
247 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
248
249 int daughtersRowCount = 0;
250 for (HRegion r: daughters) {
251
252 HRegion openRegion = HRegion.openHRegion(r.getRegionInfo(),
253 r.getLog(), r.getConf());
254 try {
255 int count = countRows(openRegion);
256 assertTrue(count > 0 && count != rowcount);
257 daughtersRowCount += count;
258 } finally {
259 openRegion.close();
260 }
261 }
262 assertEquals(rowcount, daughtersRowCount);
263
264 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
265 }
266
267
268
269
270 @SuppressWarnings("serial")
271 private class MockedFailedDaughterCreation extends IOException {}
272 private class MockedFailedDaughterOpen extends IOException {}
273
274 private int countRows(final HRegion r) throws IOException {
275 int rowcount = 0;
276 InternalScanner scanner = r.getScanner(new Scan());
277 try {
278 List<KeyValue> kvs = new ArrayList<KeyValue>();
279 boolean hasNext = true;
280 while (hasNext) {
281 hasNext = scanner.next(kvs);
282 if (!kvs.isEmpty()) rowcount++;
283 }
284 } finally {
285 scanner.close();
286 }
287 return rowcount;
288 }
289
290 static HRegion createRegion(final Path testdir, final HLog wal)
291 throws IOException {
292
293
294 HTableDescriptor htd = new HTableDescriptor("table");
295 HColumnDescriptor hcd = new HColumnDescriptor(CF);
296 htd.addFamily(hcd);
297 HRegionInfo hri = new HRegionInfo(htd, STARTROW, ENDROW);
298 return HRegion.openHRegion(hri, wal, TEST_UTIL.getConfiguration());
299 }
300 }