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.storefiletracker; 019 020import java.io.IOException; 021import org.apache.hadoop.conf.Configuration; 022import org.apache.hadoop.hbase.DoNotRetryIOException; 023import org.apache.hadoop.hbase.TableNotEnabledException; 024import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 025import org.apache.hadoop.hbase.client.TableDescriptor; 026import org.apache.hadoop.hbase.regionserver.StoreUtils; 027import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory.Trackers; 028import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException; 029import org.apache.yetus.audience.InterfaceAudience; 030 031@InterfaceAudience.Private 032public final class StoreFileTrackerValidationUtils { 033 034 private StoreFileTrackerValidationUtils() { 035 } 036 037 // should not use MigrationStoreFileTracker for new family 038 private static void checkForNewFamily(Configuration conf, TableDescriptor table, 039 ColumnFamilyDescriptor family) throws IOException { 040 Configuration mergedConf = StoreUtils.createStoreConfiguration(conf, table, family); 041 Class<? extends StoreFileTracker> tracker = StoreFileTrackerFactory.getTrackerClass(mergedConf); 042 if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) { 043 throw new DoNotRetryIOException( 044 "Should not use " + Trackers.MIGRATION + " as store file tracker for new family " 045 + family.getNameAsString() + " of table " + table.getTableName()); 046 } 047 } 048 049 /** 050 * Pre check when creating a new table. 051 * <p/> 052 * For now, only make sure that we do not use {@link Trackers#MIGRATION} for newly created tables. 053 * @throws IOException when there are check errors, the upper layer should fail the 054 * {@code CreateTableProcedure}. 055 */ 056 public static void checkForCreateTable(Configuration conf, TableDescriptor table) 057 throws IOException { 058 for (ColumnFamilyDescriptor family : table.getColumnFamilies()) { 059 checkForNewFamily(conf, table, family); 060 } 061 } 062 063 /** 064 * Pre check when modifying a table. 065 * <p/> 066 * The basic idea is when you want to change the store file tracker implementation, you should use 067 * {@link Trackers#MIGRATION} first and then change to the destination store file tracker 068 * implementation. 069 * <p/> 070 * There are several rules: 071 * <ul> 072 * <li>For newly added family, you should not use {@link Trackers#MIGRATION}.</li> 073 * <li>For modifying a family: 074 * <ul> 075 * <li>If old tracker is {@link Trackers#MIGRATION}, then: 076 * <ul> 077 * <li>The new tracker is also {@link Trackers#MIGRATION}, then they must have the same src and 078 * dst tracker.</li> 079 * <li>The new tracker is not {@link Trackers#MIGRATION}, then the new tracker must be the dst 080 * tracker of the old tracker.</li> 081 * </ul> 082 * </li> 083 * <li>If the old tracker is not {@link Trackers#MIGRATION}, then: 084 * <ul> 085 * <li>If the new tracker is {@link Trackers#MIGRATION}, then the old tracker must be the src 086 * tracker of the new tracker.</li> 087 * <li>If the new tracker is not {@link Trackers#MIGRATION}, then the new tracker must be the same 088 * with old tracker.</li> 089 * </ul> 090 * </li> 091 * </ul> 092 * </li> 093 * </ul> 094 * @throws IOException when there are check errors, the upper layer should fail the 095 * {@code ModifyTableProcedure}. 096 */ 097 public static void checkForModifyTable(Configuration conf, TableDescriptor oldTable, 098 TableDescriptor newTable, boolean isTableDisabled) throws IOException { 099 for (ColumnFamilyDescriptor newFamily : newTable.getColumnFamilies()) { 100 ColumnFamilyDescriptor oldFamily = oldTable.getColumnFamily(newFamily.getName()); 101 if (oldFamily == null) { 102 checkForNewFamily(conf, newTable, newFamily); 103 continue; 104 } 105 Configuration oldConf = StoreUtils.createStoreConfiguration(conf, oldTable, oldFamily); 106 Configuration newConf = StoreUtils.createStoreConfiguration(conf, newTable, newFamily); 107 108 Class<? extends StoreFileTracker> oldTracker = 109 StoreFileTrackerFactory.getTrackerClass(oldConf); 110 Class<? extends StoreFileTracker> newTracker = 111 StoreFileTrackerFactory.getTrackerClass(newConf); 112 113 if (MigrationStoreFileTracker.class.isAssignableFrom(oldTracker)) { 114 Class<? extends StoreFileTracker> oldSrcTracker = 115 MigrationStoreFileTracker.getSrcTrackerClass(oldConf); 116 Class<? extends StoreFileTracker> oldDstTracker = 117 MigrationStoreFileTracker.getDstTrackerClass(oldConf); 118 if (oldTracker.equals(newTracker)) { 119 // confirm that we have the same src tracker and dst tracker 120 Class<? extends StoreFileTracker> newSrcTracker = 121 MigrationStoreFileTracker.getSrcTrackerClass(newConf); 122 if (!oldSrcTracker.equals(newSrcTracker)) { 123 throw new DoNotRetryIOException("The src tracker has been changed from " 124 + StoreFileTrackerFactory.getStoreFileTrackerName(oldSrcTracker) + " to " 125 + StoreFileTrackerFactory.getStoreFileTrackerName(newSrcTracker) + " for family " 126 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 127 } 128 Class<? extends StoreFileTracker> newDstTracker = 129 MigrationStoreFileTracker.getDstTrackerClass(newConf); 130 if (!oldDstTracker.equals(newDstTracker)) { 131 throw new DoNotRetryIOException("The dst tracker has been changed from " 132 + StoreFileTrackerFactory.getStoreFileTrackerName(oldDstTracker) + " to " 133 + StoreFileTrackerFactory.getStoreFileTrackerName(newDstTracker) + " for family " 134 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 135 } 136 } else { 137 // do not allow changing from MIGRATION to its dst SFT implementation while the table is 138 // disabled. We need to open the HRegion to migrate the tracking information while the SFT 139 // implementation is MIGRATION, otherwise we may loss data. See HBASE-26611 for more 140 // details. 141 if (isTableDisabled) { 142 throw new TableNotEnabledException( 143 "Should not change store file tracker implementation from " 144 + StoreFileTrackerFactory.Trackers.MIGRATION.name() + " while table " 145 + newTable.getTableName() + " is disabled"); 146 } 147 // we can only change to the dst tracker 148 if (!newTracker.equals(oldDstTracker)) { 149 throw new DoNotRetryIOException("Should migrate tracker to " 150 + StoreFileTrackerFactory.getStoreFileTrackerName(oldDstTracker) + " but got " 151 + StoreFileTrackerFactory.getStoreFileTrackerName(newTracker) + " for family " 152 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 153 } 154 } 155 } else { 156 if (!oldTracker.equals(newTracker)) { 157 // can only change to MigrationStoreFileTracker and the src tracker should be the old 158 // tracker 159 if (!MigrationStoreFileTracker.class.isAssignableFrom(newTracker)) { 160 throw new DoNotRetryIOException( 161 "Should change to " + Trackers.MIGRATION + " first when migrating from " 162 + StoreFileTrackerFactory.getStoreFileTrackerName(oldTracker) + " for family " 163 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 164 } 165 // here we do not check whether the table is disabled, as after changing to MIGRATION, we 166 // still rely on the src SFT implementation to actually load the store files, so there 167 // will be no data loss problem. 168 Class<? extends StoreFileTracker> newSrcTracker = 169 MigrationStoreFileTracker.getSrcTrackerClass(newConf); 170 if (!oldTracker.equals(newSrcTracker)) { 171 throw new DoNotRetryIOException("Should use src tracker " 172 + StoreFileTrackerFactory.getStoreFileTrackerName(oldTracker) + " first but got " 173 + StoreFileTrackerFactory.getStoreFileTrackerName(newSrcTracker) 174 + " when migrating from " 175 + StoreFileTrackerFactory.getStoreFileTrackerName(oldTracker) + " for family " 176 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 177 } 178 Class<? extends StoreFileTracker> newDstTracker = 179 MigrationStoreFileTracker.getDstTrackerClass(newConf); 180 // the src and dst tracker should not be the same 181 if (newSrcTracker.equals(newDstTracker)) { 182 throw new DoNotRetryIOException("The src tracker and dst tracker are both " 183 + StoreFileTrackerFactory.getStoreFileTrackerName(newSrcTracker) + " for family " 184 + newFamily.getNameAsString() + " of table " + newTable.getTableName()); 185 } 186 } 187 } 188 } 189 } 190 191 /** 192 * Makes sure restoring a snapshot does not break the current SFT setup follows 193 * StoreUtils.createStoreConfiguration 194 * @param currentTableDesc Existing Table's TableDescriptor 195 * @param snapshotTableDesc Snapshot's TableDescriptor 196 * @param baseConf Current global configuration 197 * @throws RestoreSnapshotException if restore would break the current SFT setup 198 */ 199 public static void validatePreRestoreSnapshot(TableDescriptor currentTableDesc, 200 TableDescriptor snapshotTableDesc, Configuration baseConf) throws RestoreSnapshotException { 201 202 for (ColumnFamilyDescriptor cfDesc : currentTableDesc.getColumnFamilies()) { 203 ColumnFamilyDescriptor snapCFDesc = snapshotTableDesc.getColumnFamily(cfDesc.getName()); 204 // if there is no counterpart in the snapshot it will be just deleted so the config does 205 // not matter 206 if (snapCFDesc != null) { 207 Configuration currentCompositeConf = 208 StoreUtils.createStoreConfiguration(baseConf, currentTableDesc, cfDesc); 209 Configuration snapCompositeConf = 210 StoreUtils.createStoreConfiguration(baseConf, snapshotTableDesc, snapCFDesc); 211 Class<? extends StoreFileTracker> currentSFT = 212 StoreFileTrackerFactory.getTrackerClass(currentCompositeConf); 213 Class<? extends StoreFileTracker> snapSFT = 214 StoreFileTrackerFactory.getTrackerClass(snapCompositeConf); 215 216 // restoration is not possible if there is an SFT mismatch 217 if (currentSFT != snapSFT) { 218 throw new RestoreSnapshotException( 219 "Restoring Snapshot is not possible because " + " the config for column family " 220 + cfDesc.getNameAsString() + " has incompatible configuration. Current SFT: " 221 + currentSFT + " SFT from snapshot: " + snapSFT); 222 } 223 } 224 } 225 } 226}