Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/src/main/java/org/apache/iceberg/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,13 @@
protected final <T extends PendingUpdate> T appendUpdate(T update) {
checkLastOperationCommitted(update.getClass());

if (update instanceof SnapshotUpdate) {

Check warning on line 126 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / check-runtime-deps

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.

Check warning on line 126 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / build-checks (17, pull_request)

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.
((SnapshotUpdate) update).deleteWith(enqueueDelete);
}

if (update instanceof SnapshotProducer) {

Check warning on line 130 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / check-runtime-deps

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.

Check warning on line 130 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / build-checks (17, pull_request)

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.
((SnapshotProducer) update).reportWith(reporter);
((SnapshotProducer) update).deferNotifications();
}

this.updates.add(update);
Expand Down Expand Up @@ -251,7 +252,7 @@
Preconditions.checkState(
hasLastOpCommitted, "Cannot commit transaction: last operation has not committed");

switch (type) {

Check warning on line 255 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / check-runtime-deps

[StatementSwitchToExpressionSwitch] This statement switch can be converted to a new-style arrow switch

Check warning on line 255 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / build-checks (17, pull_request)

[StatementSwitchToExpressionSwitch] This statement switch can be converted to a new-style arrow switch
case CREATE_TABLE:
commitCreateTransaction();
break;
Expand All @@ -268,6 +269,8 @@
commitSimpleTransaction();
break;
}

sendDeferredNotifications();
}

private void commitCreateTransaction() {
Expand Down Expand Up @@ -426,12 +429,20 @@
.suppressFailureWhenFinished()
.run(
update -> {
if (update instanceof SnapshotProducer) {

Check warning on line 432 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / check-runtime-deps

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.

Check warning on line 432 in core/src/main/java/org/apache/iceberg/BaseTransaction.java

View workflow job for this annotation

GitHub Actions / build-checks (17, pull_request)

[PatternMatchingInstanceof] This code can be simplified to use a pattern-matching instanceof.
((SnapshotProducer) update).cleanAll();
}
});
}

private void sendDeferredNotifications() {
for (PendingUpdate update : updates) {
if (update instanceof SnapshotProducer<?> producer) {
producer.sendDeferredNotifications();
}
}
}

