001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver.wal;
019
020import static org.junit.Assert.assertEquals;
021import static org.mockito.Mockito.mock;
022import static org.mockito.Mockito.times;
023import static org.mockito.Mockito.verify;
024
025import java.util.concurrent.CountDownLatch;
026import java.util.concurrent.TimeUnit;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.testclassification.MiscTests;
031import org.apache.hadoop.hbase.testclassification.SmallTests;
032import org.apache.hadoop.hbase.wal.WALKey;
033import org.apache.hadoop.hbase.wal.WALKeyImpl;
034import org.apache.hadoop.metrics2.lib.DynamicMetricsRegistry;
035import org.junit.ClassRule;
036import org.junit.Rule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039import org.junit.rules.TestName;
040
041@Category({ MiscTests.class, SmallTests.class })
042public class TestMetricsWAL {
043  @Rule
044  public TestName name = new TestName();
045
046  @ClassRule
047  public static final HBaseClassTestRule CLASS_RULE =
048    HBaseClassTestRule.forClass(TestMetricsWAL.class);
049
050  @Test
051  public void testLogRollRequested() throws Exception {
052    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
053    MetricsWAL metricsWAL = new MetricsWAL(source);
054    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.ERROR);
055    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.LOW_REPLICATION);
056    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.SLOW_SYNC);
057    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.SIZE);
058
059    // Log roll was requested four times
060    verify(source, times(4)).incrementLogRollRequested();
061    // One was because of an IO error.
062    verify(source, times(1)).incrementErrorLogRoll();
063    // One was because of low replication on the hlog.
064    verify(source, times(1)).incrementLowReplicationLogRoll();
065    // One was because of slow sync on the hlog.
066    verify(source, times(1)).incrementSlowSyncLogRoll();
067    // One was because of hlog file length limit.
068    verify(source, times(1)).incrementSizeLogRoll();
069  }
070
071  @Test
072  public void testPostSync() throws Exception {
073    long nanos = TimeUnit.MILLISECONDS.toNanos(145);
074    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
075    MetricsWAL metricsWAL = new MetricsWAL(source);
076    metricsWAL.postSync(nanos, 1);
077    verify(source, times(1)).incrementSyncTime(145);
078  }
079
080  @Test
081  public void testSlowAppend() throws Exception {
082    String testName = name.getMethodName();
083    MetricsWALSource source = new MetricsWALSourceImpl(testName, testName, testName, testName);
084    MetricsWAL metricsWAL = new MetricsWAL(source);
085    TableName tableName = TableName.valueOf("foo");
086    WALKey walKey = new WALKeyImpl(null, tableName, -1);
087    // One not so slow append (< 1000)
088    metricsWAL.postAppend(1, 900, walKey, null);
089    // Two slow appends (> 1000)
090    metricsWAL.postAppend(1, 1010, walKey, null);
091    metricsWAL.postAppend(1, 2000, walKey, null);
092    assertEquals(2, source.getSlowAppendCount());
093  }
094
095  @Test
096  public void testWalWrittenInBytes() throws Exception {
097    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
098    MetricsWAL metricsWAL = new MetricsWAL(source);
099    TableName tableName = TableName.valueOf("foo");
100    WALKey walKey = new WALKeyImpl(null, tableName, -1);
101    metricsWAL.postAppend(100, 900, walKey, null);
102    metricsWAL.postAppend(200, 2000, walKey, null);
103    verify(source, times(1)).incrementWrittenBytes(100);
104    verify(source, times(1)).incrementWrittenBytes(200);
105  }
106
107  @Test
108  public void testPerTableWALMetrics() throws Exception {
109    MetricsWALSourceImpl source = new MetricsWALSourceImpl("foo", "foo", "foo", "foo");
110    final int numThreads = 10;
111    final int numIters = 10;
112    CountDownLatch latch = new CountDownLatch(numThreads);
113    for (int i = 0; i < numThreads; i++) {
114      final TableName tableName = TableName.valueOf("tab_" + i);
115      long size = i;
116      new Thread(() -> {
117        for (int j = 0; j < numIters; j++) {
118          source.incrementAppendCount(tableName);
119          source.incrementAppendSize(tableName, size);
120        }
121        latch.countDown();
122      }).start();
123    }
124    // Wait for threads to finish.
125    latch.await();
126    DynamicMetricsRegistry registry = source.getMetricsRegistry();
127    // Validate the metrics
128    for (int i = 0; i < numThreads; i++) {
129      TableName tableName = TableName.valueOf("tab_" + i);
130      long tableAppendCount =
131        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_COUNT, -1).value();
132      assertEquals(numIters, tableAppendCount);
133      long tableAppendSize =
134        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_SIZE, -1).value();
135      assertEquals(i * numIters, tableAppendSize);
136    }
137  }
138
139  @Test
140  public void testLogRolls() {
141    String testName = name.getMethodName();
142    MetricsWALSource source = new MetricsWALSourceImpl(testName, testName, testName, testName);
143    MetricsWAL metricsWAL = new MetricsWAL(source);
144    Path path1 = new Path("path-1");
145    int count = 1;
146    // oldPath is null but newPath is not null;
147    metricsWAL.postLogRoll(null, path1);
148    assertEquals(count, source.getSuccessfulLogRolls());
149
150    // Simulating a case where AbstractFSWAL#replaceWriter fails
151    metricsWAL.postLogRoll(path1, path1);
152    assertEquals(count, source.getSuccessfulLogRolls());
153
154    count++;
155    Path path2 = new Path("path-2");
156    metricsWAL.postLogRoll(path1, path2);
157    assertEquals(count, source.getSuccessfulLogRolls());
158  }
159}