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.assertTrue;
21  import static org.junit.Assert.assertFalse;
22  
23  import java.util.List;
24  import java.util.ArrayList;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
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.MediumTests;
34  import org.apache.hadoop.hbase.client.HTable;
35  import org.apache.hadoop.hbase.client.Put;
36  import org.apache.hadoop.hbase.regionserver.HRegionServer;
37  import org.apache.hadoop.hbase.regionserver.wal.HLog;
38  import org.apache.hadoop.hbase.util.Bytes;
39  import org.apache.hadoop.hbase.util.FSUtils;
40  import org.apache.hadoop.hdfs.MiniDFSCluster;
41  import org.junit.AfterClass;
42  import org.junit.BeforeClass;
43  import org.junit.Test;
44  import org.junit.experimental.categories.Category;
45  
46  /**
47   * Tests that verifies that the log is forced to be rolled every "hbase.regionserver.logroll.period"
48   */
49  @Category(MediumTests.class)
50  public class TestLogRollPeriod {
51    private static final Log LOG = LogFactory.getLog(TestLogRolling.class);
52  
53    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
54  
55    private final static long LOG_ROLL_PERIOD = 4000;
56  
57    @BeforeClass
58    public static void setUpBeforeClass() throws Exception {
59      // disable the ui
60      TEST_UTIL.getConfiguration().setInt("hbase.regionsever.info.port", -1);
61  
62      TEST_UTIL.getConfiguration().setLong("hbase.regionserver.logroll.period", LOG_ROLL_PERIOD);
63  
64      TEST_UTIL.startMiniCluster();
65    }
66  
67    @AfterClass
68    public static void tearDownAfterClass() throws Exception {
69      TEST_UTIL.shutdownMiniCluster();
70    }
71  
72    /**
73     * Tests that the LogRoller perform the roll even if there are no edits
74     */
75    @Test
76    public void testNoEdits() throws Exception {
77      final byte[] tableName = Bytes.toBytes("TestLogRollPeriodNoEdits");
78  
79      TEST_UTIL.createTable(tableName, Bytes.toBytes("cf"));
80      try {
81        HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
82        try {
83          HRegionServer server = TEST_UTIL.getRSForFirstRegionInTable(tableName);
84          HLog log = server.getWAL();
85          checkMinLogRolls(log, 5);
86        } finally {
87          table.close();
88        }
89      } finally {
90        TEST_UTIL.deleteTable(tableName);
91      }
92    }
93  
94    /**
95     * Tests that the LogRoller perform the roll with some data in the log
96     */
97    @Test(timeout=60000)
98    public void testWithEdits() throws Exception {
99      final byte[] tableName = Bytes.toBytes("TestLogRollPeriodWithEdits");
100     final byte[] family = Bytes.toBytes("cf");
101 
102     TEST_UTIL.createTable(tableName, family);
103     try {
104       HRegionServer server = TEST_UTIL.getRSForFirstRegionInTable(tableName);
105       HLog log = server.getWAL();
106       final HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
107 
108       Thread writerThread = new Thread("writer") {
109         @Override
110         public void run() {
111           try {
112             long row = 0;
113             while (!interrupted()) {
114               Put p = new Put(Bytes.toBytes(String.format("row%d", row)));
115               p.add(family, Bytes.toBytes("col"), Bytes.toBytes(row));
116               table.put(p);
117               row++;
118 
119               Thread.sleep(LOG_ROLL_PERIOD / 16);
120             }
121           } catch (Exception e) {
122             LOG.warn(e);
123           } 
124         }
125       };
126 
127       try {
128         writerThread.start();
129         checkMinLogRolls(log, 5);
130       } finally {
131         writerThread.interrupt();
132         writerThread.join();
133         table.close();
134       }  
135     } finally {
136       TEST_UTIL.deleteTable(tableName);
137     }
138   }
139 
140   private void checkMinLogRolls(final HLog log, final int minRolls)
141       throws Exception {
142     final List<Path> paths = new ArrayList<Path>();
143     log.registerWALActionsListener(new WALActionsListener() {
144       @Override
145       public void preLogRoll(Path oldFile, Path newFile)  {}
146       @Override
147       public void postLogRoll(Path oldFile, Path newFile) {
148         LOG.debug("postLogRoll: oldFile="+oldFile+" newFile="+newFile);
149         paths.add(newFile);
150       }
151       @Override
152       public void preLogArchive(Path oldFile, Path newFile) {}
153       @Override
154       public void postLogArchive(Path oldFile, Path newFile) {}
155       @Override
156       public void logRollRequested() {}
157       @Override
158       public void logCloseRequested() {}
159       @Override
160       public void visitLogEntryBeforeWrite(HRegionInfo info, HLogKey logKey, WALEdit logEdit) {}
161       @Override
162       public void visitLogEntryBeforeWrite(HTableDescriptor htd, HLogKey logKey, WALEdit logEdit) {}
163     });
164 
165     // Sleep until we should get at least min-LogRoll events
166     long wtime = System.currentTimeMillis();
167     Thread.sleep((minRolls + 1) * LOG_ROLL_PERIOD);
168     // Do some extra sleep in case the machine is slow,
169     // and the log-roll is not triggered exactly on LOG_ROLL_PERIOD.
170     final int NUM_RETRIES = 1 + 8 * (minRolls - paths.size());
171     for (int retry = 0; paths.size() < minRolls && retry < NUM_RETRIES; ++retry) {
172       Thread.sleep(LOG_ROLL_PERIOD / 4);
173     }
174     wtime = System.currentTimeMillis() - wtime;
175     LOG.info(String.format("got %d rolls after %dms (%dms each) - expected at least %d rolls",
176                            paths.size(), wtime, wtime / paths.size(), minRolls));
177     assertFalse(paths.size() < minRolls);
178   }
179 }