Skip to content

Commit 306efc3

Browse files
Handle FileAlreadyExistsException in S3DynamoDBLogStore
## Description Resolves #1734 It is possible that more than one concurrent reader/writers will try to fix the same incomplete entry in DynamoDB. This could result in some seeing a `FileAlreadyExistsException` when trying to copy the temp file to the delta log. We should not propagate this error to the end user since the recovery operation was successful and we can proceed. See #1734 for more details. Note, we attempt to copy a temp file in two places: 1. As part of writing N.json [here](https://github.com/delta-io/delta/blob/master/storage-s3-dynamodb/src/main/java/io/delta/storage/BaseExternalLogStore.java#L249) 2. In `fixDeltaLog` when performing a recovery for N-1.json as part of either a write or listFrom We only need to catch the exception in scenario (2). In scenario (1) we already catch _all_ seen errors. This is hard to test without manipulating the FS + external store a lot. We could manipulate `FailingFileSystem` to throw a `FileAlreadyExistsException`. Closes #1776 Signed-off-by: Allison Portis <[email protected]> GitOrigin-RevId: fcce5d5577d79dff4d071ebdd63b3ee837e5b645 (cherry picked from commit 5ad6443)
1 parent 5ee2d1a commit 306efc3

File tree

1 file changed

+12
-1
lines changed

1 file changed

+12
-1
lines changed

storage-s3-dynamodb/src/main/java/io/delta/storage/BaseExternalLogStore.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,11 @@ protected void fixDeltaLogPutCompleteDbEntry(ExternalCommitEntry entry) throws I
374374
/**
375375
* Method for assuring consistency on filesystem according to the external cache.
376376
* Method tries to rewrite TransactionLog entry from temporary path if it does not exist.
377+
*
378+
* Should never throw a FileAlreadyExistsException.
379+
* - If we see one when copying the temp file, we can assume the target file N.json already
380+
* exists and a concurrent writer has already copied the contents of T(N).
381+
* - We will never see one when writing to the external cache since overwrite=true.
377382
*/
378383
private void fixDeltaLog(FileSystem fs, ExternalCommitEntry entry) throws IOException {
379384
if (entry.complete) {
@@ -396,7 +401,13 @@ private void fixDeltaLog(FileSystem fs, ExternalCommitEntry entry) throws IOExce
396401
fixDeltaLogPutCompleteDbEntry(entry);
397402
LOG.info("fixed file {}", entry.fileName);
398403
return;
399-
} catch(Throwable e) {
404+
} catch (java.nio.file.FileAlreadyExistsException e) {
405+
LOG.info("file {} already copied: {}:",
406+
entry.fileName, e.getClass().getSimpleName(), e);
407+
copied = true;
408+
// Don't return since we still need to mark the DB entry as complete. This will
409+
// happen when we execute the main try block on the next while loop iteration
410+
} catch (Throwable e) {
400411
LOG.info("{}:", e.getClass().getSimpleName(), e);
401412
if (retry >= 3) {
402413
throw e;

0 commit comments

Comments
 (0)