Remove Unused Single Delete in BlobStoreRepository (#50024) (#50123)

* Remove Unused Single Delete in BlobStoreRepository

There are no more production uses of the non-bulk delete or the delete that throws
on missing so this commit removes both these methods.
Only the bulk delete logic remains. Where the bulk delete was derived from single deletes,
the single delete code was inlined into the bulk delete method.
Where single delete was used in tests it was replaced by bulk deleting.
This commit is contained in:
Armin Braun 2019-12-12 11:17:46 +01:00 committed by GitHub
parent 0fae4065ef
commit 6eee41e253
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 63 additions and 196 deletions

View File

@ -35,6 +35,7 @@ import java.nio.file.NoSuchFileException;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedActionException; import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -93,7 +94,7 @@ public class URLBlobContainer extends AbstractBlobContainer {
* This operation is not supported by URLBlobContainer * This operation is not supported by URLBlobContainer
*/ */
@Override @Override
public void deleteBlob(String blobName) throws IOException { public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) {
throw new UnsupportedOperationException("URL repository is read only"); throw new UnsupportedOperationException("URL repository is read only");
} }

View File

@ -109,22 +109,6 @@ public class AzureBlobContainer extends AbstractBlobContainer {
writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
logger.trace("deleteBlob({})", blobName);
try {
blobStore.deleteBlob(buildKey(blobName));
} catch (StorageException e) {
if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
throw new NoSuchFileException(e.getMessage());
}
throw new IOException(e);
} catch (URISyntaxException e) {
throw new IOException(e);
}
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
try { try {
@ -146,7 +130,18 @@ public class AzureBlobContainer extends AbstractBlobContainer {
// Executing deletes in parallel since Azure SDK 8 is using blocking IO while Azure does not provide a bulk delete API endpoint // Executing deletes in parallel since Azure SDK 8 is using blocking IO while Azure does not provide a bulk delete API endpoint
// TODO: Upgrade to newer non-blocking Azure SDK 11 and execute delete requests in parallel that way. // TODO: Upgrade to newer non-blocking Azure SDK 11 and execute delete requests in parallel that way.
for (String blobName : blobNames) { for (String blobName : blobNames) {
executor.execute(ActionRunnable.run(listener, () -> deleteBlobIgnoringIfNotExists(blobName))); executor.execute(ActionRunnable.run(listener, () -> {
logger.trace("deleteBlob({})", blobName);
try {
blobStore.deleteBlob(buildKey(blobName));
} catch (StorageException e) {
if (e.getHttpStatusCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw new IOException(e);
}
} catch (URISyntaxException e) {
throw new IOException(e);
}
}));
} }
} }
try { try {

View File

@ -72,11 +72,6 @@ class GoogleCloudStorageBlobContainer extends AbstractBlobContainer {
writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
blobStore.deleteBlob(buildKey(blobName));
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
return blobStore.deleteDirectory(path().buildAsString()); return blobStore.deleteDirectory(path().buildAsString());

View File

@ -314,19 +314,6 @@ class GoogleCloudStorageBlobStore implements BlobStore {
} }
} }
/**
* Deletes the blob from the specific bucket
*
* @param blobName name of the blob
*/
void deleteBlob(String blobName) throws IOException {
final BlobId blobId = BlobId.of(bucketName, blobName);
final boolean deleted = SocketAccess.doPrivilegedIOException(() -> client().delete(blobId));
if (deleted == false) {
throw new NoSuchFileException("Blob [" + blobName + "] does not exist");
}
}
/** /**
* Deletes the given path and all its children. * Deletes the given path and all its children.
* *
@ -403,5 +390,4 @@ class GoogleCloudStorageBlobStore implements BlobStore {
assert s != null; assert s != null;
return keyPath + s; return keyPath + s;
} }
} }

View File

@ -43,6 +43,7 @@ import java.nio.file.NoSuchFileException;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
final class HdfsBlobContainer extends AbstractBlobContainer { final class HdfsBlobContainer extends AbstractBlobContainer {
@ -59,17 +60,6 @@ final class HdfsBlobContainer extends AbstractBlobContainer {
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
} }
@Override
public void deleteBlob(String blobName) throws IOException {
try {
if (store.execute(fileContext -> fileContext.delete(new Path(path, blobName), true)) == false) {
throw new NoSuchFileException("Blob [" + blobName + "] does not exist");
}
} catch (FileNotFoundException fnfe) {
throw new NoSuchFileException("[" + blobName + "] blob not found");
}
}
// TODO: See if we can get precise result reporting. // TODO: See if we can get precise result reporting.
private static final DeleteResult DELETE_RESULT = new DeleteResult(1L, 0L); private static final DeleteResult DELETE_RESULT = new DeleteResult(1L, 0L);
@ -79,6 +69,27 @@ final class HdfsBlobContainer extends AbstractBlobContainer {
return DELETE_RESULT; return DELETE_RESULT;
} }
@Override
public void deleteBlobsIgnoringIfNotExists(final List<String> blobNames) throws IOException {
IOException ioe = null;
for (String blobName : blobNames) {
try {
store.execute(fileContext -> fileContext.delete(new Path(path, blobName), true));
} catch (final FileNotFoundException ignored) {
// This exception is ignored
} catch (IOException e) {
if (ioe == null) {
ioe = e;
} else {
ioe.addSuppressed(e);
}
}
}
if (ioe != null) {
throw ioe;
}
}
@Override @Override
public InputStream readBlob(String blobName) throws IOException { public InputStream readBlob(String blobName) throws IOException {
// FSDataInputStream does buffering internally // FSDataInputStream does buffering internally

View File

@ -107,11 +107,6 @@ class S3BlobContainer extends AbstractBlobContainer {
writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
deleteBlobIgnoringIfNotExists(blobName);
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
final AtomicLong deletedBlobs = new AtomicLong(); final AtomicLong deletedBlobs = new AtomicLong();
@ -215,16 +210,6 @@ class S3BlobContainer extends AbstractBlobContainer {
return new DeleteObjectsRequest(bucket).withKeys(blobs.toArray(Strings.EMPTY_ARRAY)).withQuiet(true); return new DeleteObjectsRequest(bucket).withKeys(blobs.toArray(Strings.EMPTY_ARRAY)).withQuiet(true);
} }
@Override
public void deleteBlobIgnoringIfNotExists(String blobName) throws IOException {
try (AmazonS3Reference clientReference = blobStore.clientReference()) {
// There is no way to know if an non-versioned object existed before the deletion
SocketAccess.doPrivilegedVoid(() -> clientReference.client().deleteObject(blobStore.bucket(), buildKey(blobName)));
} catch (final AmazonClientException e) {
throw new IOException("Exception when deleting blob [" + blobName + "]", e);
}
}
@Override @Override
public Map<String, BlobMetaData> listBlobsByPrefix(@Nullable String blobNamePrefix) throws IOException { public Map<String, BlobMetaData> listBlobsByPrefix(@Nullable String blobNamePrefix) throws IOException {
try (AmazonS3Reference clientReference = blobStore.clientReference()) { try (AmazonS3Reference clientReference = blobStore.clientReference()) {

View File

@ -71,11 +71,6 @@ public class S3BlobStoreContainerTests extends ESBlobStoreContainerTestCase {
return randomMockS3BlobStore(); return randomMockS3BlobStore();
} }
@Override
public void testDeleteBlob() {
assumeFalse("not implemented because of S3's weak consistency model", true);
}
@Override @Override
public void testVerifyOverwriteFails() { public void testVerifyOverwriteFails() {
assumeFalse("not implemented because of S3's weak consistency model", true); assumeFalse("not implemented because of S3's weak consistency model", true);

View File

@ -89,17 +89,6 @@ public interface BlobContainer {
*/ */
void writeBlobAtomic(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException; void writeBlobAtomic(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException;
/**
* Deletes the blob with the given name, if the blob exists. If the blob does not exist,
* this method may throw a {@link NoSuchFileException} if the underlying implementation supports an existence check before delete.
*
* @param blobName
* The name of the blob to delete.
* @throws NoSuchFileException if the blob does not exist
* @throws IOException if the blob exists but could not be deleted.
*/
void deleteBlob(String blobName) throws IOException;
/** /**
* Deletes this container and all its contents from the repository. * Deletes this container and all its contents from the repository.
* *
@ -109,44 +98,13 @@ public interface BlobContainer {
DeleteResult delete() throws IOException; DeleteResult delete() throws IOException;
/** /**
* Deletes the blobs with given names. Unlike {@link #deleteBlob(String)} this method will not throw an exception * Deletes the blobs with given names. This method will not throw an exception
* when one or multiple of the given blobs don't exist and simply ignore this case. * when one or multiple of the given blobs don't exist and simply ignore this case.
* *
* @param blobNames The names of the blob to delete. * @param blobNames The names of the blob to delete.
* @throws IOException if a subset of blob exists but could not be deleted. * @throws IOException if a subset of blob exists but could not be deleted.
*/ */
default void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOException { void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOException;
IOException ioe = null;
for (String blobName : blobNames) {
try {
deleteBlobIgnoringIfNotExists(blobName);
} catch (IOException e) {
if (ioe == null) {
ioe = e;
} else {
ioe.addSuppressed(e);
}
}
}
if (ioe != null) {
throw ioe;
}
}
/**
* Deletes a blob with giving name, ignoring if the blob does not exist.
*
* @param blobName
* The name of the blob to delete.
* @throws IOException if the blob exists but could not be deleted.
*/
default void deleteBlobIgnoringIfNotExists(String blobName) throws IOException {
try {
deleteBlob(blobName);
} catch (final NoSuchFileException ignored) {
// This exception is ignored
}
}
/** /**
* Lists all blobs in the container. * Lists all blobs in the container.

View File

@ -44,7 +44,9 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -112,24 +114,6 @@ public class FsBlobContainer extends AbstractBlobContainer {
return unmodifiableMap(builder); return unmodifiableMap(builder);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
Path blobPath = path.resolve(blobName);
if (Files.isDirectory(blobPath)) {
// delete directory recursively as long as it is empty (only contains empty directories),
// which is the reason we aren't deleting any files, only the directories on the post-visit
Files.walkFileTree(blobPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
} else {
Files.delete(blobPath);
}
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
final AtomicLong filesDeleted = new AtomicLong(0L); final AtomicLong filesDeleted = new AtomicLong(0L);
@ -153,6 +137,11 @@ public class FsBlobContainer extends AbstractBlobContainer {
return new DeleteResult(filesDeleted.get(), bytesDeleted.get()); return new DeleteResult(filesDeleted.get(), bytesDeleted.get());
} }
@Override
public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOException {
IOUtils.rm(blobNames.stream().map(path::resolve).toArray(Path[]::new));
}
@Override @Override
public InputStream readBlob(String name) throws IOException { public InputStream readBlob(String name) throws IOException {
final Path resolvedPath = path.resolve(name); final Path resolvedPath = path.resolve(name);
@ -166,7 +155,7 @@ public class FsBlobContainer extends AbstractBlobContainer {
@Override @Override
public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException { public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException {
if (failIfAlreadyExists == false) { if (failIfAlreadyExists == false) {
deleteBlobIgnoringIfNotExists(blobName); deleteBlobsIgnoringIfNotExists(Collections.singletonList(blobName));
} }
final Path file = path.resolve(blobName); final Path file = path.resolve(blobName);
try (OutputStream outputStream = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) { try (OutputStream outputStream = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) {
@ -189,7 +178,7 @@ public class FsBlobContainer extends AbstractBlobContainer {
moveBlobAtomic(tempBlob, blobName, failIfAlreadyExists); moveBlobAtomic(tempBlob, blobName, failIfAlreadyExists);
} catch (IOException ex) { } catch (IOException ex) {
try { try {
deleteBlobIgnoringIfNotExists(tempBlob); deleteBlobsIgnoringIfNotExists(Collections.singletonList(tempBlob));
} catch (IOException e) { } catch (IOException e) {
ex.addSuppressed(e); ex.addSuppressed(e);
} }
@ -209,7 +198,7 @@ public class FsBlobContainer extends AbstractBlobContainer {
if (failIfAlreadyExists) { if (failIfAlreadyExists) {
throw new FileAlreadyExistsException("blob [" + targetBlobPath + "] already exists, cannot overwrite"); throw new FileAlreadyExistsException("blob [" + targetBlobPath + "] already exists, cannot overwrite");
} else { } else {
deleteBlobIgnoringIfNotExists(targetBlobName); deleteBlobsIgnoringIfNotExists(Collections.singletonList(targetBlobName));
} }
} }
Files.move(sourceBlobPath, targetBlobPath, StandardCopyOption.ATOMIC_MOVE); Files.move(sourceBlobPath, targetBlobPath, StandardCopyOption.ATOMIC_MOVE);

View File

@ -979,9 +979,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp
if (isReadOnly() == false) { if (isReadOnly() == false) {
try { try {
final String testPrefix = testBlobPrefix(seed); final String testPrefix = testBlobPrefix(seed);
final BlobContainer container = blobStore().blobContainer(basePath().add(testPrefix)); blobStore().blobContainer(basePath().add(testPrefix)).delete();
container.deleteBlobsIgnoringIfNotExists(new ArrayList<>(container.listBlobs().keySet()));
blobStore().blobContainer(basePath()).deleteBlobIgnoringIfNotExists(testPrefix);
} catch (IOException exp) { } catch (IOException exp) {
throw new RepositoryVerificationException(metadata.name(), "cannot delete test data at " + basePath(), exp); throw new RepositoryVerificationException(metadata.name(), "cannot delete test data at " + basePath(), exp);
} }

View File

@ -114,13 +114,6 @@ public final class ChecksumBlobStoreFormat<T extends ToXContent> {
return readBlob(blobContainer, blobName); return readBlob(blobContainer, blobName);
} }
/**
* Deletes obj in the blob container
*/
public void delete(BlobContainer blobContainer, String name) throws IOException {
blobContainer.deleteBlob(blobName(name));
}
public String blobName(String name) { public String blobName(String name) {
return String.format(Locale.ROOT, blobNameFormat, name); return String.format(Locale.ROOT, blobNameFormat, name);
} }

View File

@ -225,10 +225,12 @@ public class MockEventuallyConsistentRepository extends BlobStoreRepository {
} }
@Override @Override
public void deleteBlob(String blobName) { public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) {
ensureNotClosed(); ensureNotClosed();
synchronized (context.actions) { synchronized (context.actions) {
context.actions.add(new BlobStoreAction(Operation.DELETE, path.buildAsString() + blobName)); for (String blobName : blobNames) {
context.actions.add(new BlobStoreAction(Operation.DELETE, path.buildAsString() + blobName));
}
} }
} }

View File

@ -91,7 +91,7 @@ public class MockEventuallyConsistentRepositoryTests extends ESTestCase {
final int lengthWritten = randomIntBetween(1, 100); final int lengthWritten = randomIntBetween(1, 100);
final byte[] blobData = randomByteArrayOfLength(lengthWritten); final byte[] blobData = randomByteArrayOfLength(lengthWritten);
blobContainer.writeBlob(blobName, new ByteArrayInputStream(blobData), lengthWritten, true); blobContainer.writeBlob(blobName, new ByteArrayInputStream(blobData), lengthWritten, true);
blobContainer.deleteBlob(blobName); blobContainer.deleteBlobsIgnoringIfNotExists(Collections.singletonList(blobName));
assertThrowsOnInconsistentRead(blobContainer, blobName); assertThrowsOnInconsistentRead(blobContainer, blobName);
blobStoreContext.forceConsistent(); blobStoreContext.forceConsistent();
expectThrows(NoSuchFileException.class, () -> blobContainer.readBlob(blobName)); expectThrows(NoSuchFileException.class, () -> blobContainer.readBlob(blobName));

View File

@ -33,6 +33,7 @@ import java.io.InputStream;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -120,22 +121,6 @@ public abstract class ESBlobStoreContainerTestCase extends ESTestCase {
} }
} }
public void testDeleteBlob() throws IOException {
try (BlobStore store = newBlobStore()) {
final String blobName = "foobar";
final BlobContainer container = store.blobContainer(new BlobPath());
expectThrows(NoSuchFileException.class, () -> container.deleteBlob(blobName));
byte[] data = randomBytes(randomIntBetween(10, scaledRandomIntBetween(1024, 1 << 16)));
final BytesArray bytesArray = new BytesArray(data);
writeBlob(container, blobName, bytesArray, randomBoolean());
container.deleteBlob(blobName); // should not raise
// blob deleted, so should raise again
expectThrows(NoSuchFileException.class, () -> container.deleteBlob(blobName));
}
}
public void testDeleteBlobs() throws IOException { public void testDeleteBlobs() throws IOException {
try (BlobStore store = newBlobStore()) { try (BlobStore store = newBlobStore()) {
final List<String> blobNames = Arrays.asList("foobar", "barfoo"); final List<String> blobNames = Arrays.asList("foobar", "barfoo");
@ -153,18 +138,6 @@ public abstract class ESBlobStoreContainerTestCase extends ESTestCase {
} }
} }
public void testDeleteBlobIgnoringIfNotExists() throws IOException {
try (BlobStore store = newBlobStore()) {
BlobPath blobPath = new BlobPath();
if (randomBoolean()) {
blobPath = blobPath.add(randomAlphaOfLengthBetween(1, 10));
}
final BlobContainer container = store.blobContainer(blobPath);
container.deleteBlobIgnoringIfNotExists("does_not_exist");
}
}
public void testVerifyOverwriteFails() throws IOException { public void testVerifyOverwriteFails() throws IOException {
try (BlobStore store = newBlobStore()) { try (BlobStore store = newBlobStore()) {
final String blobName = "foobar"; final String blobName = "foobar";
@ -174,7 +147,7 @@ public abstract class ESBlobStoreContainerTestCase extends ESTestCase {
writeBlob(container, blobName, bytesArray, true); writeBlob(container, blobName, bytesArray, true);
// should not be able to overwrite existing blob // should not be able to overwrite existing blob
expectThrows(FileAlreadyExistsException.class, () -> writeBlob(container, blobName, bytesArray, true)); expectThrows(FileAlreadyExistsException.class, () -> writeBlob(container, blobName, bytesArray, true));
container.deleteBlob(blobName); container.deleteBlobsIgnoringIfNotExists(Collections.singletonList(blobName));
writeBlob(container, blobName, bytesArray, true); // after deleting the previous blob, we should be able to write to it again writeBlob(container, blobName, bytesArray, true); // after deleting the previous blob, we should be able to write to it again
} }
} }

View File

@ -25,6 +25,7 @@ import org.elasticsearch.common.blobstore.DeleteResult;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Map; import java.util.Map;
public class BlobContainerWrapper implements BlobContainer { public class BlobContainerWrapper implements BlobContainer {
@ -55,19 +56,14 @@ public class BlobContainerWrapper implements BlobContainer {
delegate.writeBlobAtomic(blobName, inputStream, blobSize, failIfAlreadyExists); delegate.writeBlobAtomic(blobName, inputStream, blobSize, failIfAlreadyExists);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
delegate.deleteBlob(blobName);
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
return delegate.delete(); return delegate.delete();
} }
@Override @Override
public void deleteBlobIgnoringIfNotExists(final String blobName) throws IOException { public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOException {
delegate.deleteBlobIgnoringIfNotExists(blobName); delegate.deleteBlobsIgnoringIfNotExists(blobNames);
} }
@Override @Override

View File

@ -312,18 +312,6 @@ public class MockRepository extends FsRepository {
return super.readBlob(name); return super.readBlob(name);
} }
@Override
public void deleteBlob(String blobName) throws IOException {
maybeIOExceptionOrBlock(blobName);
super.deleteBlob(blobName);
}
@Override
public void deleteBlobIgnoringIfNotExists(String blobName) throws IOException {
maybeIOExceptionOrBlock(blobName);
super.deleteBlobIgnoringIfNotExists(blobName);
}
@Override @Override
public DeleteResult delete() throws IOException { public DeleteResult delete() throws IOException {
DeleteResult deleteResult = DeleteResult.ZERO; DeleteResult deleteResult = DeleteResult.ZERO;
@ -334,10 +322,12 @@ public class MockRepository extends FsRepository {
long deleteBlobCount = blobs.size(); long deleteBlobCount = blobs.size();
long deleteByteCount = 0L; long deleteByteCount = 0L;
for (String blob : blobs.values().stream().map(BlobMetaData::name).collect(Collectors.toList())) { for (String blob : blobs.values().stream().map(BlobMetaData::name).collect(Collectors.toList())) {
deleteBlobIgnoringIfNotExists(blob); maybeIOExceptionOrBlock(blob);
deleteBlobsIgnoringIfNotExists(Collections.singletonList(blob));
deleteByteCount += blobs.get(blob).length(); deleteByteCount += blobs.get(blob).length();
} }
blobStore().blobContainer(path().parent()).deleteBlob(path().toArray()[path().toArray().length - 1]); blobStore().blobContainer(path().parent()).deleteBlobsIgnoringIfNotExists(
Collections.singletonList(path().toArray()[path().toArray().length - 1]));
return deleteResult.add(deleteBlobCount, deleteByteCount); return deleteResult.add(deleteBlobCount, deleteByteCount);
} }