docs: public static Deferred<Integer> deleteRange(final TSDB tsdb, 
        final byte[] tsuid, final long start_time, final long end_time) {
      if (end_time < 1) {
        throw new IllegalArgumentException("The end timestamp has not been set");
      }
      if (end_time < start_time) {
        throw new IllegalArgumentException(
            "The end timestamp cannot be less than the start timestamp");
      }
      
      final List<Deferred<Object>> delete_requests = new ArrayList<Deferred<Object>>();
      int width = tsuid != null ? 
          Const.SALT_WIDTH() + tsuid.length + Const.TIMESTAMP_BYTES :
        Const.SALT_WIDTH() + TSDB.metrics_width() + Const.TIMESTAMP_BYTES;
      final byte[] start_row = new byte[width];
      final byte[] end_row = new byte[width];
      
      // downsample to seconds for the row keys
      final long start = start_time / 1000;
      final long end = end_time / 1000;
      final long normalized_start = (start - (start % Const.MAX_TIMESPAN));
      final long normalized_end = (end - (end % Const.MAX_TIMESPAN) + Const.MAX_TIMESPAN);
      Bytes.setInt(start_row, (int) normalized_start, 
          Const.SALT_WIDTH() + TSDB.metrics_width());
      Bytes.setInt(end_row, (int) normalized_end, 
          Const.SALT_WIDTH() + TSDB.metrics_width());
      
      if (tsuid != null) {
        // first copy the metric UID then the tags
        System.arraycopy(tsuid, 0, start_row, Const.SALT_WIDTH(), TSDB.metrics_width());
        System.arraycopy(tsuid, 0, end_row, Const.SALT_WIDTH(), TSDB.metrics_width());
        width = Const.SALT_WIDTH() + TSDB.metrics_width() + Const.TIMESTAMP_BYTES;
        final int remainder = tsuid.length - TSDB.metrics_width();
        System.arraycopy(tsuid, TSDB.metrics_width(), start_row, width, remainder);
        System.arraycopy(tsuid, TSDB.metrics_width(), end_row, width, remainder);
      }
  
      /**
       * Iterates through the scanner results in an asynchronous manner, returning
       * once the scanner returns a null result set.
       */
      final class ScannerCB implements Callback<Deferred<List<Deferred<Object>>>, 
          ArrayList<ArrayList<KeyValue>>> {
        final Scanner scanner;
  
        public ScannerCB() {
          scanner = tsdb.getClient().newScanner(tsdb.dataTable());
          scanner.setStartKey(start_row);
          scanner.setStopKey(end_row);
          scanner.setFamily(FAMILY);
          if (tsuid != null) {
            final List<String> tsuids = new ArrayList<String>(1);
            tsuids.add(UniqueId.uidToString(tsuid));
            Internal.createAndSetTSUIDFilter(scanner, tsuids);
          }
        }
        
        public Deferred<List<Deferred<Object>>> scan() {
          return scanner.nextRows().addCallbackDeferring(this);
        }
        
        @Override
        public Deferred<List<Deferred<Object>>> call (
            final ArrayList<ArrayList<KeyValue>> rows) throws Exception {
          if (rows == null || rows.isEmpty()) {
            return Deferred.fromResult(delete_requests);
          }
          
          for (final ArrayList<KeyValue> row : rows) {
            final long base_time = Internal.baseTime(tsdb, row.get(0).key());
            for (KeyValue column : row) {
              if ((column.qualifier().length == 3 || column.qualifier().length == 5)
                  && column.qualifier()[0] == PREFIX()) {
                final long timestamp = timeFromQualifier(column.qualifier(), 
                    base_time);
                if (timestamp < start_time || timestamp > end_time) {
                  continue;
                }
                final DeleteRequest delete = new DeleteRequest(tsdb.dataTable(), 
                    column.key(), FAMILY, column.qualifier());
                delete_requests.add(tsdb.getClient().delete(delete));
              }
            }
          }
          return scan();
        }
      }
  
      /** Called when the scanner is done. Delete requests may still be pending */
      final class ScannerDoneCB implements Callback<Deferred<ArrayList<Object>>, 
        List<Deferred<Object>>> {
        @Override
        public Deferred<ArrayList<Object>> call(final List<Deferred<Object>> deletes)
            throws Exception {
          return Deferred.group(delete_requests);
        }
      }
      
      /** Waits on the group of deferreds to complete before returning the count */
      final class GroupCB implements Callback<Deferred<Integer>, ArrayList<Object>> {
        @Override
        public Deferred<Integer> call(final ArrayList<Object> deletes)
            throws Exception {
          return Deferred.fromResult(deletes.size());
        }
      }
      
      Deferred<ArrayList<Object>> scanner_done = new ScannerCB().scan()
          .addCallbackDeferring(new ScannerDoneCB());
      return scanner_done.addCallbackDeferring(new GroupCB());
    }