001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver.storefiletracker;
019
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.Map;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
025import org.apache.hadoop.hbase.client.TableDescriptor;
026import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
027import org.apache.hadoop.hbase.mob.MobUtils;
028import org.apache.hadoop.hbase.procedure2.util.StringUtils;
029import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
030import org.apache.hadoop.hbase.regionserver.StoreContext;
031import org.apache.hadoop.hbase.regionserver.StoreUtils;
032import org.apache.hadoop.hbase.util.ReflectionUtils;
033import org.apache.yetus.audience.InterfaceAudience;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
038
039/**
040 * Factory method for creating store file tracker.
041 * <p/>
042 * The current implementations are:
043 * <ul>
044 * <li><em>default</em>: DefaultStoreFileTracker, see {@link DefaultStoreFileTracker}.</li>
045 * <li><em>file</em>:FileBasedStoreFileTracker, see {@link FileBasedStoreFileTracker}.</li>
046 * <li><em>migration</em>:MigrationStoreFileTracker, see {@link MigrationStoreFileTracker}.</li>
047 * </ul>
048 * @see DefaultStoreFileTracker
049 * @see FileBasedStoreFileTracker
050 * @see MigrationStoreFileTracker
051 */
052@InterfaceAudience.Private
053public final class StoreFileTrackerFactory {
054
055  private static final Logger LOG = LoggerFactory.getLogger(StoreFileTrackerFactory.class);
056
057  public static final String TRACKER_IMPL = "hbase.store.file-tracker.impl";
058
059  /**
060   * Maps between configuration names for trackers and implementation classes.
061   */
062  public enum Trackers {
063    DEFAULT(DefaultStoreFileTracker.class),
064    FILE(FileBasedStoreFileTracker.class),
065    MIGRATION(MigrationStoreFileTracker.class);
066
067    final Class<? extends StoreFileTracker> clazz;
068
069    Trackers(Class<? extends StoreFileTracker> clazz) {
070      this.clazz = clazz;
071    }
072  }
073
074  private static final Map<Class<? extends StoreFileTracker>, Trackers> CLASS_TO_ENUM = reverse();
075
076  private static Map<Class<? extends StoreFileTracker>, Trackers> reverse() {
077    Map<Class<? extends StoreFileTracker>, Trackers> map = new HashMap<>();
078    for (Trackers tracker : Trackers.values()) {
079      map.put(tracker.clazz, tracker);
080    }
081    return Collections.unmodifiableMap(map);
082  }
083
084  private StoreFileTrackerFactory() {
085  }
086
087  public static String getStoreFileTrackerName(Configuration conf) {
088    return conf.get(TRACKER_IMPL, Trackers.DEFAULT.name());
089  }
090
091  public static String getStoreFileTrackerName(Class<? extends StoreFileTracker> clazz) {
092    Trackers name = CLASS_TO_ENUM.get(clazz);
093    return name != null ? name.name() : clazz.getName();
094  }
095
096  public static Class<? extends StoreFileTracker> getTrackerClass(Configuration conf) {
097    try {
098      Trackers tracker = Trackers.valueOf(getStoreFileTrackerName(conf).toUpperCase());
099      return tracker.clazz;
100    } catch (IllegalArgumentException e) {
101      // Fall back to them specifying a class name
102      return conf.getClass(TRACKER_IMPL, Trackers.DEFAULT.clazz, StoreFileTracker.class);
103    }
104  }
105
106  public static Class<? extends StoreFileTracker> getTrackerClass(String trackerNameOrClass) {
107    try {
108      Trackers tracker = Trackers.valueOf(trackerNameOrClass.toUpperCase());
109      return tracker.clazz;
110    } catch (IllegalArgumentException e) {
111      // Fall back to them specifying a class name
112      try {
113        return Class.forName(trackerNameOrClass).asSubclass(StoreFileTracker.class);
114      } catch (ClassNotFoundException e1) {
115        throw new RuntimeException(e1);
116      }
117    }
118  }
119
120  public static StoreFileTracker create(Configuration conf, boolean isPrimaryReplica,
121    StoreContext ctx) {
122    Class<? extends StoreFileTracker> tracker;
123    if (
124      ctx != null && ctx.getRegionInfo().getEncodedName()
125        .equals(MobUtils.getMobRegionInfo(ctx.getTableName()).getEncodedName())
126    ) {
127      tracker = Trackers.DEFAULT.clazz;
128    } else {
129      tracker = getTrackerClass(conf);
130    }
131    LOG.debug("instantiating StoreFileTracker impl {}", tracker.getName());
132    return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx);
133  }
134
135  /**
136   * Used at master side when splitting/merging regions, as we do not have a Store, thus no
137   * StoreContext at master side.
138   */
139  public static StoreFileTracker create(Configuration conf, TableDescriptor td,
140    ColumnFamilyDescriptor cfd, HRegionFileSystem regionFs) {
141    return create(conf, td, cfd, regionFs, true);
142  }
143
144  public static StoreFileTracker create(Configuration conf, TableDescriptor td,
145    ColumnFamilyDescriptor cfd, HRegionFileSystem regionFs, boolean isPrimaryReplica) {
146    StoreContext ctx =
147      StoreContext.getBuilder().withColumnFamilyDescriptor(cfd).withRegionFileSystem(regionFs)
148        .withFamilyStoreDirectoryPath(regionFs.getStoreDir(cfd.getNameAsString())).build();
149    return StoreFileTrackerFactory.create(mergeConfigurations(conf, td, cfd), isPrimaryReplica,
150      ctx);
151  }
152
153  private static Configuration mergeConfigurations(Configuration global, TableDescriptor table,
154    ColumnFamilyDescriptor family) {
155    return StoreUtils.createStoreConfiguration(global, table, family);
156  }
157
158  static Class<? extends StoreFileTrackerBase>
159    getStoreFileTrackerClassForMigration(Configuration conf, String configName) {
160    String trackerName =
161      Preconditions.checkNotNull(conf.get(configName), "config %s is not set", configName);
162    try {
163      return Trackers.valueOf(trackerName.toUpperCase()).clazz
164        .asSubclass(StoreFileTrackerBase.class);
165    } catch (IllegalArgumentException e) {
166      // Fall back to them specifying a class name
167      try {
168        return Class.forName(trackerName).asSubclass(StoreFileTrackerBase.class);
169      } catch (ClassNotFoundException cnfe) {
170        throw new RuntimeException(cnfe);
171      }
172    }
173  }
174
175  /**
176   * Create store file tracker to be used as source or destination for
177   * {@link MigrationStoreFileTracker}.
178   */
179  static StoreFileTrackerBase createForMigration(Configuration conf, String configName,
180    boolean isPrimaryReplica, StoreContext ctx) {
181    Class<? extends StoreFileTrackerBase> tracker =
182      getStoreFileTrackerClassForMigration(conf, configName);
183    // prevent nest of MigrationStoreFileTracker, it will cause infinite recursion.
184    if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) {
185      throw new IllegalArgumentException("Should not specify " + configName + " as "
186        + Trackers.MIGRATION + " because it can not be nested");
187    }
188    LOG.debug("instantiating StoreFileTracker impl {} as {}", tracker.getName(), configName);
189    return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx);
190  }
191
192  public static TableDescriptor updateWithTrackerConfigs(Configuration conf,
193    TableDescriptor descriptor) {
194    // CreateTableProcedure needs to instantiate the configured SFT impl, in order to update table
195    // descriptors with the SFT impl specific configs. By the time this happens, the table has no
196    // regions nor stores yet, so it can't create a proper StoreContext.
197    if (StringUtils.isEmpty(descriptor.getValue(TRACKER_IMPL))) {
198      StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true, null);
199      TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(descriptor);
200      return tracker.updateWithTrackerConfigs(builder).build();
201    }
202    return descriptor;
203  }
204
205  public static boolean isMigration(Class<?> clazz) {
206    return MigrationStoreFileTracker.class.isAssignableFrom(clazz);
207  }
208}