View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.regionserver.wal;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertNull;
23  
24  import java.io.IOException;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.fs.FileSystem;
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.testclassification.MediumTests;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.client.Durability;
36  import org.apache.hadoop.hbase.client.Increment;
37  import org.apache.hadoop.hbase.client.Put;
38  import org.apache.hadoop.hbase.client.Result;
39  import org.apache.hadoop.hbase.regionserver.HRegion;
40  import org.apache.hadoop.hbase.util.Bytes;
41  import org.apache.hadoop.hbase.util.FSUtils;
42  import org.apache.hadoop.hbase.wal.DefaultWALProvider;
43  import org.apache.hadoop.hbase.wal.WAL;
44  import org.apache.hadoop.hbase.wal.WALFactory;
45  import org.apache.hadoop.hdfs.MiniDFSCluster;
46  import org.junit.AfterClass;
47  import org.junit.BeforeClass;
48  import org.junit.Test;
49  import org.junit.experimental.categories.Category;
50  
51  /**
52   * Tests for WAL write durability
53   */
54  @Category(MediumTests.class)
55  public class TestDurability {
56    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
57    private static FileSystem FS;
58    private static MiniDFSCluster CLUSTER;
59    private static Configuration CONF;
60    private static Path DIR;
61  
62    private static byte[] FAMILY = Bytes.toBytes("family");
63    private static byte[] ROW = Bytes.toBytes("row");
64    private static byte[] COL = Bytes.toBytes("col");
65  
66  
67    @BeforeClass
68    public static void setUpBeforeClass() throws Exception {
69      CONF = TEST_UTIL.getConfiguration();
70      TEST_UTIL.startMiniDFSCluster(1);
71  
72      CLUSTER = TEST_UTIL.getDFSCluster();
73      FS = CLUSTER.getFileSystem();
74      DIR = TEST_UTIL.getDataTestDirOnTestFS("TestDurability");
75      FSUtils.setRootDir(CONF, DIR);
76    }
77  
78    @AfterClass
79    public static void tearDownAfterClass() throws Exception {
80      TEST_UTIL.shutdownMiniCluster();
81    }
82  
83    @Test
84    public void testDurability() throws Exception {
85      final WALFactory wals = new WALFactory(CONF, null, "TestDurability");
86      byte[] tableName = Bytes.toBytes("TestDurability");
87      final WAL wal = wals.getWAL(tableName, null);
88      HRegion region = createHRegion(tableName, "region", wal, Durability.USE_DEFAULT);
89      HRegion deferredRegion = createHRegion(tableName, "deferredRegion", wal, Durability.ASYNC_WAL);
90  
91      region.put(newPut(null));
92      verifyWALCount(wals, wal, 1);
93  
94      // a put through the deferred table does not write to the wal immediately,
95      // but maybe has been successfully sync-ed by the underlying AsyncWriter +
96      // AsyncFlusher thread
97      deferredRegion.put(newPut(null));
98      // but will after we sync the wal
99      wal.sync();
100     verifyWALCount(wals, wal, 2);
101 
102     // a put through a deferred table will be sync with the put sync'ed put
103     deferredRegion.put(newPut(null));
104     wal.sync();
105     verifyWALCount(wals, wal, 3);
106     region.put(newPut(null));
107     verifyWALCount(wals, wal, 4);
108 
109     // a put through a deferred table will be sync with the put sync'ed put
110     deferredRegion.put(newPut(Durability.USE_DEFAULT));
111     wal.sync();
112     verifyWALCount(wals, wal, 5);
113     region.put(newPut(Durability.USE_DEFAULT));
114     verifyWALCount(wals, wal, 6);
115 
116     // SKIP_WAL never writes to the wal
117     region.put(newPut(Durability.SKIP_WAL));
118     deferredRegion.put(newPut(Durability.SKIP_WAL));
119     verifyWALCount(wals, wal, 6);
120     wal.sync();
121     verifyWALCount(wals, wal, 6);
122 
123     // Async overrides sync table default
124     region.put(newPut(Durability.ASYNC_WAL));
125     deferredRegion.put(newPut(Durability.ASYNC_WAL));
126     wal.sync();
127     verifyWALCount(wals, wal, 8);
128 
129     // sync overrides async table default
130     region.put(newPut(Durability.SYNC_WAL));
131     deferredRegion.put(newPut(Durability.SYNC_WAL));
132     verifyWALCount(wals, wal, 10);
133 
134     // fsync behaves like sync
135     region.put(newPut(Durability.FSYNC_WAL));
136     deferredRegion.put(newPut(Durability.FSYNC_WAL));
137     verifyWALCount(wals, wal, 12);
138   }
139 
140   @Test
141   public void testIncrement() throws Exception {
142     byte[] row1 = Bytes.toBytes("row1");
143     byte[] col1 = Bytes.toBytes("col1");
144     byte[] col2 = Bytes.toBytes("col2");
145     byte[] col3 = Bytes.toBytes("col3");
146 
147     // Setting up region
148     final WALFactory wals = new WALFactory(CONF, null, "TestIncrement");
149     byte[] tableName = Bytes.toBytes("TestIncrement");
150     final WAL wal = wals.getWAL(tableName, null);
151     HRegion region = createHRegion(tableName, "increment", wal, Durability.USE_DEFAULT);
152 
153     // col1: amount = 0, 1 write back to WAL
154     Increment inc1 = new Increment(row1);
155     inc1.addColumn(FAMILY, col1, 0);
156     Result res = region.increment(inc1);
157     assertEquals(1, res.size());
158     assertEquals(0, Bytes.toLong(res.getValue(FAMILY, col1)));
159     verifyWALCount(wals, wal, 1);
160 
161     // col1: amount = 1, 1 write back to WAL
162     inc1 = new Increment(row1);
163     inc1.addColumn(FAMILY, col1, 1);
164     res = region.increment(inc1);
165     assertEquals(1, res.size());
166     assertEquals(1, Bytes.toLong(res.getValue(FAMILY, col1)));
167     verifyWALCount(wals, wal, 2);
168 
169     // col1: amount = 0, 0 write back to WAL
170     inc1 = new Increment(row1);
171     inc1.addColumn(FAMILY, col1, 0);
172     res = region.increment(inc1);
173     assertEquals(1, res.size());
174     assertEquals(1, Bytes.toLong(res.getValue(FAMILY, col1)));
175     verifyWALCount(wals, wal, 2);
176 
177     // col1: amount = 0, col2: amount = 0, col3: amount = 0
178     // 1 write back to WAL
179     inc1 = new Increment(row1);
180     inc1.addColumn(FAMILY, col1, 0);
181     inc1.addColumn(FAMILY, col2, 0);
182     inc1.addColumn(FAMILY, col3, 0);
183     res = region.increment(inc1);
184     assertEquals(3, res.size());
185     assertEquals(1, Bytes.toLong(res.getValue(FAMILY, col1)));
186     assertEquals(0, Bytes.toLong(res.getValue(FAMILY, col2)));
187     assertEquals(0, Bytes.toLong(res.getValue(FAMILY, col3)));
188     verifyWALCount(wals, wal, 3);
189 
190     // col1: amount = 5, col2: amount = 4, col3: amount = 3
191     // 1 write back to WAL
192     inc1 = new Increment(row1);
193     inc1.addColumn(FAMILY, col1, 5);
194     inc1.addColumn(FAMILY, col2, 4);
195     inc1.addColumn(FAMILY, col3, 3);
196     res = region.increment(inc1);
197     assertEquals(3, res.size());
198     assertEquals(6, Bytes.toLong(res.getValue(FAMILY, col1)));
199     assertEquals(4, Bytes.toLong(res.getValue(FAMILY, col2)));
200     assertEquals(3, Bytes.toLong(res.getValue(FAMILY, col3)));
201     verifyWALCount(wals, wal, 4);
202   }
203   
204   /*
205    * Test when returnResults set to false in increment it should not return the result instead it
206    * resturn null.
207    */
208   @Test
209   public void testIncrementWithReturnResultsSetToFalse() throws Exception {
210     byte[] row1 = Bytes.toBytes("row1");
211     byte[] col1 = Bytes.toBytes("col1");
212 
213     // Setting up region
214     final WALFactory wals = new WALFactory(CONF, null, "testIncrementWithReturnResultsSetToFalse");
215     byte[] tableName = Bytes.toBytes("testIncrementWithReturnResultsSetToFalse");
216     final WAL wal = wals.getWAL(tableName, null);
217     HRegion region = createHRegion(tableName, "increment", wal, Durability.USE_DEFAULT);
218 
219     Increment inc1 = new Increment(row1);
220     inc1.setReturnResults(false);
221     inc1.addColumn(FAMILY, col1, 1);
222     Result res = region.increment(inc1);
223     assertNull(res);
224   }
225 
226   private Put newPut(Durability durability) {
227     Put p = new Put(ROW);
228     p.add(FAMILY, COL, COL);
229     if (durability != null) {
230       p.setDurability(durability);
231     }
232     return p;
233   }
234 
235   private void verifyWALCount(WALFactory wals, WAL log, int expected) throws Exception {
236     Path walPath = DefaultWALProvider.getCurrentFileName(log);
237     WAL.Reader reader = wals.createReader(FS, walPath);
238     int count = 0;
239     WAL.Entry entry = new WAL.Entry();
240     while (reader.next(entry) != null) count++;
241     reader.close();
242     assertEquals(expected, count);
243   }
244 
245   // lifted from TestAtomicOperation
246   private HRegion createHRegion (byte [] tableName, String callingMethod,
247       WAL log, Durability durability)
248     throws IOException {
249       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
250       htd.setDurability(durability);
251       HColumnDescriptor hcd = new HColumnDescriptor(FAMILY);
252       htd.addFamily(hcd);
253       HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
254       Path path = new Path(DIR + callingMethod);
255       if (FS.exists(path)) {
256         if (!FS.delete(path, true)) {
257           throw new IOException("Failed delete of " + path);
258         }
259       }
260       return HRegion.createHRegion(info, path, CONF, htd, log);
261     }
262 
263 }