docs: public static Deferred<Boolean> deleteTree(final TSDB tsdb, 
        final int tree_id, final boolean delete_definition) {
      if (tree_id < 1 || tree_id > 65535) {
        throw new IllegalArgumentException("Invalid Tree ID");
      }
  
      // scan all of the rows starting with the tree ID. We can't just delete the
      // rows as there may be other types of data. Thus we have to check the
      // qualifiers of every column to see if it's safe to delete
      final byte[] start = idToBytes(tree_id);
      final byte[] end = idToBytes(tree_id + 1);
      final Scanner scanner = tsdb.getClient().newScanner(tsdb.treeTable());
      scanner.setStartKey(start);
      scanner.setStopKey(end);   
      scanner.setFamily(TREE_FAMILY);
      
      final Deferred<Boolean> completed = new Deferred<Boolean>();
      
      /**
       * Scanner callback that loops through all rows between tree id and 
       * tree id++ searching for tree related columns to delete.
       */
      final class DeleteTreeScanner implements Callback<Deferred<Boolean>, 
        ArrayList<ArrayList<KeyValue>>> {
    
        // list where we'll store delete requests for waiting on
        private final ArrayList<Deferred<Object>> delete_deferreds = 
          new ArrayList<Deferred<Object>>();
        
        /**
         * Fetches the next set of rows from the scanner and adds this class as
         * a callback
         * @return The list of delete requests when the scanner returns a null set
         */
        public Deferred<Boolean> deleteTree() {
          return scanner.nextRows().addCallbackDeferring(this);
        }
        
        @Override
        public Deferred<Boolean> call(ArrayList<ArrayList<KeyValue>> rows)
            throws Exception {
          if (rows == null) {
            completed.callback(true);
            return null;
          }
          
          for (final ArrayList<KeyValue> row : rows) {
            // one delete request per row. We'll almost always delete the whole
            // row, so just preallocate the entire row.
            ArrayList<byte[]> qualifiers = new ArrayList<byte[]>(row.size());
            for (KeyValue column : row) {
              // tree
              if (delete_definition && Bytes.equals(TREE_QUALIFIER, column.qualifier())) {
                LOG.trace("Deleting tree defnition in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
                
              // branches
              } else if (Bytes.equals(Branch.BRANCH_QUALIFIER(), column.qualifier())) {
                LOG.trace("Deleting branch in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
              
              // leaves
              } else if (column.qualifier().length > Leaf.LEAF_PREFIX().length &&
                  Bytes.memcmp(Leaf.LEAF_PREFIX(), column.qualifier(), 0, 
                      Leaf.LEAF_PREFIX().length) == 0) {
                LOG.trace("Deleting leaf in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
                
              // collisions
              } else if (column.qualifier().length > COLLISION_PREFIX.length && 
                  Bytes.memcmp(COLLISION_PREFIX, column.qualifier(), 0, 
                      COLLISION_PREFIX.length) == 0) {
                LOG.trace("Deleting collision in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
                
              // not matched
              } else if (column.qualifier().length > NOT_MATCHED_PREFIX.length && 
                  Bytes.memcmp(NOT_MATCHED_PREFIX, column.qualifier(), 0, 
                      NOT_MATCHED_PREFIX.length) == 0) {
                LOG.trace("Deleting not matched in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
                
              // tree rule
              } else if (delete_definition && column.qualifier().length > TreeRule.RULE_PREFIX().length && 
                  Bytes.memcmp(TreeRule.RULE_PREFIX(), column.qualifier(), 0, 
                      TreeRule.RULE_PREFIX().length) == 0) {
                LOG.trace("Deleting tree rule in row: " + 
                    Branch.idToString(column.key()));
                qualifiers.add(column.qualifier());
              } 
            }
            
            if (qualifiers.size() > 0) {
              final DeleteRequest delete = new DeleteRequest(tsdb.treeTable(), 
                  row.get(0).key(), TREE_FAMILY, 
                  qualifiers.toArray(new byte[qualifiers.size()][])
                  );
              delete_deferreds.add(tsdb.getClient().delete(delete));
            }
          }
          
          /**
           * Callback used as a kind of buffer so that we don't wind up loading
           * thousands or millions of delete requests into memory and possibly run
           * into a StackOverflowError or general OOM. The scanner defaults are
           * our limit so each pass of the scanner will wait for the previous set
           * of deferreds to complete before continuing
           */
          final class ContinueCB implements Callback<Deferred<Boolean>, 
            ArrayList<Object>> {
            
            public Deferred<Boolean> call(ArrayList<Object> objects) {
              LOG.debug("Purged [" + objects.size() + "] columns, continuing");
              delete_deferreds.clear();
              // call ourself again to get the next set of rows from the scanner
              return deleteTree();
            }
            
          }
          
          // call ourself again after waiting for the existing delete requests 
          // to complete
          Deferred.group(delete_deferreds).addCallbackDeferring(new ContinueCB());
          return null;
        }    
      }
      
      // start the scanner
      new DeleteTreeScanner().deleteTree();
      return completed;
    }