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;
019
020import edu.umd.cs.findbugs.annotations.NonNull;
021import edu.umd.cs.findbugs.annotations.Nullable;
022import java.io.IOException;
023import java.util.Set;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.ConcurrentMap;
026import java.util.concurrent.locks.ReadWriteLock;
027import org.apache.hadoop.hbase.CatalogFamilyFormat;
028import org.apache.hadoop.hbase.ClientMetaTableAccessor;
029import org.apache.hadoop.hbase.MetaTableAccessor;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.TableNotFoundException;
032import org.apache.hadoop.hbase.client.Result;
033import org.apache.hadoop.hbase.client.TableState;
034import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException;
035import org.apache.hadoop.hbase.util.IdReadWriteLock;
036import org.apache.hadoop.hbase.util.IdReadWriteLockWithObjectPool;
037import org.apache.yetus.audience.InterfaceAudience;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
042
043/**
044 * This is a helper class used to manage table states. This class uses hbase:meta as its store for
045 * table state so hbase:meta must be online before accessing its methods.
046 */
047@InterfaceAudience.Private
048public class TableStateManager {
049
050  private static final Logger LOG = LoggerFactory.getLogger(TableStateManager.class);
051
052  private final IdReadWriteLock<TableName> tnLock = new IdReadWriteLockWithObjectPool<>();
053  private final MasterServices master;
054
055  private final ConcurrentMap<TableName, TableState.State> tableName2State =
056    new ConcurrentHashMap<>();
057
058  TableStateManager(MasterServices master) {
059    this.master = master;
060  }
061
062  /**
063   * Set table state to provided. Caller should lock table on write.
064   * @param tableName table to change state for
065   * @param newState  new state
066   */
067  public void setTableState(TableName tableName, TableState.State newState) throws IOException {
068    ReadWriteLock lock = tnLock.getLock(tableName);
069    lock.writeLock().lock();
070    try {
071      updateMetaState(tableName, newState);
072    } finally {
073      lock.writeLock().unlock();
074    }
075  }
076
077  public boolean isTableState(TableName tableName, TableState.State... states) {
078    try {
079      TableState tableState = getTableState(tableName);
080      return tableState.isInStates(states);
081    } catch (IOException e) {
082      LOG.error("Unable to get table " + tableName + " state", e);
083      // XXX: is it safe to just return false here?
084      return false;
085    }
086  }
087
088  public void setDeletedTable(TableName tableName) throws IOException {
089    if (tableName.equals(TableName.META_TABLE_NAME)) {
090      // Can't delete the hbase:meta table.
091      return;
092    }
093    ReadWriteLock lock = tnLock.getLock(tableName);
094    lock.writeLock().lock();
095    try {
096      MetaTableAccessor.deleteTableState(master.getConnection(), tableName);
097    } finally {
098      tableName2State.remove(tableName);
099      lock.writeLock().unlock();
100    }
101  }
102
103  public boolean isTablePresent(TableName tableName) throws IOException {
104    ReadWriteLock lock = tnLock.getLock(tableName);
105    lock.readLock().lock();
106    try {
107      return readMetaState(tableName) != null;
108    } finally {
109      lock.readLock().unlock();
110    }
111  }
112
113  /**
114   * Return all tables in given states.
115   * @param states filter by states
116   * @return tables in given states
117   */
118  Set<TableName> getTablesInStates(TableState.State... states) throws IOException {
119    // Only be called in region normalizer, will not use cache.
120    final Set<TableName> rv = Sets.newHashSet();
121    MetaTableAccessor.fullScanTables(master.getConnection(), new ClientMetaTableAccessor.Visitor() {
122      @Override
123      public boolean visit(Result r) throws IOException {
124        TableState tableState = CatalogFamilyFormat.getTableState(r);
125        if (tableState != null && tableState.inStates(states)) {
126          rv.add(tableState.getTableName());
127        }
128        return true;
129      }
130    });
131    return rv;
132  }
133
134  @NonNull
135  public TableState getTableState(TableName tableName) throws IOException {
136    ReadWriteLock lock = tnLock.getLock(tableName);
137    lock.readLock().lock();
138    try {
139      TableState currentState = readMetaState(tableName);
140      if (currentState == null) {
141        throw new TableNotFoundException("No state found for " + tableName);
142      }
143      return currentState;
144    } finally {
145      lock.readLock().unlock();
146    }
147  }
148
149  private void updateMetaState(TableName tableName, TableState.State newState) throws IOException {
150    if (tableName.equals(TableName.META_TABLE_NAME)) {
151      if (
152        TableState.State.DISABLING.equals(newState) || TableState.State.DISABLED.equals(newState)
153      ) {
154        throw new IllegalArgumentIOException("Cannot disable meta table; " + newState);
155      }
156      // Otherwise, just return; no need to set ENABLED on meta -- it is always ENABLED.
157      return;
158    }
159    boolean succ = false;
160    try {
161      MetaTableAccessor.updateTableState(master.getConnection(), tableName, newState);
162      tableName2State.put(tableName, newState);
163      succ = true;
164    } finally {
165      if (!succ) {
166        this.tableName2State.remove(tableName);
167      }
168    }
169  }
170
171  @Nullable
172  private TableState readMetaState(TableName tableName) throws IOException {
173    TableState.State state = tableName2State.get(tableName);
174    if (state != null) {
175      return new TableState(tableName, state);
176    }
177    TableState tableState = MetaTableAccessor.getTableState(master.getConnection(), tableName);
178    if (tableState != null) {
179      tableName2State.putIfAbsent(tableName, tableState.getState());
180    }
181    return tableState;
182  }
183}