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    MetricsWALSource source = new MetricsWALSourceImpl(name, name, name, name);
082    MetricsWAL metricsWAL = new MetricsWAL(source);
083    TableName tableName = TableName.valueOf("foo");
084    WALKey walKey = new WALKeyImpl(null, tableName, -1);
085    // One not so slow append (< 1000)
086    metricsWAL.postAppend(1, 900, walKey, null);
087    // Two slow appends (> 1000)
088    metricsWAL.postAppend(1, 1010, walKey, null);
089    metricsWAL.postAppend(1, 2000, walKey, null);
090    assertEquals(2, source.getSlowAppendCount());
091  }
092
093  @Test
094  public void testWalWrittenInBytes() throws Exception {
095    MetricsWALSource source = mock(MetricsWALSourceImpl.class);
096    MetricsWAL metricsWAL = new MetricsWAL(source);
097    TableName tableName = TableName.valueOf("foo");
098    WALKey walKey = new WALKeyImpl(null, tableName, -1);
099    metricsWAL.postAppend(100, 900, walKey, null);
100    metricsWAL.postAppend(200, 2000, walKey, null);
101    verify(source, times(1)).incrementWrittenBytes(100);
102    verify(source, times(1)).incrementWrittenBytes(200);
103  }
104
105  @Test
106  public void testPerTableWALMetrics() throws Exception {
107    MetricsWALSourceImpl source = new MetricsWALSourceImpl("foo", "foo", "foo", "foo");
108    final int numThreads = 10;
109    final int numIters = 10;
110    CountDownLatch latch = new CountDownLatch(numThreads);
111    for (int i = 0; i < numThreads; i++) {
112      final TableName tableName = TableName.valueOf("tab_" + i);
113      long size = i;
114      new Thread(() -> {
115        for (int j = 0; j < numIters; j++) {
116          source.incrementAppendCount(tableName);
117          source.incrementAppendSize(tableName, size);
118        }
119        latch.countDown();
120      }).start();
121    }
122    // Wait for threads to finish.
123    latch.await();
124    DynamicMetricsRegistry registry = source.getMetricsRegistry();
125    // Validate the metrics
126    for (int i = 0; i < numThreads; i++) {
127      TableName tableName = TableName.valueOf("tab_" + i);
128      long tableAppendCount =
129        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_COUNT, -1).value();
130      assertEquals(numIters, tableAppendCount);
131      long tableAppendSize =
132        registry.getCounter(tableName + "." + MetricsWALSource.APPEND_SIZE, -1).value();
133      assertEquals(i * numIters, tableAppendSize);
134    }
135  }
136
137  @Test
138  public void testLogRolls() {
139    MetricsWALSource source = new MetricsWALSourceImpl(name, name, name, name);
140    MetricsWAL metricsWAL = new MetricsWAL(source);
141    Path path1 = new Path("path-1");
142    int count = 1;
143    // oldPath is null but newPath is not null;
144    metricsWAL.postLogRoll(null, path1);
145    assertEquals(count, source.getSuccessfulLogRolls());
146
147    // Simulating a case where AbstractFSWAL#replaceWriter fails
148    metricsWAL.postLogRoll(path1, path1);
149    assertEquals(count, source.getSuccessfulLogRolls());
150
151    count++;
152    Path path2 = new Path("path-2");
153    metricsWAL.postLogRoll(path1, path2);
154    assertEquals(count, source.getSuccessfulLogRolls());
155  }
156}