Skip to content

Commit 675e0d3

Browse files
author
Duo Zhang
committed
HBASE-26264 Add more checks to prevent misconfiguration on store file tracker
1 parent 989028b commit 675e0d3

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
@@ -614,8 +614,7 @@ private List<Path> mergeStoreFiles(MasterProcedureEnv env, HRegionFileSystem reg
614614
String family = hcd.getNameAsString();
615615
Configuration trackerConfig =
616616
StoreFileTrackerFactory.mergeConfigurations(env.getMasterConfiguration(), htd, hcd);
617-
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, true,
618-
family, regionFs);
617+
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, family, regionFs);
619618
final Collection<StoreFileInfo> storeFiles = tracker.load();
620619
if (storeFiles != null && storeFiles.size() > 0) {
621620
for (StoreFileInfo storeFileInfo : storeFiles) {

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
@@ -668,8 +668,7 @@ private Pair<List<Path>, List<Path>> splitStoreFiles(final MasterProcedureEnv en
668668
String family = cfd.getNameAsString();
669669
Configuration trackerConfig = StoreFileTrackerFactory.
670670
mergeConfigurations(env.getMasterConfiguration(), htd, htd.getColumnFamily(cfd.getName()));
671-
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, true,
672-
family, regionFs);
671+
StoreFileTracker tracker = StoreFileTrackerFactory.create(trackerConfig, family, regionFs);
673672
Collection<StoreFileInfo> sfis = tracker.load();
674673
if (sfis == null) {
675674
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
@@ -277,15 +277,17 @@ private boolean prepareCreate(final MasterProcedureEnv env) throws IOException {
277277
MasterProcedureUtil.checkGroupNotEmpty(rsGroupInfo, forWhom);
278278
}
279279

280+
// check for store file tracker configurations
281+
StoreFileTrackerFactory.checkForCreateTable(env.getMasterConfiguration(), tableDescriptor);
282+
280283
return true;
281284
}
282285

283286
private void preCreate(final MasterProcedureEnv env)
284287
throws IOException, InterruptedException {
285288
if (!getTableName().isSystemTable()) {
286-
ProcedureSyncWait.getMasterQuotaManager(env)
287-
.checkNamespaceTableAndRegionQuota(
288-
getTableName(), (newRegions != null ? newRegions.size() : 0));
289+
ProcedureSyncWait.getMasterQuotaManager(env).checkNamespaceTableAndRegionQuota(getTableName(),
290+
(newRegions != null ? newRegions.size() : 0));
289291
}
290292

291293
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
@@ -38,6 +38,7 @@
3838
import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
3939
import org.apache.hadoop.hbase.master.zksyncer.MetaLocationSyncer;
4040
import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
41+
import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
4142
import org.apache.hadoop.hbase.replication.ReplicationException;
4243
import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
4344
import org.apache.hadoop.hbase.util.Bytes;
@@ -325,6 +326,10 @@ private void prepareModify(final MasterProcedureEnv env) throws IOException {
325326
modifiedTableDescriptor.getRegionServerGroup(), forWhom);
326327
MasterProcedureUtil.checkGroupNotEmpty(rsGroupInfo, forWhom);
327328
}
329+
330+
// check for store file tracker configurations
331+
StoreFileTrackerFactory.checkForModifyTable(env.getMasterConfiguration(),
332+
unmodifiedTableDescriptor, modifiedTableDescriptor);
328333
}
329334

330335
/**

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
@@ -626,7 +626,7 @@ private void insertRegionFilesIntoStoreTracker(List<Path> allFiles, MasterProced
626626
Configuration config = StoreFileTrackerFactory.mergeConfigurations(conf, tblDesc,
627627
tblDesc.getColumnFamily(Bytes.toBytes(familyName)));
628628
return StoreFileTrackerFactory.
629-
create(config, true, familyName, regionFs);
629+
create(config, familyName, regionFs);
630630
});
631631
fileInfoMap.computeIfAbsent(familyName, l -> new ArrayList<>());
632632
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,11 +15,13 @@
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;
2223
import org.apache.hadoop.hbase.CompoundConfiguration;
24+
import org.apache.hadoop.hbase.DoNotRetryIOException;
2325
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
2426
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
2527
import org.apache.hadoop.hbase.client.TableDescriptor;
@@ -111,13 +113,13 @@ 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,
@@ -126,30 +128,35 @@ public static Configuration mergeConfigurations(Configuration global, TableDescr
126128
.addStringMap(family.getConfiguration()).addBytesMap(family.getValues());
127129
}
128130

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

0 commit comments

Comments
 (0)