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.util; 019 020import static org.junit.jupiter.api.Assertions.assertThrows; 021 022import java.io.IOException; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.hbase.DoNotRetryIOException; 025import org.apache.hadoop.hbase.TableName; 026import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 027import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 028import org.apache.hadoop.hbase.conf.ConfigKey; 029import org.apache.hadoop.hbase.regionserver.BloomType; 030import org.apache.hadoop.hbase.regionserver.DefaultStoreEngine; 031import org.apache.hadoop.hbase.regionserver.HStore; 032import org.apache.hadoop.hbase.regionserver.compactions.FIFOCompactionPolicy; 033import org.apache.hadoop.hbase.testclassification.MiscTests; 034import org.apache.hadoop.hbase.testclassification.SmallTests; 035import org.junit.jupiter.api.Tag; 036import org.junit.jupiter.api.Test; 037 038@Tag(MiscTests.TAG) 039@Tag(SmallTests.TAG) 040public class TestTableDescriptorChecker { 041 042 @Test 043 public void testSanityCheck() throws IOException { 044 Configuration conf = new Configuration(); 045 TableDescriptorBuilder t = TableDescriptorBuilder.newBuilder(TableName.valueOf("test")); 046 ColumnFamilyDescriptorBuilder cf = ColumnFamilyDescriptorBuilder.newBuilder("cf".getBytes()); 047 t.setColumnFamily(cf.build()); 048 049 // Empty configuration. Should be fine. 050 TableDescriptorChecker.sanityCheck(conf, t.build()); 051 052 // Declare configuration type as int. 053 String key = "hbase.hstore.compaction.ratio"; 054 ConfigKey.INT(key, v -> v > 0); 055 056 // Error in table configuration. 057 t.setValue(key, "xx"); 058 assertThrows(DoNotRetryIOException.class, 059 () -> TableDescriptorChecker.sanityCheck(conf, t.build()), 060 "Should have thrown IllegalArgumentException"); 061 062 // Fix the error. 063 t.setValue(key, "1"); 064 TableDescriptorChecker.sanityCheck(conf, t.build()); 065 066 // Verify column family configuration. 067 for (boolean viaSetValue : new boolean[] { true, false }) { 068 // Error in column family configuration. 069 if (viaSetValue) { 070 cf.setValue(key, "xx"); 071 } else { 072 cf.setConfiguration(key, "xx"); 073 } 074 t.removeColumnFamily("cf".getBytes()); 075 t.setColumnFamily(cf.build()); 076 assertThrows(DoNotRetryIOException.class, 077 () -> TableDescriptorChecker.sanityCheck(conf, t.build()), 078 "Should have thrown IllegalArgumentException"); 079 080 // Fix the error. 081 if (viaSetValue) { 082 cf.setValue(key, ""); 083 } else { 084 cf.setConfiguration(key, ""); 085 } 086 t.removeColumnFamily("cf".getBytes()); 087 t.setColumnFamily(cf.build()); 088 TableDescriptorChecker.sanityCheck(conf, t.build()); 089 } 090 } 091 092 @Test 093 public void testBloomFilterPrefixLengthValidation() throws IOException { 094 Configuration conf = new Configuration(); 095 String key = BloomFilterUtil.PREFIX_LENGTH_KEY; 096 097 for (boolean viaSetValue : new boolean[] { true, false }) { 098 ColumnFamilyDescriptorBuilder cf = ColumnFamilyDescriptorBuilder.newBuilder("cf".getBytes()) 099 .setBloomFilterType(BloomType.ROWPREFIX_FIXED_LENGTH); 100 TableDescriptorBuilder t = TableDescriptorBuilder.newBuilder(TableName.valueOf("test")); 101 102 // Invalid: prefix length must be > 0 for ROWPREFIX_FIXED_LENGTH 103 if (viaSetValue) { 104 cf.setValue(key, "0"); 105 } else { 106 cf.setConfiguration(key, "0"); 107 } 108 t.setColumnFamily(cf.build()); 109 assertThrows(DoNotRetryIOException.class, 110 () -> TableDescriptorChecker.sanityCheck(conf, t.build()), 111 "Should reject ROWPREFIX_FIXED_LENGTH with prefix length 0 set via " 112 + (viaSetValue ? "setValue" : "setConfiguration")); 113 114 // Fix the error. 115 if (viaSetValue) { 116 cf.setValue(key, "5"); 117 } else { 118 cf.setConfiguration(key, "5"); 119 } 120 t.removeColumnFamily("cf".getBytes()); 121 t.setColumnFamily(cf.build()); 122 TableDescriptorChecker.sanityCheck(conf, t.build()); 123 } 124 } 125 126 @Test 127 public void testFifoCompactionPolicyValidation() throws IOException { 128 Configuration conf = new Configuration(); 129 String key = DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY; 130 131 // FIFO compaction requires a non-default TTL. The policy must be honored whether it is set on 132 // the column family via setValue (the shell path since HBASE-20819) or setConfiguration. 133 for (boolean viaSetValue : new boolean[] { true, false }) { 134 ColumnFamilyDescriptorBuilder cf = ColumnFamilyDescriptorBuilder.newBuilder("cf".getBytes()); 135 TableDescriptorBuilder t = TableDescriptorBuilder.newBuilder(TableName.valueOf("test")); 136 137 if (viaSetValue) { 138 cf.setValue(key, FIFOCompactionPolicy.class.getName()); 139 cf.setValue(HStore.BLOCKING_STOREFILES_KEY, "1000"); 140 } else { 141 cf.setConfiguration(key, FIFOCompactionPolicy.class.getName()); 142 cf.setConfiguration(HStore.BLOCKING_STOREFILES_KEY, "1000"); 143 } 144 t.setColumnFamily(cf.build()); 145 assertThrows(DoNotRetryIOException.class, 146 () -> TableDescriptorChecker.sanityCheck(conf, t.build()), 147 "Should reject FIFO compaction with default TTL set via " 148 + (viaSetValue ? "setValue" : "setConfiguration")); 149 150 // Fix the error: FIFO needs a finite TTL and no min versions. 151 cf.setTimeToLive(3600).setMinVersions(0); 152 t.removeColumnFamily("cf".getBytes()); 153 t.setColumnFamily(cf.build()); 154 TableDescriptorChecker.sanityCheck(conf, t.build()); 155 } 156 } 157}