View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
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            // It should fail because the WAL fail also
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           // It should fail because the WAL fail also
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 }