private void deleteUncommittedFiles(Iterable<String> paths) {
CatalogUtil.deleteFiles(ops.io(), paths, "uncommitted");
}
Expand Down
28 changes: 25 additions & 3 deletions core/src/main/java/org/apache/iceberg/SnapshotProducer.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public void accept(String file) {
// Encryption keys required by the manifest list written in the current commit attempt.
private FileEncryptionKeys manifestListEncryptionKeys;
private boolean stageOnly = false;
private boolean deferNotifications = false;
private Object deferredEvent = null;
private Consumer<String> deleteFunc = defaultDelete;
private SnapshotAncestryValidator snapshotAncestryValidator =
SnapshotAncestryValidator.NON_VALIDATING;
Expand Down Expand Up @@ -212,6 +214,14 @@ protected ThisT reportWith(MetricsReporter newReporter) {
return self();
}

/**
* Stops {@link #commit()} from notifying listeners and reporting commit metrics; the caller must
* call {@link #sendDeferredNotifications()} once the last commit is visible in the table.
*/
void deferNotifications() {
this.deferNotifications = true;
}

/**
* A setter for the target branch on which snapshot producer operation should be performed
*
Expand Down Expand Up @@ -577,15 +587,27 @@ public void commit() {
}

try {
notifyListeners();
Object event = updateEvent();
if (deferNotifications) {
this.deferredEvent = event;
} else {
notifyListeners(event);
}
} catch (Throwable e) {
LOG.warn("Failed to notify event listeners", e);
}
}

void sendDeferredNotifications() {
try {
notifyListeners(deferredEvent);
} catch (Throwable e) {
LOG.warn("Failed to notify event listeners", e);
}
}

private void notifyListeners() {
private void notifyListeners(Object event) {
try {
Object event = updateEvent();
if (event != null) {
Listeners.notifyAll(event);

Expand Down
30 changes: 30 additions & 0 deletions core/src/test/java/org/apache/iceberg/TestCommitReporting.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import org.apache.iceberg.ScanPlanningAndReportingTestBase.TestMetricsReporter;
import org.apache.iceberg.metrics.CommitMetricsResult;
import org.apache.iceberg.metrics.CommitReport;
import org.apache.iceberg.metrics.MetricsReport;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableSet;
import org.apache.iceberg.relocated.com.google.common.collect.Lists;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;

Expand Down Expand Up @@ -185,6 +187,34 @@ public void addAndDeleteDeleteFiles() {
assertThat(metrics.manifestsReplaced().value()).isEqualTo(1L);
}

@TestTemplate
void oneCommitReportForRetriedTransaction() {
List<MetricsReport> reports = Lists.newArrayList();
Table table =
TestTables.create(
tableDir,
"retried-txn",
SCHEMA,
SPEC,
SortOrder.unsorted(),
formatVersion,
reports::add);

Transaction transaction = table.newTransaction();
transaction.newAppend().appendFile(FILE_A).commit();
// a concurrent commit makes the transaction re-apply its append
table.newAppend().appendFile(FILE_B).commit();
transaction.commitTransaction();

Snapshot committed = table.currentSnapshot();
assertThat(reports)
.map(CommitReport.class::cast)
.filteredOn(report -> report.snapshotId() == committed.snapshotId())
.singleElement()
.extracting(CommitReport::sequenceNumber)
.isEqualTo(committed.sequenceNumber());
}

@TestTemplate
public void addAndDeleteManifests() {
String tableName = "add-and-delete-manifests";
Expand Down
26 changes: 26 additions & 0 deletions core/src/test/java/org/apache/iceberg/TestCreateSnapshotEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
package org.apache.iceberg;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.apache.iceberg.events.CreateSnapshotEvent;
import org.apache.iceberg.events.Listener;
import org.apache.iceberg.events.Listeners;
import org.apache.iceberg.exceptions.CommitFailedException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;

Expand Down Expand Up @@ -77,6 +79,30 @@ public void testAppendAndDeleteCommitEvent() {
.containsEntry("total-data-files", "1");
}

@TestTemplate
void snapshotEventAfterTransactionCommit() {
Transaction transaction = table.newTransaction();
transaction.newAppend().appendFile(FILE_A).commit();
assertThat(currentEvent).isNull();

transaction.commitTransaction();
assertThat(currentEvent).isNotNull();
assertThat(currentEvent.snapshotId()).isEqualTo(table.currentSnapshot().snapshotId());
}

@TestTemplate
void noSnapshotEventWhenTransactionCommitFails() {
table.updateProperties().set(TableProperties.COMMIT_NUM_RETRIES, "0").commit();
Transaction transaction = table.newTransaction();
transaction.newAppend().appendFile(FILE_A).commit();
table.ops().failCommits(1);

assertThatThrownBy(transaction::commitTransaction)
.isInstanceOf(CommitFailedException.class)
.hasMessage("Injected failure");
assertThat(currentEvent).isNull();
}

class MyListener implements Listener<CreateSnapshotEvent> {
@Override
public void notify(CreateSnapshotEvent event) {
Expand Down
32 changes: 14 additions & 18 deletions core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -3528,32 +3528,28 @@ public void testCatalogWithCustomMetricsReporter() throws IOException {

TableIdentifier identifier = TableIdentifier.of(NS, "custom_metrics_reporter_table");
// append file through createTransaction() and check and reset counter
catalogWithCustomReporter
.buildTable(identifier, SCHEMA)
.createTransaction()
.newFastAppend()
.appendFile(dataFile)
.commit();
Transaction create =
catalogWithCustomReporter.buildTable(identifier, SCHEMA).createTransaction();
create.newFastAppend().appendFile(dataFile).commit();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isZero();
create.commitTransaction();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isEqualTo(1);
CustomMetricsReporter.COMMIT_COUNTER.set(0);

// append file through createOrReplaceTransaction() and check and reset counter
catalogWithCustomReporter
.buildTable(identifier, SCHEMA)
.createOrReplaceTransaction()
.newFastAppend()
.appendFile(dataFile)
.commit();
Transaction createOrReplace =
catalogWithCustomReporter.buildTable(identifier, SCHEMA).createOrReplaceTransaction();
createOrReplace.newFastAppend().appendFile(dataFile).commit();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isZero();
createOrReplace.commitTransaction();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isEqualTo(1);
CustomMetricsReporter.COMMIT_COUNTER.set(0);

// append file through replaceTransaction() and check and reset counter
catalogWithCustomReporter
.buildTable(TABLE, SCHEMA)
.replaceTransaction()
.newFastAppend()
.appendFile(dataFile)
.commit();
Transaction replace = catalogWithCustomReporter.buildTable(TABLE, SCHEMA).replaceTransaction();
replace.newFastAppend().appendFile(dataFile).commit();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isZero();
replace.commitTransaction();
assertThat(CustomMetricsReporter.COMMIT_COUNTER.get()).isEqualTo(1);
CustomMetricsReporter.COMMIT_COUNTER.set(0);

Expand Down
Loading