This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new efed7dbb0d [core][flink] Add option to skip expired partitions in
compaction job (#7537)
efed7dbb0d is described below
commit efed7dbb0d8eef775fffbbb9b1871a31012d9176
Author: sanshi <[email protected]>
AuthorDate: Tue Jun 23 18:58:41 2026 +0800
[core][flink] Add option to skip expired partitions in compaction job
(#7537)
Add a new option `compaction.skip-expired-partitions` (default: false) that
allows the Flink compaction job to skip partitions already expired under the
`values-time` partition expiration strategy, avoiding unnecessary compaction
work on soon-to-be-deleted dataPlease continue.
---
docs/docs/maintenance/dedicated-compaction.mdx | 90 +++++++++++++++
docs/generated/core_configuration.html | 42 ++++---
.../main/java/org/apache/paimon/CoreOptions.java | 35 ++++++
.../paimon/partition/PartitionExpireStrategy.java | 5 +-
.../paimon/partition/PartitionTimeExtractor.java | 5 +-
.../PartitionUpdateTimeExpireStrategy.java | 2 +
.../PartitionValuesTimeExpireStrategy.java | 2 +
.../flink/source/CompactorSourceBuilder.java | 21 ++++
.../paimon/flink/action/CompactActionITCase.java | 127 +++++++++++++++++++++
9 files changed, 309 insertions(+), 20 deletions(-)
diff --git a/docs/docs/maintenance/dedicated-compaction.mdx
b/docs/docs/maintenance/dedicated-compaction.mdx
index 70a1d68740..7695b09970 100644
--- a/docs/docs/maintenance/dedicated-compaction.mdx
+++ b/docs/docs/maintenance/dedicated-compaction.mdx
@@ -450,4 +450,94 @@ Example: compact historical partitions for tables in
database
</TabItem>
+</Tabs>
+
+## Skip Expired Partitions in Compaction Job
+
+When [partition expiration](./manage-partitions#expiring-partitions) is
configured with
+`partition.expiration-strategy = values-time`, a compaction job may waste
resources compacting
+partitions that are already expired and will soon be deleted.
+
+You can set `compaction.skip-expired-partitions = true` to make the compaction
job skip these
+already-expired partitions, avoiding unnecessary compaction work on data that
will be deleted shortly.
+
+:::info
+
+This option requires `partition.expiration-strategy = values-time` to be
configured on the table.
+It only supports primary key tables with fixed or dynamic bucket mode. Unaware
bucket (append-only) tables
+and `compact_database` in `combined` mode are not supported — the option will
be silently ignored in those cases.
+
+Even when this option is enabled, expired partitions may still be deleted
during the compaction commit
+phase as a side effect of partition expiration triggered by committing the
remaining active partitions.
+This is expected behavior — the option avoids unnecessary compaction IO on
expired partitions, but does
+not prevent those partitions from being expired.
+
+:::
+
+### For Table
+
+<Tabs groupId="skip-expired-partitions-compact">
+
+<TabItem value="flink-sql" label="Flink SQL">
+
+Run the following sql:
+
+```sql
+-- skip expired partitions compact table
+CALL sys.compact(`table` => 'default.T', options =>
'compaction.skip-expired-partitions=true')
+```
+
+</TabItem>
+
+<TabItem value="flink-action-jar" label="Flink Action Jar">
+
+```bash
+<FLINK_HOME>/bin/flink run \
+ /path/to/paimon-flink-action-@@VERSION@@.jar \
+ compact \
+ --warehouse <warehouse-path> \
+ --database <database-name> \
+ --table <table-name> \
+ --table_conf compaction.skip-expired-partitions=true \
+ [--catalog_conf <paimon-catalog-conf> [--catalog_conf
<paimon-catalog-conf> ...]]
+```
+
+</TabItem>
+
+</Tabs>
+
+### For Databases
+
+<Tabs groupId="skip-expired-partitions-compact-database">
+
+<TabItem value="flink-sql" label="Flink SQL">
+
+Run the following sql:
+
+```sql
+-- skip expired partitions compact database
+CALL sys.compact_database(
+ including_databases => 'includingDatabases',
+ mode => 'divided',
+ table_options => 'compaction.skip-expired-partitions=true'
+)
+```
+
+</TabItem>
+
+<TabItem value="flink-action-jar" label="Flink Action Jar">
+
+```bash
+<FLINK_HOME>/bin/flink run \
+ /path/to/paimon-flink-action-@@VERSION@@.jar \
+ compact_database \
+ --warehouse <warehouse-path> \
+ --including_databases <database-name|name-regular-expr> \
+ --mode divided \
+ --table_conf compaction.skip-expired-partitions=true \
+ [--catalog_conf <paimon-catalog-conf> [--catalog_conf
<paimon-catalog-conf> ...]]
+```
+
+</TabItem>
+
</Tabs>
\ No newline at end of file
diff --git a/docs/generated/core_configuration.html
b/docs/generated/core_configuration.html
index 9d1a962463..f525a00b64 100644
--- a/docs/generated/core_configuration.html
+++ b/docs/generated/core_configuration.html
@@ -404,6 +404,12 @@ under the License.
<td>Integer</td>
<td>Percentage flexibility while comparing sorted run size for
changelog mode table. If the candidate sorted run(s) size is 1% smaller than
the next sorted run's size, then include next sorted run into this candidate
set.</td>
</tr>
+ <tr>
+ <td><h5>compaction.skip-expired-partitions</h5></td>
+ <td style="word-wrap: break-word;">false</td>
+ <td>Boolean</td>
+ <td>Whether to skip compacting partitions that are already expired
according to 'partition.expiration-time'. Only effective when
'partition.expiration-time' is set and 'partition.expiration-strategy' is
'values-time'. Note: even when this option is enabled, expired partitions may
still be deleted during the compaction commit phase as a side effect of
partition expiration triggered by committing the remaining active
partitions.</td>
+ </tr>
<tr>
<td><h5>compaction.small-file-ratio</h5></td>
<td style="word-wrap: break-word;">0.7</td>
@@ -909,6 +915,24 @@ Mainly to resolve data skew on primary keys. We recommend
starting with 64 mb wh
<td>Integer</td>
<td>Level threshold of lookup to generate remote lookup files.
Level files below this threshold will not generate remote lookup files.</td>
</tr>
+ <tr>
+ <td><h5>manifest-sort.enabled</h5></td>
+ <td style="word-wrap: break-word;">false</td>
+ <td>Boolean</td>
+ <td>Whether to invoke manifest sort rewrite during commit.<br
/>Note: enabling this changes the semantics of 'manifest.merge-min-count'. In
the sort rewrite path, small manifest files within the rewrite budget are
sorted and merged directly, so the minimum-count gate no longer prevents
merging a small number of under-budget manifest files when full compaction is
not triggered.</td>
+ </tr>
+ <tr>
+ <td><h5>manifest-sort.max-rewrite-size</h5></td>
+ <td style="word-wrap: break-word;">256 mb</td>
+ <td>MemorySize</td>
+ <td>Maximum total size of manifest files to rewrite in a single
sort rewrite pass. Sections exceeding this limit are skipped. Set to a larger
value to allow more aggressive sort rewriting. The cap only limits the sorted
rewrite portion and full/minor cleanup may still happen beyond it.</td>
+ </tr>
+ <tr>
+ <td><h5>manifest-sort.partition-field</h5></td>
+ <td style="word-wrap: break-word;">(none)</td>
+ <td>String</td>
+ <td>Partition field name to sort manifest entries by. Validated by
schema validation, if not configured, defaults to the first partition
field.</td>
+ </tr>
<tr>
<td><h5>manifest.compression</h5></td>
<td style="word-wrap: break-word;">"zstd"</td>
@@ -939,24 +963,6 @@ Mainly to resolve data skew on primary keys. We recommend
starting with 64 mb wh
<td>Integer</td>
<td>To avoid frequent manifest merges, this parameter specifies
the minimum number of ManifestFileMeta to merge.<br />Note: when
'manifest-sort.enabled' is true, this minimum-count gate is only applied to the
trailing sub-segment of a section that exceeds
'manifest-sort.max-rewrite-size'. Small under-budget sections are sorted and
rewritten directly, so two small manifest files may be merged into one even
when their count is below this threshold and full compaction is not tri [...]
</tr>
- <tr>
- <td><h5>manifest-sort.enabled</h5></td>
- <td style="word-wrap: break-word;">false</td>
- <td>Boolean</td>
- <td>Whether to invoke manifest sort rewrite during commit.<br
/>Note: enabling this changes the semantics of 'manifest.merge-min-count'. In
the sort rewrite path, small manifest files within the rewrite budget are
sorted and merged directly, so the minimum-count gate no longer prevents
merging a small number of under-budget manifest files when full compaction is
not triggered.</td>
- </tr>
- <tr>
- <td><h5>manifest-sort.partition-field</h5></td>
- <td style="word-wrap: break-word;">(none)</td>
- <td>String</td>
- <td>Partition field name to sort manifest entries by. Validated by
schema validation, if not configured, defaults to the first partition
field.</td>
- </tr>
- <tr>
- <td><h5>manifest-sort.max-rewrite-size</h5></td>
- <td style="word-wrap: break-word;">256 mb</td>
- <td>MemorySize</td>
- <td>Maximum total size of manifest files to rewrite in a single
sort rewrite pass. Sections exceeding this limit are skipped. Set to a larger
value to allow more aggressive sort rewriting. The cap only limits the sorted
rewrite portion and full/minor cleanup may still happen beyond it.</td>
- </tr>
<tr>
<td><h5>manifest.target-file-size</h5></td>
<td style="word-wrap: break-word;">8 mb</td>
diff --git a/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
b/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
index bf85dd66dc..5eb86a56bc 100644
--- a/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
+++ b/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
@@ -1221,6 +1221,23 @@ public class CoreOptions implements Serializable {
"Whether only overwrite dynamic partition when
overwriting a partitioned table with "
+ "dynamic partition columns. Works only
when the table has partition keys.");
+ /** The strategy for partition expiration. */
+ public enum PartitionExpireStrategy {
+ VALUES_TIME("values-time"),
+ UPDATE_TIME("update-time");
+
+ private final String value;
+
+ PartitionExpireStrategy(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
public static final ConfigOption<String> PARTITION_EXPIRATION_STRATEGY =
key("partition.expiration-strategy")
.stringType()
@@ -1266,6 +1283,20 @@ public class CoreOptions implements Serializable {
+ "By default, all partitions to be
expired will be expired together, which may cause a risk of out-of-memory. "
+ "Use this parameter to divide partition
expiration process and mitigate memory pressure.");
+ public static final ConfigOption<Boolean>
COMPACTION_SKIP_EXPIRED_PARTITIONS =
+ key("compaction.skip-expired-partitions")
+ .booleanType()
+ .defaultValue(false)
+ .withDescription(
+ "Whether to skip compacting partitions that are
already expired "
+ + "according to
'partition.expiration-time'. "
+ + "Only effective when
'partition.expiration-time' is set "
+ + "and 'partition.expiration-strategy' is
'values-time'. "
+ + "Note: even when this option is enabled,
expired partitions "
+ + "may still be deleted during the
compaction commit phase "
+ + "as a side effect of partition
expiration triggered by "
+ + "committing the remaining active
partitions.");
+
public static final ConfigOption<String> PARTITION_TIMESTAMP_FORMATTER =
key("partition.timestamp-formatter")
.stringType()
@@ -3597,6 +3628,10 @@ public class CoreOptions implements Serializable {
return options.get(PARTITION_EXPIRATION_STRATEGY);
}
+ public boolean compactionSkipExpiredPartitions() {
+ return options.get(COMPACTION_SKIP_EXPIRED_PARTITIONS);
+ }
+
@Nullable
public String dataFileExternalPaths() {
return options.get(DATA_FILE_EXTERNAL_PATHS);
diff --git
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
index 0921a65697..4371fe86fa 100644
---
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
+++
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
@@ -29,6 +29,7 @@ import org.apache.paimon.utils.RowDataToObjectArrayConverter;
import javax.annotation.Nullable;
+import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -37,7 +38,9 @@ import java.util.Map;
import java.util.Optional;
/** Strategy for partition expiration. */
-public abstract class PartitionExpireStrategy {
+public abstract class PartitionExpireStrategy implements Serializable {
+
+ private static final long serialVersionUID = 1L;
protected final List<String> partitionKeys;
protected final String partitionDefaultName;
diff --git
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionTimeExtractor.java
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionTimeExtractor.java
index 0016619e21..afc58c8513 100644
---
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionTimeExtractor.java
+++
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionTimeExtractor.java
@@ -20,6 +20,7 @@ package org.apache.paimon.partition;
import javax.annotation.Nullable;
+import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -43,7 +44,9 @@ import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoField.YEAR;
/** Time extractor to extract time from partition values. */
-public class PartitionTimeExtractor {
+public class PartitionTimeExtractor implements Serializable {
+
+ private static final long serialVersionUID = 1L;
private static final DateTimeFormatter TIMESTAMP_FORMATTER =
new DateTimeFormatterBuilder()
diff --git
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionUpdateTimeExpireStrategy.java
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionUpdateTimeExpireStrategy.java
index 3cb7a405d2..628f4c3733 100644
---
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionUpdateTimeExpireStrategy.java
+++
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionUpdateTimeExpireStrategy.java
@@ -34,6 +34,8 @@ import java.util.stream.Collectors;
*/
public class PartitionUpdateTimeExpireStrategy extends PartitionExpireStrategy
{
+ private static final long serialVersionUID = 1L;
+
public PartitionUpdateTimeExpireStrategy(CoreOptions options, RowType
partitionType) {
super(partitionType, options.partitionDefaultName());
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
index 94e26d6f37..6238817da4 100644
---
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
+++
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
@@ -45,6 +45,8 @@ import static
org.apache.paimon.CoreOptions.PARTITION_EXPIRATION_STRATEGY;
*/
public class PartitionValuesTimeExpireStrategy extends PartitionExpireStrategy
{
+ private static final long serialVersionUID = 1L;
+
private static final Logger LOG =
LoggerFactory.getLogger(PartitionValuesTimeExpireStrategy.class);
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/source/CompactorSourceBuilder.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/source/CompactorSourceBuilder.java
index d4edb5cbaa..83486a8624 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/source/CompactorSourceBuilder.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/source/CompactorSourceBuilder.java
@@ -25,6 +25,7 @@ import org.apache.paimon.flink.LogicalTypeConversion;
import org.apache.paimon.manifest.PartitionEntry;
import org.apache.paimon.options.Options;
import org.apache.paimon.partition.PartitionPredicate;
+import org.apache.paimon.partition.PartitionValuesTimeExpireStrategy;
import org.apache.paimon.table.FileStoreTable;
import org.apache.paimon.table.source.ReadBuilder;
import org.apache.paimon.table.system.CompactBucketsTable;
@@ -141,6 +142,26 @@ public class CompactorSourceBuilder {
});
dataStream = new DataStreamSource<>(filterStream);
}
+ CoreOptions coreOptions = table.coreOptions();
+ if (coreOptions.compactionSkipExpiredPartitions()
+ && coreOptions.partitionExpireTime() != null
+ && CoreOptions.PartitionExpireStrategy.VALUES_TIME
+ .toString()
+ .equals(coreOptions.partitionExpireStrategy())) {
+ RowType partitionType = table.schema().logicalPartitionType();
+ Duration expireTime = coreOptions.partitionExpireTime();
+ PartitionValuesTimeExpireStrategy expireStrategy =
+ new PartitionValuesTimeExpireStrategy(coreOptions,
partitionType);
+ SingleOutputStreamOperator<RowData> filterStream =
+ dataStream.filter(
+ rowData -> {
+ LocalDateTime expireDateTime =
+ LocalDateTime.now().minus(expireTime);
+ BinaryRow partition =
deserializeBinaryRow(rowData.getBinary(1));
+ return
!expireStrategy.isExpired(expireDateTime, partition);
+ });
+ dataStream = new DataStreamSource<>(filterStream);
+ }
Integer parallelism =
Options.fromMap(table.options()).get(FlinkConnectorOptions.SCAN_PARALLELISM);
if (parallelism != null) {
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/action/CompactActionITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/action/CompactActionITCase.java
index 646a20ac70..fedd98ceea 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/action/CompactActionITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/action/CompactActionITCase.java
@@ -62,6 +62,8 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.time.Duration;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -930,6 +932,131 @@ public class CompactActionITCase extends
CompactActionITCaseBase {
assertThat(value).isEqualTo(30000);
}
+ @Test
+ @Timeout(60)
+ public void testSkipExpiredPartitions() throws Exception {
+ // Use a date far in the past (expired) and today's date (not expired)
+ String expiredDt =
+
LocalDate.now().minusDays(30).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+ String activeDt =
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+
+ Map<String, String> tableOptions = new HashMap<>();
+ tableOptions.put(CoreOptions.WRITE_ONLY.key(), "true");
+ tableOptions.put(CoreOptions.PARTITION_EXPIRATION_TIME.key(), "7 d");
+ tableOptions.put(
+ CoreOptions.PARTITION_EXPIRATION_STRATEGY.key(),
+ CoreOptions.PartitionExpireStrategy.VALUES_TIME.toString());
+ tableOptions.put(CoreOptions.PARTITION_TIMESTAMP_FORMATTER.key(),
"yyyyMMdd");
+ // Prevent partition expiration from running during the compact commit.
+
tableOptions.put(CoreOptions.PARTITION_EXPIRATION_CHECK_INTERVAL.key(), "999
d");
+
+ FileStoreTable table =
+ prepareTable(
+ Collections.singletonList("dt"),
+ Arrays.asList("dt", "k"),
+ Collections.emptyList(),
+ tableOptions);
+
+ // Write two batches to each partition so each has multiple files
+ writeData(
+ rowData(1, 100, 15, BinaryString.fromString(expiredDt)),
+ rowData(1, 100, 15, BinaryString.fromString(activeDt)));
+
+ writeData(
+ rowData(2, 100, 15, BinaryString.fromString(expiredDt)),
+ rowData(2, 100, 15, BinaryString.fromString(activeDt)));
+
+ checkLatestSnapshot(table, 2, Snapshot.CommitKind.APPEND);
+
+ CompactAction action =
+ createAction(
+ CompactAction.class,
+ "compact",
+ "--warehouse",
+ warehouse,
+ "--database",
+ database,
+ "--table",
+ tableName,
+ "--table_conf",
+ CoreOptions.COMPACTION_SKIP_EXPIRED_PARTITIONS.key() +
"=true");
+ StreamExecutionEnvironment env =
streamExecutionEnvironmentBuilder().batchMode().build();
+ action.withStreamExecutionEnvironment(env).build();
+ env.execute();
+
+ checkLatestSnapshot(table, 3, Snapshot.CommitKind.COMPACT);
+
+ List<DataSplit> splits = table.newSnapshotReader().read().dataSplits();
+ for (DataSplit split : splits) {
+ String dt = split.partition().getString(0).toString();
+ if (dt.equals(activeDt)) {
+ // active partition should be compacted into 1 file
+ assertThat(split.dataFiles().size()).isEqualTo(1);
+ } else {
+ // expired partition should be skipped, still has 2 files
+ assertThat(split.dataFiles().size()).isEqualTo(2);
+ }
+ }
+ }
+
+ @Test
+ @Timeout(60)
+ public void testNotSkipExpiredPartitionsByDefault() throws Exception {
+ String expiredDt =
+
LocalDate.now().minusDays(30).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+ String activeDt =
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+
+ Map<String, String> tableOptions = new HashMap<>();
+ tableOptions.put(CoreOptions.WRITE_ONLY.key(), "true");
+ tableOptions.put(CoreOptions.PARTITION_EXPIRATION_TIME.key(), "7 d");
+ tableOptions.put(
+ CoreOptions.PARTITION_EXPIRATION_STRATEGY.key(),
+ CoreOptions.PartitionExpireStrategy.VALUES_TIME.toString());
+ tableOptions.put(CoreOptions.PARTITION_TIMESTAMP_FORMATTER.key(),
"yyyyMMdd");
+ // COMPACTION_SKIP_EXPIRED_PARTITIONS is not set, default is false
+ // Prevent partition expiration from running during the compact commit.
+
tableOptions.put(CoreOptions.PARTITION_EXPIRATION_CHECK_INTERVAL.key(), "999
d");
+
+ FileStoreTable table =
+ prepareTable(
+ Collections.singletonList("dt"),
+ Arrays.asList("dt", "k"),
+ Collections.emptyList(),
+ tableOptions);
+
+ writeData(
+ rowData(1, 100, 15, BinaryString.fromString(expiredDt)),
+ rowData(1, 100, 15, BinaryString.fromString(activeDt)));
+
+ writeData(
+ rowData(2, 100, 15, BinaryString.fromString(expiredDt)),
+ rowData(2, 100, 15, BinaryString.fromString(activeDt)));
+
+ checkLatestSnapshot(table, 2, Snapshot.CommitKind.APPEND);
+
+ CompactAction action =
+ createAction(
+ CompactAction.class,
+ "compact",
+ "--warehouse",
+ warehouse,
+ "--database",
+ database,
+ "--table",
+ tableName);
+ StreamExecutionEnvironment env =
streamExecutionEnvironmentBuilder().batchMode().build();
+ action.withStreamExecutionEnvironment(env).build();
+ env.execute();
+
+ checkLatestSnapshot(table, 3, Snapshot.CommitKind.COMPACT);
+
+ // both expired and active partitions should be compacted into 1 file
+ List<DataSplit> splits = table.newSnapshotReader().read().dataSplits();
+ for (DataSplit split : splits) {
+ assertThat(split.dataFiles().size()).isEqualTo(1);
+ }
+ }
+
private void setFirstRowId(List<CommitMessage> commitables, long
firstRowId) {
commitables.forEach(
c -> {