Skip to content

Commit dcaea11

Browse files
committed
HBASE-26264 Add more checks to prevent misconfiguration on store file tracker (#3681)
Signed-off-by: Josh Elser <elserj@apache.org>
1 parent 49c4099 commit dcaea11

File tree

8 files changed

+422
-22
lines changed

8 files changed

+422
-22
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,8 +615,7 @@ private List<Path> mergeStoreFiles(MasterProcedureEnv env, HRegionFileSystem reg
615615
String family = hcd.getNameAsString();
616616
Configuration trackerConfig =
617617
StoreFileTrackerFactory.mergeConfigurations(env.getMasterConfiguration(), htd, hcd);
618-
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, true,
619-
family, regionFs);
618+
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, family, regionFs);
620619
final Collection<StoreFileInfo> storeFiles = tracker.load();
621620
if (storeFiles != null && storeFiles.size() > 0) {
622621
final Configuration storeConfiguration =

hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,7 @@ private Pair<List<Path>, List<Path>> splitStoreFiles(final MasterProcedureEnv en
670670
String family = cfd.getNameAsString();
671671
Configuration trackerConfig = StoreFileTrackerFactory.
672672
mergeConfigurations(env.getMasterConfiguration(), htd, htd.getColumnFamily(cfd.getName()));
673-
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, true,
674-
family, regionFs);
673+
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, family, regionFs);
675674
Collection<StoreFileInfo> sfis = tracker.load();
676675
if (sfis == null) {
677676
continue;

hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateTableProcedure.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,17 @@ private boolean prepareCreate(final MasterProcedureEnv env) throws IOException {
257257
return false;
258258
}
259259

260+
// check for store file tracker configurations
261+
StoreFileTrackerFactory.checkForCreateTable(env.getMasterConfiguration(), tableDescriptor);
262+
260263
return true;
261264
}
262265

263266
private void preCreate(final MasterProcedureEnv env)
264267
throws IOException, InterruptedException {
265268
if (!getTableName().isSystemTable()) {
266-
ProcedureSyncWait.getMasterQuotaManager(env)
267-
.checkNamespaceTableAndRegionQuota(
268-
getTableName(), (newRegions != null ? newRegions.size() : 0));
269+
ProcedureSyncWait.getMasterQuotaManager(env).checkNamespaceTableAndRegionQuota(getTableName(),
270+
(newRegions != null ? newRegions.size() : 0));
269271
}
270272

271273
TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableDescriptor);

hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
3838
import org.apache.hadoop.hbase.master.zksyncer.MetaLocationSyncer;
3939
import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
40+
import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
4041
import org.apache.hadoop.hbase.replication.ReplicationException;
4142
import org.apache.hadoop.hbase.util.Bytes;
4243
import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
@@ -317,6 +318,10 @@ private void prepareModify(final MasterProcedureEnv env) throws IOException {
317318

318319
this.deleteColumnFamilyInModify = isDeleteColumnFamily(unmodifiedTableDescriptor,
319320
modifiedTableDescriptor);
321+
322+
// check for store file tracker configurations
323+
StoreFileTrackerFactory.checkForModifyTable(env.getMasterConfiguration(),
324+
unmodifiedTableDescriptor, modifiedTableDescriptor);
320325
}
321326

322327
/**

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionFileSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ private void insertRegionFilesIntoStoreTracker(List<Path> allFiles, MasterProced
629629
Configuration config = StoreFileTrackerFactory.mergeConfigurations(conf, tblDesc,
630630
tblDesc.getColumnFamily(Bytes.toBytes(familyName)));
631631
return StoreFileTrackerFactory.
632-
create(config, true, familyName, regionFs);
632+
create(config, familyName, regionFs);
633633
});
634634
fileInfoMap.computeIfAbsent(familyName, l -> new ArrayList<>());
635635
List<StoreFileInfo> infos = fileInfoMap.get(familyName);

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/MigrationStoreFileTracker.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,12 @@ public void persistConfiguration(TableDescriptorBuilder builder) {
9898
builder.setValue(DST_IMPL, dst.getTrackerName());
9999
}
100100
}
101+
102+
static Class<? extends StoreFileTracker> getSrcTrackerClass(Configuration conf) {
103+
return StoreFileTrackerFactory.getStoreFileTrackerClassForMigration(conf, SRC_IMPL);
104+
}
105+
106+
static Class<? extends StoreFileTracker> getDstTrackerClass(Configuration conf) {
107+
return StoreFileTrackerFactory.getStoreFileTrackerClassForMigration(conf, DST_IMPL);
108+
}
101109
}

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileTrackerFactory.java

Lines changed: 159 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
*/
1616
package org.apache.hadoop.hbase.regionserver.storefiletracker;
1717

