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.LinkedList;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.hbase.master.AssignmentManager;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
34  import org.apache.zookeeper.KeeperException;
35  
36  /**
37   * Helper class for table state tracking for use by {@link AssignmentManager}.
38   * Reads, caches and sets state up in zookeeper.  If multiple read/write
39   * clients, will make for confusion.  Read-only clients other than
40   * AssignmentManager interested in learning table state can use the
41   * read-only utility methods in {@link ZKTableReadOnly}.
42   *
43   * <p>To save on trips to the zookeeper ensemble, internally we cache table
44   * state.
45   */
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<String, TableState> cache =
61      new HashMap<String, TableState>();
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     * States a Table can be in.
67     * Compatibility note: ENABLED does not exist in 0.92 releases.  In 0.92, the absence of
68     * the znode indicates the table is enabled.
69     */
70    public static enum TableState {
71      ENABLED,
72      DISABLED,
73      DISABLING,
74      ENABLING
75    };
76  
77    public ZKTable(final ZooKeeperWatcher zkw) throws KeeperException {
78      super();
79      this.watcher = zkw;
80      populateTableStates();
81    }
82  
83    /**
84     * Gets a list of all the tables set as disabled in zookeeper.
85     * @throws KeeperException
86     */
87    private void populateTableStates()
88    throws KeeperException {
89      synchronized (this.cache) {
90        List<String> children =
91          ZKUtil.listChildrenNoWatch(this.watcher, this.watcher.masterTableZNode);
92        if (children == null) return;
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     return ZKTableReadOnly.getTableState(zkw, zkw.masterTableZNode, child);
110   }
111 
112   /**
113    * Sets the specified table as DISABLED in zookeeper.  Fails silently if the
114    * table is already disabled in zookeeper.  Sets no watches.
115    * @param tableName
116    * @throws KeeperException unexpected zookeeper exception
117    */
118   public void setDisabledTable(String tableName)
119   throws KeeperException {
120     synchronized (this.cache) {
121       if (!isDisablingOrDisabledTable(tableName)) {
122         LOG.warn("Moving table " + tableName + " state to disabled but was " +
123           "not first in disabling state: " + this.cache.get(tableName));
124       }
125       setTableState(tableName, TableState.DISABLED);
126     }
127   }
128 
129   /**
130    * Sets the specified table as DISABLING in zookeeper.  Fails silently if the
131    * table is already disabled in zookeeper.  Sets no watches.
132    * @param tableName
133    * @throws KeeperException unexpected zookeeper exception
134    */
135   public void setDisablingTable(final String tableName)
136   throws KeeperException {
137     synchronized (this.cache) {
138       if (!isEnabledOrDisablingTable(tableName)) {
139         LOG.warn("Moving table " + tableName + " state to disabling but was " +
140           "not first in enabled state: " + this.cache.get(tableName));
141       }
142       setTableState(tableName, TableState.DISABLING);
143     }
144   }
145 
146   /**
147    * Sets the specified table as ENABLING in zookeeper.  Fails silently if the
148    * table is already disabled in zookeeper.  Sets no watches.
149    * @param tableName
150    * @throws KeeperException unexpected zookeeper exception
151    */
152   public void setEnablingTable(final String tableName)
153   throws KeeperException {
154     synchronized (this.cache) {
155       if (!isDisabledOrEnablingTable(tableName)) {
156         LOG.warn("Moving table " + tableName + " state to enabling but was " +
157           "not first in disabled state: " + this.cache.get(tableName));
158       }
159       setTableState(tableName, TableState.ENABLING);
160     }
161   }
162 
163   /**
164    * Sets the specified table as ENABLING in zookeeper atomically
165    * If the table is already in ENABLING state, no operation is performed
166    * @param tableName
167    * @return if the operation succeeds or not
168    * @throws KeeperException unexpected zookeeper exception
169    */
170   public boolean checkAndSetEnablingTable(final String tableName)
171     throws KeeperException {
172     synchronized (this.cache) {
173       if (isEnablingOrEnabledTable(tableName)) {
174         return false;
175       }
176       setTableState(tableName, TableState.ENABLING);
177       return true;
178     }
179   }
180   
181   /**
182    * If the table is found in ENABLING state the inmemory state is removed.
183    * This helps in cases where CreateTable is to be retried by the client incase of failures.
184    * If deleteZNode is true - the znode is also deleted
185    * @param tableName
186    * @param deleteZNode
187    * @throws KeeperException
188    */
189   public void removeEnablingTable(final String tableName, boolean deleteZNode)
190       throws KeeperException {
191     synchronized (this.cache) {
192       if (isEnablingTable(tableName)) {
193         this.cache.remove(tableName);
194         if (deleteZNode) {
195           ZKUtil.deleteNodeFailSilent(this.watcher,
196               ZKUtil.joinZNode(this.watcher.masterTableZNode, tableName));
197         }
198       }
199 
200     }
201   }
202 
203   /**
204    * Sets the specified table as ENABLING in zookeeper atomically
205    * If the table isn't in DISABLED state, no operation is performed
206    * @param tableName
207    * @return if the operation succeeds or not
208    * @throws KeeperException unexpected zookeeper exception
209    */
210   public boolean checkDisabledAndSetEnablingTable(final String tableName)
211     throws KeeperException {
212     synchronized (this.cache) {
213       if (!isDisabledTable(tableName)) {
214         return false;
215       }
216       setTableState(tableName, TableState.ENABLING);
217       return true;
218     }
219   }
220 
221   /**
222    * Sets the specified table as DISABLING in zookeeper atomically
223    * If the table isn't in ENABLED state, no operation is performed
224    * @param tableName
225    * @return if the operation succeeds or not
226    * @throws KeeperException unexpected zookeeper exception
227    */
228   public boolean checkEnabledAndSetDisablingTable(final String tableName)
229     throws KeeperException {
230     synchronized (this.cache) {
231       if (this.cache.get(tableName) != null && !isEnabledTable(tableName)) {
232         return false;
233       }
234       setTableState(tableName, TableState.DISABLING);
235       return true;
236     }
237   }
238 
239   private void setTableState(final String tableName, final TableState state)
240   throws KeeperException {
241     String znode = ZKUtil.joinZNode(this.watcher.masterTableZNode, tableName);
242     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
243       ZKUtil.createAndFailSilent(this.watcher, znode);
244     }
245     String znode92 = ZKUtil.joinZNode(this.watcher.masterTableZNode92, tableName);
246     boolean settingToEnabled = (state == TableState.ENABLED);
247     // 0.92 format znode differs in that it is deleted to represent ENABLED,
248     // so only create if we are not setting to enabled.
249     if (!settingToEnabled) {
250       if (ZKUtil.checkExists(this.watcher, znode92) == -1) {
251         ZKUtil.createAndFailSilent(this.watcher, znode92);
252       }
253     }
254     synchronized (this.cache) {
255       List<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
256       if (settingToEnabled) {
257         ops.add(ZKUtilOp.deleteNodeFailSilent(znode92));
258       }
259       else {
260         ops.add(ZKUtilOp.setData(znode92, Bytes.toBytes(state.toString())));
261       }
262       // If not running multi-update either because of configuration or failure,
263       // set the current format znode after the 0.92 format znode.
264       // This is so in the case of failure, the AssignmentManager is guaranteed to
265       // see the state was not applied, since it uses the current format znode internally.
266       ops.add(ZKUtilOp.setData(znode, Bytes.toBytes(state.toString())));
267       ZKUtil.multiOrSequential(this.watcher, ops, true);
268       this.cache.put(tableName, state);
269     }
270   }
271 
272   public boolean isDisabledTable(final String tableName) {
273     return isTableState(tableName, TableState.DISABLED);
274   }
275 
276   public boolean isDisablingTable(final String tableName) {
277     return isTableState(tableName, TableState.DISABLING);
278   }
279 
280   public boolean isEnablingTable(final String tableName) {
281     return isTableState(tableName, TableState.ENABLING);
282   }
283 
284   public boolean isEnabledTable(String tableName) {
285     return isTableState(tableName, TableState.ENABLED);
286   }
287 
288   public boolean isDisablingOrDisabledTable(final String tableName) {
289     synchronized (this.cache) {
290       return isDisablingTable(tableName) || isDisabledTable(tableName);
291     }
292   }
293 
294   public boolean isEnablingOrEnabledTable(final String tableName) {
295     synchronized (this.cache) {
296       return isEnablingTable(tableName) || isEnabledTable(tableName);
297     }
298   }
299 
300   public boolean isEnabledOrDisablingTable(final String tableName) {
301     synchronized (this.cache) {
302       return isEnabledTable(tableName) || isDisablingTable(tableName);
303     }
304   }
305 
306   public boolean isDisabledOrEnablingTable(final String tableName) {
307     synchronized (this.cache) {
308       return isDisabledTable(tableName) || isEnablingTable(tableName);
309     }
310   }
311 
312   private boolean isTableState(final String tableName, final TableState state) {
313     synchronized (this.cache) {
314       TableState currentState = this.cache.get(tableName);
315       return ZKTableReadOnly.isTableState(currentState, state);
316     }
317   }
318 
319   /**
320    * Deletes the table in zookeeper.  Fails silently if the
321    * table is not currently disabled in zookeeper.  Sets no watches.
322    * @param tableName
323    * @throws KeeperException unexpected zookeeper exception
324    */
325   public void setDeletedTable(final String tableName)
326   throws KeeperException {
327     synchronized (this.cache) {
328       List<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
329       ops.add(ZKUtilOp.deleteNodeFailSilent(
330         ZKUtil.joinZNode(this.watcher.masterTableZNode92, tableName)));
331       // If not running multi-update either because of configuration or failure,
332       // delete the current format znode after the 0.92 format znode.  This is so in the case of
333       // failure, the AssignmentManager is guaranteed to see the table was not deleted, since it
334       // uses the current format znode internally.
335       ops.add(ZKUtilOp.deleteNodeFailSilent(
336         ZKUtil.joinZNode(this.watcher.masterTableZNode, tableName)));
337       ZKUtil.multiOrSequential(this.watcher, ops, true);
338       if (this.cache.remove(tableName) == null) {
339         LOG.warn("Moving table " + tableName + " state to deleted but was " +
340           "already deleted");
341       }
342     }
343   }
344   
345   /**
346    * Sets the ENABLED state in the cache and creates or force updates a node to
347    * ENABLED state for the specified table
348    * 
349    * @param tableName
350    * @throws KeeperException
351    */
352   public void setEnabledTable(final String tableName) throws KeeperException {
353     setTableState(tableName, TableState.ENABLED);
354   }
355 
356   /**
357    * check if table is present .
358    * 
359    * @param tableName
360    * @return true if the table is present
361    */
362   public boolean isTablePresent(final String tableName) {
363     synchronized (this.cache) {
364       TableState state = this.cache.get(tableName);
365       return !(state == null);
366     }
367   }
368   
369   /**
370    * Gets a list of all the tables set as disabled in zookeeper.
371    * @return Set of disabled tables, empty Set if none
372    */
373   public Set<String> getDisabledTables() {
374     Set<String> disabledTables = new HashSet<String>();
375     synchronized (this.cache) {
376       Set<String> tables = this.cache.keySet();
377       for (String table: tables) {
378         if (isDisabledTable(table)) disabledTables.add(table);
379       }
380     }
381     return disabledTables;
382   }
383 
384 }