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 ;
23+ import org .apache .hadoop .hbase .DoNotRetryIOException ;
2224import org .apache .hadoop .hbase .client .ColumnFamilyDescriptor ;
2325import org .apache .hadoop .hbase .client .ColumnFamilyDescriptorBuilder ;
2426import 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