|
44 | 44 | import org.elasticsearch.index.translog.BufferedChecksumStreamOutput; |
45 | 45 | import org.elasticsearch.repositories.blobstore.ChecksumBlobStoreFormat; |
46 | 46 | import org.elasticsearch.repositories.blobstore.LegacyBlobStoreFormat; |
| 47 | +import org.elasticsearch.snapshots.mockstore.BlobContainerWrapper; |
47 | 48 |
|
48 | 49 | import java.io.EOFException; |
49 | 50 | import java.io.IOException; |
@@ -278,6 +279,67 @@ public Void call() throws Exception { |
278 | 279 | } |
279 | 280 | } |
280 | 281 |
|
| 282 | + public void testAtomicWriteFailures() throws Exception { |
| 283 | + final String name = randomAlphaOfLength(10); |
| 284 | + final BlobObj blobObj = new BlobObj("test"); |
| 285 | + final ChecksumBlobStoreFormat<BlobObj> checksumFormat = new ChecksumBlobStoreFormat<>(BLOB_CODEC, "%s", BlobObj::fromXContent, |
| 286 | + xContentRegistry(), randomBoolean(), randomBoolean() ? XContentType.SMILE : XContentType.JSON); |
| 287 | + |
| 288 | + final BlobStore blobStore = createTestBlobStore(); |
| 289 | + final BlobContainer blobContainer = blobStore.blobContainer(BlobPath.cleanPath()); |
| 290 | + |
| 291 | + { |
| 292 | + IOException writeBlobException = expectThrows(IOException.class, () -> { |
| 293 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 294 | + @Override |
| 295 | + public void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException { |
| 296 | + throw new IOException("Exception thrown in writeBlob() for " + blobName); |
| 297 | + } |
| 298 | + }; |
| 299 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 300 | + }); |
| 301 | + |
| 302 | + assertEquals("Exception thrown in writeBlob() for pending-" + name, writeBlobException.getMessage()); |
| 303 | + assertEquals(0, writeBlobException.getSuppressed().length); |
| 304 | + } |
| 305 | + { |
| 306 | + IOException moveException = expectThrows(IOException.class, () -> { |
| 307 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 308 | + @Override |
| 309 | + public void move(String sourceBlobName, String targetBlobName) throws IOException { |
| 310 | + throw new IOException("Exception thrown in move() for " + sourceBlobName); |
| 311 | + } |
| 312 | + }; |
| 313 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 314 | + }); |
| 315 | + assertEquals("Exception thrown in move() for pending-" + name, moveException.getMessage()); |
| 316 | + assertEquals(0, moveException.getSuppressed().length); |
| 317 | + } |
| 318 | + { |
| 319 | + IOException moveThenDeleteException = expectThrows(IOException.class, () -> { |
| 320 | + BlobContainer wrapper = new BlobContainerWrapper(blobContainer) { |
| 321 | + @Override |
| 322 | + public void move(String sourceBlobName, String targetBlobName) throws IOException { |
| 323 | + throw new IOException("Exception thrown in move() for " + sourceBlobName); |
| 324 | + } |
| 325 | + |
| 326 | + @Override |
| 327 | + public void deleteBlob(String blobName) throws IOException { |
| 328 | + throw new IOException("Exception thrown in deleteBlob() for " + blobName); |
| 329 | + } |
| 330 | + }; |
| 331 | + checksumFormat.writeAtomic(blobObj, wrapper, name); |
| 332 | + }); |
| 333 | + |
| 334 | + assertEquals("Exception thrown in move() for pending-" + name, moveThenDeleteException.getMessage()); |
| 335 | + assertEquals(1, moveThenDeleteException.getSuppressed().length); |
| 336 | + |
| 337 | + final Throwable suppressedThrowable = moveThenDeleteException.getSuppressed()[0]; |
| 338 | + assertTrue(suppressedThrowable instanceof IOException); |
| 339 | + assertEquals("Exception thrown in deleteBlob() for pending-" + name, suppressedThrowable.getMessage()); |
| 340 | + } |
| 341 | + } |
| 342 | + |
281 | 343 | protected BlobStore createTestBlobStore() throws IOException { |
282 | 344 | Settings settings = Settings.builder().build(); |
283 | 345 | return new FsBlobStore(settings, randomRepoPath()); |
|
0 commit comments