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.jupiter.api.Assertions.assertThrows; 021 022import java.io.IOException; 023import org.apache.hadoop.fs.Path; 024import org.apache.hadoop.hbase.HBaseTestingUtil; 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 028import org.apache.hadoop.hbase.client.Get; 029import org.apache.hadoop.hbase.client.Put; 030import org.apache.hadoop.hbase.client.RegionInfo; 031import org.apache.hadoop.hbase.client.RegionInfoBuilder; 032import org.apache.hadoop.hbase.client.RowTooBigException; 033import org.apache.hadoop.hbase.client.TableDescriptor; 034import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.apache.hadoop.hbase.testclassification.RegionServerTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.junit.jupiter.api.AfterAll; 039import org.junit.jupiter.api.BeforeAll; 040import org.junit.jupiter.api.Tag; 041import org.junit.jupiter.api.Test; 042 043/** 044 * Test case to check HRS throws {@link org.apache.hadoop.hbase.client.RowTooBigException} when row 045 * size exceeds configured limits. 046 */ 047@Tag(RegionServerTests.TAG) 048@Tag(MediumTests.TAG) 049public class TestRowTooBig { 050 051 private final static HBaseTestingUtil HTU = new HBaseTestingUtil(); 052 private static Path rootRegionDir; 053 private static final TableDescriptor TEST_TD = TableDescriptorBuilder 054 .newBuilder(TableName.valueOf(TestRowTooBig.class.getSimpleName())).build(); 055 056 @BeforeAll 057 public static void before() throws Exception { 058 HTU.startMiniCluster(); 059 HTU.getConfiguration().setLong(HConstants.TABLE_MAX_ROWSIZE_KEY, 10 * 1024 * 1024L); 060 rootRegionDir = HTU.getDataTestDirOnTestFS("TestRowTooBig"); 061 } 062 063 @AfterAll 064 public static void after() throws Exception { 065 HTU.shutdownMiniCluster(); 066 } 067 068 /** 069 * Usecase: - create a row with 5 large cells (5 Mb each) - flush memstore but don't compact 070 * storefiles. - try to Get whole row. OOME happened before we actually get to reading results, 071 * but during seeking, as each StoreFile gets it's own scanner, and each scanner seeks after the 072 * first KV. 073 */ 074 @Test 075 public void testScannersSeekOnFewLargeCells() throws IOException { 076 byte[] row1 = Bytes.toBytes("row1"); 077 byte[] fam1 = Bytes.toBytes("fam1"); 078 079 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TEST_TD) 080 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build(); 081 082 final RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 083 HRegion region = HBaseTestingUtil.createRegionAndWAL(hri, rootRegionDir, HTU.getConfiguration(), 084 tableDescriptor); 085 try { 086 // Add 5 cells to memstore 087 for (int i = 0; i < 5; i++) { 088 Put put = new Put(row1); 089 090 byte[] value = new byte[5 * 1024 * 1024]; 091 put.addColumn(fam1, Bytes.toBytes("col_" + i), value); 092 region.put(put); 093 region.flush(true); 094 } 095 096 Get get = new Get(row1); 097 assertThrows(RowTooBigException.class, () -> region.get(get)); 098 } finally { 099 HBaseTestingUtil.closeRegionAndWAL(region); 100 } 101 } 102 103 /** 104 * Usecase: - create a row with 1M cells, 10 bytes in each - flush & run major compaction - try to 105 * Get whole row. OOME happened in StoreScanner.next(..). 106 */ 107 @Test 108 public void testScanAcrossManySmallColumns() throws IOException { 109 byte[] row1 = Bytes.toBytes("row1"); 110 byte[] fam1 = Bytes.toBytes("fam1"); 111 112 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TEST_TD) 113 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fam1)).build(); 114 115 final RegionInfo hri = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 116 HRegion region = HBaseTestingUtil.createRegionAndWAL(hri, rootRegionDir, HTU.getConfiguration(), 117 tableDescriptor); 118 try { 119 // Add to memstore 120 for (int i = 0; i < 10; i++) { 121 Put put = new Put(row1); 122 for (int j = 0; j < 10 * 10000; j++) { 123 byte[] value = new byte[10]; 124 put.addColumn(fam1, Bytes.toBytes("col_" + i + "_" + j), value); 125 } 126 region.put(put); 127 region.flush(true); 128 } 129 region.compact(true); 130 131 Get get = new Get(row1); 132 assertThrows(RowTooBigException.class, () -> region.get(get)); 133 } finally { 134 HBaseTestingUtil.closeRegionAndWAL(region); 135 } 136 } 137}