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 (isEnablingOrEnabledTable(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 isEnablingOrEnabledTable(final TableName tableName) {
232     synchronized (this.cache) {
233       return isEnablingTable(tableName) || isEnabledTable(tableName);
234     }
235   }
236 
237   public boolean isEnabledOrDisablingTable(final TableName tableName) {
238     synchronized (this.cache) {
239       return isEnabledTable(tableName) || isDisablingTable(tableName);
240     }
241   }
242 
243   public boolean isDisabledOrEnablingTable(final TableName tableName) {
244     synchronized (this.cache) {
245       return isDisabledTable(tableName) || isEnablingTable(tableName);
246     }
247   }
248 
249   private boolean isTableState(final TableName tableName, final ZooKeeperProtos.Table.State state) {
250     synchronized (this.cache) {
251       ZooKeeperProtos.Table.State currentState = this.cache.get(tableName);
252       return ZKTableReadOnly.isTableState(currentState, state);
253     }
254   }
255 
256   /**
257    * Deletes the table in zookeeper.  Fails silently if the
258    * table is not currently disabled in zookeeper.  Sets no watches.
259    * @param tableName
260    * @throws KeeperException unexpected zookeeper exception
261    */
262   public void setDeletedTable(final TableName tableName)
263   throws KeeperException {
264     synchronized (this.cache) {
265       if (this.cache.remove(tableName) == null) {
266         LOG.warn("Moving table " + tableName + " state to deleted but was " +
267           "already deleted");
268       }
269       ZKUtil.deleteNodeFailSilent(this.watcher,
270         ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
271     }
272   }
273   
274   /**
275    * Sets the ENABLED state in the cache and creates or force updates a node to
276    * ENABLED state for the specified table
277    * 
278    * @param tableName
279    * @throws KeeperException
280    */
281   public void setEnabledTable(final TableName tableName) throws KeeperException {
282     setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
283   }
284 
285   /**
286    * check if table is present .
287    * 
288    * @param tableName
289    * @return true if the table is present
290    */
291   public boolean isTablePresent(final TableName tableName) {
292     synchronized (this.cache) {
293       ZooKeeperProtos.Table.State state = this.cache.get(tableName);
294       return !(state == null);
295     }
296   }
297   
298   /**
299    * Gets a list of all the tables set as disabled in zookeeper.
300    * @return Set of disabled tables, empty Set if none
301    */
302   public Set<TableName> getDisabledTables() {
303     Set<TableName> disabledTables = new HashSet<TableName>();
304     synchronized (this.cache) {
305       Set<TableName> tables = this.cache.keySet();
306       for (TableName table: tables) {
307         if (isDisabledTable(table)) disabledTables.add(table);
308       }
309     }
310     return disabledTables;
311   }
312 
313   /**
314    * Gets a list of all the tables set as disabled in zookeeper.
315    * @return Set of disabled tables, empty Set if none
316    * @throws KeeperException
317    */
318   public static Set<TableName> getDisabledTables(ZooKeeperWatcher zkw)
319       throws KeeperException {
320     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED);
321   }
322 
323   /**
324    * Gets a list of all the tables set as disabling in zookeeper.
325    * @return Set of disabling tables, empty Set if none
326    * @throws KeeperException
327    */
328   public static Set<TableName> getDisablingTables(ZooKeeperWatcher zkw)
329       throws KeeperException {
330     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLING);
331   }
332 
333   /**
334    * Gets a list of all the tables set as enabling in zookeeper.
335    * @return Set of enabling tables, empty Set if none
336    * @throws KeeperException
337    */
338   public static Set<TableName> getEnablingTables(ZooKeeperWatcher zkw)
339       throws KeeperException {
340     return getAllTables(zkw, ZooKeeperProtos.Table.State.ENABLING);
341   }
342 
343   /**
344    * Gets a list of all the tables set as disabled in zookeeper.
345    * @return Set of disabled tables, empty Set if none
346    * @throws KeeperException
347    */
348   public static Set<TableName> getDisabledOrDisablingTables(ZooKeeperWatcher zkw)
349       throws KeeperException {
350     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED,
351       ZooKeeperProtos.Table.State.DISABLING);
352   }
353   
354   /**
355    * If the table is found in ENABLING state the inmemory state is removed. This
356    * helps in cases where CreateTable is to be retried by the client incase of
357    * failures.  If deleteZNode is true - the znode is also deleted
358    * 
359    * @param tableName
360    * @param deleteZNode
361    * @throws KeeperException
362    */
363   public void removeEnablingTable(final TableName tableName, boolean deleteZNode)
364       throws KeeperException {
365     synchronized (this.cache) {
366       if (isEnablingTable(tableName)) {
367         this.cache.remove(tableName);
368         if (deleteZNode) {
369           ZKUtil.deleteNodeFailSilent(this.watcher,
370               ZKUtil.joinZNode(this.watcher.tableZNode, tableName.getNameAsString()));
371         }
372       }
373     }
374   }
375 
376 
377   /**
378    * Gets a list of all the tables of specified states in zookeeper.
379    * @return Set of tables of specified states, empty Set if none
380    * @throws KeeperException
381    */
382   static Set<TableName> getAllTables(final ZooKeeperWatcher zkw,
383       final ZooKeeperProtos.Table.State... states) throws KeeperException {
384     Set<TableName> allTables = new HashSet<TableName>();
385     List<String> children =
386       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
387     if(children == null) return allTables;
388     for (String child: children) {
389       TableName tableName = TableName.valueOf(child);
390       ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(zkw, tableName);
391       for (ZooKeeperProtos.Table.State expectedState: states) {
392         if (state == expectedState) {
393           allTables.add(tableName);
394           break;
395         }
396       }
397     }
398     return allTables;
399   }
400 }