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.protobuf.ProtobufUtil;
25  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
26  import org.apache.zookeeper.KeeperException;
27  
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  
34  /**
35   * Helper class for table state tracking for use by 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 in {@link ZKTableReadOnly}.
40   *
41   * <p>To save on trips to the zookeeper ensemble, internally we cache table
42   * state.
43   */
44  @InterfaceAudience.Private
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, ZooKeeperProtos.Table.State> cache =
60      new HashMap<String, ZooKeeperProtos.Table.State>();
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    public ZKTable(final ZooKeeperWatcher zkw) throws KeeperException {
66      super();
67      this.watcher = zkw;
68      populateTableStates();
69    }
70  
71    /**
72     * Gets a list of all the tables set as disabled in zookeeper.
73     * @throws KeeperException
74     */
75    private void populateTableStates()
76    throws KeeperException {
77      synchronized (this.cache) {
78        List<String> children = ZKUtil.listChildrenNoWatch(this.watcher, this.watcher.tableZNode);
79        if (children == null) return;
80        for (String child: children) {
81          ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(this.watcher, child);
82          if (state != null) this.cache.put(child, state);
83        }
84      }
85    }
86  
87    /**
88     * Sets the specified table as DISABLED in zookeeper.  Fails silently if the
89     * table is already disabled in zookeeper.  Sets no watches.
90     * @param tableName
91     * @throws KeeperException unexpected zookeeper exception
92     */
93    public void setDisabledTable(String tableName)
94    throws KeeperException {
95      synchronized (this.cache) {
96        if (!isDisablingOrDisabledTable(tableName)) {
97          LOG.warn("Moving table " + tableName + " state to disabled but was " +
98            "not first in disabling state: " + this.cache.get(tableName));
99        }
100       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLED);
101     }
102   }
103 
104   /**
105    * Sets the specified table as DISABLING in zookeeper.  Fails silently if the
106    * table is already disabled in zookeeper.  Sets no watches.
107    * @param tableName
108    * @throws KeeperException unexpected zookeeper exception
109    */
110   public void setDisablingTable(final String tableName)
111   throws KeeperException {
112     synchronized (this.cache) {
113       if (!isEnabledOrDisablingTable(tableName)) {
114         LOG.warn("Moving table " + tableName + " state to disabling but was " +
115           "not first in enabled state: " + this.cache.get(tableName));
116       }
117       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
118     }
119   }
120 
121   /**
122    * Sets the specified table as ENABLING in zookeeper.  Fails silently if the
123    * table is already disabled in zookeeper.  Sets no watches.
124    * @param tableName
125    * @throws KeeperException unexpected zookeeper exception
126    */
127   public void setEnablingTable(final String tableName)
128   throws KeeperException {
129     synchronized (this.cache) {
130       if (!isDisabledOrEnablingTable(tableName)) {
131         LOG.warn("Moving table " + tableName + " state to enabling but was " +
132           "not first in disabled state: " + this.cache.get(tableName));
133       }
134       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
135     }
136   }
137 
138   /**
139    * Sets the specified table as ENABLING in zookeeper atomically
140    * If the table is already in ENABLING state, no operation is performed
141    * @param tableName
142    * @return if the operation succeeds or not
143    * @throws KeeperException unexpected zookeeper exception
144    */
145   public boolean checkAndSetEnablingTable(final String tableName)
146     throws KeeperException {
147     synchronized (this.cache) {
148       if (isEnablingTable(tableName)) {
149         return false;
150       }
151       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
152       return true;
153     }
154   }
155 
156   /**
157    * Sets the specified table as ENABLING in zookeeper atomically
158    * If the table isn't in DISABLED state, no operation is performed
159    * @param tableName
160    * @return if the operation succeeds or not
161    * @throws KeeperException unexpected zookeeper exception
162    */
163   public boolean checkDisabledAndSetEnablingTable(final String tableName)
164     throws KeeperException {
165     synchronized (this.cache) {
166       if (!isDisabledTable(tableName)) {
167         return false;
168       }
169       setTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
170       return true;
171     }
172   }
173 
174   /**
175    * Sets the specified table as DISABLING in zookeeper atomically
176    * If the table isn't in ENABLED state, no operation is performed
177    * @param tableName
178    * @return if the operation succeeds or not
179    * @throws KeeperException unexpected zookeeper exception
180    */
181   public boolean checkEnabledAndSetDisablingTable(final String tableName)
182     throws KeeperException {
183     synchronized (this.cache) {
184       if (this.cache.get(tableName) != null && !isEnabledTable(tableName)) {
185         return false;
186       }
187       setTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
188       return true;
189     }
190   }
191 
192   private void setTableState(final String tableName, final ZooKeeperProtos.Table.State state)
193   throws KeeperException {
194     String znode = ZKUtil.joinZNode(this.watcher.tableZNode, tableName);
195     if (ZKUtil.checkExists(this.watcher, znode) == -1) {
196       ZKUtil.createAndFailSilent(this.watcher, znode);
197     }
198     synchronized (this.cache) {
199       ZooKeeperProtos.Table.Builder builder = ZooKeeperProtos.Table.newBuilder();
200       builder.setState(state);
201       byte [] data = ProtobufUtil.prependPBMagic(builder.build().toByteArray());
202       ZKUtil.setData(this.watcher, znode, data);
203       this.cache.put(tableName, state);
204     }
205   }
206 
207   public boolean isDisabledTable(final String tableName) {
208     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED);
209   }
210 
211   public boolean isDisablingTable(final String tableName) {
212     return isTableState(tableName, ZooKeeperProtos.Table.State.DISABLING);
213   }
214 
215   public boolean isEnablingTable(final String tableName) {
216     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLING);
217   }
218 
219   public boolean isEnabledTable(String tableName) {
220     return isTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
221   }
222 
223   public boolean isDisablingOrDisabledTable(final String tableName) {
224     synchronized (this.cache) {
225       return isDisablingTable(tableName) || isDisabledTable(tableName);
226     }
227   }
228 
229   public boolean isEnabledOrDisablingTable(final String tableName) {
230     synchronized (this.cache) {
231       return isEnabledTable(tableName) || isDisablingTable(tableName);
232     }
233   }
234 
235   public boolean isDisabledOrEnablingTable(final String tableName) {
236     synchronized (this.cache) {
237       return isDisabledTable(tableName) || isEnablingTable(tableName);
238     }
239   }
240 
241   private boolean isTableState(final String tableName, final ZooKeeperProtos.Table.State state) {
242     synchronized (this.cache) {
243       ZooKeeperProtos.Table.State currentState = this.cache.get(tableName);
244       return ZKTableReadOnly.isTableState(currentState, state);
245     }
246   }
247 
248   /**
249    * Deletes the table in zookeeper.  Fails silently if the
250    * table is not currently disabled in zookeeper.  Sets no watches.
251    * @param tableName
252    * @throws KeeperException unexpected zookeeper exception
253    */
254   public void setDeletedTable(final String tableName)
255   throws KeeperException {
256     synchronized (this.cache) {
257       if (this.cache.remove(tableName) == null) {
258         LOG.warn("Moving table " + tableName + " state to deleted but was " +
259           "already deleted");
260       }
261       ZKUtil.deleteNodeFailSilent(this.watcher,
262         ZKUtil.joinZNode(this.watcher.tableZNode, tableName));
263     }
264   }
265   
266   /**
267    * Sets the ENABLED state in the cache and creates or force updates a node to
268    * ENABLED state for the specified table
269    * 
270    * @param tableName
271    * @throws KeeperException
272    */
273   public void setEnabledTable(final String tableName) throws KeeperException {
274     setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
275   }
276 
277   /**
278    * check if table is present .
279    * 
280    * @param tableName
281    * @return true if the table is present
282    */
283   public boolean isTablePresent(final String tableName) {
284     synchronized (this.cache) {
285       ZooKeeperProtos.Table.State state = this.cache.get(tableName);
286       return !(state == null);
287     }
288   }
289   
290   /**
291    * Gets a list of all the tables set as disabled in zookeeper.
292    * @return Set of disabled tables, empty Set if none
293    */
294   public Set<String> getDisabledTables() {
295     Set<String> disabledTables = new HashSet<String>();
296     synchronized (this.cache) {
297       Set<String> tables = this.cache.keySet();
298       for (String table: tables) {
299         if (isDisabledTable(table)) disabledTables.add(table);
300       }
301     }
302     return disabledTables;
303   }
304 
305   /**
306    * Gets a list of all the tables set as disabled in zookeeper.
307    * @return Set of disabled tables, empty Set if none
308    * @throws KeeperException
309    */
310   public static Set<String> getDisabledTables(ZooKeeperWatcher zkw)
311       throws KeeperException {
312     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED);
313   }
314 
315   /**
316    * Gets a list of all the tables set as disabling in zookeeper.
317    * @return Set of disabling tables, empty Set if none
318    * @throws KeeperException
319    */
320   public static Set<String> getDisablingTables(ZooKeeperWatcher zkw)
321       throws KeeperException {
322     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLING);
323   }
324 
325   /**
326    * Gets a list of all the tables set as enabling in zookeeper.
327    * @return Set of enabling tables, empty Set if none
328    * @throws KeeperException
329    */
330   public static Set<String> getEnablingTables(ZooKeeperWatcher zkw)
331       throws KeeperException {
332     return getAllTables(zkw, ZooKeeperProtos.Table.State.ENABLING);
333   }
334 
335   /**
336    * Gets a list of all the tables set as disabled in zookeeper.
337    * @return Set of disabled tables, empty Set if none
338    * @throws KeeperException
339    */
340   public static Set<String> getDisabledOrDisablingTables(ZooKeeperWatcher zkw)
341       throws KeeperException {
342     return getAllTables(zkw, ZooKeeperProtos.Table.State.DISABLED,
343       ZooKeeperProtos.Table.State.DISABLING);
344   }
345   
346   /**
347    * If the table is found in ENABLING state the inmemory state is removed. This
348    * helps in cases where CreateTable is to be retried by the client incase of
349    * failures
350    * 
351    * @param tableName
352    */
353   public void removeEnablingTable(final String tableName) {
354     synchronized (this.cache) {
355       if (isEnablingTable(tableName)) {
356         this.cache.remove(tableName);
357       }
358 
359     }
360   }
361 
362 
363   /**
364    * Gets a list of all the tables of specified states in zookeeper.
365    * @return Set of tables of specified states, empty Set if none
366    * @throws KeeperException
367    */
368   static Set<String> getAllTables(final ZooKeeperWatcher zkw,
369       final ZooKeeperProtos.Table.State... states) throws KeeperException {
370     Set<String> allTables = new HashSet<String>();
371     List<String> children =
372       ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
373     for (String child: children) {
374       ZooKeeperProtos.Table.State state = ZKTableReadOnly.getTableState(zkw, child);
375       for (ZooKeeperProtos.Table.State expectedState: states) {
376         if (state == expectedState) {
377           allTables.add(child);
378           break;
379         }
380       }
381     }
382     return allTables;
383   }
384 }