View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.zookeeper;
21  
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.master.AssignmentManager;
31  import org.apache.hadoop.hbase.util.Bytes;
32  import org.apache.zookeeper.KeeperException;
33  
34  /**
35   * Helper class for table state tracking for use by {@link AssignmentManager}.
36   * Reads, caches and sets state up in zookeeper.  If multiple read/write
37   * clients, will make for confusion.  Read-only clients other than
38   * AssignmentManager interested in learning table state can use the
39   * read-only utility methods {@link #isEnabledTable(ZooKeeperWatcher, String)}
40   * and {@link #isDisabledTable(ZooKeeperWatcher, String)}.
41   * 
42   * <p>To save on trips to the zookeeper ensemble, internally we cache table
43   * state.
44   */
45  public class ZKTable {
46    // A znode will exist under the table directory if it is in any of the
47    // following states: {@link TableState#ENABLING} , {@link TableState#DISABLING},
48    // or {@link TableState#DISABLED}.  If {@link TableState#ENABLED}, there will
49    // be no entry for a table in zk.  Thats how it currently works.
50  
51    private static final Log LOG = LogFactory.getLog(ZKTable.class);
52    private final ZooKeeperWatcher watcher;
53  
54    /**
55     * Cache of what we found in zookeeper so we don't have to go to zk ensemble
56     * for every query.  Synchronize access rather than use concurrent Map because
57     * synchronization needs to span query of zk.
58     */
59    private final Map<String, TableState> cache =
60      new HashMap<String, TableState>();
61  
62    // TODO: Make it so always a table znode. Put table schema here as well as table state.
63    // Have watcher on table znode so all are notified of state or schema change.
64    /**
65     * States a Table can be in.
66     * {@link TableState#ENABLED} is not used currently; its the absence of state
67     * in zookeeper that indicates an enabled table currently.
68     */
69    public static enum TableState {
70      ENABLED,
71      DISABLED,
72      DISABLING,
73      ENABLING
74    };
75  
76    public ZKTable(final ZooKeeperWatcher zkw) throws KeeperException {
77      super();
78      this.watcher = zkw;
79      populateTableStates();
80    }
81  
82    /**
83     * Gets a list of all the tables set as disabled in zookeeper.
84     * @param zkw
85     * @return list of disabled tables, empty list if none
86     * @throws KeeperException
87     */
88    private void populateTableStates()
89    throws KeeperException {
90      synchronized (this.cache) {
91        List<String> children =
92          ZKUtil.listChildrenNoWatch(this.watcher, this.watcher.tableZNode);
93        for (String child: children) {
94          TableState state = getTableState(this.watcher, child);
95          if (state != null) this.cache.put(child, state);
96        }
97      }
98    }
99  
100   /**
101    * @param zkw
102    * @param child
103    * @return Null or {@link TableState} found in znode.
104    * @throws KeeperException
105    */
106   private static TableState getTableState(final ZooKeeperWatcher zkw,
107       final String child)
108   throws KeeperException {
109     String znode = ZKUtil.joinZNode(zkw.tableZNode, child);
110     byte [] data = ZKUtil.getData(zkw, znode);
111     if (data == null || data.length <= 0) {
112       // Null if table is enabled.
113       return null;
114     }
115     String str = Bytes.toString(data);
116     try {
117       return TableState.valueOf(str);
118     } catch (IllegalArgumentException e) {
119       throw new IllegalArgumentException(str);
120     }
121   }
122 
123   /**
124    * Sets the specified table as DISABLED 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 setDisabledTable(String tableName)
130   throws KeeperException {
131     synchronized (this.cache) {
132       if (!isDisablingOrDisabledTable(tableName)) {
133         LOG.warn("Moving table " + tableName + " state to disabled but was " +
134           "not first in disabling state: " + this.cache.get(tableName));
135       }
136       setTableState(tableName, TableState.DISABLED);
137     }
138   }
139 
140   /**
141    * Sets the specified table as DISABLING in zookeeper.  Fails silently if the
142    * table is already disabled in zookeeper.  Sets no watches.
143    * @param tableName
144    * @throws KeeperException unexpected zookeeper exception
145    */
146   public void setDisablingTable(final String tableName)
147   throws KeeperException {
148     synchronized (this.cache) {
149       if (!isEnabledOrDisablingTable(tableName)) {
150         LOG.warn("Moving table " + tableName + " state to disabling but was " +
151           "not first in enabled state: " + this.cache.get(tableName));
152       }
153       setTableState(tableName, TableState.DISABLING);
154     }
155   }
156 
157   /**
158    * Sets the specified table as ENABLING in zookeeper.  Fails silently if the
159    * table is already disabled in zookeeper.  Sets no watches.
160    * @param tableName
161    * @throws KeeperException unexpected zookeeper exception
162    */
163   public void setEnablingTable(final String tableName)
164   throws KeeperException {
165     synchronized (this.cache) {
166       if (!isDisabledOrEnablingTable(tableName)) {
167         LOG.warn("Moving table " + tableName + " state to disabling but was " +
168           "not first in enabled state: " + this.cache.get(tableName));
169       }
170       setTableState(tableName, TableState.ENABLING);
171     }
172   }
173 
174   private void setTableState(final String tableName, final TableState state)
175   throws KeeperException {
176     String znode = ZKUtil.joinZNode(this.watcher.tableZNode, tableName);
177     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
178       ZKUtil.createAndFailSilent(this.watcher, znode);
179     }
180     synchronized (this.cache) {
181       ZKUtil.setData(this.watcher, znode, Bytes.toBytes(state.toString()));
182       this.cache.put(tableName, state);
183     }
184   }
185 
186   public boolean isDisabledTable(final String tableName) {
187     return isTableState(tableName, TableState.DISABLED);
188   }
189 
190   /**
191    * Go to zookeeper and see if state of table is {@link TableState#DISABLED}.
192    * This method does not use cache as {@link #isDisabledTable(String)} does.
193    * This method is for clients other than {@link AssignmentManager}
194    * @param zkw
195    * @param tableName
196    * @return True if table is enabled.
197    * @throws KeeperException
198    */
199   public static boolean isDisabledTable(final ZooKeeperWatcher zkw,
200       final String tableName)
201   throws KeeperException {
202     TableState state = getTableState(zkw, tableName);
203     return isTableState(TableState.DISABLED, state);
204   }
205 
206   public boolean isDisablingTable(final String tableName) {
207     return isTableState(tableName, TableState.DISABLING);
208   }
209 
210   public boolean isEnablingTable(final String tableName) {
211     return isTableState(tableName, TableState.ENABLING);
212   }
213 
214   public boolean isEnabledTable(String tableName) {
215     synchronized (this.cache) {
216       // No entry in cache means enabled table.
217       return !this.cache.containsKey(tableName);
218     }
219   }
220 
221   /**
222    * Go to zookeeper and see if state of table is {@link TableState#ENABLED}.
223    * This method does not use cache as {@link #isEnabledTable(String)} does.
224    * This method is for clients other than {@link AssignmentManager}
225    * @param zkw
226    * @param tableName
227    * @return True if table is enabled.
228    * @throws KeeperException
229    */
230   public static boolean isEnabledTable(final ZooKeeperWatcher zkw,
231       final String tableName)
232   throws KeeperException {
233     return getTableState(zkw, tableName) == null;
234   }
235 
236   public boolean isDisablingOrDisabledTable(final String tableName) {
237     synchronized (this.cache) {
238       return isDisablingTable(tableName) || isDisabledTable(tableName);
239     }
240   }
241 
242   /**
243    * Go to zookeeper and see if state of table is {@link TableState#DISABLING}
244    * of {@link TableState#DISABLED}.
245    * This method does not use cache as {@link #isEnabledTable(String)} does.
246    * This method is for clients other than {@link AssignmentManager}.
247    * @param zkw
248    * @param tableName
249    * @return True if table is enabled.
250    * @throws KeeperException
251    */
252   public static boolean isDisablingOrDisabledTable(final ZooKeeperWatcher zkw,
253       final String tableName)
254   throws KeeperException {
255     TableState state = getTableState(zkw, tableName);
256     return isTableState(TableState.DISABLING, state) ||
257       isTableState(TableState.DISABLED, state);
258   }
259 
260   public boolean isEnabledOrDisablingTable(final String tableName) {
261     synchronized (this.cache) {
262       return isEnabledTable(tableName) || isDisablingTable(tableName);
263     }
264   }
265 
266   public boolean isDisabledOrEnablingTable(final String tableName) {
267     synchronized (this.cache) {
268       return isDisabledTable(tableName) || isEnablingTable(tableName);
269     }
270   }
271 
272   private boolean isTableState(final String tableName, final TableState state) {
273     synchronized (this.cache) {
274       TableState currentState = this.cache.get(tableName);
275       return isTableState(currentState, state);
276     }
277   }
278 
279   private static boolean isTableState(final TableState expectedState,
280       final TableState currentState) {
281     return currentState != null && currentState.equals(expectedState);
282   }
283 
284   /**
285    * Enables the table in zookeeper.  Fails silently if the
286    * table is not currently disabled in zookeeper.  Sets no watches.
287    * @param tableName
288    * @throws KeeperException unexpected zookeeper exception
289    */
290   public void setEnabledTable(final String tableName)
291   throws KeeperException {
292     synchronized (this.cache) {
293       if (this.cache.remove(tableName) == null) {
294         LOG.warn("Moving table " + tableName + " state to enabled but was " +
295           "already enabled");
296       }
297       ZKUtil.deleteNodeFailSilent(this.watcher,
298         ZKUtil.joinZNode(this.watcher.tableZNode, tableName));
299     }
300   }
301 
302   /**
303    * Gets a list of all the tables set as disabled in zookeeper.
304    * @return Set of disabled tables, empty Set if none
305    */
306   public Set<String> getDisabledTables() {
307     Set<String> disabledTables = new HashSet<String>();
308     synchronized (this.cache) {
309       Set<String> tables = this.cache.keySet();
310       for (String table: tables) {
311         if (isDisabledTable(table)) disabledTables.add(table);
312       }
313     }
314     return disabledTables;
315   }
316 
317   /**
318    * Gets a list of all the tables set as disabled in zookeeper.
319    * @return Set of disabled tables, empty Set if none
320    * @throws KeeperException 
321    */
322   public static Set<String> getDisabledTables(ZooKeeperWatcher zkw)
323   throws KeeperException {
324     Set<String> disabledTables = new HashSet<String>();
325     List<String> children =
326       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
327     for (String child: children) {
328       TableState state = getTableState(zkw, child);
329       if (state == TableState.DISABLED) disabledTables.add(child);
330     }
331     return disabledTables;
332   }
333 
334   /**
335    * Gets a list of all the tables set as disabled in zookeeper.
336    * @return Set of disabled tables, empty Set if none
337    * @throws KeeperException 
338    */
339   public static Set<String> getDisabledOrDisablingTables(ZooKeeperWatcher zkw)
340   throws KeeperException {
341     Set<String> disabledTables = new HashSet<String>();
342     List<String> children =
343       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
344     for (String child: children) {
345       TableState state = getTableState(zkw, child);
346       if (state == TableState.DISABLED || state == TableState.DISABLING)
347         disabledTables.add(child);
348     }
349     return disabledTables;
350   }
351 }