1515 */
1616package org .apache .hadoop .hbase .regionserver .storefiletracker ;
1717
18+ import java .io .IOException ;
1819import java .util .Collections ;
1920import java .util .HashMap ;
2021import java .util .Map ;
2122import org .apache .hadoop .conf .Configuration ;
2223import org .apache .hadoop .hbase .CompoundConfiguration ;
24+ import org .apache .hadoop .hbase .DoNotRetryIOException ;
2325import org .apache .hadoop .hbase .client .ColumnFamilyDescriptor ;
2426import org .apache .hadoop .hbase .client .ColumnFamilyDescriptorBuilder ;
2527import 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