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  package org.apache.hadoop.hbase.regionserver.wal;
19  
20  import static org.junit.Assert.assertFalse;
21  
22  import java.io.IOException;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.hadoop.fs.FileSystem;
27  import org.apache.hadoop.fs.Path;
28  import org.apache.hadoop.hbase.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.HTableDescriptor;
31  import org.apache.hadoop.hbase.KeyValue;
32  import org.apache.hadoop.hbase.SmallTests;
33  import org.apache.hadoop.hbase.util.Bytes;
34  import org.junit.Test;
35  import org.junit.experimental.categories.Category;
36  
37  /**
38   * Test many concurrent appenders to an {@link #HLog} while rolling the log.
39   */
40  @Category(SmallTests.class)
41  public class TestLogRollingNoCluster {
42    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
43    private final static byte [] EMPTY_1K_ARRAY = new byte[1024];
44    private static final int THREAD_COUNT = 100; // Spin up this many threads
45  
46    /**
47     * Spin up a bunch of threads and have them all append to a WAL.  Roll the
48     * WAL frequently to try and trigger NPE.
49     * @throws IOException
50     * @throws InterruptedException
51     */
52    @Test
53    public void testContendedLogRolling() throws IOException, InterruptedException {
54      FileSystem fs = FileSystem.get(TEST_UTIL.getConfiguration());
55      Path dir = TEST_UTIL.getDataTestDir();
56      HLog wal = HLogFactory.createHLog(fs, dir, "logs",
57        TEST_UTIL.getConfiguration());
58      
59      Appender [] appenders = null;
60  
61      final int count = THREAD_COUNT;
62      appenders = new Appender[count];
63      try {
64        for (int i = 0; i < count; i++) {
65          // Have each appending thread write 'count' entries
66          appenders[i] = new Appender(wal, i, count);
67        }
68        for (int i = 0; i < count; i++) {
69          appenders[i].start();
70        }
71        for (int i = 0; i < count; i++) {
72          //ensure that all threads are joined before closing the wal
73          appenders[i].join();
74        }
75      } finally {
76        wal.close();
77      }
78      for (int i = 0; i < count; i++) {
79        assertFalse(appenders[i].isException());
80      }
81    }
82  
83    /**
84     * Appender thread.  Appends to passed wal file.
85     */
86    static class Appender extends Thread {
87      private final Log log;
88      private final HLog wal;
89      private final int count;
90      private Exception e = null;
91  
92      Appender(final HLog wal, final int index, final int count) {
93        super("" + index);
94        this.wal = wal;
95        this.count = count;
96        this.log = LogFactory.getLog("Appender:" + getName());
97      }
98  
99      /**
100      * @return Call when the thread is done.
101      */
102     boolean isException() {
103       return !isAlive() && this.e != null;
104     }
105 
106     Exception getException() {
107       return this.e;
108     }
109 
110     @Override
111     public void run() {
112       this.log.info(getName() +" started");
113       try {
114         for (int i = 0; i < this.count; i++) {
115           long now = System.currentTimeMillis();
116           // Roll every ten edits if the log has anything in it.
117           if (i % 10 == 0 && ((FSHLog) this.wal).getNumEntries() > 0) {
118             this.wal.rollWriter();
119           }
120           WALEdit edit = new WALEdit();
121           byte[] bytes = Bytes.toBytes(i);
122           edit.add(new KeyValue(bytes, bytes, bytes, now, EMPTY_1K_ARRAY));
123 
124           this.wal.append(HRegionInfo.FIRST_META_REGIONINFO,
125               HTableDescriptor.META_TABLEDESC.getTableName(),
126               edit, now, HTableDescriptor.META_TABLEDESC);
127         }
128         String msg = getName() + " finished";
129         if (isException())
130           this.log.info(msg, getException());
131         else
132           this.log.info(msg);
133       } catch (Exception e) {
134         this.e = e;
135         log.info("Caught exception from Appender:" + getName(), e);
136       }
137     }
138   }
139 
140   //@org.junit.Rule
141   //public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
142   //  new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
143 }