public static BlobId reserveMetadataBlobId(String partitionClass, List<PartitionId> partitionIdsToExclude,
      ReservedMetadataIdMetrics reservedMetadataIdMetrics, ClusterMap clusterMap, short accountId, short containerId,
      boolean isEncrypted, RouterConfig routerConfig) {
    PartitionId selected = clusterMap.getRandomFullyWritablePartition(partitionClass, partitionIdsToExclude);
    if (selected == null) {
      reservedMetadataIdMetrics.numFailedPartitionReserveAttempts.inc();
      return null;
    }
    if (!partitionClass.equals(selected.getPartitionClass())) {
      logger.debug(
          "While reserving metadata chunk id, no partitions for partitionClass='{}' found, partitionClass='{}' used"
              + " instead for metadata chunk.", partitionClass, selected.getPartitionClass());
      reservedMetadataIdMetrics.numUnexpectedReservedPartitionClassCount.inc();
    }
    return new BlobId(routerConfig.routerBlobidCurrentVersion, BlobId.BlobIdType.NATIVE,
        clusterMap.getLocalDatacenterId(), accountId, containerId, selected, isEncrypted,
        BlobId.BlobDataType.METADATA);
  }
Choose a random {@link PartitionId} for putting the metadata chunk and return a {@link BlobId} that belongs to the
chosen partition id.
@param partitionClass the partition class to choose partitions from.
@param partitionIdsToExclude the list of {@link PartitionId}s that should be excluded from consideration.
@param reservedMetadataIdMetrics the {@link ReservedMetadataIdMetrics} object.
@param clusterMap the {@link ClusterMap} object.
@param accountId the account id to be encoded in the blob id.
@param containerId the container id to be encoded in the blob id.
@param isEncrypted {@code true} is the blob is encrypted. {@code false} otherwise.
@param routerConfig the {@link RouterConfig} object.
@return the chosen {@link BlobId}.
ambry-clustermap/src/main/java/com/github/ambry/clustermap/ClusterMapUtils.java