HBASE-19726 Failed to start HMaster due to infinite retrying on meta assign
[hbase.git] / hbase-server / src / main / java / org / apache / hadoop / hbase / master / TableStateManager.java
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
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
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.
17 */
18 package org.apache.hadoop.hbase.master;
19
20 import java.io.IOException;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.locks.ReadWriteLock;
25 import java.util.concurrent.locks.ReentrantReadWriteLock;
26
27 import org.apache.hadoop.hbase.client.TableDescriptor;
28 import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException;
29 import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
30 import edu.umd.cs.findbugs.annotations.NonNull;
31 import edu.umd.cs.findbugs.annotations.Nullable;
32
33 import org.apache.hadoop.hbase.MetaTableAccessor;
34 import org.apache.hadoop.hbase.TableDescriptors;
35 import org.apache.hadoop.hbase.TableName;
36 import org.apache.hadoop.hbase.TableNotFoundException;
37 import org.apache.yetus.audience.InterfaceAudience;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.apache.hadoop.hbase.client.Connection;
41 import org.apache.hadoop.hbase.client.Result;
42 import org.apache.hadoop.hbase.client.TableState;
43
44 /**
45 * This is a helper class used to manage table states.
46 * States persisted in tableinfo and cached internally.
47 * TODO: Cache state. Cut down on meta looksups.
48 */
49 @InterfaceAudience.Private
50 public class TableStateManager {
51 private static final Logger LOG = LoggerFactory.getLogger(TableStateManager.class);
52
53 private final ReadWriteLock lock = new ReentrantReadWriteLock();
54 private final MasterServices master;
55
56 public TableStateManager(MasterServices master) {
57 this.master = master;
58 }
59
60 /**
61 * Set table state to provided.
62 * Caller should lock table on write.
63 * @param tableName table to change state for
64 * @param newState new state
65 * @throws IOException
66 */
67 public void setTableState(TableName tableName, TableState.State newState) throws IOException {
68 lock.writeLock().lock();
69 try {
70 updateMetaState(tableName, newState);
71 } finally {
72 lock.writeLock().unlock();
73 }
74
75 }
76
77 /**
78 * Set table state to provided but only if table in specified states
79 * Caller should lock table on write.
80 * @param tableName table to change state for
81 * @param newState new state
82 * @param states states to check against
83 * @return null if succeed or table state if failed
84 * @throws IOException
85 */
86 public TableState.State setTableStateIfInStates(TableName tableName,
87 TableState.State newState,
88 TableState.State... states)
89 throws IOException {
90 lock.writeLock().lock();
91 try {
92 TableState currentState = readMetaState(tableName);
93 if (currentState == null) {
94 throw new TableNotFoundException(tableName);
95 }
96 if (currentState.inStates(states)) {
97 updateMetaState(tableName, newState);
98 return null;
99 } else {
100 return currentState.getState();
101 }
102 } finally {
103 lock.writeLock().unlock();
104 }
105
106 }
107
108 /**
109 * Set table state to provided but only if table not in specified states
110 * Caller should lock table on write.
111 * @param tableName table to change state for
112 * @param newState new state
113 * @param states states to check against
114 * @throws IOException
115 */
116 public boolean setTableStateIfNotInStates(TableName tableName,
117 TableState.State newState,
118 TableState.State... states)
119 throws IOException {
120 TableState currentState = readMetaState(tableName);
121 if (currentState == null) {
122 throw new TableNotFoundException(tableName);
123 }
124 if (!currentState.inStates(states)) {
125 updateMetaState(tableName, newState);
126 return true;
127 } else {
128 return false;
129 }
130 }
131
132 public boolean isTableState(TableName tableName, TableState.State... states) {
133 try {
134 TableState.State tableState = getTableState(tableName);
135 return TableState.isInStates(tableState, states);
136 } catch (IOException e) {
137 LOG.error("Unable to get table " + tableName + " state", e);
138 return false;
139 }
140 }
141
142 public void setDeletedTable(TableName tableName) throws IOException {
143 if (tableName.equals(TableName.META_TABLE_NAME))
144 return;
145 MetaTableAccessor.deleteTableState(master.getConnection(), tableName);
146 }
147
148 public boolean isTablePresent(TableName tableName) throws IOException {
149 return readMetaState(tableName) != null;
150 }
151
152 /**
153 * Return all tables in given states.
154 *
155 * @param states filter by states
156 * @return tables in given states
157 * @throws IOException
158 */
159 public Set<TableName> getTablesInStates(final TableState.State... states) throws IOException {
160 final Set<TableName> rv = Sets.newHashSet();
161 MetaTableAccessor.fullScanTables(master.getConnection(), new MetaTableAccessor.Visitor() {
162 @Override
163 public boolean visit(Result r) throws IOException {
164 TableState tableState = MetaTableAccessor.getTableState(r);
165 if (tableState != null && tableState.inStates(states))
166 rv.add(tableState.getTableName());
167 return true;
168 }
169 });
170 return rv;
171 }
172
173 @NonNull
174 public TableState.State getTableState(TableName tableName) throws IOException {
175 TableState currentState = readMetaState(tableName);
176 if (currentState == null) {
177 throw new TableNotFoundException(tableName);
178 }
179 return currentState.getState();
180 }
181
182 protected void updateMetaState(TableName tableName, TableState.State newState)
183 throws IOException {
184 if (tableName.equals(TableName.META_TABLE_NAME)) {
185 if (TableState.State.DISABLING.equals(newState) ||
186 TableState.State.DISABLED.equals(newState)) {
187 throw new IllegalArgumentIOException("Cannot disable the meta table; " + newState);
188 }
189 // Otherwise, just return; no need to set ENABLED on meta -- it is always ENABLED.
190 return;
191 }
192 MetaTableAccessor.updateTableState(master.getConnection(), tableName, newState);
193 }
194
195 @Nullable
196 protected TableState readMetaState(TableName tableName) throws IOException {
197 if (tableName.equals(TableName.META_TABLE_NAME)) {
198 return new TableState(tableName, TableState.State.ENABLED);
199 }
200 return MetaTableAccessor.getTableState(master.getConnection(), tableName);
201 }
202
203 public void start() throws IOException {
204 TableDescriptors tableDescriptors = master.getTableDescriptors();
205 Connection connection = master.getConnection();
206 fixTableStates(tableDescriptors, connection);
207 }
208
209 public static void fixTableStates(TableDescriptors tableDescriptors, Connection connection)
210 throws IOException {
211 final Map<String, TableDescriptor> allDescriptors =
212 tableDescriptors.getAllDescriptors();
213 final Map<String, TableState> states = new HashMap<>();
214 MetaTableAccessor.fullScanTables(connection, new MetaTableAccessor.Visitor() {
215 @Override
216 public boolean visit(Result r) throws IOException {
217 TableState state = MetaTableAccessor.getTableState(r);
218 if (state != null)
219 states.put(state.getTableName().getNameAsString(), state);
220 return true;
221 }
222 });
223 for (Map.Entry<String, TableDescriptor> entry : allDescriptors.entrySet()) {
224 String table = entry.getKey();
225 if (table.equals(TableName.META_TABLE_NAME.getNameAsString()))
226 continue;
227 if (!states.containsKey(table)) {
228 LOG.warn("Found table with no state, assuming ENABLED");
229 MetaTableAccessor.updateTableState(connection, TableName.valueOf(table),
230 TableState.State.ENABLED);
231 }
232 }
233 }
234 }