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

tkhurana pushed a commit to branch 5.2
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.2 by this push:
     new 271c2a5cf9 PHOENIX-7666 Index query failure with SkipScanFilter (#2223)
271c2a5cf9 is described below

commit 271c2a5cf9b106afc0a528b9a447d955c161c5ba
Author: tkhurana <[email protected]>
AuthorDate: Fri Jul 18 16:36:54 2025 -0700

    PHOENIX-7666 Index query failure with SkipScanFilter (#2223)
---
 .../apache/phoenix/index/GlobalIndexChecker.java   | 16 ++---
 .../end2end/index/GlobalIndexCheckerIT.java        | 69 ++++++++++++++++++++++
 2 files changed, 78 insertions(+), 7 deletions(-)

diff --git 
a/phoenix-core-server/src/main/java/org/apache/phoenix/index/GlobalIndexChecker.java
 
b/phoenix-core-server/src/main/java/org/apache/phoenix/index/GlobalIndexChecker.java
index 906aef4a69..8e82f17036 100644
--- 
a/phoenix-core-server/src/main/java/org/apache/phoenix/index/GlobalIndexChecker.java
+++ 
b/phoenix-core-server/src/main/java/org/apache/phoenix/index/GlobalIndexChecker.java
@@ -457,15 +457,17 @@ public class GlobalIndexChecker extends 
BaseScannerRegionObserver implements Reg
         Bytes.compareTo(row.get(0).getRowArray(), row.get(0).getRowOffset(),
           row.get(0).getRowLength(), indexRowKey, 0, indexRowKey.length) != 0
       ) {
-        // This means the index row has been deleted before opening the new 
scanner. We got a
-        // different row
-        // If this row is "verified" (or empty) then we are good to go.
+        // We got a different index row. This means either the unverified 
index row has
+        // been deleted by the single row rebuild as part of the read repair 
process,
+        // or the filters on the scan object skip the rebuilt index row.
+        // If this new row is "verified" (or empty) then we are good to go.
         if (verifyRowAndRemoveEmptyColumn(row)) {
           return;
         }
         // The row is "unverified". Rewind the scanner and let the row be 
scanned again
         // so that it can be repaired
         scanner.close();
+        indexScan.withStartRow(CellUtil.cloneRow(row.get(0)), true);
         scanner = ((DelegateRegionScanner) 
delegate).getNewRegionScanner(indexScan);
         hasMore = true;
         row.clear();
@@ -619,8 +621,8 @@ public class GlobalIndexChecker extends 
BaseScannerRegionObserver implements Reg
           metricsSource.updateIndexRepairTime(indexName,
             EnvironmentEdgeManager.currentTimeMillis() - repairStart);
           if (shouldLog()) {
-            LOG.info("Index row repair on region {} took {} ms.",
-              env.getRegionInfo().getRegionNameAsString(), repairTime);
+            LOG.info("Index row repair on region {} row ts {} took {} ms.",
+              env.getRegionInfo().getRegionNameAsString(), ts, repairTime);
           }
         } catch (IOException e) {
           repairTime = EnvironmentEdgeManager.currentTimeMillis() - 
repairStart;
@@ -628,8 +630,8 @@ public class GlobalIndexChecker extends 
BaseScannerRegionObserver implements Reg
           metricsSource.updateIndexRepairFailureTime(indexName,
             EnvironmentEdgeManager.currentTimeMillis() - repairStart);
           if (shouldLog()) {
-            LOG.warn("Index row repair failure on region {} took {} ms.",
-              env.getRegionInfo().getRegionNameAsString(), repairTime);
+            LOG.warn("Index row repair failure on region {} row ts {} took {} 
ms.",
+              env.getRegionInfo().getRegionNameAsString(), ts, repairTime);
           }
           throw e;
         }
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.java
index 27146e9b81..c13e9c4091 100644
--- 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.java
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.java
@@ -1290,6 +1290,75 @@ public class GlobalIndexCheckerIT extends BaseTest {
     }
   }
 
+  @Test
+  public void testUnverifiedIndexRowWithSkipScanFilter2() throws Exception {
+    if (async) {
+      // ignoring running this test with async = true
+      return;
+    }
+
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String dataTableName = generateUniqueName();
+      String indexName = generateUniqueName();
+      conn.createStatement()
+        .execute("create table " + dataTableName
+          + " (id varchar(10) not null primary key, val1 varchar(10), val2 
varchar(10), "
+          + "val3 varchar(10))" + tableDDLOptions);
+      conn.createStatement().execute("CREATE INDEX " + indexName + " on " + 
dataTableName
+        + " (val1, val2) include (val3)" + this.indexDDLOptions);
+      IndexRegionObserver.setFailPostIndexUpdatesForTesting(true);
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('a', 'val1', 
'val2a', 'val3')");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('b', 'val1', 
'val2a', 'val31')");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('c', 'val1', 
'val2c', 'val3')");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('d', 'val1', 
'val2d', 'val3')");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('e', 'val1', 
'val2e', null)");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('f', 'val1', 
'val2f', null)");
+      conn.createStatement()
+        .execute("upsert into " + dataTableName + " " + "values ('g', 'val1', 
'val2g', null)");
+      conn.commit();
+      // Fail phase 3
+      IndexRegionObserver.setFailPostIndexUpdatesForTesting(false);
+      String selectSql = "SELECT id from " + dataTableName
+        + " WHERE val1 = 'val1' AND val2 IN ('val2a', 'val2d') AND val3 = 
'val3'";
+      List<String> expectedIDs = Lists.newArrayList("a", "d");
+      List<String> actualIDs = Lists.newArrayList();
+      try (ResultSet rs = conn.createStatement().executeQuery(selectSql)) {
+        PhoenixResultSet prs = rs.unwrap(PhoenixResultSet.class);
+        String actualExplainPlan = 
QueryUtil.getExplainPlan(prs.getUnderlyingIterator());
+        assertTrue(actualExplainPlan.contains(indexName));
+        while (rs.next()) {
+          actualIDs.add(rs.getString("id"));
+        }
+        assertEquals(expectedIDs, actualIDs);
+      } catch (AssertionError e) {
+        TestUtil.dumpTable(conn, TableName.valueOf(indexName));
+        throw e;
+      }
+      selectSql = "SELECT id from " + dataTableName
+        + " WHERE val1 = 'val1' AND val2 IN ('val2e', 'val2g') AND val3 is 
null";
+      expectedIDs = Lists.newArrayList("e", "g");
+      actualIDs = Lists.newArrayList();
+      try (ResultSet rs = conn.createStatement().executeQuery(selectSql)) {
+        PhoenixResultSet prs = rs.unwrap(PhoenixResultSet.class);
+        String actualExplainPlan = 
QueryUtil.getExplainPlan(prs.getUnderlyingIterator());
+        assertTrue(actualExplainPlan.contains(indexName));
+        while (rs.next()) {
+          actualIDs.add(rs.getString("id"));
+        }
+        assertEquals(expectedIDs, actualIDs);
+      } catch (AssertionError e) {
+        TestUtil.dumpTable(conn, TableName.valueOf(indexName));
+        throw e;
+      }
+    }
+  }
+
   @Test
   public void testUnverifiedIndexRowWithFirstKeyOnlyFilter() throws Exception {
     if (async) {

Reply via email to