2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.master
;
20 import edu
.umd
.cs
.findbugs
.annotations
.NonNull
;
21 import edu
.umd
.cs
.findbugs
.annotations
.Nullable
;
22 import java
.io
.IOException
;
23 import java
.util
.HashMap
;
26 import java
.util
.concurrent
.ConcurrentHashMap
;
27 import java
.util
.concurrent
.ConcurrentMap
;
28 import java
.util
.concurrent
.locks
.ReadWriteLock
;
29 import org
.apache
.hadoop
.hbase
.MetaTableAccessor
;
30 import org
.apache
.hadoop
.hbase
.TableDescriptors
;
31 import org
.apache
.hadoop
.hbase
.TableName
;
32 import org
.apache
.hadoop
.hbase
.TableNotFoundException
;
33 import org
.apache
.hadoop
.hbase
.client
.Connection
;
34 import org
.apache
.hadoop
.hbase
.client
.Result
;
35 import org
.apache
.hadoop
.hbase
.client
.TableDescriptor
;
36 import org
.apache
.hadoop
.hbase
.client
.TableState
;
37 import org
.apache
.hadoop
.hbase
.exceptions
.IllegalArgumentIOException
;
38 import org
.apache
.hadoop
.hbase
.util
.IdReadWriteLock
;
39 import org
.apache
.hadoop
.hbase
.util
.IdReadWriteLockWithObjectPool
;
40 import org
.apache
.hadoop
.hbase
.util
.ZKDataMigrator
;
41 import org
.apache
.hadoop
.hbase
.zookeeper
.ZKUtil
;
42 import org
.apache
.hadoop
.hbase
.zookeeper
.ZNodePaths
;
43 import org
.apache
.yetus
.audience
.InterfaceAudience
;
44 import org
.apache
.zookeeper
.KeeperException
;
45 import org
.slf4j
.Logger
;
46 import org
.slf4j
.LoggerFactory
;
48 import org
.apache
.hbase
.thirdparty
.com
.google
.common
.collect
.Sets
;
51 * This is a helper class used to manage table states. This class uses hbase:meta as its store for
52 * table state so hbase:meta must be online before {@link #start()} is called.
54 // TODO: Make this a guava Service
55 @InterfaceAudience.Private
56 public class TableStateManager
{
58 private static final Logger LOG
= LoggerFactory
.getLogger(TableStateManager
.class);
60 * Set this key to false in Configuration to disable migrating table state from zookeeper so
63 private static final String MIGRATE_TABLE_STATE_FROM_ZK_KEY
=
64 "hbase.migrate.table.state.from.zookeeper";
66 private final IdReadWriteLock
<TableName
> tnLock
= new IdReadWriteLockWithObjectPool
<>();
67 protected final MasterServices master
;
69 private final ConcurrentMap
<TableName
, TableState
.State
> tableName2State
=
70 new ConcurrentHashMap
<>();
72 public TableStateManager(MasterServices master
) {
77 * Set table state to provided. Caller should lock table on write.
78 * @param tableName table to change state for
79 * @param newState new state
81 public void setTableState(TableName tableName
, TableState
.State newState
) throws IOException
{
82 ReadWriteLock lock
= tnLock
.getLock(tableName
);
83 lock
.writeLock().lock();
85 updateMetaState(tableName
, newState
);
87 lock
.writeLock().unlock();
92 * Set table state to provided but only if table in specified states Caller should lock table on
94 * @param tableName table to change state for
95 * @param newState new state
96 * @param states states to check against
97 * @return null if succeed or table state if failed
99 public TableState
setTableStateIfInStates(TableName tableName
, TableState
.State newState
,
100 TableState
.State
... states
) throws IOException
{
101 ReadWriteLock lock
= tnLock
.getLock(tableName
);
102 lock
.writeLock().lock();
104 TableState currentState
= readMetaState(tableName
);
105 if (currentState
== null) {
106 throw new TableNotFoundException(tableName
);
108 if (currentState
.inStates(states
)) {
109 updateMetaState(tableName
, newState
);
115 lock
.writeLock().unlock();
120 * Set table state to provided but only if table not in specified states Caller should lock table
122 * @param tableName table to change state for
123 * @param newState new state
124 * @param states states to check against
126 public boolean setTableStateIfNotInStates(TableName tableName
, TableState
.State newState
,
127 TableState
.State
... states
) throws IOException
{
128 ReadWriteLock lock
= tnLock
.getLock(tableName
);
129 lock
.writeLock().lock();
131 TableState currentState
= readMetaState(tableName
);
132 if (currentState
== null) {
133 throw new TableNotFoundException(tableName
);
135 if (!currentState
.inStates(states
)) {
136 updateMetaState(tableName
, newState
);
142 lock
.writeLock().unlock();
146 public boolean isTableState(TableName tableName
, TableState
.State
... states
) {
148 TableState tableState
= getTableState(tableName
);
149 return tableState
.isInStates(states
);
150 } catch (IOException e
) {
151 LOG
.error("Unable to get table " + tableName
+ " state", e
);
152 // XXX: is it safe to just return false here?
157 public void setDeletedTable(TableName tableName
) throws IOException
{
158 if (tableName
.equals(TableName
.META_TABLE_NAME
)) {
161 ReadWriteLock lock
= tnLock
.getLock(tableName
);
162 lock
.writeLock().lock();
164 MetaTableAccessor
.deleteTableState(master
.getConnection(), tableName
);
165 metaStateDeleted(tableName
);
167 tableName2State
.remove(tableName
);
168 lock
.writeLock().unlock();
172 public boolean isTablePresent(TableName tableName
) throws IOException
{
173 ReadWriteLock lock
= tnLock
.getLock(tableName
);
174 lock
.readLock().lock();
176 return readMetaState(tableName
) != null;
178 lock
.readLock().unlock();
183 * Return all tables in given states.
184 * @param states filter by states
185 * @return tables in given states
187 public Set
<TableName
> getTablesInStates(TableState
.State
... states
) throws IOException
{
188 // Only be called in region normalizer, will not use cache.
189 final Set
<TableName
> rv
= Sets
.newHashSet();
190 MetaTableAccessor
.fullScanTables(master
.getConnection(), new MetaTableAccessor
.Visitor() {
192 public boolean visit(Result r
) throws IOException
{
193 TableState tableState
= MetaTableAccessor
.getTableState(r
);
194 if (tableState
!= null && tableState
.inStates(states
)) {
195 rv
.add(tableState
.getTableName());
203 public static class TableStateNotFoundException
extends TableNotFoundException
{
204 TableStateNotFoundException(TableName tableName
) {
205 super(tableName
.getNameAsString());
210 public TableState
getTableState(TableName tableName
) throws IOException
{
211 ReadWriteLock lock
= tnLock
.getLock(tableName
);
212 lock
.readLock().lock();
214 TableState currentState
= readMetaState(tableName
);
215 if (currentState
== null) {
216 throw new TableStateNotFoundException(tableName
);
220 lock
.readLock().unlock();
224 private void updateMetaState(TableName tableName
, TableState
.State newState
) throws IOException
{
225 if (tableName
.equals(TableName
.META_TABLE_NAME
)) {
226 if (TableState
.State
.DISABLING
.equals(newState
) ||
227 TableState
.State
.DISABLED
.equals(newState
)) {
228 throw new IllegalArgumentIOException("Cannot disable the meta table; " + newState
);
230 // Otherwise, just return; no need to set ENABLED on meta -- it is always ENABLED.
233 boolean succ
= false;
235 MetaTableAccessor
.updateTableState(master
.getConnection(), tableName
, newState
);
236 tableName2State
.put(tableName
, newState
);
240 tableName2State
.remove(tableName
);
243 metaStateUpdated(tableName
, newState
);
246 protected void metaStateUpdated(TableName tableName
, TableState
.State newState
)
250 protected void metaStateDeleted(TableName tableName
) throws IOException
{
254 private TableState
readMetaState(TableName tableName
) throws IOException
{
255 TableState
.State state
= tableName2State
.get(tableName
);
257 return new TableState(tableName
, state
);
259 TableState tableState
= MetaTableAccessor
.getTableState(master
.getConnection(), tableName
);
260 if (tableState
!= null) {
261 tableName2State
.putIfAbsent(tableName
, tableState
.getState());
266 public void start() throws IOException
{
267 TableDescriptors tableDescriptors
= master
.getTableDescriptors();
269 Connection connection
= master
.getConnection();
270 fixTableStates(tableDescriptors
, connection
);
273 private void fixTableStates(TableDescriptors tableDescriptors
, Connection connection
)
275 Map
<String
, TableState
> states
= new HashMap
<>();
276 // NOTE: Full hbase:meta table scan!
277 MetaTableAccessor
.fullScanTables(connection
, new MetaTableAccessor
.Visitor() {
279 public boolean visit(Result r
) throws IOException
{
280 TableState state
= MetaTableAccessor
.getTableState(r
);
281 states
.put(state
.getTableName().getNameAsString(), state
);
285 for (TableDescriptor tableDesc
: tableDescriptors
.getAll().values()) {
286 TableName tableName
= tableDesc
.getTableName();
287 if (TableName
.isMetaTableName(tableName
)) {
288 // This table is always enabled. No fixup needed. No entry in hbase:meta needed.
289 // Call through to fixTableState though in case a super class wants to do something.
290 fixTableState(new TableState(tableName
, TableState
.State
.ENABLED
));
293 TableState tableState
= states
.get(tableName
.getNameAsString());
294 if (tableState
== null) {
295 LOG
.warn(tableName
+ " has no table state in hbase:meta, assuming ENABLED");
296 MetaTableAccessor
.updateTableState(connection
, tableName
, TableState
.State
.ENABLED
);
297 fixTableState(new TableState(tableName
, TableState
.State
.ENABLED
));
298 tableName2State
.put(tableName
, TableState
.State
.ENABLED
);
300 fixTableState(tableState
);
301 tableName2State
.put(tableName
, tableState
.getState());
307 * For subclasses in case they want to do fixup post hbase:meta.
309 protected void fixTableState(TableState tableState
) throws IOException
{
313 * This code is for case where a hbase2 Master is starting for the first time. ZooKeeper is where
314 * we used to keep table state. On first startup, read zookeeper and update hbase:meta with the
315 * table states found in zookeeper. This is tricky as we'll do this check every time we startup
316 * until mirroring is disabled. See the {@link #MIGRATE_TABLE_STATE_FROM_ZK_KEY} flag. Original
317 * form of this migration came in with HBASE-13032. It deleted all znodes when done. We can't do
318 * that if we want to support hbase-1.x clients who need to be able to read table state out of zk.
319 * See {@link MirroringTableStateManager}.
320 * @deprecated Since 2.0.0. Remove in hbase-3.0.0.
323 private void migrateZooKeeper() throws IOException
{
324 if (!this.master
.getConfiguration().getBoolean(MIGRATE_TABLE_STATE_FROM_ZK_KEY
, true)) {
328 for (Map
.Entry
<TableName
, TableState
.State
> entry
: ZKDataMigrator
329 .queryForTableStates(this.master
.getZooKeeper()).entrySet()) {
330 if (this.master
.getTableDescriptors().get(entry
.getKey()) == null) {
331 deleteZooKeeper(entry
.getKey());
332 LOG
.info("Purged table state entry from zookeepr for table not in hbase:meta: " +
336 TableState ts
= null;
338 ts
= getTableState(entry
.getKey());
339 } catch (TableStateNotFoundException e
) {
340 // This can happen; table exists but no TableState.
343 TableState
.State zkstate
= entry
.getValue();
344 // Only migrate if it is an enable or disabled table. If in-between -- ENABLING or
345 // DISABLING then we have a problem; we are starting up an hbase-2 on a cluster with
346 // RIT. It is going to be rough!
347 if (zkstate
.equals(TableState
.State
.ENABLED
) ||
348 zkstate
.equals(TableState
.State
.DISABLED
)) {
349 LOG
.info("Migrating table state from zookeeper to hbase:meta; tableName=" +
350 entry
.getKey() + ", state=" + entry
.getValue());
351 updateMetaState(entry
.getKey(), entry
.getValue());
353 LOG
.warn("Table={} has no state and zookeeper state is in-between={} (neither " +
354 "ENABLED or DISABLED); NOT MIGRATING table state", entry
.getKey(), zkstate
);
357 // What if the table states disagree? Defer to the hbase:meta setting rather than have the
358 // hbase-1.x support prevail.
360 } catch (KeeperException
| InterruptedException e
) {
361 LOG
.warn("Failed reading table state from zookeeper", e
);
366 * Utility method that knows how to delete the old hbase-1.x table state znode. Used also by the
367 * Mirroring subclass.
368 * @deprecated Since 2.0.0. To be removed in hbase-3.0.0.
371 protected void deleteZooKeeper(TableName tableName
) {
373 // Delete from ZooKeeper
374 String znode
= ZNodePaths
.joinZNode(this.master
.getZooKeeper().getZNodePaths().tableZNode
,
375 tableName
.getNameAsString());
376 ZKUtil
.deleteNodeFailSilent(this.master
.getZooKeeper(), znode
);
377 } catch (KeeperException e
) {
378 LOG
.warn("Failed deleting table state from zookeeper", e
);