View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.zookeeper;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.TableName;
25  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
26  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
27  import org.apache.zookeeper.KeeperException;
28  
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  
35  /**
36   * Helper class for table state tracking for use by AssignmentManager.
37   * Reads, caches and sets state up in zookeeper.  If multiple read/write
38   * clients, will make for confusion.  Read-only clients other than
39   * AssignmentManager interested in learning table state can use the
40   * read-only utility methods in {@link ZKTableReadOnly}.
41   *
42   * <p>To save on trips to the zookeeper ensemble, internally we cache table
43   * state.
44   */
45  @InterfaceAudience.Private
46  public class ZKTable {
47    // A znode will exist under the table directory if it is in any of the
48    // following states: {@link TableState#ENABLING} , {@link TableState#DISABLING},
49    // or {@link TableState#DISABLED}.  If {@link TableState#ENABLED}, there will
50    // be no entry for a table in zk.  Thats how it currently works.
51  
52    private static final Log LOG = LogFactory.getLog(ZKTable.class);
53    private final ZooKeeperWatcher watcher;
54  
55    /**
56     * Cache of what we found in zookeeper so we don't have to go to zk ensemble
57     * for every query.  Synchronize access rather than use concurrent Map because
58     * synchronization needs to span query of zk.
59     */
60    private final Map<TableName, ZooKeeperProtos.Table.State> cache =
61      new HashMap<TableName, ZooKeeperProtos.Table.State>();
62  
63    // TODO: Make it so always a table znode. Put table schema here as well as table state.
64    // Have watcher on table znode so all are notified of state or schema change.
65  
66    public ZKTable(final ZooKeeperWatcher zkw) throws KeeperException {
67      super();
68      this.watcher = zkw;
69      populateTableStates();
70    }
71  
72    /**
73     * Gets a list of all the tables set as disabled in zookeeper.
74     * @throws KeeperException
75     */
76    private void populateTableStates()
77    throws KeeperException {
78      synchronized (this.cache) {
79        List<String> children = ZKUtil.listChildrenNoWatch(this.watcher, this.watcher.tableZNode);
80        if (children == null) return;
81        for (String child: children) {
82          TableName tableName = TableName.valueOf(child);
83          ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(this.watcher, tableName);
84          if (state != null) this.cache.put(tableName, state);
85        }
86      }
87    }
88  
89    /**
90     * Sets the specified table as DISABLED in zookeeper.  Fails silently if the
91     * table is already disabled in zookeeper.  Sets no watches.
92     * @param tableName
93     * @throws KeeperException unexpected zookeeper exception
94     */
95    public void setDisabledTable(TableName tableName)
96    throws KeeperException {
97      synchronized (this.cache) {
98        if (!isDisablingOrDisabledTable(tableName)) {
99          LOG.warn("Moving table " + tableName + " state to disabled but was " +
100           "not first in disabling state: " + this.cache.get(tableName));
101       }
102       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLED);
103     }
104   }
105 
106   /**
107    * Sets the specified table as DISABLING in zookeeper.  Fails silently if the
108    * table is already disabled in zookeeper.  Sets no watches.
109    * @param tableName
110    * @throws KeeperException unexpected zookeeper exception
111    */
112   public void setDisablingTable(final TableName tableName)
113   throws KeeperException {
114     synchronized (this.cache) {
115       if (!isEnabledOrDisablingTable(tableName)) {
116         LOG.warn("Moving table " + tableName + " state to disabling but was " +
117           "not first in enabled state: " + this.cache.get(tableName));
118       }
119       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
120     }
121   }
122 
123   /**
124    * Sets the specified table as ENABLING in zookeeper.  Fails silently if the
125    * table is already disabled in zookeeper.  Sets no watches.
126    * @param tableName
127    * @throws KeeperException unexpected zookeeper exception
128    */
129   public void setEnablingTable(final TableName tableName)
130   throws KeeperException {
131     synchronized (this.cache) {
132       if (!isDisabledOrEnablingTable(tableName)) {
133         LOG.warn("Moving table " + tableName + " state to enabling but was " +
134           "not first in disabled state: " + this.cache.get(tableName));
135       }
136       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
137     }
138   }
139 
140   /**
141    * Sets the specified table as ENABLING in zookeeper atomically
142    * If the table is already in ENABLING state, no operation is performed
143    * @param tableName
144    * @return if the operation succeeds or not
145    * @throws KeeperException unexpected zookeeper exception
146    */
147   public boolean checkAndSetEnablingTable(final TableName tableName)
148     throws KeeperException {
149     synchronized (this.cache) {
150       if (isEnablingOrEnabledTable(tableName)) {
151         // If the table is in the one of the states from the states list, the cache
152         // might be out-of-date, try to find it out from the master source (zookeeper server).
153         //
154         // Note: this adds extra zookeeper server calls and might have performance impact.
155         // However, this is not the happy path so we should not reach here often. Therefore,
156         // the performance impact should be minimal to none.
157         ZooKeeperProtos.Table.State currentState =
158             ZKTableReadOnly.getTableState(this.watcher, tableName);
159 
160         if (currentState == null ||
161            currentState == ZooKeeperProtos.Table.State.ENABLING ||
162            currentState == ZooKeeperProtos.Table.State.ENABLED) {
163           return false;
164         }
165       }
166       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
167       return true;
168     }
169   }
170 
171   /**
172    * Sets the specified table as ENABLING in zookeeper atomically
173    * If the table isn't in DISABLED state, no operation is performed
174    * @param tableName
175    * @return if the operation succeeds or not
176    * @throws KeeperException unexpected zookeeper exception
177    */
178   public boolean checkDisabledAndSetEnablingTable(final TableName tableName)
179     throws KeeperException {
180     synchronized (this.cache) {
181       if (!isDisabledTable(tableName)) {
182         return false;
183       }
184       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
185       return true;
186     }
187   }
188 
189   /**
190    * Sets the specified table as DISABLING in zookeeper atomically
191    * If the table isn't in ENABLED state, no operation is performed
192    * @param tableName
193    * @return if the operation succeeds or not
194    * @throws KeeperException unexpected zookeeper exception
195    */
196   public boolean checkEnabledAndSetDisablingTable(final TableName tableName)
197     throws KeeperException {
198     synchronized (this.cache) {
199       if (this.cache.get(tableName) != null && !isEnabledTable(tableName)) {
200         return false;
201       }
202       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
203       return true;
204     }
205   }
206 
207   private void setTableState(final TableName tableName, final ZooKeeperProtos.Table.State state)
208   throws KeeperException {
209     String znode = ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString());
210     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
211       ZKUtil.createAndFailSilent(this.watcher, znode);
212     }
213     synchronized (this.cache) {
214       ZooKeeperProtos.Table.Builder builder = ZooKeeperProtos.Table.newBuilder();
215       builder.setState(state);
216       byte [] data = ProtobufUtil.prependPBMagic(builder.build().toByteArray());
217       ZKUtil.setData(this.watcher, znode, data);
218       this.cache.put(tableName, state);
219     }
220   }
221 
222   public boolean isDisabledTable(final TableName tableName) {
223     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED);
224   }
225 
226   public boolean isDisablingTable(final TableName tableName) {
227     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
228   }
229 
230   public boolean isEnablingTable(final TableName tableName) {
231     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
232   }
233 
234   public boolean isEnabledTable(TableName tableName) {
235     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
236   }
237 
238   public boolean isDisablingOrDisabledTable(final TableName tableName) {
239     synchronized (this.cache) {
240       return isDisablingTable(tableName) || isDisabledTable(tableName);
241     }
242   }
243 
244   public boolean isEnablingOrEnabledTable(final TableName tableName) {
245     synchronized (this.cache) {
246       return isEnablingTable(tableName) || isEnabledTable(tableName);
247     }
248   }
249 
250   public boolean isEnabledOrDisablingTable(final TableName tableName) {
251     synchronized (this.cache) {
252       return isEnabledTable(tableName) || isDisablingTable(tableName);
253     }
254   }
255 
256   public boolean isDisabledOrEnablingTable(final TableName tableName) {
257     synchronized (this.cache) {
258       return isDisabledTable(tableName) || isEnablingTable(tableName);
259     }
260   }
261 
262   private boolean isTableState(final TableName tableName, final ZooKeeperProtos.Table.State state) {
263     synchronized (this.cache) {
264       ZooKeeperProtos.Table.State currentState = this.cache.get(tableName);
265       return ZKTableReadOnly.isTableState(currentState, state);
266     }
267   }
268 
269   /**
270    * Deletes the table in zookeeper.  Fails silently if the
271    * table is not currently disabled in zookeeper.  Sets no watches.
272    * @param tableName
273    * @throws KeeperException unexpected zookeeper exception
274    */
275   public void setDeletedTable(final TableName tableName)
276   throws KeeperException {
277     synchronized (this.cache) {
278       if (this.cache.remove(tableName) == null) {
279         LOG.warn("Moving table " + tableName + " state to deleted but was " +
280           "already deleted");
281       }
282       ZKUtil.deleteNodeFailSilent(this.watcher,
283         ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
284     }
285   }
286   
287   /**
288    * Sets the ENABLED state in the cache and creates or force updates a node to
289    * ENABLED state for the specified table
290    * 
291    * @param tableName
292    * @throws KeeperException
293    */
294   public void setEnabledTable(final TableName tableName) throws KeeperException {
295     setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
296   }
297 
298   /**
299    * check if table is present .
300    * 
301    * @param tableName
302    * @return true if the table is present
303    */
304   public boolean isTablePresent(final TableName tableName) {
305     synchronized (this.cache) {
306       ZooKeeperProtos.Table.State state = this.cache.get(tableName);
307       return !(state == null);
308     }
309   }
310   
311   /**
312    * Gets a list of all the tables set as disabled in zookeeper.
313    * @return Set of disabled tables, empty Set if none
314    */
315   public Set<TableName> getDisabledTables() {
316     Set<TableName> disabledTables = new HashSet<TableName>();
317     synchronized (this.cache) {
318       Set<TableName> tables = this.cache.keySet();
319       for (TableName table: tables) {
320         if (isDisabledTable(table)) disabledTables.add(table);
321       }
322     }
323     return disabledTables;
324   }
325 
326   /**
327    * Gets a list of all the tables set as disabled in zookeeper.
328    * @return Set of disabled tables, empty Set if none
329    * @throws KeeperException
330    */
331   public static Set<TableName> getDisabledTables(ZooKeeperWatcher zkw)
332       throws KeeperException {
333     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED);
334   }
335 
336   /**
337    * Gets a list of all the tables set as disabling in zookeeper.
338    * @return Set of disabling tables, empty Set if none
339    * @throws KeeperException
340    */
341   public static Set<TableName> getDisablingTables(ZooKeeperWatcher zkw)
342       throws KeeperException {
343     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLING);
344   }
345 
346   /**
347    * Gets a list of all the tables set as enabling in zookeeper.
348    * @return Set of enabling tables, empty Set if none
349    * @throws KeeperException
350    */
351   public static Set<TableName> getEnablingTables(ZooKeeperWatcher zkw)
352       throws KeeperException {
353     return getAllTables(zkw, ZooKeeperProtos.Table.State.ENABLING);
354   }
355 
356   /**
357    * Gets a list of all the tables set as disabled in zookeeper.
358    * @return Set of disabled tables, empty Set if none
359    * @throws KeeperException
360    */
361   public static Set<TableName> getDisabledOrDisablingTables(ZooKeeperWatcher zkw)
362       throws KeeperException {
363     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED,
364       ZooKeeperProtos.Table.State.DISABLING);
365   }
366   
367   /**
368    * If the table is found in ENABLING state the inmemory state is removed. This
369    * helps in cases where CreateTable is to be retried by the client incase of
370    * failures.  If deleteZNode is true - the znode is also deleted
371    * 
372    * @param tableName
373    * @param deleteZNode
374    * @throws KeeperException
375    */
376   public void removeEnablingTable(final TableName tableName, boolean deleteZNode)
377       throws KeeperException {
378     synchronized (this.cache) {
379       if (isEnablingTable(tableName)) {
380         this.cache.remove(tableName);
381         if (deleteZNode) {
382           ZKUtil.deleteNodeFailSilent(this.watcher,
383               ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
384         }
385       }
386     }
387   }
388 
389 
390   /**
391    * Gets a list of all the tables of specified states in zookeeper.
392    * @return Set of tables of specified states, empty Set if none
393    * @throws KeeperException
394    */
395   static Set<TableName> getAllTables(final ZooKeeperWatcher zkw,
396       final ZooKeeperProtos.Table.State... states) throws KeeperException {
397     Set<TableName> allTables = new HashSet<TableName>();
398     List<String> children =
399       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
400     if(children == null) return allTables;
401     for (String child: children) {
402       TableName tableName = TableName.valueOf(child);
403       ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(zkw, tableName);
404       for (ZooKeeperProtos.Table.State expectedState: states) {
405         if (state == expectedState) {
406           allTables.add(tableName);
407           break;
408         }
409       }
410     }
411     return allTables;
412   }
413 }