15
15
*/
16
16
package org .apache .hadoop .hbase .regionserver .storefiletracker ;
17
17
18
+ import java .io .IOException ;
18
19
import java .util .Collections ;
19
20
import java .util .HashMap ;
20
21
import java .util .Map ;
21
22
import org .apache .hadoop .conf .Configuration ;
23
+ import org .apache .hadoop .hbase .DoNotRetryIOException ;
22
24
import org .apache .hadoop .hbase .client .ColumnFamilyDescriptor ;
23
25
import org .apache .hadoop .hbase .client .ColumnFamilyDescriptorBuilder ;
24
26
import org .apache .hadoop .hbase .client .TableDescriptor ;
@@ -111,44 +113,49 @@ public static StoreFileTracker create(Configuration conf, boolean isPrimaryRepli
111
113
* Used at master side when splitting/merging regions, as we do not have a Store, thus no
112
114
* StoreContext at master side.
113
115
*/
114
- public static StoreFileTracker create (Configuration conf , boolean isPrimaryReplica , String family ,
116
+ public static StoreFileTracker create (Configuration conf , String family ,
115
117
HRegionFileSystem regionFs ) {
116
118
ColumnFamilyDescriptorBuilder fDescBuilder =
117
119
ColumnFamilyDescriptorBuilder .newBuilder (Bytes .toBytes (family ));
118
120
StoreContext ctx = StoreContext .getBuilder ().withColumnFamilyDescriptor (fDescBuilder .build ())
119
121
.withRegionFileSystem (regionFs ).build ();
120
- return StoreFileTrackerFactory .create (conf , isPrimaryReplica , ctx );
122
+ return StoreFileTrackerFactory .create (conf , true , ctx );
121
123
}
122
124
123
125
public static Configuration mergeConfigurations (Configuration global , TableDescriptor table ,
124
126
ColumnFamilyDescriptor family ) {
125
127
return StoreUtils .createStoreConfiguration (global , table , family );
126
128
}
127
129
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 ) {
134
132
String trackerName =
135
133
Preconditions .checkNotNull (conf .get (configName ), "config %s is not set" , configName );
136
- Class <? extends StoreFileTrackerBase > tracker ;
137
134
try {
138
- tracker =
139
- Trackers . valueOf ( trackerName . toUpperCase ()). clazz .asSubclass (StoreFileTrackerBase .class );
135
+ return Trackers . valueOf ( trackerName . toUpperCase ()). clazz
136
+ .asSubclass (StoreFileTrackerBase .class );
140
137
} catch (IllegalArgumentException e ) {
141
138
// Fall back to them specifying a class name
142
139
try {
143
- tracker = Class .forName (trackerName ).asSubclass (StoreFileTrackerBase .class );
140
+ return Class .forName (trackerName ).asSubclass (StoreFileTrackerBase .class );
144
141
} catch (ClassNotFoundException cnfe ) {
145
142
throw new RuntimeException (cnfe );
146
143
}
147
144
}
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 );
148
155
// prevent nest of MigrationStoreFileTracker, it will cause infinite recursion.
149
156
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" );
152
159
}
153
160
LOG .info ("instantiating StoreFileTracker impl {} as {}" , tracker .getName (), configName );
154
161
return ReflectionUtils .newInstance (tracker , conf , isPrimaryReplica , ctx );
@@ -161,4 +168,142 @@ public static void persistTrackerConfig(Configuration conf, TableDescriptorBuild
161
168
StoreFileTracker tracker = StoreFileTrackerFactory .create (conf , true , context );
162
169
tracker .persistConfiguration (builder );
163
170
}
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
+ }
164
309
}
0 commit comments