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.client;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Set;
26 import java.util.concurrent.atomic.AtomicBoolean;
27 import org.apache.hadoop.hbase.Cell;
28 import org.apache.hadoop.hbase.CellUtil;
29 import org.apache.hadoop.hbase.HBaseTestingUtility;
30 import org.apache.hadoop.hbase.HColumnDescriptor;
31 import org.apache.hadoop.hbase.HConstants;
32 import org.apache.hadoop.hbase.HRegionInfo;
33 import org.apache.hadoop.hbase.HTableDescriptor;
34 import org.apache.hadoop.hbase.TableName;
35 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
36 import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
37 import org.apache.hadoop.hbase.regionserver.wal.WALCoprocessorHost;
38 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
39 import org.apache.hadoop.hbase.testclassification.SmallTests;
40 import org.apache.hadoop.hbase.util.Bytes;
41 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
42 import org.apache.hadoop.hbase.wal.WAL;
43 import org.apache.hadoop.hbase.wal.WALFactory;
44 import org.apache.hadoop.hbase.wal.WALKey;
45 import org.junit.AfterClass;
46 import org.junit.Assert;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertTrue;
49 import org.junit.BeforeClass;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.experimental.categories.Category;
53 import org.junit.rules.TestName;
54
55 @Category(SmallTests.class)
56 public class TestRollbackFromClient {
57 @Rule
58 public TestName name = new TestName();
59 private final static HBaseTestingUtility TEST_UTIL
60 = new HBaseTestingUtility();
61 private static final byte[] FAMILY = Bytes.toBytes("testFamily");
62 private static final int SLAVES = 3;
63 private static final byte[] ROW = Bytes.toBytes("testRow");
64 private static final byte[] QUALIFIER = Bytes.toBytes("testQualifier");
65 private static final byte[] QUALIFIER_V2 = Bytes.toBytes("testQualifierV2");
66 private static final byte[] VALUE = Bytes.toBytes("testValue");
67
68 @BeforeClass
69 public static void setUpBeforeClass() throws Exception {
70 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 2);
71 TEST_UTIL.getConfiguration().set(WALFactory.WAL_PROVIDER, FailedDefaultWALProvider.class.getName());
72 TEST_UTIL.startMiniCluster(SLAVES);
73 }
74
75 @AfterClass
76 public static void tearDownAfterClass() throws Exception {
77 TEST_UTIL.shutdownMiniCluster();
78 }
79
80 @Test
81 public void testAppendRollback() throws IOException {
82 Updater updateForEmptyTable = new Updater() {
83 @Override
84 public int updateData(Table table, byte[] family) {
85 try {
86 Append append = new Append(ROW);
87 append.add(FAMILY, QUALIFIER, VALUE);
88 append.add(FAMILY, QUALIFIER_V2, VALUE);
89 FailedHLog.SHOULD_FAIL.set(true);
90 table.append(append);
91 } catch (IOException e) {
92
93 } finally {
94 FailedHLog.SHOULD_FAIL.set(false);
95 }
96 return 0;
97 }
98 };
99 testRollback(updateForEmptyTable, 1, null);
100 testRollback(updateForEmptyTable, 2, null);
101
102 final Append preAppend = new Append(ROW);
103 preAppend.add(FAMILY, QUALIFIER, VALUE);
104 Cell initCell = preAppend.getCellList(FAMILY).get(0);
105 Updater updateForNonEmptyTable = new Updater() {
106 @Override
107 public int updateData(Table table, byte[] family) throws IOException {
108 table.append(preAppend);
109 try {
110 Append append = new Append(ROW);
111 append.add(FAMILY, QUALIFIER, VALUE);
112 append.add(FAMILY, QUALIFIER_V2, VALUE);
113 FailedHLog.SHOULD_FAIL.set(true);
114 table.append(append);
115 Assert.fail("It should fail because the WAL sync is failed");
116 } catch (IOException e) {
117 } finally {
118 FailedHLog.SHOULD_FAIL.set(false);
119 }
120 return 1;
121 }
122 };
123 testRollback(updateForNonEmptyTable, 1, initCell);
124 testRollback(updateForNonEmptyTable, 2, initCell);
125 }
126
127 @Test
128 public void testIncrementRollback() throws IOException {
129 Updater updateForEmptyTable = new Updater() {
130 @Override
131 public int updateData(Table table, byte[] family) {
132 try {
133 Increment inc = new Increment(ROW);
134 inc.addColumn(FAMILY, QUALIFIER, 1);
135 inc.addColumn(FAMILY, QUALIFIER_V2, 2);
136 FailedHLog.SHOULD_FAIL.set(true);
137 table.increment(inc);
138 } catch (IOException e) {
139
140 } finally {
141 FailedHLog.SHOULD_FAIL.set(false);
142 }
143 return 0;
144 }
145 };
146 testRollback(updateForEmptyTable, 1, null);
147 testRollback(updateForEmptyTable, 2, null);
148
149 final Increment preIncrement = new Increment(ROW);
150 preIncrement.addColumn(FAMILY, QUALIFIER, 1);
151 Cell initCell = preIncrement.getCellList(FAMILY).get(0);
152 Updater updateForNonEmptyTable = new Updater() {
153 @Override
154 public int updateData(Table table, byte[] family) throws IOException {
155 table.increment(preIncrement);
156 try {
157 Increment inc = new Increment(ROW);
158 inc.addColumn(FAMILY, QUALIFIER, 1);
159 inc.addColumn(FAMILY, QUALIFIER_V2, 2);
160 FailedHLog.SHOULD_FAIL.set(true);
161 table.increment(inc);
162 Assert.fail("It should fail because the WAL sync is failed");
163 } catch (IOException e) {
164 } finally {
165 FailedHLog.SHOULD_FAIL.set(false);
166 }
167 return 1;
168 }
169 };
170 testRollback(updateForNonEmptyTable, 1, initCell);
171 testRollback(updateForNonEmptyTable, 2, initCell);
172 }
173
174 @Test
175 public void testPutRollback() throws IOException {
176 Updater updateForEmptyTable = new Updater() {
177 @Override
178 public int updateData(Table table, byte[] family) {
179 try {
180 Put put = new Put(ROW);
181 put.addColumn(FAMILY, QUALIFIER, VALUE);
182 FailedHLog.SHOULD_FAIL.set(true);
183 table.put(put);
184 Assert.fail("It should fail because the WAL sync is failed");
185 } catch (IOException e) {
186 } finally {
187 FailedHLog.SHOULD_FAIL.set(false);
188 }
189 return 0;
190 }
191 };
192 testRollback(updateForEmptyTable, 1, null);
193 testRollback(updateForEmptyTable, 2, null);
194
195 final Put prePut = new Put(ROW);
196 prePut.addColumn(FAMILY, QUALIFIER, Bytes.toBytes("aaaaaaaaaaaaaaaaaaaaaa"));
197 Cell preCell = prePut.getCellList(FAMILY).get(0);
198 Updater updateForNonEmptyTable = new Updater() {
199 @Override
200 public int updateData(Table table, byte[] family) throws IOException {
201 table.put(prePut);
202 try {
203 Put put = new Put(ROW);
204 put.addColumn(FAMILY, QUALIFIER, VALUE);
205 FailedHLog.SHOULD_FAIL.set(true);
206 table.put(put);
207 Assert.fail("It should fail because the WAL sync is failed");
208 } catch (IOException e) {
209 } finally {
210 FailedHLog.SHOULD_FAIL.set(false);
211 }
212 return 1;
213 }
214 };
215 testRollback(updateForNonEmptyTable, 1, preCell);
216 testRollback(updateForNonEmptyTable, 2, preCell);
217 }
218
219 private void testRollback(Updater updater, int versions, Cell initCell) throws IOException {
220 TableName tableName = TableName.valueOf(this.name.getMethodName());
221 HTableDescriptor desc = new HTableDescriptor(tableName);
222 HColumnDescriptor col = new HColumnDescriptor(FAMILY);
223 col.setMaxVersions(versions);
224 desc.addFamily(col);
225 TEST_UTIL.getHBaseAdmin().createTable(desc);
226 int expected;
227 List<Cell> cells;
228 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
229 Table table = conn.getTable(tableName)) {
230 expected = updater.updateData(table, FAMILY);
231 cells = getAllCells(table);
232 }
233 TEST_UTIL.getHBaseAdmin().disableTable(tableName);
234 TEST_UTIL.getHBaseAdmin().deleteTable(tableName);
235 assertEquals(expected, cells.size());
236 if (initCell != null && cells.isEmpty()) {
237 Cell cell = cells.get(0);
238 assertTrue("row isn't matched", CellUtil.matchingRow(initCell, cell));
239 assertTrue("column isn't matched", CellUtil.matchingColumn(initCell, cell));
240 assertTrue("qualifier isn't matched", CellUtil.matchingQualifier(initCell, cell));
241 assertTrue("value isn't matched", CellUtil.matchingValue(initCell, cell));
242 }
243 }
244
245 interface Updater {
246 int updateData(Table table, byte[] family) throws IOException;
247 }
248
249 private static List<Cell> getAllCells(Table table) throws IOException {
250 List<Cell> cells = new ArrayList<>();
251 try (ResultScanner scanner = table.getScanner(new Scan())) {
252 for (Result r : scanner) {
253 cells.addAll(r.listCells());
254 }
255 return cells;
256 }
257 }
258
259 public static class FailedDefaultWALProvider extends DefaultWALProvider {
260 @Override
261 public WAL getWAL(final byte[] identifier, byte[] namespace) throws IOException {
262 WAL wal = super.getWAL(identifier, namespace);
263 return new FailedHLog(wal);
264 }
265 }
266
267 public static class FailedHLog implements WAL {
268 private static final AtomicBoolean SHOULD_FAIL = new AtomicBoolean(false);
269 private final WAL delegation;
270 FailedHLog(final WAL delegation) {
271 this.delegation = delegation;
272 }
273 @Override
274 public void registerWALActionsListener(WALActionsListener listener) {
275 delegation.registerWALActionsListener(listener);
276 }
277
278 @Override
279 public boolean unregisterWALActionsListener(WALActionsListener listener) {
280 return delegation.unregisterWALActionsListener(listener);
281 }
282
283 @Override
284 public byte[][] rollWriter() throws FailedLogCloseException, IOException {
285 return delegation.rollWriter();
286 }
287
288 @Override
289 public byte[][] rollWriter(boolean force) throws FailedLogCloseException, IOException {
290 return delegation.rollWriter(force);
291 }
292
293 @Override
294 public void shutdown() throws IOException {
295 delegation.shutdown();
296 }
297
298 @Override
299 public void close() throws IOException {
300 delegation.close();
301 }
302
303 @Override
304 public long append(HTableDescriptor htd, HRegionInfo info, WALKey key, WALEdit edits, boolean inMemstore) throws IOException {
305 return delegation.append(htd, info, key, edits, inMemstore);
306 }
307
308 @Override
309 public void sync() throws IOException {
310 delegation.sync();
311 }
312
313 @Override
314 public void sync(long txid) throws IOException {
315 sync(txid, false);
316 }
317
318 @Override
319 public void sync(boolean forceSync) throws IOException {
320 delegation.sync(forceSync);
321 }
322
323 @Override
324 public void sync(long txid, boolean forceSync) throws IOException {
325 if (SHOULD_FAIL.get()) {
326 throw new IOException("[TESTING] we need the failure!!!");
327 }
328 delegation.sync(txid, forceSync);
329 }
330
331 @Override
332 public Long startCacheFlush(byte[] encodedRegionName, Set<byte[]> families) {
333 return delegation.startCacheFlush(encodedRegionName, families);
334 }
335
336 @Override
337 public void completeCacheFlush(byte[] encodedRegionName) {
338 delegation.completeCacheFlush(encodedRegionName);
339 }
340
341 @Override
342 public void abortCacheFlush(byte[] encodedRegionName) {
343 delegation.abortCacheFlush(encodedRegionName);
344 }
345
346 @Override
347 public WALCoprocessorHost getCoprocessorHost() {
348 return delegation.getCoprocessorHost();
349 }
350
351 @Override
352 public long getEarliestMemstoreSeqNum(byte[] encodedRegionName) {
353 return delegation.getEarliestMemstoreSeqNum(encodedRegionName);
354 }
355
356 @Override
357 public long getEarliestMemstoreSeqNum(byte[] encodedRegionName, byte[] familyName) {
358 return delegation.getEarliestMemstoreSeqNum(encodedRegionName, familyName);
359 }
360 }
361 }