class BulkStorageDistribution {

    public Map<String, String> bulkCreateFromWorkspaceWithRetries(int tenantId,
        List<String> allOfferIds, Map<String, Driver> storageDrivers,
        Map<String, StorageOffer> storageOffers,
        DataCategory dataCategory, String workspaceContainerGUID,
        List<String> workspaceObjectURIs, List<String> objectIds, String requester)
        throws StorageException {

        List<String> remainingOfferIds = new ArrayList<>(allOfferIds);
        Map<String, ObjectInfo> objectInfos = null;

        List<String> events = new ArrayList<>();

        try {

            for (int attempt = 1; attempt <= nbReties; attempt++) {

                BulkPutResult bulkOutResult =
                    bulkPutTransferManager.bulkSendDataToOffers(workspaceContainerGUID, tenantId,
                        dataCategory, remainingOfferIds, storageDrivers, storageOffers, workspaceObjectURIs, objectIds);

                if (bulkOutResult.getObjectInfos() != null) {
                    objectInfos = bulkOutResult.getObjectInfos().stream()
                        .collect(Collectors.toMap(ObjectInfo::getObjectId, objectInfo -> objectInfo));
                }

                for (Map.Entry<String, OfferBulkPutStatus> entry : bulkOutResult.getStatusByOfferIds().entrySet()) {

                    String offerId = entry.getKey();
                    OfferBulkPutStatus status = entry.getValue();

                    events.add(offerId + ATTEMPT + attempt + " : " + status);

                    if (status == OfferBulkPutStatus.OK) {
                        remainingOfferIds.remove(offerId);
                    }
                }

                boolean hasBlockerError =
                    bulkOutResult.getStatusByOfferIds().values().contains(OfferBulkPutStatus.BLOCKER);
                if (hasBlockerError) {
                    throw new StorageInconsistentStateException("A fatal error occurred during bulk object storage");
                }

                if (remainingOfferIds.isEmpty()) {
                    // Build response
                    return objectInfos.entrySet().stream()
                        .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getDigest()));
                }
            }

            throw new StorageException("Could not proceed bulk put operation after " + nbReties + " attempts");

        } finally {
            // Log events to storage log
            logStorageEvents(tenantId, dataCategory, objectIds, requester, remainingOfferIds, objectInfos, events);
        }
    }

    public  BulkStorageDistribution(int nbReties, WorkspaceClientFactory workspaceClientFactory,
        StorageLog storageLogService, TransfertTimeoutHelper transfertTimeoutHelper);
    @VisibleForTesting  BulkStorageDistribution(int nbReties, StorageLog storageLogService,
        DigestType digestType, BulkPutTransferManager bulkPutTransferManager);

}