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.master.procedure;
019
020import static org.apache.hadoop.hbase.NamespaceDescriptor.DEFAULT_NAMESPACE;
021import static org.apache.hadoop.hbase.NamespaceDescriptor.SYSTEM_NAMESPACE;
022import static org.apache.hadoop.hbase.master.TableNamespaceManager.insertNamespaceToMeta;
023import static org.apache.hadoop.hbase.master.procedure.AbstractStateMachineNamespaceProcedure.createDirectory;
024
025import java.io.IOException;
026import java.util.Arrays;
027import java.util.concurrent.CountDownLatch;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.fs.FileSystem;
030import org.apache.hadoop.fs.LocatedFileStatus;
031import org.apache.hadoop.fs.Path;
032import org.apache.hadoop.fs.RemoteIterator;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.client.RegionInfoBuilder;
035import org.apache.hadoop.hbase.client.TableDescriptor;
036import org.apache.hadoop.hbase.io.hfile.HFile;
037import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
038import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
039import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
040import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
041import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
042import org.apache.hadoop.hbase.regionserver.HRegion;
043import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
044import org.apache.hadoop.hbase.util.CommonFSUtils;
045import org.apache.hadoop.hbase.util.FSTableDescriptors;
046import org.apache.hadoop.hbase.util.RetryCounter;
047import org.apache.yetus.audience.InterfaceAudience;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.InitMetaState;
052import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.InitMetaStateData;
053import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos;
054
055/**
056 * This procedure is used to initialize meta table for a new hbase deploy. It will just schedule an
057 * {@link TransitRegionStateProcedure} to assign meta.
058 */
059@InterfaceAudience.Private
060public class InitMetaProcedure extends AbstractStateMachineTableProcedure<InitMetaState> {
061
062  private static final Logger LOG = LoggerFactory.getLogger(InitMetaProcedure.class);
063
064  private CountDownLatch latch = new CountDownLatch(1);
065
066  private RetryCounter retryCounter;
067
068  @Override
069  public TableName getTableName() {
070    return TableName.META_TABLE_NAME;
071  }
072
073  @Override
074  public TableOperationType getTableOperationType() {
075    return TableOperationType.CREATE;
076  }
077
078  private static TableDescriptor writeFsLayout(Path rootDir, Configuration conf)
079    throws IOException {
080    LOG.info("BOOTSTRAP: creating hbase:meta region");
081    FileSystem fs = rootDir.getFileSystem(conf);
082    Path tableDir = CommonFSUtils.getTableDir(rootDir, TableName.META_TABLE_NAME);
083    if (fs.exists(tableDir) && !deleteMetaTableDirectoryIfPartial(fs, tableDir)) {
084      LOG.warn("Can not delete partial created meta table, continue...");
085    }
086    // Bootstrapping, make sure blockcache is off. Else, one will be
087    // created here in bootstrap and it'll need to be cleaned up. Better to
088    // not make it in first place. Turn off block caching for bootstrap.
089    // Enable after.
090    TableDescriptor metaDescriptor =
091      FSTableDescriptors.tryUpdateAndGetMetaTableDescriptor(conf, fs, rootDir);
092    HRegion
093      .createHRegion(RegionInfoBuilder.FIRST_META_REGIONINFO, rootDir, conf, metaDescriptor, null)
094      .close();
095    return metaDescriptor;
096  }
097
098  @Override
099  protected Flow executeFromState(MasterProcedureEnv env, InitMetaState state)
100    throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException {
101    LOG.debug("Execute {}", this);
102    try {
103      switch (state) {
104        case INIT_META_WRITE_FS_LAYOUT:
105          Configuration conf = env.getMasterConfiguration();
106          Path rootDir = CommonFSUtils.getRootDir(conf);
107          TableDescriptor td = writeFsLayout(rootDir, conf);
108          env.getMasterServices().getTableDescriptors().update(td, true);
109          setNextState(InitMetaState.INIT_META_ASSIGN_META);
110          return Flow.HAS_MORE_STATE;
111        case INIT_META_ASSIGN_META:
112          LOG.info("Going to assign meta");
113          addChildProcedure(env.getAssignmentManager()
114            .createAssignProcedures(Arrays.asList(RegionInfoBuilder.FIRST_META_REGIONINFO)));
115          setNextState(InitMetaState.INIT_META_CREATE_NAMESPACES);
116          return Flow.HAS_MORE_STATE;
117        case INIT_META_CREATE_NAMESPACES:
118          LOG.info("Going to create {} and {} namespaces", DEFAULT_NAMESPACE, SYSTEM_NAMESPACE);
119          createDirectory(env, DEFAULT_NAMESPACE);
120          createDirectory(env, SYSTEM_NAMESPACE);
121          // here the TableNamespaceManager has not been initialized yet, so we have to insert the
122          // record directly into meta table, later the TableNamespaceManager will load these two
123          // namespaces when starting.
124          insertNamespaceToMeta(env.getMasterServices().getConnection(), DEFAULT_NAMESPACE);
125          insertNamespaceToMeta(env.getMasterServices().getConnection(), SYSTEM_NAMESPACE);
126
127          return Flow.NO_MORE_STATE;
128        default:
129          throw new UnsupportedOperationException("unhandled state=" + state);
130      }
131    } catch (IOException e) {
132      if (retryCounter == null) {
133        retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration());
134      }
135      long backoff = retryCounter.getBackoffTimeAndIncrementAttempts();
136      LOG.warn("Failed to init meta, suspend {}secs", backoff, e);
137      setTimeout(Math.toIntExact(backoff));
138      setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT);
139      skipPersistence();
140      throw new ProcedureSuspendedException();
141    }
142  }
143
144  @Override
145  protected boolean waitInitialized(MasterProcedureEnv env) {
146    // we do not need to wait for master initialized, we are part of the initialization.
147    return false;
148  }
149
150  @Override
151  protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) {
152    setState(ProcedureProtos.ProcedureState.RUNNABLE);
153    env.getProcedureScheduler().addFront(this);
154    return false;
155  }
156
157  @Override
158  protected void rollbackState(MasterProcedureEnv env, InitMetaState state)
159    throws IOException, InterruptedException {
160    throw new UnsupportedOperationException();
161  }
162
163  @Override
164  protected InitMetaState getState(int stateId) {
165    return InitMetaState.forNumber(stateId);
166  }
167
168  @Override
169  protected int getStateId(InitMetaState state) {
170    return state.getNumber();
171  }
172
173  @Override
174  protected InitMetaState getInitialState() {
175    return InitMetaState.INIT_META_WRITE_FS_LAYOUT;
176  }
177
178  @Override
179  protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
180    super.serializeStateData(serializer);
181    serializer.serialize(InitMetaStateData.getDefaultInstance());
182  }
183
184  @Override
185  protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
186    super.deserializeStateData(serializer);
187    serializer.deserialize(InitMetaStateData.class);
188  }
189
190  @Override
191  protected void completionCleanup(MasterProcedureEnv env) {
192    latch.countDown();
193  }
194
195  public void await() throws InterruptedException {
196    latch.await();
197  }
198
199  private static boolean deleteMetaTableDirectoryIfPartial(FileSystem rootDirectoryFs,
200    Path metaTableDir) throws IOException {
201    boolean isPartial = true;
202    try {
203      TableDescriptor metaDescriptor =
204        FSTableDescriptors.getTableDescriptorFromFs(rootDirectoryFs, metaTableDir);
205      // when entering the state of INIT_META_WRITE_FS_LAYOUT, if a meta table directory is found,
206      // the meta table should not have any useful data and considers as partial.
207      // if we find any valid HFiles, operator should fix the meta e.g. via HBCK.
208      if (metaDescriptor != null && metaDescriptor.getColumnFamilyCount() > 0) {
209        RemoteIterator<LocatedFileStatus> iterator = rootDirectoryFs.listFiles(metaTableDir, true);
210        while (iterator.hasNext()) {
211          LocatedFileStatus status = iterator.next();
212          if (
213            StoreFileInfo.isHFile(status.getPath())
214              && HFile.isHFileFormat(rootDirectoryFs, status.getPath())
215          ) {
216            isPartial = false;
217            break;
218          }
219        }
220      }
221    } finally {
222      if (!isPartial) {
223        throw new IOException("Meta table is not partial, please sideline this meta directory "
224          + "or run HBCK to fix this meta table, e.g. rebuild the server hostname in ZNode for the "
225          + "meta region");
226      }
227      return rootDirectoryFs.delete(metaTableDir, true);
228    }
229
230  }
231}