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