docs: public void rename(final String oldname, final String newname) {
      final byte[] row = getId(oldname);
      final String row_string = fromBytes(row);
      {
        byte[] id = null;
        try {
          id = getId(newname);
        } catch (NoSuchUniqueName e) {
          // OK, we don't want the new name to be assigned.
        }
        if (id != null) {
          throw new IllegalArgumentException("When trying rename(\"" + oldname
            + "\", \"" + newname + "\") on " + this + ": new name already"
            + " assigned ID=" + Arrays.toString(id));
        }
      }
  
      if (renaming_id_names.contains(row_string)
          || renaming_id_names.contains(newname)) {
        throw new IllegalArgumentException("Ongoing rename on the same ID(\""
          + Arrays.toString(row) + "\") or an identical new name(\"" + newname
          + "\")");
      }
      renaming_id_names.add(row_string);
      renaming_id_names.add(newname);
  
      final byte[] newnameb = toBytes(newname);
  
      // Update the reverse mapping first, so that if we die before updating
      // the forward mapping we don't run the risk of "publishing" a
      // partially assigned ID.  The reverse mapping on its own is harmless
      // but the forward mapping without reverse mapping is bad.
      try {
        final PutRequest reverse_mapping = new PutRequest(
          table, row, NAME_FAMILY, kind, newnameb);
        hbasePutWithRetry(reverse_mapping, MAX_ATTEMPTS_PUT,
                          INITIAL_EXP_BACKOFF_DELAY);
      } catch (HBaseException e) {
        LOG.error("When trying rename(\"" + oldname
          + "\", \"" + newname + "\") on " + this + ": Failed to update reverse"
          + " mapping for ID=" + Arrays.toString(row), e);
        renaming_id_names.remove(row_string);
        renaming_id_names.remove(newname);
        throw e;
      }
  
      // Now create the new forward mapping.
      try {
        final PutRequest forward_mapping = new PutRequest(
          table, newnameb, ID_FAMILY, kind, row);
        hbasePutWithRetry(forward_mapping, MAX_ATTEMPTS_PUT,
                          INITIAL_EXP_BACKOFF_DELAY);
      } catch (HBaseException e) {
        LOG.error("When trying rename(\"" + oldname
          + "\", \"" + newname + "\") on " + this + ": Failed to create the"
          + " new forward mapping with ID=" + Arrays.toString(row), e);
        renaming_id_names.remove(row_string);
        renaming_id_names.remove(newname);
        throw e;
      }
  
      // Update cache.
      addIdToCache(newname, row);            // add     new name -> ID
      if (use_lru) {
        lru_id_cache.put(fromBytes(row), newname);
        lru_name_cache.invalidate(oldname);
      } else {
        id_cache.put(fromBytes(row), newname);  // update  ID -> new name
        name_cache.remove(oldname);             // remove  old name -> ID
      }
  
      // Delete the old forward mapping.
      try {
        final DeleteRequest old_forward_mapping = new DeleteRequest(
          table, toBytes(oldname), ID_FAMILY, kind);
        client.delete(old_forward_mapping).joinUninterruptibly();
      } catch (HBaseException e) {
        LOG.error("When trying rename(\"" + oldname
          + "\", \"" + newname + "\") on " + this + ": Failed to remove the"
          + " old forward mapping for ID=" + Arrays.toString(row), e);
        throw e;
      } catch (Exception e) {
        final String msg = "Unexpected exception when trying rename(\"" + oldname
          + "\", \"" + newname + "\") on " + this + ": Failed to remove the"
          + " old forward mapping for ID=" + Arrays.toString(row);
        LOG.error("WTF?  " + msg, e);
        throw new RuntimeException(msg, e);
      } finally {
        renaming_id_names.remove(row_string);
        renaming_id_names.remove(newname);
      }
      // Success!
    }