docs: def MultiDelete(self, urns, token=None):
      """Drop all the information about given objects.
  
      DANGEROUS! This recursively deletes all objects contained within the
      specified URN.
  
      Args:
        urns: Urns of objects to remove.
        token: The Security Token to use for opening this item.
  
      Raises:
        ValueError: If one of the urns is too short. This is a safety check to
        ensure the root is not removed.
      """
      urns = [rdfvalue.RDFURN(urn) for urn in urns]
  
      if token is None:
        token = data_store.default_token
  
      for urn in urns:
        if urn.Path() == "/":
          raise ValueError("Can't delete root URN. Please enter a valid URN")
  
      deletion_pool = DeletionPool(token=token)
      deletion_pool.MultiMarkForDeletion(urns)
  
      marked_root_urns = deletion_pool.root_urns_for_deletion
      marked_urns = deletion_pool.urns_for_deletion
  
      logging.debug(u"Found %d objects to remove when removing %s",
                    len(marked_urns), urns)
  
      logging.debug(u"Removing %d root objects when removing %s: %s",
                    len(marked_root_urns), urns, marked_root_urns)
  
      pool = data_store.DB.GetMutationPool()
      for root in marked_root_urns:
        # Only the index of the parent object should be updated. Everything
        # below the target object (along with indexes) is going to be
        # deleted.
        self._DeleteChildFromIndex(root, mutation_pool=pool)
  
      for urn_to_delete in marked_urns:
        try:
          self.intermediate_cache.ExpireObject(urn_to_delete.Path())
        except KeyError:
          pass
  
      pool.DeleteSubjects(marked_urns)
      pool.Flush()
  
      # Ensure this is removed from the cache as well.
      self.Flush()
  
      logging.debug("Removed %d objects", len(marked_urns))