HBASE-23223 Support the offsetLock of bucketCache to use strong ref (#764)
[hbase.git] / hbase-server / src / main / java / org / apache / hadoop / hbase / master / TableStateManager.java
blob0ebd25beeadbe9f122696dad54c9546acc66d3e2
1 /**
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;
24 import java.util.Map;
25 import java.util.Set;
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;
50 /**
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);
59 /**
60 * Set this key to false in Configuration to disable migrating table state from zookeeper so
61 * hbase:meta table.
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) {
73 this.master = master;
76 /**
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();
84 try {
85 updateMetaState(tableName, newState);
86 } finally {
87 lock.writeLock().unlock();
91 /**
92 * Set table state to provided but only if table in specified states Caller should lock table on
93 * write.
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();
103 try {
104 TableState currentState = readMetaState(tableName);
105 if (currentState == null) {
106 throw new TableNotFoundException(tableName);
108 if (currentState.inStates(states)) {
109 updateMetaState(tableName, newState);
110 return null;
111 } else {
112 return currentState;
114 } finally {
115 lock.writeLock().unlock();
120 * Set table state to provided but only if table not in specified states Caller should lock table
121 * on write.
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();
130 try {
131 TableState currentState = readMetaState(tableName);
132 if (currentState == null) {
133 throw new TableNotFoundException(tableName);
135 if (!currentState.inStates(states)) {
136 updateMetaState(tableName, newState);
137 return true;
138 } else {
139 return false;
141 } finally {
142 lock.writeLock().unlock();
146 public boolean isTableState(TableName tableName, TableState.State... states) {
147 try {
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?
153 return false;
157 public void setDeletedTable(TableName tableName) throws IOException {
158 if (tableName.equals(TableName.META_TABLE_NAME)) {
159 return;
161 ReadWriteLock lock = tnLock.getLock(tableName);
162 lock.writeLock().lock();
163 try {
164 MetaTableAccessor.deleteTableState(master.getConnection(), tableName);
165 metaStateDeleted(tableName);
166 } finally {
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();
175 try {
176 return readMetaState(tableName) != null;
177 } finally {
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() {
191 @Override
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());
197 return true;
200 return rv;
203 public static class TableStateNotFoundException extends TableNotFoundException {
204 TableStateNotFoundException(TableName tableName) {
205 super(tableName.getNameAsString());
209 @NonNull
210 public TableState getTableState(TableName tableName) throws IOException {
211 ReadWriteLock lock = tnLock.getLock(tableName);
212 lock.readLock().lock();
213 try {
214 TableState currentState = readMetaState(tableName);
215 if (currentState == null) {
216 throw new TableStateNotFoundException(tableName);
218 return currentState;
219 } finally {
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.
231 return;
233 boolean succ = false;
234 try {
235 MetaTableAccessor.updateTableState(master.getConnection(), tableName, newState);
236 tableName2State.put(tableName, newState);
237 succ = true;
238 } finally {
239 if (!succ) {
240 tableName2State.remove(tableName);
243 metaStateUpdated(tableName, newState);
246 protected void metaStateUpdated(TableName tableName, TableState.State newState)
247 throws IOException {
250 protected void metaStateDeleted(TableName tableName) throws IOException {
253 @Nullable
254 private TableState readMetaState(TableName tableName) throws IOException {
255 TableState.State state = tableName2State.get(tableName);
256 if (state != null) {
257 return new TableState(tableName, state);
259 TableState tableState = MetaTableAccessor.getTableState(master.getConnection(), tableName);
260 if (tableState != null) {
261 tableName2State.putIfAbsent(tableName, tableState.getState());
263 return tableState;
266 public void start() throws IOException {
267 TableDescriptors tableDescriptors = master.getTableDescriptors();
268 migrateZooKeeper();
269 Connection connection = master.getConnection();
270 fixTableStates(tableDescriptors, connection);
273 private void fixTableStates(TableDescriptors tableDescriptors, Connection connection)
274 throws IOException {
275 Map<String, TableState> states = new HashMap<>();
276 // NOTE: Full hbase:meta table scan!
277 MetaTableAccessor.fullScanTables(connection, new MetaTableAccessor.Visitor() {
278 @Override
279 public boolean visit(Result r) throws IOException {
280 TableState state = MetaTableAccessor.getTableState(r);
281 states.put(state.getTableName().getNameAsString(), state);
282 return true;
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));
291 continue;
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);
299 } else {
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.
322 @Deprecated
323 private void migrateZooKeeper() throws IOException {
324 if (!this.master.getConfiguration().getBoolean(MIGRATE_TABLE_STATE_FROM_ZK_KEY, true)) {
325 return;
327 try {
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: " +
333 entry.getKey());
334 continue;
336 TableState ts = null;
337 try {
338 ts = getTableState(entry.getKey());
339 } catch (TableStateNotFoundException e) {
340 // This can happen; table exists but no TableState.
342 if (ts == null) {
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());
352 } else {
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.
370 @Deprecated
371 protected void deleteZooKeeper(TableName tableName) {
372 try {
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);