|
38 | 38 | import org.elasticsearch.common.xcontent.XContentType; |
39 | 39 | import org.elasticsearch.index.translog.BufferedChecksumStreamOutput; |
40 | 40 | import org.elasticsearch.repositories.blobstore.ChecksumBlobStoreFormat; |
| 41 | +import org.elasticsearch.snapshots.mockstore.BlobContainerWrapper; |
41 | 42 |
|
42 | 43 | import java.io.EOFException; |
43 | 44 | import java.io.IOException; |
@@ -210,6 +211,67 @@ public Void call() throws Exception { |
210 | 211 | } |
211 | 212 | } |
212 | 213 |
|
| 214 | + public void testAtomicWriteFailures() throws Exception { |
| 215 | + final String name = randomAlphaOfLength(10); |
| 216 | + final BlobObj blobObj = new BlobObj("test"); |
| 217 | + final ChecksumBlobStoreFormat<BlobObj> checksumFormat = new ChecksumBlobStoreFormat<>(BLOB_CODEC, "%s", BlobObj::fromXContent, |
| 218 | + xContentRegistry(), randomBoolean(), randomBoolean() ? XContentType.SMILE : XContentType.JSON); |
| 219 | + |
| 220 | + final BlobStore blobStore = createTestBlobStore(); |
| 221 | + final BlobContainer blobContainer = blobStore.blobContainer(BlobPath.cleanPath()); |
| 222 | + |
| 223 | + { |
| 224 | + IOException writeBlobException = expectThrows(IOException.class, () -> { |
| 225 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 226 | + @Override |
| 227 | + public void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException { |
| 228 | + throw new IOException("Exception thrown in writeBlob() for " + blobName); |
| 229 | + } |
| 230 | + }; |
| 231 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 232 | + }); |
| 233 | + |
| 234 | + assertEquals("Exception thrown in writeBlob() for pending-" + name, writeBlobException.getMessage()); |
| 235 | + assertEquals(0, writeBlobException.getSuppressed().length); |
| 236 | + } |
| 237 | + { |
| 238 | + IOException moveException = expectThrows(IOException.class, () -> { |
| 239 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 240 | + @Override |
| 241 | + public void move(String sourceBlobName, String targetBlobName) throws IOException { |
| 242 | + throw new IOException("Exception thrown in move() for " + sourceBlobName); |
| 243 | + } |
| 244 | + }; |
| 245 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 246 | + }); |
| 247 | + assertEquals("Exception thrown in move() for pending-" + name, moveException.getMessage()); |
| 248 | + assertEquals(0, moveException.getSuppressed().length); |
| 249 | + } |
| 250 | + { |
| 251 | + IOException moveThenDeleteException = expectThrows(IOException.class, () -> { |
| 252 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 253 | + @Override |
| 254 | + public void move(String sourceBlobName, String targetBlobName) throws IOException { |
| 255 | + throw new IOException("Exception thrown in move() for " + sourceBlobName); |
| 256 | + } |
| 257 | + |
| 258 | + @Override |
| 259 | + public void deleteBlob(String blobName) throws IOException { |
| 260 | + throw new IOException("Exception thrown in deleteBlob() for " + blobName); |
| 261 | + } |
| 262 | + }; |
| 263 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 264 | + }); |
| 265 | + |
| 266 | + assertEquals("Exception thrown in move() for pending-" + name, moveThenDeleteException.getMessage()); |
| 267 | + assertEquals(1, moveThenDeleteException.getSuppressed().length); |
| 268 | + |
| 269 | + final Throwable suppressedThrowable = moveThenDeleteException.getSuppressed()[0]; |
| 270 | + assertTrue(suppressedThrowable instanceof IOException); |
| 271 | + assertEquals("Exception thrown in deleteBlob() for pending-" + name, suppressedThrowable.getMessage()); |
| 272 | + } |
| 273 | + } |
| 274 | + |
213 | 275 | protected BlobStore createTestBlobStore() throws IOException { |
214 | 276 | Settings settings = Settings.builder().build(); |
215 | 277 | return new FsBlobStore(settings, randomRepoPath()); |
|
0 commit comments