18+
import java.io.IOException;
1819
import java.util.Collections;
1920
import java.util.HashMap;
2021
import java.util.Map;
2122
import org.apache.hadoop.conf.Configuration;
23+
import org.apache.hadoop.hbase.DoNotRetryIOException;
2224
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
2325
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
2426
import org.apache.hadoop.hbase.client.TableDescriptor;
@@ -111,44 +113,49 @@ public static StoreFileTracker create(Configuration conf, boolean isPrimaryRepli
111113
* Used at master side when splitting/merging regions, as we do not have a Store, thus no
112114
* StoreContext at master side.
113115
*/
114-
public static StoreFileTracker create(Configuration conf, boolean isPrimaryReplica, String family,
116+
public static StoreFileTracker create(Configuration conf, String family,
115117
HRegionFileSystem regionFs) {
116118
ColumnFamilyDescriptorBuilder fDescBuilder =
117119
ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family));
118120
StoreContext ctx = StoreContext.getBuilder().withColumnFamilyDescriptor(fDescBuilder.build())
119121
.withRegionFileSystem(regionFs).build();
120-
return StoreFileTrackerFactory.create(conf, isPrimaryReplica, ctx);
122+
return StoreFileTrackerFactory.create(conf, true, ctx);
121123
}
122124

123125
public static Configuration mergeConfigurations(Configuration global, TableDescriptor table,
124126
ColumnFamilyDescriptor family) {
125127
return StoreUtils.createStoreConfiguration(global, table, family);
126128
}
127129

