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.client;
019
020import static org.hamcrest.MatcherAssert.assertThat;
021import static org.hamcrest.Matchers.containsString;
022import static org.hamcrest.Matchers.startsWith;
023import static org.junit.jupiter.api.Assertions.assertEquals;
024import static org.mockito.ArgumentMatchers.any;
025import static org.mockito.Mockito.doAnswer;
026import static org.mockito.Mockito.mock;
027import static org.mockito.Mockito.never;
028import static org.mockito.Mockito.times;
029import static org.mockito.Mockito.verify;
030import static org.mockito.Mockito.when;
031
032import java.io.IOException;
033import java.util.IdentityHashMap;
034import java.util.concurrent.atomic.AtomicReference;
035import org.apache.hadoop.hbase.HRegionLocation;
036import org.apache.hadoop.hbase.ServerName;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.client.AsyncBatchRpcRetryingCaller.RegionRequest;
039import org.apache.hadoop.hbase.testclassification.ClientTests;
040import org.apache.hadoop.hbase.testclassification.SmallTests;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
043import org.junit.jupiter.api.AfterEach;
044import org.junit.jupiter.api.BeforeEach;
045import org.junit.jupiter.api.Tag;
046import org.junit.jupiter.api.Test;
047import org.mockito.invocation.InvocationOnMock;
048import org.mockito.stubbing.Answer;
049
050@Tag(ClientTests.TAG)
051@Tag(SmallTests.TAG)
052public class TestAsyncBatchRpcRetryingCaller {
053
054  private org.apache.logging.log4j.core.Appender mockAppender;
055
056  @BeforeEach
057  public void setUp() {
058    mockAppender = mock(org.apache.logging.log4j.core.Appender.class);
059    when(mockAppender.getName()).thenReturn("mockAppender");
060    when(mockAppender.isStarted()).thenReturn(true);
061    ((org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager
062      .getLogger(AsyncBatchRpcRetryingCaller.class)).addAppender(mockAppender);
063
064  }
065
066  @AfterEach
067  public void tearDown() {
068    ((org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager
069      .getLogger(AsyncBatchRpcRetryingCaller.class)).removeAppender(mockAppender);
070  }
071
072  @Test
073  public void testLogAction() {
074    AtomicReference<org.apache.logging.log4j.Level> level = new AtomicReference<>();
075    AtomicReference<String> msg = new AtomicReference<String>();
076    doAnswer(new Answer<Void>() {
077
078      @Override
079      public Void answer(InvocationOnMock invocation) throws Throwable {
080        org.apache.logging.log4j.core.LogEvent logEvent =
081          invocation.getArgument(0, org.apache.logging.log4j.core.LogEvent.class);
082        level.set(logEvent.getLevel());
083        msg.set(logEvent.getMessage().getFormattedMessage());
084        return null;
085      }
086    }).when(mockAppender).append(any(org.apache.logging.log4j.core.LogEvent.class));
087    TableName tn = TableName.valueOf("async");
088    ServerName sn = ServerName.valueOf("host", 12345, EnvironmentEdgeManager.currentTime());
089    RegionRequest request =
090      new RegionRequest(new HRegionLocation(RegionInfoBuilder.newBuilder(tn).build(), sn));
091    Action put = new Action(new Put(Bytes.toBytes("a")), 0);
092    Action get = new Action(new Get(Bytes.toBytes("b")), 1);
093    Action incr = new Action(new Increment(Bytes.toBytes("c")), 2);
094    Action del = new Action(new Delete(Bytes.toBytes("d")), 3);
095    request.actions.add(put);
096    request.actions.add(get);
097    request.actions.add(incr);
098    request.actions.add(del);
099    IdentityHashMap<Action, Throwable> action2Error = new IdentityHashMap<>();
100    AsyncBatchRpcRetryingCaller.logActionsException(1, 2, request, action2Error, sn);
101    verify(mockAppender, never()).append(any());
102    AsyncBatchRpcRetryingCaller.logActionsException(5, 4, request, action2Error, sn);
103    verify(mockAppender, never()).append(any());
104
105    action2Error.put(get, new IOException("get error"));
106    action2Error.put(incr, new IOException("incr error"));
107    AsyncBatchRpcRetryingCaller.logActionsException(5, 4, request, action2Error, sn);
108    verify(mockAppender, times(1)).append(any());
109    assertEquals(org.apache.logging.log4j.Level.WARN, level.get());
110
111    String logMsg = msg.get();
112    assertThat(logMsg,
113      startsWith("Process batch for " + request.loc.getRegion().getRegionNameAsString() + " on "
114        + sn.toString() + ", 2/4 actions failed, tries=5, sampled 2 errors:"));
115    assertThat(logMsg, containsString("=> java.io.IOException: get error"));
116    assertThat(logMsg, containsString("=> java.io.IOException: incr error"));
117  }
118}