kadirozde commented on a change in pull request #469: PHOENIX-5156 Consistent 
Global Indexes for Non-Transactional Tables
URL: https://github.com/apache/phoenix/pull/469#discussion_r280639210
 
 

 ##########
 File path: 
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.java
 ##########
 @@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.phoenix.end2end.BaseUniqueNamesOwnClusterIT;
+import org.apache.phoenix.end2end.IndexToolIT;
+import org.apache.phoenix.hbase.index.Indexer;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class GlobalIndexCheckerIT extends BaseUniqueNamesOwnClusterIT {
+    private final boolean async;
+    private final String tableDDLOptions;
+
+    public GlobalIndexCheckerIT(boolean async, boolean encoded) {
+        this.async = async;
+        StringBuilder optionBuilder = new StringBuilder();
+        if (!encoded) {
+            optionBuilder.append(" COLUMN_ENCODED_BYTES=0 ");
+        }
+        this.tableDDLOptions = optionBuilder.toString();
+    }
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        IndexToolIT.setup();
+    }
+
+    @Parameters(
+            name = "async={0},encoded={1}")
+    public static Collection<Object[]> data() {
+        List<Object[]> list = Lists.newArrayListWithExpectedSize(4);
+        boolean[] Booleans = new boolean[]{true, false};
+            for (boolean async : Booleans) {
+                for (boolean encoded : Booleans) {
+                    list.add(new Object[]{async, encoded});
+                }
+            }
+        return list;
+    }
+
+    public static void assertExplainPlan(Connection conn, String selectSql,
+                                         String dataTableFullName, String 
indexTableFullName) throws SQLException {
+        ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + 
selectSql);
+        String actualExplainPlan = QueryUtil.getExplainPlan(rs);
+        IndexToolIT.assertExplainPlan(false, actualExplainPlan, 
dataTableFullName, indexTableFullName);
+    }
+
+    private void populateTable(String tableName) throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute("create table " + tableName +
+                " (id varchar(10) not null primary key, val1 varchar(10), val2 
varchar(10), val3 varchar(10))" + tableDDLOptions);
+        conn.createStatement().execute("upsert into " + tableName + " values 
('a', 'ab', 'abc', 'abcd')");
+        conn.commit();
+        conn.createStatement().execute("upsert into " + tableName + " values 
('b', 'bc', 'bcd', 'bcde')");
+        conn.commit();
+        conn.close();
+    }
+
+    @Test
+    public void skipPostIndexDeleteUpdate() throws Exception {
+        String dataTableName = generateUniqueName();
+        populateTable(dataTableName);
+        Connection conn = DriverManager.getConnection(getUrl());
+        String indexName = generateUniqueName();
+        conn.createStatement().execute("CREATE INDEX " + indexName + " on " +
+                dataTableName + " (val1) include (val2, val3)" + (async ? 
"ASYNC" : ""));
+        if (async) {
+            // run the index MR job.
+            IndexToolIT.runIndexTool(true, false, null, dataTableName, 
indexName);
+        }
+        String selectSql =  "SELECT id from " + dataTableName + " WHERE val1  
= 'ab'";
+        ResultSet rs = conn.createStatement().executeQuery(selectSql);
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertFalse(rs.next());
+
+        // Configure Indexer to skip the last write phase (i.e., the post 
index update phase) where the verify flag is set
+        // to true and/or index rows are deleted and check that this does not 
impact the correctness
+        Indexer.setSkipPostIndexUpdatesForTesting(true);
+        String dml = "DELETE from " + dataTableName + " WHERE id  = 'a'";
+        assertEquals(1, conn.createStatement().executeUpdate(dml));
+        conn.commit();
+
+        // The index rows are actually not deleted yet because Indexer skipped 
delete operation. However, they are
+        // made unverified in the pre index update phase (i.e., the first 
write phase)
+        dml = "DELETE from " + dataTableName + " WHERE val1  = 'ab'";
 
 Review comment:
   Both delete operations are on the data table. A delete row operation on the 
data table should also result in deletion of the corresponding index table row. 
In the new design, in order to delete an index table row, the row is first 
marked unverified in the first write phase (i.e., before deleting the data 
table row) and then actually deleted in the third write phase. Now skipping 
post index updates means here is skipping the third write phase.  The first 
delete DML "DELETE from " + dataTableName + " WHERE id  = 'a'" does not need to 
scan index in order to efficiently locate the row to be deleted since id is the 
primary index of the data table row. During the execution of this DML, the 
index row will not be deleted but left unverified because the last write phase 
is skipped. The second DML , "DELETE from " + dataTableName + " WHERE id  = 
'a'" needs to use the index table to locate the row (i.e., to get the primary 
key for the corresponding row in the data table). Now, the row where id = 'a' 
does not exist in the data table but exists in the index row. However, the 
index row is unverified. This will trigger deletion of index row during 
read-repair and the scan on the index table will return no rows thus there will 
be no rows to delete. To make this clear even more, I will add a SQL query to 
count the number of index rows at the end to  make sure there is only one row 
left in the index table. The whole point of this test is that failing to 
complete the last write phase does not impact correctness.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to