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 (isEnablingTable(tableName)) {
174         return false;
175       }
176       setTableState(tableName, TableState.ENABLING);
177       return true;
178     }
179   }
180 
181   /**
182    * Sets the specified table as ENABLING in zookeeper atomically
183    * If the table isn't in DISABLED state, no operation is performed
184    * @param tableName
185    * @return if the operation succeeds or not
186    * @throws KeeperException unexpected zookeeper exception
187    */
188   public boolean checkDisabledAndSetEnablingTable(final String tableName)
189     throws KeeperException {
190     synchronized (this.cache) {
191       if (!isDisabledTable(tableName)) {
192         return false;
193       }
194       setTableState(tableName, TableState.ENABLING);
195       return true;
196     }
197   }
198 
199   /**
200    * Sets the specified table as DISABLING in zookeeper atomically
201    * If the table isn't in ENABLED state, no operation is performed
202    * @param tableName
203    * @return if the operation succeeds or not
204    * @throws KeeperException unexpected zookeeper exception
205    */
206   public boolean checkEnabledAndSetDisablingTable(final String tableName)
207     throws KeeperException {
208     synchronized (this.cache) {
209       if (this.cache.get(tableName) != null && !isEnabledTable(tableName)) {
210         return false;
211       }
212       setTableState(tableName, TableState.DISABLING);
213       return true;
214     }
215   }
216 
217   private void setTableState(final String tableName, final TableState state)
218   throws KeeperException {
219     String znode = ZKUtil.joinZNode(this.watcher.masterTableZNode, tableName);
220     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
221       ZKUtil.createAndFailSilent(this.watcher, znode);
222     }
223     String znode92 = ZKUtil.joinZNode(this.watcher.masterTableZNode92, tableName);
224     boolean settingToEnabled = (state == TableState.ENABLED);
225     // 0.92 format znode differs in that it is deleted to represent ENABLED,
226     // so only create if we are not setting to enabled.
227     if (!settingToEnabled) {
228       if (ZKUtil.checkExists(this.watcher, znode92) == -1) {
229         ZKUtil.createAndFailSilent(this.watcher, znode92);
230       }
231     }
232     synchronized (this.cache) {
233       List<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
234       if (settingToEnabled) {
235         ops.add(ZKUtilOp.deleteNodeFailSilent(znode92));
236       }
237       else {
238         ops.add(ZKUtilOp.setData(znode92, Bytes.toBytes(state.toString())));
239       }
240       // If not running multi-update either because of configuration or failure,
241       // set the current format znode after the 0.92 format znode.
242       // This is so in the case of failure, the AssignmentManager is guaranteed to
243       // see the state was not applied, since it uses the current format znode internally.
244       ops.add(ZKUtilOp.setData(znode, Bytes.toBytes(state.toString())));
245       ZKUtil.multiOrSequential(this.watcher, ops, true);
246       this.cache.put(tableName, state);
247     }
248   }
249 
250   public boolean isDisabledTable(final String tableName) {
251     return isTableState(tableName, TableState.DISABLED);
252   }
253 
254   public boolean isDisablingTable(final String tableName) {
255     return isTableState(tableName, TableState.DISABLING);
256   }
257 
258   public boolean isEnablingTable(final String tableName) {
259     return isTableState(tableName, TableState.ENABLING);
260   }
261 
262   public boolean isEnabledTable(String tableName) {
263     return isTableState(tableName, TableState.ENABLED);
264   }
265 
266   public boolean isDisablingOrDisabledTable(final String tableName) {
267     synchronized (this.cache) {
268       return isDisablingTable(tableName) || isDisabledTable(tableName);
269     }
270   }
271 
272   public boolean isEnabledOrDisablingTable(final String tableName) {
273     synchronized (this.cache) {
274       return isEnabledTable(tableName) || isDisablingTable(tableName);
275     }
276   }
277 
278   public boolean isDisabledOrEnablingTable(final String tableName) {
279     synchronized (this.cache) {
280       return isDisabledTable(tableName) || isEnablingTable(tableName);
281     }
282   }
283 
284   private boolean isTableState(final String tableName, final TableState state) {
285     synchronized (this.cache) {
286       TableState currentState = this.cache.get(tableName);
287       return ZKTableReadOnly.isTableState(currentState, state);
288     }
289   }
290 
291   /**
292    * Deletes the table in zookeeper.  Fails silently if the
293    * table is not currently disabled in zookeeper.  Sets no watches.
294    * @param tableName
295    * @throws KeeperException unexpected zookeeper exception
296    */
297   public void setDeletedTable(final String tableName)
298   throws KeeperException {
299     synchronized (this.cache) {
300       List<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
301       ops.add(ZKUtilOp.deleteNodeFailSilent(
302         ZKUtil.joinZNode(this.watcher.masterTableZNode92, tableName)));
303       // If not running multi-update either because of configuration or failure,
304       // delete the current format znode after the 0.92 format znode.  This is so in the case of
305       // failure, the AssignmentManager is guaranteed to see the table was not deleted, since it
306       // uses the current format znode internally.
307       ops.add(ZKUtilOp.deleteNodeFailSilent(
308         ZKUtil.joinZNode(this.watcher.masterTableZNode, tableName)));
309       ZKUtil.multiOrSequential(this.watcher, ops, true);
310       if (this.cache.remove(tableName) == null) {
311         LOG.warn("Moving table " + tableName + " state to deleted but was " +
312           "already deleted");
313       }
314     }
315   }
316   
317   /**
318    * Sets the ENABLED state in the cache and creates or force updates a node to
319    * ENABLED state for the specified table
320    * 
321    * @param tableName
322    * @throws KeeperException
323    */
324   public void setEnabledTable(final String tableName) throws KeeperException {
325     setTableState(tableName, TableState.ENABLED);
326   }
327 
328   /**
329    * check if table is present .
330    * 
331    * @param tableName
332    * @return true if the table is present
333    */
334   public boolean isTablePresent(final String tableName) {
335     synchronized (this.cache) {
336       TableState state = this.cache.get(tableName);
337       return !(state == null);
338     }
339   }
340   
341   /**
342    * Gets a list of all the tables set as disabled in zookeeper.
343    * @return Set of disabled tables, empty Set if none
344    */
345   public Set<String> getDisabledTables() {
346     Set<String> disabledTables = new HashSet<String>();
347     synchronized (this.cache) {
348       Set<String> tables = this.cache.keySet();
349       for (String table: tables) {
350         if (isDisabledTable(table)) disabledTables.add(table);
351       }
352     }
353     return disabledTables;
354   }
355 
356 }