This is an automated email from the ASF dual-hosted git repository.

kuczoram pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git


The following commit(s) were added to refs/heads/master by this push:
     new b4517e1a991 HIVE-30026: (ACID Compaction) Extend the query for finding 
potential compactions with ordering (#6768)
b4517e1a991 is described below

commit b4517e1a99160f4ed6cc3a8920f05413d096f926
Author: Marta Kuczora <[email protected]>
AuthorDate: Mon Sep 21 09:36:39 2026 +0200

    HIVE-30026: (ACID Compaction) Extend the query for finding potential 
compactions with ordering (#6768)
---
 .../metastore/txn/TestCompactionTxnHandler.java    |  80 ++++++++++++++
 .../hive/ql/txn/compactor/TestInitiator.java       | 117 +++++++++++++++++++++
 .../FindPotentialCompactionsFunction.java          |   7 +-
 .../txn/jdbc/queries/AbortedTxnHandler.java        |   3 +-
 .../jdbc/queries/CompactionCandidateHandler.java   |   8 +-
 5 files changed, 208 insertions(+), 7 deletions(-)

diff --git 
a/ql/src/test/org/apache/hadoop/hive/metastore/txn/TestCompactionTxnHandler.java
 
b/ql/src/test/org/apache/hadoop/hive/metastore/txn/TestCompactionTxnHandler.java
index 52ebac46c79..2693914f7e3 100644
--- 
a/ql/src/test/org/apache/hadoop/hive/metastore/txn/TestCompactionTxnHandler.java
+++ 
b/ql/src/test/org/apache/hadoop/hive/metastore/txn/TestCompactionTxnHandler.java
@@ -998,6 +998,86 @@ public void testFindPotentialCompactions_limitFetchSize() 
throws Exception {
     assertEquals(1, potentials.size());
   }
 
+  /**
+   * If the number of candidates is bigger than the compactor.fetch.size 
value, it can happen that the
+   * Initiator doesn't schedule all tables for compaction.
+   * For example: we have 4 tables, which are all eligible for compaction, but 
the fetch size is 2.
+   * In the first initiator run, it will pick two of these tables. It would be 
expected that during the second
+   * run of the initiator it would pick the other two tables, so all 4 tables 
have been scheduled for compaction.
+   * Since there was no ordering when selecting the tables for compaction, 
this was not always the case.
+   * It could happen that some tables never got compacted.
+   *
+   * Changing the initiator to pick the tables ordered by CTC_TXNID, so the 
tables with the oldest TXNID would be
+   * picked first. This way we can be sure that all tables will be scheduled 
for compaction at some point.
+   */
+  @Test
+  public void testFindPotentialCompactionsFairness() throws Exception {
+    MetastoreConf.setLongVar(conf, COMPACTOR_FETCH_SIZE, 3);
+
+    commitOrAbortOneTableUpdate("table_c", false);
+    commitOrAbortOneTableUpdate("table_a", false);
+    commitOrAbortOneTableUpdate("table_d", false);
+    commitOrAbortOneTableUpdate("table_b", false); // newest — must be dropped 
by the LIMIT
+
+    Set<CompactionInfo> potentials = txnHandler.findPotentialCompactions(100, 
-1L);
+    assertEquals(3, potentials.size());
+
+    Set<String> names = potentials.stream().map(ci -> 
ci.tableName).collect(Collectors.toSet());
+    assertTrue("oldest (table_c) must be picked", names.contains("table_c"));
+    assertTrue("second-oldest (table_a) must be picked", 
names.contains("table_a"));
+    assertTrue("third-oldest (table_d) must be picked", 
names.contains("table_d"));
+    assertFalse("newest (table_b) must be excluded by the fetch.size window",
+        names.contains("table_b"));
+  }
+
+  /**
+   * If the number of candidates is bigger than the compactor.fetch.size 
limit, the aborted
+   * txn candidates will never be picked up by the initiator. Because the 
"remaining budget" will always
+   * be 0 in the FindPotentialCompactionsFunction. This affects only the cases 
when the aborted txn clean-up
+   * is done by the compaction cycle and not just the cleaner.
+   *
+   * Changing how the compaction candidates are fetched to be sure that the 
aborted txn are also picked up.
+   */
+  @Test
+  public void 
testFindPotentialCompactionsIncludesAbortsUnderFetchSizePressure() throws 
Exception {
+    // Route aborted-txn cleanup through the compaction path
+    MetastoreConf.setBoolVar(conf, 
MetastoreConf.ConfVars.COMPACTOR_CLEAN_ABORTS_USING_CLEANER, false);
+    MetastoreConf.setLongVar(conf, COMPACTOR_FETCH_SIZE, 2);
+
+    // Two committed candidates — fills the entire fetch.size budget.
+    commitOrAbortOneTableUpdate("committed_a", false);
+    commitOrAbortOneTableUpdate("committed_b", false);
+
+    // One aborted candidate with enough aborts to exceed abortedThreshold=0.
+    commitOrAbortOneTableUpdate("aborted_only", true);
+
+    Set<CompactionInfo> potentials = txnHandler.findPotentialCompactions(0, 
-1L);
+
+    Set<String> names = potentials.stream().map(ci -> 
ci.tableName).collect(Collectors.toSet());
+    assertTrue("committed candidates must be picked", 
names.contains("committed_a")
+        && names.contains("committed_b"));
+    assertTrue("aborted candidate must not be starved by fetch.size pressure",
+        names.contains("aborted_only"));
+  }
+
+  /**
+   * Opens one txn, takes a SHARED_WRITE lock on the (db,table), and commits 
or aborts.
+   * This produces exactly one COMPLETED_TXN_COMPONENTS row.
+   */
+  private void commitOrAbortOneTableUpdate(String table, boolean abort) throws 
Exception {
+    long txnId = openTxn();
+    LockComponent lc = createLockComponent(
+        LockType.SHARED_WRITE, LockLevel.DB, "default", table, null, 
DataOperationType.UPDATE);
+    LockRequest req = new LockRequest(Collections.singletonList(lc), "me", 
"localhost");
+    req.setTxnid(txnId);
+    assertSame(LockState.ACQUIRED, txnHandler.lock(req).getState());
+    if (abort) {
+      txnHandler.abortTxn(new AbortTxnRequest(txnId));
+    } else {
+      txnHandler.commitTxn(new CommitTxnRequest(txnId));
+    }
+  }
+
   @Test
   public void testFindNextToClean_limitFetchSize() throws Exception {
     MetastoreConf.setLongVar(conf, COMPACTOR_FETCH_SIZE, 1);
diff --git 
a/ql/src/test/org/apache/hadoop/hive/ql/txn/compactor/TestInitiator.java 
b/ql/src/test/org/apache/hadoop/hive/ql/txn/compactor/TestInitiator.java
index 53b9d0881f2..b34b2c96da3 100644
--- a/ql/src/test/org/apache/hadoop/hive/ql/txn/compactor/TestInitiator.java
+++ b/ql/src/test/org/apache/hadoop/hive/ql/txn/compactor/TestInitiator.java
@@ -49,11 +49,15 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
@@ -1114,6 +1118,119 @@ public void testMetaCache() throws Exception {
     Mockito.verify(initiator, times(2)).resolvePartition(Mockito.any());
   }
 
+  @Test
+  public void testCompactionInitWithSmallFetchSize() throws Exception {
+    MetastoreConf.setLongVar(conf, 
MetastoreConf.ConfVars.COMPACTOR_FETCH_SIZE, 2);
+    Set<String> compactedTables = new HashSet<>();
+    Map<String, Table> tables = new HashMap<>();
+    String[] tableNames = {"testtablea", "testtableb", "testtablec", 
"testtabled"};
+    for (int i = 0; i < 4; i++) {
+      Table t = newTable("default", tableNames[i], false);
+      tables.put(t.getTableName(), t);
+      prepareTable(t, true, 1L);
+    }
+
+    startInitiator();
+    startWorker();
+    startWorker();
+    startCleaner();
+    startCleaner();
+    ShowCompactResponse rsp = txnHandler.showCompact(new ShowCompactRequest());
+    compactedTables.addAll(rsp.getCompacts().stream().map(c -> 
c.getTablename()).collect(Collectors.toSet()));
+
+    for (String tableName: compactedTables) {
+      prepareTable(tables.get(tableName), false, 5L);
+    }
+
+    startInitiator();
+    startWorker();
+    startWorker();
+    startCleaner();
+    startCleaner();
+    rsp = txnHandler.showCompact(new ShowCompactRequest());
+    compactedTables.addAll(rsp.getCompacts().stream().map(c -> 
c.getTablename()).collect(Collectors.toSet()));
+
+    Assert.assertEquals(4, compactedTables.size());
+    for (String tableName: tableNames) {
+      assertTrue(tableName + " isn't scheduled for compaction", 
compactedTables.contains(tableName));
+    }
+  }
+
+  @Test
+  public void testCompactionInitWithSmallFetchSizeWithAbortedTxn() throws 
Exception{
+    MetastoreConf.setLongVar(conf, 
MetastoreConf.ConfVars.COMPACTOR_FETCH_SIZE, 2);
+    MetastoreConf.setBoolVar(conf, 
MetastoreConf.ConfVars.COMPACTOR_CLEAN_ABORTS_USING_CLEANER, false);
+    HiveConf.setIntVar(conf, 
HiveConf.ConfVars.HIVE_COMPACTOR_ABORTEDTXN_THRESHOLD, 2);
+
+    Set<String> compactedTables = new HashSet<>();
+    String[] tableNames = {"testtablea", "testtableb", "testtablec", 
"testtabled"};
+    List<Table> tables = new ArrayList<>();
+    for (int i = 0; i < 2; i++) {
+      Table t = newTable("default", tableNames[i], false);
+      tables.add(t);
+      prepareTable(t, true, 1L);
+    }
+    long txnId = 10L;
+    for (int i = 2; i < 4; i++) {
+      Table t = newTable("default", tableNames[i], false);
+      tables.add(t);
+      Set<Long> abortedTxns = new HashSet<>();
+      for (int j = 1; j < 6; j++) {
+        abortedTxns.add(txnId + j);
+      }
+      prepareTable(t, true, 1L, abortedTxns);
+      txnId = 15L;
+    }
+
+    startInitiator();
+    startWorker();
+    startWorker();
+    startCleaner();
+    startCleaner();
+    ShowCompactResponse rsp = txnHandler.showCompact(new ShowCompactRequest());
+    compactedTables.addAll(rsp.getCompacts().stream().map(c -> 
c.getTablename()).collect(Collectors.toSet()));
+
+    for (int i = 0; i < 2; i++) {
+      prepareTable(tables.get(i), false, 5L);
+    }
+
+    startInitiator();
+    startWorker();
+    startWorker();
+    startCleaner();
+    startCleaner();
+    rsp = txnHandler.showCompact(new ShowCompactRequest());
+    compactedTables.addAll(rsp.getCompacts().stream().map(c -> 
c.getTablename()).collect(Collectors.toSet()));
+
+    Assert.assertEquals(4, compactedTables.size());
+    for (String tableName: tableNames) {
+      assertTrue(tableName + " isn't scheduled for compaction", 
compactedTables.contains(tableName));
+    }
+  }
+
+  private void prepareTable(Table table, boolean addBase, long writeId) throws 
Exception {
+    prepareTable(table, addBase, writeId, null);
+  }
+
+  private void prepareTable(Table table, boolean addBase, long writeId, 
Set<Long> abortedTxns) throws Exception {
+    int txnNum = 0;
+    if (addBase) {
+      addBaseFile(table, null, writeId, 20);
+      txnNum++;
+    }
+    for (int i = 1; i < 5; i++) {
+      addDeltaFile(table, null, writeId + i, writeId + i, 2);
+      txnNum++;
+    }
+    LockComponent comp = new LockComponent(LockType.SHARED_WRITE, 
LockLevel.TABLE, table.getDbName());
+    comp.setTablename(table.getTableName());
+    comp.setOperationType(DataOperationType.INSERT);
+    List<LockComponent> components = new ArrayList<LockComponent>(1);
+    components.add(comp);
+    LockRequest req = new LockRequest(components, "me", "localhost");
+    burnThroughTransactions(table.getDbName(), table.getTableName(), txnNum, 
null, abortedTxns, req);
+  }
+
   private static FindNextCompactRequest aFindNextCompactRequest(String 
workerId, String workerVersion) {
     FindNextCompactRequest request = new FindNextCompactRequest();
     request.setWorkerId(workerId);
diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/FindPotentialCompactionsFunction.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/FindPotentialCompactionsFunction.java
index c24ea97efdb..86b953b27d5 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/FindPotentialCompactionsFunction.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/FindPotentialCompactionsFunction.java
@@ -48,11 +48,12 @@ public FindPotentialCompactionsFunction(Configuration conf, 
int abortedThreshold
 
   @Override
   public Set<CompactionInfo> execute(MultiDataSourceJdbcResource jdbcResource) 
throws MetaException {
+    // Each source gets its own fetchSize budget instead of sharing a single 
pool.
+    // Sharing the pool starved aborted-txn cleanup whenever there were >= 
fetchSize committed candidates.
     Set<CompactionInfo> candidates = new HashSet<>(jdbcResource.execute(
         new CompactionCandidateHandler(lastChecked, fetchSize)));
-    int remaining = fetchSize - candidates.size();
-    if (collectAbortedTxns && remaining > 0) {
-      candidates.addAll(jdbcResource.execute(new 
AbortedTxnHandler(abortedTimeThreshold, abortedThreshold, remaining)));
+    if (collectAbortedTxns) {
+      candidates.addAll(jdbcResource.execute(new 
AbortedTxnHandler(abortedTimeThreshold, abortedThreshold, fetchSize)));
     }
     return candidates;
   }
diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/AbortedTxnHandler.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/AbortedTxnHandler.java
index 861c6998aad..a8187052466 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/AbortedTxnHandler.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/AbortedTxnHandler.java
@@ -47,7 +47,8 @@ public String getParameterizedQueryString(DatabaseProduct 
databaseProduct) throw
     return databaseProduct.addLimitClause(fetchSize, " \"TC_DATABASE\", 
\"TC_TABLE\", \"TC_PARTITION\", " +
         "MIN(\"TXN_STARTED\"), COUNT(*) FROM \"TXNS\", \"TXN_COMPONENTS\" " +
         " WHERE \"TXN_ID\" = \"TC_TXNID\" AND \"TXN_STATE\" = :state " +
-        "GROUP BY \"TC_DATABASE\", \"TC_TABLE\", \"TC_PARTITION\" ");
+        "GROUP BY \"TC_DATABASE\", \"TC_TABLE\", \"TC_PARTITION\" " +
+        "ORDER BY MIN(\"TXN_STARTED\") ASC ");
   }
 
   @Override
diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/CompactionCandidateHandler.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/CompactionCandidateHandler.java
index eb290167799..4561ea523b9 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/CompactionCandidateHandler.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/queries/CompactionCandidateHandler.java
@@ -43,8 +43,8 @@ public class CompactionCandidateHandler implements 
QueryHandler<Set<CompactionIn
   //language=SQL
   @Override
   public String getParameterizedQueryString(DatabaseProduct databaseProduct) 
throws MetaException {
-    return databaseProduct.addLimitClause(fetchSize, 
-        "DISTINCT \"TC\".\"CTC_DATABASE\", \"TC\".\"CTC_TABLE\", 
\"TC\".\"CTC_PARTITION\" " +
+    return databaseProduct.addLimitClause(fetchSize,
+        "\"TC\".\"CTC_DATABASE\", \"TC\".\"CTC_TABLE\", 
\"TC\".\"CTC_PARTITION\" " +
         "FROM \"COMPLETED_TXN_COMPONENTS\" \"TC\" " + (checkInterval > 0 ?
         "LEFT JOIN ( " +
             "  SELECT \"C1\".* FROM \"COMPLETED_COMPACTIONS\" \"C1\" " +
@@ -57,7 +57,9 @@ public String getParameterizedQueryString(DatabaseProduct 
databaseProduct) throw
             ") \"C\" " +
             "ON \"TC\".\"CTC_DATABASE\" = \"C\".\"CC_DATABASE\" AND 
\"TC\".\"CTC_TABLE\" = \"C\".\"CC_TABLE\" " +
             "  AND (\"TC\".\"CTC_PARTITION\" = \"C\".\"CC_PARTITION\" OR 
(\"TC\".\"CTC_PARTITION\" IS NULL AND \"C\".\"CC_PARTITION\" IS NULL)) " +
-            "WHERE \"C\".\"CC_ID\" IS NOT NULL OR " + 
databaseProduct.isWithinCheckInterval("\"TC\".\"CTC_TIMESTAMP\"", 
checkInterval) : ""));
+            "WHERE \"C\".\"CC_ID\" IS NOT NULL OR " + 
databaseProduct.isWithinCheckInterval("\"TC\".\"CTC_TIMESTAMP\"", 
checkInterval) : "") +
+        " GROUP BY \"TC\".\"CTC_DATABASE\", \"TC\".\"CTC_TABLE\", 
\"TC\".\"CTC_PARTITION\" " +
+        " ORDER BY MIN(\"TC\".\"CTC_TXNID\") ASC");
   }
 
   @Override

Reply via email to