@Override
  public UploadResult uploadBackup(
      Exhibitor exhibitor,
      BackupMetaData backup,
      File source,
      final Map<String, String> configValues)
      throws Exception {
    List<BackupMetaData> availableBackups = getAvailableBackups(exhibitor, configValues);
    if (availableBackups.contains(backup)) {
      return UploadResult.DUPLICATE;
    }

    RetryPolicy retryPolicy = makeRetryPolicy(configValues);
    Throttle throttle = makeThrottle(configValues);

    String key = toKey(backup);
    InitiateMultipartUploadRequest initRequest =
        new InitiateMultipartUploadRequest(configValues.get(CONFIG_BUCKET.getKey()), key);
    InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest);

    CompressorIterator compressorIterator = compressor.compress(source);
    try {
      List<PartETag> eTags = Lists.newArrayList();
      int index = 0;
      for (; ; ) {
        ByteBuffer chunk = compressorIterator.next();
        if (chunk == null) {
          break;
        }
        throttle.throttle(chunk.limit());

        PartETag eTag = uploadChunkWithRetry(chunk, initResponse, index++, retryPolicy);
        eTags.add(eTag);
      }

      completeUpload(initResponse, eTags);
    } catch (Exception e) {
      abortUpload(initResponse);
      throw e;
    } finally {
      Closeables.closeQuietly(compressorIterator);
    }

    UploadResult result = UploadResult.SUCCEEDED;
    for (BackupMetaData existing : availableBackups) {
      if (existing.getName().equals(backup.getName())) {
        deleteBackup(exhibitor, existing, configValues);
        result = UploadResult.REPLACED_OLD_VERSION;
      }
    }
    return result;
  }
  @Override
  public void downloadBackup(
      Exhibitor exhibitor,
      BackupMetaData backup,
      File destination,
      Map<String, String> configValues)
      throws Exception {
    S3Object object = s3Client.getObject(configValues.get(CONFIG_BUCKET.getKey()), toKey(backup));

    long startMs = System.currentTimeMillis();
    RetryPolicy retryPolicy = makeRetryPolicy(configValues);
    int retryCount = 0;
    boolean done = false;
    while (!done) {
      Throttle throttle = makeThrottle(configValues);
      InputStream in = null;
      FileOutputStream out = null;
      try {
        out = new FileOutputStream(destination);
        in = object.getObjectContent();

        FileChannel channel = out.getChannel();
        CompressorIterator compressorIterator = compressor.decompress(in);
        for (; ; ) {
          ByteBuffer bytes = compressorIterator.next();
          if (bytes == null) {
            break;
          }

          throttle.throttle(bytes.limit());
          channel.write(bytes);
        }

        done = true;
      } catch (Exception e) {
        if (!retryPolicy.allowRetry(retryCount++, System.currentTimeMillis() - startMs)) {
          done = true;
        }
      } finally {
        Closeables.closeQuietly(in);
        Closeables.closeQuietly(out);
      }
    }
  }