@Override
  synchronized public boolean uploadBlob(BlobId blobId, long blobSize, CloudBlobMetadata cloudBlobMetadata,
      InputStream blobInputStream) throws CloudStorageException {
    StoreErrorCodes serverError = hardError != null ? hardError : serverErrors.size() > 0 ? serverErrors.poll() : null;
    if (serverError != null) {
      throw new CloudStorageException("uploadBlob simulated error",
          new StoreException("uploadBlob simulated error", serverError));
    }

    if (map.containsKey(blobId)) {
      return false;
    }
    // Note: blobSize can be -1 when we dont know the actual blob size being uploaded.
    // So we have to do buffered reads to handle that case.
    int bufferSz = (blobSize == -1) ? 1024 : (int) blobSize;
    byte[] buffer = new byte[bufferSz];
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    try {
      int bytesRead = blobInputStream.read(buffer);
      while (bytesRead > 0) {
        outputStream.write(buffer, 0, bytesRead);
        bytesUploadedCounter.addAndGet(Math.max(bytesRead, 0));
        bytesRead = blobInputStream.read(buffer);
      }
    } catch (IOException ex) {
      throw new RuntimeException(ex);
    }
    cloudBlobMetadata.setLastUpdateTime(System.currentTimeMillis());
    map.put(blobId, new Pair<>(cloudBlobMetadata, outputStream.toByteArray()));
    changeFeed.add(blobId);
    blobsUploadedCounter.incrementAndGet();
    if (blobIdsToTrack.remove(blobId)) {
      uploadLatch.countDown();
    }
    return true;
  }
Instantiate {@link LatchBasedInMemoryCloudDestination}.
@param blobIdsToTrack a list of blobs that {@link LatchBasedInMemoryCloudDestination} tracks.
@param azureReplicationFeedType {@link AzureReplicationFeed.FeedType} object.
ambry-vcr/src/test/java/com/github/ambry/vcr/LatchBasedInMemoryCloudDestination.java