Skip to content

Commit 0dfaa4b

Browse files
authored
[AMORO-4290][AMS] Propagate internal Iceberg table commit failures (#4291)
Log internal commit errors and propagate CommitFailedException to REST clients instead of swallowing failures. Add unit tests for failure propagation and metadata cleanup errors.
1 parent 7be0710 commit 0dfaa4b

2 files changed

Lines changed: 107 additions & 1 deletion

File tree

amoro-ams/src/main/java/org/apache/amoro/server/table/internal/IcebergInternalTableOperations.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import org.apache.iceberg.io.FileIO;
3333
import org.apache.iceberg.io.LocationProvider;
3434
import org.apache.iceberg.io.OutputFile;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
3537

3638
import java.util.Map;
3739
import java.util.Objects;
@@ -41,6 +43,8 @@
4143
/** Iceberg table operations {@link TableOperations} */
4244
public class IcebergInternalTableOperations extends PersistentBase implements TableOperations {
4345

46+
private static final Logger LOG = LoggerFactory.getLogger(IcebergInternalTableOperations.class);
47+
4448
private final ServerTableIdentifier identifier;
4549

4650
private TableMetadata current;
@@ -104,7 +108,13 @@ public void commit(TableMetadata base, TableMetadata metadata) {
104108
org.apache.amoro.server.table.TableMetadata updatedMetadata = doCommit();
105109
checkCommitSuccess(updatedMetadata, newMetadataFileLocation);
106110
} catch (Exception e) {
107-
io.deleteFile(newMetadataFileLocation);
111+
LOG.error("Commit internal iceberg table failed, try to delete the staged metadata files", e);
112+
try {
113+
io.deleteFile(newMetadataFileLocation);
114+
} catch (Exception cleanupException) {
115+
LOG.warn("Delete staged metadata files failed, ignore it", cleanupException);
116+
}
117+
throw new CommitFailedException(e);
108118
} finally {
109119
this.tableMetadata = null;
110120
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.amoro.server.table.internal;
20+
21+
import static org.junit.jupiter.api.Assertions.assertSame;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
import static org.mockito.ArgumentMatchers.anyString;
24+
import static org.mockito.Mockito.doReturn;
25+
import static org.mockito.Mockito.doThrow;
26+
import static org.mockito.Mockito.mock;
27+
import static org.mockito.Mockito.spy;
28+
import static org.mockito.Mockito.verify;
29+
import static org.mockito.Mockito.when;
30+
31+
import org.apache.amoro.ServerTableIdentifier;
32+
import org.apache.iceberg.PartitionSpec;
33+
import org.apache.iceberg.Schema;
34+
import org.apache.iceberg.SortOrder;
35+
import org.apache.iceberg.TableMetadata;
36+
import org.apache.iceberg.exceptions.CommitFailedException;
37+
import org.apache.iceberg.io.FileIO;
38+
import org.apache.iceberg.types.Types;
39+
import org.junit.jupiter.api.Test;
40+
41+
import java.util.Collections;
42+
43+
public class TestIcebergInternalTableOperations {
44+
45+
@Test
46+
public void testCommitFailureIsPropagated() {
47+
FileIO io = mock(FileIO.class);
48+
CommitFailedException failure = new CommitFailedException("concurrent commit");
49+
when(io.newOutputFile(anyString())).thenThrow(failure);
50+
51+
IcebergInternalTableOperations operations = newOperations(io);
52+
TableMetadata metadata = operations.current();
53+
54+
CommitFailedException actual =
55+
assertThrows(CommitFailedException.class, () -> operations.commit(metadata, metadata));
56+
57+
assertSame(failure, actual.getCause());
58+
verify(io).deleteFile(anyString());
59+
}
60+
61+
@Test
62+
public void testCleanupFailureDoesNotMaskCommitFailure() {
63+
FileIO io = mock(FileIO.class);
64+
CommitFailedException failure = new CommitFailedException("concurrent commit");
65+
when(io.newOutputFile(anyString())).thenThrow(failure);
66+
doThrow(new RuntimeException("cleanup failed")).when(io).deleteFile(anyString());
67+
68+
IcebergInternalTableOperations operations = newOperations(io);
69+
TableMetadata metadata = operations.current();
70+
71+
CommitFailedException actual =
72+
assertThrows(CommitFailedException.class, () -> operations.commit(metadata, metadata));
73+
74+
assertSame(failure, actual.getCause());
75+
verify(io).deleteFile(anyString());
76+
}
77+
78+
private IcebergInternalTableOperations newOperations(FileIO io) {
79+
Schema schema = new Schema(Types.NestedField.required(1, "id", Types.LongType.get()));
80+
TableMetadata metadata =
81+
TableMetadata.newTableMetadata(
82+
schema,
83+
PartitionSpec.unpartitioned(),
84+
SortOrder.unsorted(),
85+
"file:/tmp/amoro-test-table",
86+
Collections.emptyMap());
87+
IcebergInternalTableOperations operations =
88+
spy(
89+
new IcebergInternalTableOperations(
90+
mock(ServerTableIdentifier.class),
91+
mock(org.apache.amoro.server.table.TableMetadata.class),
92+
io));
93+
doReturn(metadata).when(operations).current();
94+
return operations;
95+
}
96+
}

0 commit comments

Comments
 (0)