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; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.mockito.Mockito.atLeastOnce; 024import static org.mockito.Mockito.mock; 025import static org.mockito.Mockito.reset; 026import static org.mockito.Mockito.verify; 027 028import java.io.IOException; 029import java.util.Arrays; 030import java.util.List; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtility; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.ipc.HBaseRpcController; 037import org.apache.hadoop.hbase.testclassification.MediumTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.log4j.Appender; 040import org.apache.log4j.Level; 041import org.apache.log4j.LogManager; 042import org.apache.log4j.spi.LoggingEvent; 043import org.junit.After; 044import org.junit.Before; 045import org.junit.ClassRule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.runner.RunWith; 049import org.junit.runners.Parameterized; 050import org.mockito.ArgumentCaptor; 051import org.mockito.Mockito; 052 053import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 054import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException; 055 056import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; 057import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.Action; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRequest; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 061 062/** 063 * Tests logging of large batch commands via Multi. Tests are fast, but uses a mini-cluster (to test 064 * via "Multi" commands) so classified as MediumTests 065 */ 066@RunWith(Parameterized.class) 067@Category(MediumTests.class) 068public class TestMultiLogThreshold { 069 070 @ClassRule 071 public static final HBaseClassTestRule CLASS_RULE = 072 HBaseClassTestRule.forClass(TestMultiLogThreshold.class); 073 074 private static final TableName NAME = TableName.valueOf("tableName"); 075 private static final byte[] TEST_FAM = Bytes.toBytes("fam"); 076 077 private HBaseTestingUtility util; 078 private Configuration conf; 079 private int threshold; 080 private HRegionServer rs; 081 private RSRpcServices services; 082 083 private Appender appender; 084 085 @Parameterized.Parameter 086 public static boolean rejectLargeBatchOp; 087 088 @Parameterized.Parameters 089 public static List<Object[]> params() { 090 return Arrays.asList(new Object[] { false }, new Object[] { true }); 091 } 092 093 @Before 094 public void setupTest() throws Exception { 095 util = new HBaseTestingUtility(); 096 conf = util.getConfiguration(); 097 threshold = 098 conf.getInt(HConstants.BATCH_ROWS_THRESHOLD_NAME, HConstants.BATCH_ROWS_THRESHOLD_DEFAULT); 099 conf.setBoolean("hbase.rpc.rows.size.threshold.reject", rejectLargeBatchOp); 100 util.startMiniCluster(); 101 util.createTable(NAME, TEST_FAM); 102 rs = util.getRSForFirstRegionInTable(NAME); 103 appender = mock(Appender.class); 104 LogManager.getLogger(RSRpcServices.class).addAppender(appender); 105 } 106 107 @After 108 public void tearDown() throws Exception { 109 LogManager.getLogger(RSRpcServices.class).removeAppender(appender); 110 util.shutdownMiniCluster(); 111 } 112 113 private enum ActionType { 114 REGION_ACTIONS, ACTIONS; 115 } 116 117 /** 118 * Sends a multi request with a certain amount of rows, will populate Multi command with either 119 * "rows" number of RegionActions with one Action each or one RegionAction with "rows" number of 120 * Actions 121 */ 122 private void sendMultiRequest(int rows, ActionType actionType) 123 throws ServiceException, IOException { 124 RpcController rpcc = Mockito.mock(HBaseRpcController.class); 125 MultiRequest.Builder builder = MultiRequest.newBuilder(); 126 int numRAs = 1; 127 int numAs = 1; 128 switch (actionType) { 129 case REGION_ACTIONS: 130 numRAs = rows; 131 break; 132 case ACTIONS: 133 numAs = rows; 134 break; 135 } 136 for (int i = 0; i < numRAs; i++) { 137 RegionAction.Builder rab = RegionAction.newBuilder(); 138 rab.setRegion(RequestConverter.buildRegionSpecifier( 139 HBaseProtos.RegionSpecifier.RegionSpecifierType.REGION_NAME, 140 new String("someStuff" + i).getBytes())); 141 for (int j = 0; j < numAs; j++) { 142 Action.Builder ab = Action.newBuilder(); 143 rab.addAction(ab.build()); 144 } 145 builder.addRegionAction(rab.build()); 146 } 147 services = new RSRpcServices(rs); 148 services.multi(rpcc, builder.build()); 149 } 150 151 private void assertLogBatchWarnings(boolean expected) { 152 ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class); 153 verify(appender, atLeastOnce()).doAppend(captor.capture()); 154 boolean actual = false; 155 for (LoggingEvent event : captor.getAllValues()) { 156 if (event.getLevel() == Level.WARN && 157 event.getRenderedMessage().contains("Large batch operation detected")) { 158 actual = true; 159 break; 160 } 161 } 162 reset(appender); 163 assertEquals(expected, actual); 164 } 165 166 @Test 167 public void testMultiLogThresholdRegionActions() throws ServiceException, IOException { 168 try { 169 sendMultiRequest(threshold + 1, ActionType.REGION_ACTIONS); 170 assertFalse(rejectLargeBatchOp); 171 } catch (ServiceException e) { 172 assertTrue(rejectLargeBatchOp); 173 } 174 assertLogBatchWarnings(true); 175 176 sendMultiRequest(threshold, ActionType.REGION_ACTIONS); 177 assertLogBatchWarnings(false); 178 179 try { 180 sendMultiRequest(threshold + 1, ActionType.ACTIONS); 181 assertFalse(rejectLargeBatchOp); 182 } catch (ServiceException e) { 183 assertTrue(rejectLargeBatchOp); 184 } 185 assertLogBatchWarnings(true); 186 187 sendMultiRequest(threshold, ActionType.ACTIONS); 188 assertLogBatchWarnings(false); 189 } 190}