diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMultiRespectsLimitsMemstore.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMultiRespectsLimitsMemstore.java new file mode 100644 index 0000000000..0bfda973d4 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMultiRespectsLimitsMemstore.java @@ -0,0 +1,146 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.client; + +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilderFactory; +import org.apache.hadoop.hbase.CellBuilderType; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + + +@Category({MediumTests.class, ClientTests.class}) +public class TestMultiRespectsLimitsMemstore { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestMultiRespectsLimitsMemstore.class); + + private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private final static byte[] FAMILY = Bytes.toBytes("D"); + + // set MAX_RESULT_SIZE to 1 MB, which small than MemStore Chunk Size (2MB) + public static final int MAX_SIZE = 1024 * 1024; + + private static final Logger LOG = LoggerFactory.getLogger(TestMultiRespectsLimitsMemstore.class); + + @Rule + public TestName name = new TestName(); + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + TEST_UTIL.getConfiguration().setLong( + HConstants.HBASE_SERVER_SCANNER_MAX_RESULT_SIZE_KEY, + MAX_SIZE); + TEST_UTIL.getConfiguration().setLong( + HConstants.HBASE_CLIENT_RETRIES_NUMBER, + 0); + + // Only start on regionserver so that all regions are on the same server. + TEST_UTIL.startMiniCluster(1); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + + @Test + public void testBlockMultiLimits() throws Exception { + final TableName tableName = TableName.valueOf(name.getMethodName()); + HTableDescriptor desc = new HTableDescriptor(tableName); + HColumnDescriptor hcd = new HColumnDescriptor(FAMILY); + hcd.setDataBlockEncoding(DataBlockEncoding.FAST_DIFF); + desc.addFamily(hcd); + TEST_UTIL.getAdmin().createTable(desc); + Table t = TEST_UTIL.getConnection().getTable(tableName); + + byte[] row = Bytes.toBytes("TEST"); + byte[][] cols = new byte[][]{ + Bytes.toBytes("0"), // Get this + Bytes.toBytes("1"), // Buffer + Bytes.toBytes("2"), // Buffer + Bytes.toBytes("3"), // Get This + Bytes.toBytes("4"), // Buffer + Bytes.toBytes("5"), // Buffer + }; + + + byte[] value = new byte[100]; + ThreadLocalRandom.current().nextBytes(value); + + for (byte[] col : cols) { + Put p = new Put(row); + p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) + .setRow(row) + .setFamily(FAMILY) + .setQualifier(col) + .setTimestamp(p.getTimestamp()) + .setType(Cell.Type.Put) + .setValue(value) + .build()); + t.put(p); + } + + + List gets = new ArrayList<>(2); + Get g0 = new Get(row); + g0.addColumn(FAMILY, cols[0]); + gets.add(g0); + + Get g2 = new Get(row); + g2.addColumn(FAMILY, cols[3]); + gets.add(g2); + + // if should return two 100 bytes kv + // however as the data is get from memstore and first kv hold reference 2MB chunk + // there wil MultiActionResultTooLarge thrown + + boolean hasMultiActionResultTooLarge = false; + try { + Result[] results = t.get(gets); + } catch (Exception ex) { + LOG.error("failed get result", ex); + hasMultiActionResultTooLarge = ex.getMessage().contains("MultiActionResultTooLarge"); + } + Assert.assertTrue(hasMultiActionResultTooLarge); + } +}