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.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 (isEnablingTable(tableName)) {
151         return false;
152       }
153       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
154       return true;
155     }
156   }
157 
158   /**
159    * Sets the specified table as ENABLING in zookeeper atomically
160    * If the table isn't in DISABLED state, no operation is performed
161    * @param tableName
162    * @return if the operation succeeds or not
163    * @throws KeeperException unexpected zookeeper exception
164    */
165   public boolean checkDisabledAndSetEnablingTable(final TableName tableName)
166     throws KeeperException {
167     synchronized (this.cache) {
168       if (!isDisabledTable(tableName)) {
169         return false;
170       }
171       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
172       return true;
173     }
174   }
175 
176   /**
177    * Sets the specified table as DISABLING in zookeeper atomically
178    * If the table isn't in ENABLED state, no operation is performed
179    * @param tableName
180    * @return if the operation succeeds or not
181    * @throws KeeperException unexpected zookeeper exception
182    */
183   public boolean checkEnabledAndSetDisablingTable(final TableName tableName)
184     throws KeeperException {
185     synchronized (this.cache) {
186       if (this.cache.get(tableName) != null && !isEnabledTable(tableName)) {
187         return false;
188       }
189       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
190       return true;
191     }
192   }
193 
194   private void setTableState(final TableName tableName, final ZooKeeperProtos.Table.State state)
195   throws KeeperException {
196     String znode = ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString());
197     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
198       ZKUtil.createAndFailSilent(this.watcher, znode);
199     }
200     synchronized (this.cache) {
201       ZooKeeperProtos.Table.Builder builder = ZooKeeperProtos.Table.newBuilder();
202       builder.setState(state);
203       byte [] data = ProtobufUtil.prependPBMagic(builder.build().toByteArray());
204       ZKUtil.setData(this.watcher, znode, data);
205       this.cache.put(tableName, state);
206     }
207   }
208 
209   public boolean isDisabledTable(final TableName tableName) {
210     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED);
211   }
212 
213   public boolean isDisablingTable(final TableName tableName) {
214     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
215   }
216 
217   public boolean isEnablingTable(final TableName tableName) {
218     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
219   }
220 
221   public boolean isEnabledTable(TableName tableName) {
222     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
223   }
224 
225   public boolean isDisablingOrDisabledTable(final TableName tableName) {
226     synchronized (this.cache) {
227       return isDisablingTable(tableName) || isDisabledTable(tableName);
228     }
229   }
230 
231   public boolean isEnabledOrDisablingTable(final TableName tableName) {
232     synchronized (this.cache) {
233       return isEnabledTable(tableName) || isDisablingTable(tableName);
234     }
235   }
236 
237   public boolean isDisabledOrEnablingTable(final TableName tableName) {
238     synchronized (this.cache) {
239       return isDisabledTable(tableName) || isEnablingTable(tableName);
240     }
241   }
242 
243   private boolean isTableState(final TableName tableName, final ZooKeeperProtos.Table.State state) {
244     synchronized (this.cache) {
245       ZooKeeperProtos.Table.State currentState = this.cache.get(tableName);
246       return ZKTableReadOnly.isTableState(currentState, state);
247     }
248   }
249 
250   /**
251    * Deletes the table in zookeeper.  Fails silently if the
252    * table is not currently disabled in zookeeper.  Sets no watches.
253    * @param tableName
254    * @throws KeeperException unexpected zookeeper exception
255    */
256   public void setDeletedTable(final TableName tableName)
257   throws KeeperException {
258     synchronized (this.cache) {
259       if (this.cache.remove(tableName) == null) {
260         LOG.warn("Moving table " + tableName + " state to deleted but was " +
261           "already deleted");
262       }
263       ZKUtil.deleteNodeFailSilent(this.watcher,
264         ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
265     }
266   }
267   
268   /**
269    * Sets the ENABLED state in the cache and creates or force updates a node to
270    * ENABLED state for the specified table
271    * 
272    * @param tableName
273    * @throws KeeperException
274    */
275   public void setEnabledTable(final TableName tableName) throws KeeperException {
276     setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
277   }
278 
279   /**
280    * check if table is present .
281    * 
282    * @param tableName
283    * @return true if the table is present
284    */
285   public boolean isTablePresent(final TableName tableName) {
286     synchronized (this.cache) {
287       ZooKeeperProtos.Table.State state = this.cache.get(tableName);
288       return !(state == null);
289     }
290   }
291   
292   /**
293    * Gets a list of all the tables set as disabled in zookeeper.
294    * @return Set of disabled tables, empty Set if none
295    */
296   public Set<TableName> getDisabledTables() {
297     Set<TableName> disabledTables = new HashSet<TableName>();
298     synchronized (this.cache) {
299       Set<TableName> tables = this.cache.keySet();
300       for (TableName table: tables) {
301         if (isDisabledTable(table)) disabledTables.add(table);
302       }
303     }
304     return disabledTables;
305   }
306 
307   /**
308    * Gets a list of all the tables set as disabled in zookeeper.
309    * @return Set of disabled tables, empty Set if none
310    * @throws KeeperException
311    */
312   public static Set<TableName> getDisabledTables(ZooKeeperWatcher zkw)
313       throws KeeperException {
314     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED);
315   }
316 
317   /**
318    * Gets a list of all the tables set as disabling in zookeeper.
319    * @return Set of disabling tables, empty Set if none
320    * @throws KeeperException
321    */
322   public static Set<TableName> getDisablingTables(ZooKeeperWatcher zkw)
323       throws KeeperException {
324     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLING);
325   }
326 
327   /**
328    * Gets a list of all the tables set as enabling in zookeeper.
329    * @return Set of enabling tables, empty Set if none
330    * @throws KeeperException
331    */
332   public static Set<TableName> getEnablingTables(ZooKeeperWatcher zkw)
333       throws KeeperException {
334     return getAllTables(zkw, ZooKeeperProtos.Table.State.ENABLING);
335   }
336 
337   /**
338    * Gets a list of all the tables set as disabled in zookeeper.
339    * @return Set of disabled tables, empty Set if none
340    * @throws KeeperException
341    */
342   public static Set<TableName> getDisabledOrDisablingTables(ZooKeeperWatcher zkw)
343       throws KeeperException {
344     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED,
345       ZooKeeperProtos.Table.State.DISABLING);
346   }
347   
348   /**
349    * If the table is found in ENABLING state the inmemory state is removed. This
350    * helps in cases where CreateTable is to be retried by the client incase of
351    * failures.  If deleteZNode is true - the znode is also deleted
352    * 
353    * @param tableName
354    * @param deleteZNode
355    * @throws KeeperException
356    */
357   public void removeEnablingTable(final TableName tableName, boolean deleteZNode)
358       throws KeeperException {
359     synchronized (this.cache) {
360       if (isEnablingTable(tableName)) {
361         this.cache.remove(tableName);
362         if (deleteZNode) {
363           ZKUtil.deleteNodeFailSilent(this.watcher,
364               ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
365         }
366       }
367     }
368   }
369 
370 
371   /**
372    * Gets a list of all the tables of specified states in zookeeper.
373    * @return Set of tables of specified states, empty Set if none
374    * @throws KeeperException
375    */
376   static Set<TableName> getAllTables(final ZooKeeperWatcher zkw,
377       final ZooKeeperProtos.Table.State... states) throws KeeperException {
378     Set<TableName> allTables = new HashSet<TableName>();
379     List<String> children =
380       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
381     if(children == null) return allTables;
382     for (String child: children) {
383       TableName tableName = TableName.valueOf(child);
384       ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(zkw, tableName);
385       for (ZooKeeperProtos.Table.State expectedState: states) {
386         if (state == expectedState) {
387           allTables.add(tableName);
388           break;
389         }
390       }
391     }
392     return allTables;
393   }
394 }