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 d2a22e0947 [core] Fix level 1 inflation in universal compaction (#9185)
d2a22e0947 is described below
commit d2a22e0947656162a342acf4c8171aaf92a9ca7e
Author: tsreaper <[email protected]>
AuthorDate: Wed Aug 12 13:32:24 2026 +0800
[core] Fix level 1 inflation in universal compaction (#9185)
---
.../mergetree/compact/UniversalCompaction.java | 27 ++++++++--------------
.../mergetree/compact/UniversalCompactionTest.java | 15 ++++++++++++
2 files changed, 24 insertions(+), 18 deletions(-)
diff --git
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/UniversalCompaction.java
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/UniversalCompaction.java
index 396f3c89a2..ce525ffe49 100644
---
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/UniversalCompaction.java
+++
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/UniversalCompaction.java
@@ -116,18 +116,9 @@ public class UniversalCompaction implements
CompactStrategy {
candidateCount++;
}
- if (candidateCount == 0) {
- return Optional.empty();
- }
-
- // Level 1 must be compacted with level 0 to avoid producing level 0.
Include it in the
- // initial candidates so that the size-ratio check also considers
level 2 based on the
- // combined size of level 0 and level 1.
- if (candidateCount < runs.size() && runs.get(candidateCount).level()
== 1) {
- candidateCount++;
- }
-
- return Optional.of(pickForSizeRatio(numLevels - 1, runs,
candidateCount, true));
+ return candidateCount == 0
+ ? Optional.empty()
+ : Optional.of(pickForSizeRatio(numLevels - 1, runs,
candidateCount, true));
}
@VisibleForTesting
@@ -172,22 +163,22 @@ public class UniversalCompaction implements
CompactStrategy {
public CompactUnit pickForSizeRatio(
int maxLevel, List<LevelSortedRun> runs, int candidateCount,
boolean forcePick) {
long candidateSize = candidateSize(runs, candidateCount);
+ boolean compactionTriggered = forcePick || candidateCount > 1;
for (int i = candidateCount; i < runs.size(); i++) {
LevelSortedRun next = runs.get(i);
if (candidateSize * (100.0 + sizeRatio + ratioForOffPeak()) / 100.0
< next.run().totalSize()) {
- break;
+ if (!compactionTriggered || next.level() > 1) {
+ break;
+ }
}
candidateSize += next.run().totalSize();
candidateCount++;
+ compactionTriggered = true;
}
- if (forcePick || candidateCount > 1) {
- return createUnit(runs, maxLevel, candidateCount);
- }
-
- return null;
+ return compactionTriggered ? createUnit(runs, maxLevel,
candidateCount) : null;
}
private int ratioForOffPeak() {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
index 5ac14f9293..a189a3adaf 100644
---
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/UniversalCompactionTest.java
@@ -168,6 +168,21 @@ public class UniversalCompactionTest {
assertThat(results).isEqualTo(new long[] {1, 2, 3});
}
+ @Test
+ public void testFileNumCompactionConsidersLevel2() {
+ UniversalCompaction compaction = ofTesting(200, 1, 3);
+
+ // File count first picks the two level 0 runs. Together with level 1,
they are large
+ // enough to pick level 2 as well.
+ Optional<CompactUnit> pick =
+ compaction.pick(
+ 3, Arrays.asList(level(0, 10), level(0, 20), level(1,
97), level(2, 100)));
+
+ assertThat(pick).isPresent();
+ assertThat(pick.get().files()).hasSize(4);
+ assertThat(pick.get().outputLevel()).isEqualTo(2);
+ }
+
@Test
public void testExtremeCaseNoOutputLevel0() {
UniversalCompaction compaction = ofTesting(200, 1, 5);