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 TEST_UTIL.getDataTestDir(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
98 .doThrow(new MockedFailedDaughterOpen())
99 .when(spiedUponSt)
100 .openDaughterRegion((Server) Mockito.anyObject(),
101 (HRegion) Mockito.anyObject());
102
103
104 boolean expectedException = false;
105 Server mockServer = Mockito.mock(Server.class);
106 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
107 try {
108 spiedUponSt.execute(mockServer, null);
109 } catch (IOException e) {
110 if (e.getCause() != null &&
111 e.getCause() instanceof MockedFailedDaughterOpen) {
112 expectedException = true;
113 }
114 }
115 assertTrue(expectedException);
116
117 assertFalse(spiedUponSt.rollback(null, null));
118
119
120
121 Path tableDir = this.parent.getRegionDir().getParent();
122 Path daughterADir =
123 new Path(tableDir, spiedUponSt.getFirstDaughter().getEncodedName());
124 Path daughterBDir =
125 new Path(tableDir, spiedUponSt.getSecondDaughter().getEncodedName());
126 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterADir));
127 assertTrue(TEST_UTIL.getTestFileSystem().exists(daughterBDir));
128 }
129
130
131
132
133
134 @Test public void testPrepare() throws IOException {
135 prepareGOOD_SPLIT_ROW();
136 }
137
138 private SplitTransaction prepareGOOD_SPLIT_ROW() {
139 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
140 assertTrue(st.prepare());
141 return st;
142 }
143
144
145
146
147 @Test public void testPrepareWithBadSplitRow() throws IOException {
148
149 SplitTransaction st = new SplitTransaction(this.parent, STARTROW);
150 assertFalse(st.prepare());
151 st = new SplitTransaction(this.parent, HConstants.EMPTY_BYTE_ARRAY);
152 assertFalse(st.prepare());
153 st = new SplitTransaction(this.parent, new byte [] {'A', 'A', 'A'});
154 assertFalse(st.prepare());
155 st = new SplitTransaction(this.parent, ENDROW);
156 assertFalse(st.prepare());
157 }
158
159 @Test public void testPrepareWithClosedRegion() throws IOException {
160 this.parent.close();
161 SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
162 assertFalse(st.prepare());
163 }
164
165 @Test public void testWholesomeSplit() throws IOException {
166 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
167 assertTrue(rowcount > 0);
168 int parentRowCount = countRows(this.parent);
169 assertEquals(rowcount, parentRowCount);
170
171
172 SplitTransaction st = prepareGOOD_SPLIT_ROW();
173
174
175 Server mockServer = Mockito.mock(Server.class);
176 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
177 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
178
179 assertTrue(this.fs.exists(st.getSplitDir()));
180
181 assertTrue(this.parent.isClosed());
182
183
184
185 assertEquals(0, this.fs.listStatus(st.getSplitDir()).length);
186
187 assertTrue(Bytes.equals(this.parent.getStartKey(),
188 daughters.getFirst().getStartKey()));
189 assertTrue(Bytes.equals(GOOD_SPLIT_ROW,
190 daughters.getFirst().getEndKey()));
191 assertTrue(Bytes.equals(daughters.getSecond().getStartKey(),
192 GOOD_SPLIT_ROW));
193 assertTrue(Bytes.equals(this.parent.getEndKey(),
194 daughters.getSecond().getEndKey()));
195
196 int daughtersRowCount = 0;
197 for (HRegion r: daughters) {
198
199 HRegion openRegion = HRegion.openHRegion(this.testdir, r.getRegionInfo(),
200 r.getTableDesc(), r.getLog(), r.getConf());
201 try {
202 int count = countRows(openRegion);
203 assertTrue(count > 0 && count != rowcount);
204 daughtersRowCount += count;
205 } finally {
206 openRegion.close();
207 }
208 }
209 assertEquals(rowcount, daughtersRowCount);
210
211 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
212 }
213
214 @Test public void testRollback() throws IOException {
215 final int rowcount = TEST_UTIL.loadRegion(this.parent, CF);
216 assertTrue(rowcount > 0);
217 int parentRowCount = countRows(this.parent);
218 assertEquals(rowcount, parentRowCount);
219
220
221 SplitTransaction st = prepareGOOD_SPLIT_ROW();
222 SplitTransaction spiedUponSt = spy(st);
223 when(spiedUponSt.createDaughterRegion(spiedUponSt.getSecondDaughter(), null)).
224 thenThrow(new MockedFailedDaughterCreation());
225
226 boolean expectedException = false;
227 Server mockServer = Mockito.mock(Server.class);
228 when(mockServer.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration());
229 try {
230 spiedUponSt.execute(mockServer, null);
231 } catch (MockedFailedDaughterCreation e) {
232 expectedException = true;
233 }
234 assertTrue(expectedException);
235
236 assertTrue(spiedUponSt.rollback(null, null));
237
238
239 int parentRowCount2 = countRows(this.parent);
240 assertEquals(parentRowCount, parentRowCount2);
241
242
243 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getFirstDaughter())));
244 assertTrue(!this.fs.exists(HRegion.getRegionDir(this.testdir, st.getSecondDaughter())));
245 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
246
247
248 assertTrue(st.prepare());
249 PairOfSameType<HRegion> daughters = st.execute(mockServer, null);
250
251 int daughtersRowCount = 0;
252 for (HRegion r: daughters) {
253
254 HRegion openRegion = HRegion.openHRegion(this.testdir, r.getRegionInfo(),
255 r.getTableDesc(), r.getLog(), r.getConf());
256 try {
257 int count = countRows(openRegion);
258 assertTrue(count > 0 && count != rowcount);
259 daughtersRowCount += count;
260 } finally {
261 openRegion.close();
262 }
263 }
264 assertEquals(rowcount, daughtersRowCount);
265
266 assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread());
267 }
268
269
270
271
272 @SuppressWarnings("serial")
273 private class MockedFailedDaughterCreation extends IOException {}
274 private class MockedFailedDaughterOpen extends IOException {}
275
276 private int countRows(final HRegion r) throws IOException {
277 int rowcount = 0;
278 InternalScanner scanner = r.getScanner(new Scan());
279 try {
280 List<KeyValue> kvs = new ArrayList<KeyValue>();
281 boolean hasNext = true;
282 while (hasNext) {
283 hasNext = scanner.next(kvs);
284 if (!kvs.isEmpty()) rowcount++;
285 }
286 } finally {
287 scanner.close();
288 }
289 return rowcount;
290 }
291
292 static HRegion createRegion(final Path testdir, final HLog wal)
293 throws IOException {
294
295
296 HTableDescriptor htd = new HTableDescriptor("table");
297 HColumnDescriptor hcd = new HColumnDescriptor(CF);
298 htd.addFamily(hcd);
299 HRegionInfo hri = new HRegionInfo(htd.getName(), STARTROW, ENDROW);
300 HRegion.createHRegion(hri, testdir, TEST_UTIL.getConfiguration(), htd);
301 return HRegion.openHRegion(testdir, hri, htd, wal,
302 TEST_UTIL.getConfiguration());
303 }
304 }