Skip to content

Commit 0efac76

Browse files
author
Ali Beyad
committed
Clarify the semantics of the BlobContainer interface
This commit clarifies the behavior that must be adhered to by any implementors of the BlobContainer interface. This is done through expanded Javadocs. Closes #18157 Closes #15580
1 parent 9a936d3 commit 0efac76

File tree

9 files changed

+129
-20
lines changed

9 files changed

+129
-20
lines changed

core/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,60 +27,138 @@
2727
import java.util.Map;
2828

2929
/**
30-
*
30+
* An interface for managing a repository of blob entries, where each blob entry is just a named group of bytes.
3131
*/
3232
public interface BlobContainer {
3333

34+
/**
35+
* Gets the {@link BlobPath} that defines the implementation specific paths to where the blobs are contained.
36+
*
37+
* @return the BlobPath where the blobs are contained
38+
*/
3439
BlobPath path();
3540

41+
/**
42+
* Tests whether a blob with the given blob name exists in the container.
43+
*
44+
* @param blobName
45+
* The name of the blob whose existence is to be determined.
46+
* @return {@code true} if a blob exists in the {@link BlobContainer} with the given name, and {@code false} otherwise.
47+
* @throws IOException if any error occurred while attempting to ascertain if the blob exists
48+
*/
3649
boolean blobExists(String blobName);
3750

3851
/**
39-
* Creates a new InputStream for the given blob name
52+
* Creates a new {@link InputStream} for the given blob name.
53+
*
54+
* @param blobName
55+
* The name of the blob to get an {@link InputStream} for.
56+
* @return The {@code InputStream} to read the blob.
57+
* @throws IOException if the blob does not exist or can not be read.
4058
*/
4159
InputStream readBlob(String blobName) throws IOException;
4260

4361
/**
44-
* Reads blob content from the input stream and writes it to the blob store
62+
* Reads blob content from the input stream and writes it to the container in a new blob with the given name.
63+
* This method assumes the container does not already contain a blob of the same blobName. If a blob by the
64+
* same name already exists, the operation will fail and an {@link IOException} will be thrown.
65+
*
66+
* @param blobName
67+
* The name of the blob to write the contents of the input stream to.
68+
* @param inputStream
69+
* The input stream from which to retrieve the bytes to write to the blob.
70+
* @param blobSize
71+
* The size of the blob to be written, in bytes. It is implementation dependent whether
72+
* this value is used in writing the blob to the repository.
73+
* @throws IOException if the input stream could not be read, a blob by the same name already exists,
74+
* or the target blob could not be written to.
4575
*/
4676
void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException;
4777

4878
/**
49-
* Writes bytes to the blob
79+
* Writes the input bytes to a new blob in the container with the given name. This method assumes the
80+
* container does not already contain a blob of the same blobName. If a blob by the same name already
81+
* exists, the operation will fail and an {@link IOException} will be thrown.
82+
*
83+
* TODO: Remove this in favor of a single {@link #writeBlob(String, InputStream, long)} method.
84+
* See https://github.com/elastic/elasticsearch/issues/18528
85+
*
86+
* @param blobName
87+
* The name of the blob to write the contents of the input stream to.
88+
* @param bytes
89+
* The bytes to write to the blob.
90+
* @throws IOException if a blob by the same name already exists, or the target blob could not be written to.
5091
*/
5192
void writeBlob(String blobName, BytesReference bytes) throws IOException;
5293

5394
/**
54-
* Deletes a blob with giving name.
95+
* Deletes a blob with giving name, if the blob exists. If the blob does not exist, this method throws an IOException.
5596
*
56-
* If a blob exists but cannot be deleted an exception has to be thrown.
97+
* @param blobName
98+
* The name of the blob to delete.
99+
* @throws IOException if the blob does not exist, or if the blob exists but could not be deleted.
57100
*/
58101
void deleteBlob(String blobName) throws IOException;
59102

60103
/**
61-
* Deletes blobs with giving names.
104+
* Deletes blobs with the given names. If any subset of the names do not exist in the container, this method has no
105+
* effect for those names, and will delete the blobs for those names that do exist. If any of the blobs failed
106+
* to delete, those blobs that were processed before it and successfully deleted will remain deleted. An exception
107+
* is thrown at the first blob entry that fails to delete (TODO: is this the right behavior? Should we collect
108+
* all the failed deletes into a single IOException instead?)
62109
*
63-
* If a blob exists but cannot be deleted an exception has to be thrown.
110+
* TODO: remove, see https://github.com/elastic/elasticsearch/issues/18529
111+
*
112+
* @param blobNames
113+
* The collection of blob names to delete from the container.
114+
* @throws IOException if any of the blobs in the collection exists but could not be deleted.
64115
*/
65116
void deleteBlobs(Collection<String> blobNames) throws IOException;
66117

67118
/**
68-
* Deletes all blobs in the container that match the specified prefix.
119+
* Deletes all blobs in the container that match the specified prefix. If any of the blobs failed to delete,
120+
* those blobs that were processed before it and successfully deleted will remain deleted. An exception is
121+
* thrown at the first blob entry that fails to delete (TODO: is this the right behavior? Should we collect
122+
* all the failed deletes into a single IOException instead?)
123+
*
124+
* TODO: remove, see: https://github.com/elastic/elasticsearch/issues/18529
125+
*
126+
* @param blobNamePrefix
127+
* The prefix to match against blob names in the container. Any blob whose name has the prefix will be deleted.
128+
* @throws IOException if any of the matching blobs failed to delete.
69129
*/
70130
void deleteBlobsByPrefix(String blobNamePrefix) throws IOException;
71131

72132
/**
73-
* Lists all blobs in the container
133+
* Lists all blobs in the container.
134+
*
135+
* @return A map of all the blobs in the container. The keys in the map are the names of the blobs and
136+
* the values are {@link BlobMetaData}, containing basic information about each blob.
137+
* @throws IOException if there were any failures in reading from the blob container.
74138
*/
75139
Map<String, BlobMetaData> listBlobs() throws IOException;
76140

77141
/**
78-
* Lists all blobs in the container that match specified prefix
142+
* Lists all blobs in the container that match the specified prefix.
143+
*
144+
* @param blobNamePrefix
145+
* The prefix to match against blob names in the container.
146+
* @return A map of the matching blobs in the container. The keys in the map are the names of the blobs
147+
* and the values are {@link BlobMetaData}, containing basic information about each blob.
148+
* @throws IOException if there were any failures in reading from the blob container.
79149
*/
80150
Map<String, BlobMetaData> listBlobsByPrefix(String blobNamePrefix) throws IOException;
81151

82152
/**
83-
* Atomically renames source blob into target blob
153+
* Atomically renames the source blob into the target blob. If the source blob does not exist or the
154+
* target blob already exists, an exception is thrown.
155+
*
156+
* @param sourceBlobName
157+
* The blob to rename.
158+
* @param targetBlobName
159+
* The name of the blob after the renaming.
160+
* @throws IOException if the source blob does not exist, the target blob already exists,
161+
* or there were any failures in reading from the blob container.
84162
*/
85163
void move(String sourceBlobName, String targetBlobName) throws IOException;
86164
}

