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