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