core/src/main/java/org/elasticsearch/common/blobstore/BlobMetaData.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020
package org.elasticsearch.common.blobstore;
2121

2222
/**
23-
*
23+
* An interface for providing basic metadata about a blob.
2424
*/
2525
public interface BlobMetaData {
2626

27+
/**
28+
* Gets the name of the blob.
29+
*/
2730
String name();
2831

32+
/**
33+
* Gets the size of the blob in bytes.
34+
*/
2935
long length();
3036
}

core/src/main/java/org/elasticsearch/common/blobstore/BlobPath.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919

2020
package org.elasticsearch.common.blobstore;
2121

22-
2322
import java.util.ArrayList;
2423
import java.util.Collections;
2524
import java.util.Iterator;
2625
import java.util.List;
2726

2827
/**
29-
*
28+
* The list of paths where a blob can reside. The contents of the paths are dependent upon the implementation of {@link BlobContainer}.
3029
*/
3130
public class BlobPath implements Iterable<String> {
3231

core/src/main/java/org/elasticsearch/common/blobstore/BlobStore.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@
2222
import java.io.IOException;
2323

2424
/**
25-
*
25+
* An interface for storing blobs.
2626
*/
2727
public interface BlobStore extends Closeable {
2828

29+
/**
30+
* Get a blob container instance for storing blobs at the given {@link BlobPath}.
31+
*/
2932
BlobContainer blobContainer(BlobPath path);
3033

34+
/**
35+
* Delete the blob store at the given {@link BlobPath}.
36+
*/
3137
void delete(BlobPath path) throws IOException;
3238

3339
}

core/src/main/java/org/elasticsearch/common/blobstore/fs/FsBlobContainer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@
4141
import static java.util.Collections.unmodifiableMap;
4242

4343
/**
44+
* A file system based implementation of {@link org.elasticsearch.common.blobstore.BlobContainer}.
45+
* All blobs in the container are stored on a file system, the location of which is specified by the {@link BlobPath}.
4446
*
47+
* Note that the methods in this implementation of {@link org.elasticsearch.common.blobstore.BlobContainer} may
48+
* additionally throw a {@link java.lang.SecurityException} if the configured {@link java.lang.SecurityManager}
49+
* does not permit read and/or write access to the underlying files.
4550
*/
4651
public class FsBlobContainer extends AbstractBlobContainer {
4752

core/src/main/java/org/elasticsearch/common/blobstore/support/AbstractBlobContainer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import java.util.Map;
3131

3232
/**
33-
*
33+
* A base abstract blob container that implements higher level container methods.
3434
*/
3535
public abstract class AbstractBlobContainer implements BlobContainer {
3636

@@ -55,11 +55,11 @@ public void deleteBlobsByPrefix(final String blobNamePrefix) throws IOException
5555

5656
@Override
5757
public void deleteBlobs(Collection<String> blobNames) throws IOException {
58-
for(String blob: blobNames) {
58+
for (String blob: blobNames) {
5959
deleteBlob(blob);
6060
}
6161
}
62-
62+
6363
@Override
6464
public void writeBlob(String blobName, BytesReference bytes) throws IOException {
6565
try (InputStream stream = bytes.streamInput()) {

core/src/test/java/org/elasticsearch/snapshots/BlobStoreFormatIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ protected void randomCorruption(BlobContainer blobContainer, String blobName) th
283283
int location = randomIntBetween(0, buffer.length - 1);
284284
buffer[location] = (byte) (buffer[location] ^ 42);
285285
} while (originalChecksum == checksum(buffer));
286+
blobContainer.deleteBlob(blobName); // delete original before writing new blob
286287
blobContainer.writeBlob(blobName, new BytesArray(buffer));
287288
}
288289

plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsBlobContainer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,4 @@ public boolean accept(Path path) {
154154
public Map<String, BlobMetaData> listBlobs() throws IOException {
155155
return listBlobsByPrefix(null);
156156
}
157-
}
157+
}

test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,19 @@ public void testMoveAndList() throws IOException {
112112
}
113113
}
114114

115+
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/15579")
116+
public void testOverwriteFails() throws IOException {
117+
try (final BlobStore store = newBlobStore()) {
118+
final String blobName = "foobar";
119+
final BlobContainer container = store.blobContainer(new BlobPath());
120+
byte[] data = randomBytes(randomIntBetween(10, scaledRandomIntBetween(1024, 1 << 16)));
121+
final BytesArray bytesArray = new BytesArray(data);
122+
container.writeBlob(blobName, bytesArray);
123+
expectThrows(IOException.class, () -> container.writeBlob(blobName, bytesArray));
124+
container.deleteBlob(blobName);
125+
container.writeBlob(blobName, bytesArray); // deleted it, so should be able to write it again
126+
}
127+
}
128+
115129
protected abstract BlobStore newBlobStore() throws IOException;
116130
}

0 commit comments

Comments
 (0)