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.jupiter.api.Assertions.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.TableName;
029import org.apache.hadoop.hbase.testclassification.MiscTests;
030import org.apache.hadoop.hbase.testclassification.SmallTests;
031import org.apache.hadoop.hbase.wal.WALKey;
032import org.apache.hadoop.hbase.wal.WALKeyImpl;
033import org.apache.hadoop.metrics2.lib.DynamicMetricsRegistry;
034import org.junit.jupiter.api.BeforeEach;
035import org.junit.jupiter.api.Tag;
036import org.junit.jupiter.api.Test;
037import org.junit.jupiter.api.TestInfo;
038
039@Tag(MiscTests.TAG)
040@Tag(SmallTests.TAG)
041public class TestMetricsWAL {
042  private String name;
043
044  @BeforeEach
045  public void initTestName(TestInfo testInfo) {
046    name = testInfo.getTestMethod().get().getName();
047  }
048
049  @Test
050  public void testLogRollRequested() throws Exception {
051    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
052    MetricsWAL metricsWAL = new MetricsWAL(source);
053    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.ERROR);
054    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.LOW_REPLICATION);
055    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.SLOW_SYNC);
056    metricsWAL.logRollRequested(WALActionsListener.RollRequestReason.SIZE);
057
058    // Log roll was requested four times
059    verify(source, times(4)).incrementLogRollRequested();
060    // One was because of an IO error.
061    verify(source, times(1)).incrementErrorLogRoll();
062    // One was because of low replication on the hlog.
063    verify(source, times(1)).incrementLowReplicationLogRoll();
064    // One was because of slow sync on the hlog.
065    verify(source, times(1)).incrementSlowSyncLogRoll();
066    // One was because of hlog file length limit.
067    verify(source, times(1)).incrementSizeLogRoll();
068  }
069
070  @Test
071  public void testPostSync() throws Exception {
072    long nanos = TimeUnit.MILLISECONDS.toNanos(145);
073    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
074    MetricsWAL metricsWAL = new MetricsWAL(source);
075    metricsWAL.postSync(nanos, 1);
076    verify(source, times(1)).incrementSyncTime(145);
077  }
078
079  @Test
080  public void testSlowAppend() throws Exception {
081    String testName = name;
082    MetricsWALSource source = new MetricsWALSourceImpl(testName, testName, testName, testName);
083    MetricsWAL metricsWAL = new MetricsWAL(source);
084    TableName tableName = TableName.valueOf("foo");
085    WALKey walKey = new WALKeyImpl(null, tableName, -1);
086    // One not so slow append (< 1000)
087    metricsWAL.postAppend(1, 900, walKey, null);
088    // Two slow appends (> 1000)
089    metricsWAL.postAppend(1, 1010, walKey, null);
090    metricsWAL.postAppend(1, 2000, walKey, null);
091    assertEquals(2, source.getSlowAppendCount());
092  }
093
094  @Test
095  public void testWalWrittenInBytes() throws Exception {
096    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
097    MetricsWAL metricsWAL = new MetricsWAL(source);
098    TableName tableName = TableName.valueOf("foo");
099    WALKey walKey = new WALKeyImpl(null, tableName, -1);
100    metricsWAL.postAppend(100, 900, walKey, null);
101    metricsWAL.postAppend(200, 2000, walKey, null);
102    verify(source, times(1)).incrementWrittenBytes(100);
103    verify(source, times(1)).incrementWrittenBytes(200);
104  }
105
106  @Test
107  public void testPerTableWALMetrics() throws Exception {
108    MetricsWALSourceImpl source = new MetricsWALSourceImpl("foo", "foo", "foo", "foo");
109    final int numThreads = 10;
110    final int numIters = 10;
111    CountDownLatch latch = new CountDownLatch(numThreads);
112    for (int i = 0; i < numThreads; i++) {
113      final TableName tableName = TableName.valueOf("tab_" + i);
114      long size = i;
115      new Thread(() -> {
116        for (int j = 0; j < numIters; j++) {
117          source.incrementAppendCount(tableName);
118          source.incrementAppendSize(tableName, size);
119        }
120        latch.countDown();
121      }).start();
122    }
123    // Wait for threads to finish.
124    latch.await();
125    DynamicMetricsRegistry registry = source.getMetricsRegistry();
126    // Validate the metrics
127    for (int i = 0; i < numThreads; i++) {
128      TableName tableName = TableName.valueOf("tab_" + i);
129      long tableAppendCount =
130        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_COUNT, -1).value();
131      assertEquals(numIters, tableAppendCount);
132      long tableAppendSize =
133        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_SIZE, -1).value();
134      assertEquals(i * numIters, tableAppendSize);
135    }
136  }
137
138  @Test
139  public void testLogRolls() {
140    String testName = name;
141    MetricsWALSource source = new MetricsWALSourceImpl(testName, testName, testName, testName);
142    MetricsWAL metricsWAL = new MetricsWAL(source);
143    Path path1 = new Path("path-1");
144    int count = 1;
145    // oldPath is null but newPath is not null;
146    metricsWAL.postLogRoll(null, path1);
147    assertEquals(count, source.getSuccessfulLogRolls());
148
149    // Simulating a case where AbstractFSWAL#replaceWriter fails
150    metricsWAL.postLogRoll(path1, path1);
151    assertEquals(count, source.getSuccessfulLogRolls());
152
153    count++;
154    Path path2 = new Path("path-2");
155    metricsWAL.postLogRoll(path1, path2);
156    assertEquals(count, source.getSuccessfulLogRolls());
157  }
158}