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