128-
/**
129-
* Create store file tracker to be used as source or destination for
130-
* {@link MigrationStoreFileTracker}.
131-
*/
132-
static StoreFileTrackerBase createForMigration(Configuration conf, String configName,
133-
boolean isPrimaryReplica, StoreContext ctx) {
130+
static Class<? extends StoreFileTrackerBase>
131+
getStoreFileTrackerClassForMigration(Configuration conf, String configName) {
134132
String trackerName =
135133
Preconditions.checkNotNull(conf.get(configName), "config %s is not set", configName);
136-
Class<? extends StoreFileTrackerBase> tracker;
137134
try {
138-
tracker =
139-
Trackers.valueOf(trackerName.toUpperCase()).clazz.asSubclass(StoreFileTrackerBase.class);
135+
return Trackers.valueOf(trackerName.toUpperCase()).clazz
136+
.asSubclass(StoreFileTrackerBase.class);
140137
} catch (IllegalArgumentException e) {
141138
// Fall back to them specifying a class name
142139
try {
143-
tracker = Class.forName(trackerName).asSubclass(StoreFileTrackerBase.class);
140+
return Class.forName(trackerName).asSubclass(StoreFileTrackerBase.class);
144141
} catch (ClassNotFoundException cnfe) {
145142
throw new RuntimeException(cnfe);
146143
}
147144
}
145+
}
146+
147+
/**
148+
* Create store file tracker to be used as source or destination for
149+
* {@link MigrationStoreFileTracker}.
150+
*/
151+
static StoreFileTrackerBase createForMigration(Configuration conf, String configName,
152+
boolean isPrimaryReplica, StoreContext ctx) {
153+
Class<? extends StoreFileTrackerBase> tracker =
154+
getStoreFileTrackerClassForMigration(conf, configName);
148155
// prevent nest of MigrationStoreFileTracker, it will cause infinite recursion.
149156
if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) {
150-
throw new IllegalArgumentException("Should not specify " + configName + " as " +
151-
Trackers.MIGRATION + " because it can not be nested");
157+
throw new IllegalArgumentException("Should not specify " + configName + " as "
158+
+ Trackers.MIGRATION + " because it can not be nested");
152159
}
153160
LOG.info("instantiating StoreFileTracker impl {} as {}", tracker.getName(), configName);
154161
return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx);
@@ -161,4 +168,142 @@ public static void persistTrackerConfig(Configuration conf, TableDescriptorBuild
161168
StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true, context);
162169
tracker.persistConfiguration(builder);
163170
}
171+
172+
// should not use MigrationStoreFileTracker for new family
173+
private static void checkForNewFamily(Configuration conf, TableDescriptor table,
174+
ColumnFamilyDescriptor family) throws IOException {
175+
Configuration mergedConf = mergeConfigurations(conf, table, family);
176+
Class<? extends StoreFileTracker> tracker = getTrackerClass(mergedConf);
177+
if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) {
178+
throw new DoNotRetryIOException(
179+
"Should not use " + Trackers.MIGRATION + " as store file tracker for new family "
180+
+ family.getNameAsString() + " of table " + table.getTableName());
181+
}
182+
}
183+
184+
/**
185+
* Pre check when creating a new table.
186+
* <p/>
187+
* For now, only make sure that we do not use {@link Trackers#MIGRATION} for newly created tables.
188+
* @throws IOException when there are check errors, the upper layer should fail the
189+
* {@code CreateTableProcedure}.
190+
*/
191+
public static void checkForCreateTable(Configuration conf, TableDescriptor table)
192+
throws IOException {
193+
for (ColumnFamilyDescriptor family : table.getColumnFamilies()) {
194+
checkForNewFamily(conf, table, family);
195+
}
196+
}
197+
198+
199+
/**
200+
* Pre check when modifying a table.
201+
* <p/>
202+
* The basic idea is when you want to change the store file tracker implementation, you should use
203+
* {@link Trackers#MIGRATION} first and then change to the destination store file tracker
204+
* implementation.
205+
* <p/>
206+
* There are several rules:
207+
* <ul>
208+
* <li>For newly added family, you should not use {@link Trackers#MIGRATION}.</li>
209+
* <li>For modifying a family:
210+
* <ul>
211+
* <li>If old tracker is {@link Trackers#MIGRATION}, then:
212+
* <ul>
213+
* <li>The new tracker is also {@link Trackers#MIGRATION}, then they must have the same src and
214+
* dst tracker.</li>
215+
* <li>The new tracker is not {@link Trackers#MIGRATION}, then the new tracker must be the dst
216+
* tracker of the old tracker.</li>
217+
* </ul>
218+
* </li>
219+
* <li>If the old tracker is not {@link Trackers#MIGRATION}, then:
220+
* <ul>
221+
* <li>If the new tracker is {@link Trackers#MIGRATION}, then the old tracker must be the src
222+
* tracker of the new tracker.</li>
223+
* <li>If the new tracker is not {@link Trackers#MIGRATION}, then the new tracker must be the same
224+
* with old tracker.</li>
225+
* </ul>
226+
* </li>
227+
* </ul>
228+
* </li>
229+
* </ul>
230+
* @throws IOException when there are check errors, the upper layer should fail the
231+
* {@code ModifyTableProcedure}.
232+
*/
233+
public static void checkForModifyTable(Configuration conf, TableDescriptor oldTable,
234+
TableDescriptor newTable) throws IOException {
235+
for (ColumnFamilyDescriptor newFamily : newTable.getColumnFamilies()) {
236+
ColumnFamilyDescriptor oldFamily = oldTable.getColumnFamily(newFamily.getName());
237+
if (oldFamily == null) {
238+
checkForNewFamily(conf, newTable, newFamily);
239+
continue;
240+
}
241+
Configuration oldConf = mergeConfigurations(conf, oldTable, oldFamily);
242+
Configuration newConf = mergeConfigurations(conf, newTable, newFamily);
243+
244+
Class<? extends StoreFileTracker> oldTracker = getTrackerClass(oldConf);
245+
Class<? extends StoreFileTracker> newTracker = getTrackerClass(newConf);
246+
247+
if (MigrationStoreFileTracker.class.isAssignableFrom(oldTracker)) {
248+
Class<? extends StoreFileTracker> oldSrcTracker =
249+
MigrationStoreFileTracker.getSrcTrackerClass(oldConf);
250+
Class<? extends StoreFileTracker> oldDstTracker =
251+
MigrationStoreFileTracker.getDstTrackerClass(oldConf);
252+
if (oldTracker.equals(newTracker)) {
253+
// confirm that we have the same src tracker and dst tracker
254+
Class<? extends StoreFileTracker> newSrcTracker =
255+
MigrationStoreFileTracker.getSrcTrackerClass(newConf);
256+
if (!oldSrcTracker.equals(newSrcTracker)) {
257+
throw new DoNotRetryIOException(
258+
"The src tracker has been changed from " + getStoreFileTrackerName(oldSrcTracker)
259+
+ " to " + getStoreFileTrackerName(newSrcTracker) + " for family "
260+
+ newFamily.getNameAsString() + " of table " + newTable.getTableName());
261+
}
262+
Class<? extends StoreFileTracker> newDstTracker =
263+
MigrationStoreFileTracker.getDstTrackerClass(newConf);
264+
if (!oldDstTracker.equals(newDstTracker)) {
265+
throw new DoNotRetryIOException(
266+
"The dst tracker has been changed from " + getStoreFileTrackerName(oldDstTracker)
267+
+ " to " + getStoreFileTrackerName(newDstTracker) + " for family "
268+
+ newFamily.getNameAsString() + " of table " + newTable.getTableName());
269+
}
270+
} else {
271+
// we can only change to the dst tracker
272+
if (!newTracker.equals(oldDstTracker)) {
273+
throw new DoNotRetryIOException(
274+
"Should migrate tracker to " + getStoreFileTrackerName(oldDstTracker) + " but got "
275+
+ getStoreFileTrackerName(newTracker) + " for family " + newFamily.getNameAsString()
276+
+ " of table " + newTable.getTableName());
277+
}
278+
}
279+
} else {
280+
if (!oldTracker.equals(newTracker)) {
281+
// can only change to MigrationStoreFileTracker and the src tracker should be the old
282+
// tracker
283+
if (!MigrationStoreFileTracker.class.isAssignableFrom(newTracker)) {
284+
throw new DoNotRetryIOException("Should change to " + Trackers.MIGRATION
285+
+ " first when migrating from " + getStoreFileTrackerName(oldTracker) + " for family "
286+
+ newFamily.getNameAsString() + " of table " + newTable.getTableName());
287+
}
288+
Class<? extends StoreFileTracker> newSrcTracker =
289+
MigrationStoreFileTracker.getSrcTrackerClass(newConf);
290+
if (!oldTracker.equals(newSrcTracker)) {
291+
throw new DoNotRetryIOException(
292+
"Should use src tracker " + getStoreFileTrackerName(oldTracker) + " first but got "
293+
+ getStoreFileTrackerName(newSrcTracker) + " when migrating from "
294+
+ getStoreFileTrackerName(oldTracker) + " for family " + newFamily.getNameAsString()
295+
+ " of table " + newTable.getTableName());
296+
}
297+
Class<? extends StoreFileTracker> newDstTracker =
298+
MigrationStoreFileTracker.getDstTrackerClass(newConf);
299+
// the src and dst tracker should not be the same
300+
if (newSrcTracker.equals(newDstTracker)) {
301+
throw new DoNotRetryIOException("The src tracker and dst tracker are both "
302+
+ getStoreFileTrackerName(newSrcTracker) + " for family "
303+
+ newFamily.getNameAsString() + " of table " + newTable.getTableName());
304+
}
305+
}
306+
}
307+
}
308+
}
164309
}

0 commit comments

Comments
 (0)