You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by nn...@apache.org on 2017/09/28 06:11:20 UTC

[geode] branch develop updated: GEODE-3520: isValid API made public

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

nnag pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new acbd66c  GEODE-3520: isValid API made public
acbd66c is described below

commit acbd66ce97b53506e927cc5ba881d7b2ba9b1b53
Author: nabarun <nn...@pivotal.io>
AuthorDate: Tue Aug 29 14:45:37 2017 -0700

    GEODE-3520: isValid API made public
    
    	* In case an index update corrupts the index, the index will be marked invalid
    	* Queries will not use that index while executing queries.
    	* The API isValid had been made public so that users can find out if the index ia valid or not
    	* A new field has been added to the list index gfsh command which shows the isValid flag.
    	* If while loading the data index during its creation and it causes an exception, the index creation will fail.
---
 .../java/org/apache/geode/cache/query/Index.java   |   8 +
 .../cache/query/internal/index/AbstractIndex.java  |   3 +-
 .../cache/query/internal/index/IndexManager.java   |  27 +-
 .../cache/query/internal/index/IndexProtocol.java  |   2 -
 .../geode/internal/cache/PartitionedRegion.java    |   1 +
 .../internal/cli/commands/ListIndexCommand.java    |   1 +
 .../internal/cli/domain/IndexDetails.java          |  18 +-
 .../apache/geode/cache/query/data/Portfolio.java   |   4 +
 .../query/dunit/CorruptedIndexIntegrationTest.java | 290 +++++++++++++++++++++
 .../query/transaction/QueryAndJtaJUnitTest.java    |  26 --
 .../cli/commands/IndexCommandsDUnitTest.java       |  18 ++
 .../geode/codeAnalysis/sanctionedSerializables.txt |   4 +-
 12 files changed, 365 insertions(+), 37 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/Index.java b/geode-core/src/main/java/org/apache/geode/cache/query/Index.java
index b0ff47e..21867d0 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/Index.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/Index.java
@@ -80,6 +80,14 @@ public interface Index {
    */
   public String getFromClause();
 
+
+  /**
+   * Get whether index is valid to be used in queries.
+   */
+  public default boolean isValid() {
+    return false;
+  }
+
   /**
    * Get the canonicalized fromClause for this index.
    */
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/AbstractIndex.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/AbstractIndex.java
index c0e7885..a59b049 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/AbstractIndex.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/AbstractIndex.java
@@ -484,6 +484,7 @@ public abstract class AbstractIndex implements IndexProtocol {
   @Override
   public boolean addIndexMapping(RegionEntry entry) throws IMQException {
     addMapping(entry);
+
     // if no exception, then success
     return true;
   }
@@ -516,7 +517,7 @@ public abstract class AbstractIndex implements IndexProtocol {
     return true;
   }
 
-  @Override
+
   public boolean isValid() {
     return this.isValid;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexManager.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexManager.java
index 2433178..12e7fd0 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexManager.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexManager.java
@@ -1052,7 +1052,8 @@ public class IndexManager {
             }
             IndexProtocol index = (IndexProtocol) ind;
 
-            if (((AbstractIndex) index).isPopulated() && index.getType() != IndexType.PRIMARY_KEY) {
+            if (index.isValid() && ((AbstractIndex) index).isPopulated()
+                && index.getType() != IndexType.PRIMARY_KEY) {
               // Asif : If the current Index contains an entry inspite
               // of add operation , this can only mean that Index
               // has already acted on it during creation, so do not
@@ -1063,9 +1064,7 @@ public class IndexManager {
                       this.region.getFullPath(), entry.getKey());
                 }
                 start = ((AbstractIndex) index).updateIndexUpdateStats();
-
-                index.addIndexMapping(entry);
-
+                addIndexMapping(entry, index);
                 ((AbstractIndex) index).updateIndexUpdateStats(start);
               }
             }
@@ -1101,7 +1100,7 @@ public class IndexManager {
               }
               start = ((AbstractIndex) index).updateIndexUpdateStats();
 
-              index.addIndexMapping(entry);
+              addIndexMapping(entry, index);
 
               ((AbstractIndex) index).updateIndexUpdateStats(start);
             }
@@ -1156,6 +1155,24 @@ public class IndexManager {
     }
   }
 
+  private void addIndexMapping(RegionEntry entry, IndexProtocol index) throws IMQException {
+    try {
+      index.addIndexMapping(entry);
+    } catch (Exception exception) {
+      index.markValid(false);
+      setPRIndexAsInvalid((AbstractIndex) index);
+      logger.warn("Put operation for the entry corrupted the index : "
+          + ((AbstractIndex) index).indexName + " with the exception : \n " + exception);
+    }
+  }
+
+  private void setPRIndexAsInvalid(AbstractIndex index) {
+    if (index.prIndex != null) {
+      AbstractIndex prIndex = (AbstractIndex) index.prIndex;
+      prIndex.markValid(false);
+    }
+  }
+
   private void waitBeforeUpdate() {
     synchronized (indexes) {
       ++numCreators;
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexProtocol.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexProtocol.java
index ab07f15..a444387 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexProtocol.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/index/IndexProtocol.java
@@ -157,8 +157,6 @@ public interface IndexProtocol extends Index {
       throws TypeMismatchException, FunctionDomainException, NameResolutionException,
       QueryInvocationTargetException;
 
-  boolean isValid();
-
   void markValid(boolean b);
 
   void initializeIndex(boolean loadEntries) throws IMQException;
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
index 907df88..0ada1f9 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/PartitionedRegion.java
@@ -9626,6 +9626,7 @@ public class PartitionedRegion extends LocalRegion
           }
         }
       } // End of bucket list
+      parIndex.markValid(true);
       return parIndex;
     }
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListIndexCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListIndexCommand.java
index 847426c..c892c3c 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListIndexCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListIndexCommand.java
@@ -78,6 +78,7 @@ public class ListIndexCommand implements GfshCommand {
         indexData.accumulate("Type", StringUtils.defaultString(indexDetails.getIndexType()));
         indexData.accumulate("Indexed Expression", indexDetails.getIndexedExpression());
         indexData.accumulate("From Clause", indexDetails.getFromClause());
+        indexData.accumulate("Valid Index", indexDetails.getIsValid());
 
         if (showStats) {
           final IndexStatisticsDetailsAdapter adapter =
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexDetails.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexDetails.java
index 335302e..8dc67b6 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexDetails.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/IndexDetails.java
@@ -18,6 +18,7 @@ package org.apache.geode.management.internal.cli.domain;
 import org.apache.commons.lang.StringUtils;
 import org.apache.geode.cache.query.Index;
 import org.apache.geode.cache.query.IndexStatistics;
+import org.apache.geode.cache.query.internal.index.AbstractIndex;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.lang.ObjectUtils;
 
@@ -36,6 +37,7 @@ import java.io.Serializable;
 @SuppressWarnings("unused")
 public class IndexDetails implements Comparable<IndexDetails>, Serializable {
 
+  private static final long serialVersionUID = -2198907141534201288L;
   private IndexStatisticsDetails indexStatisticsDetails;
 
   private IndexType indexType;
@@ -45,6 +47,7 @@ public class IndexDetails implements Comparable<IndexDetails>, Serializable {
   private String projectionAttributes;
   private String memberName;
   private String regionName;
+  private String isValid;
 
   private final String indexName;
   private final String memberId;
@@ -84,12 +87,20 @@ public class IndexDetails implements Comparable<IndexDetails>, Serializable {
     setMemberName(member.getName());
     setProjectionAttributes(index.getProjectionAttributes());
     setRegionName(index.getRegion().getName());
-
+    if (index instanceof AbstractIndex) {
+      setIsValid(((AbstractIndex) index).isValid());
+    } else {
+      setIsValid(false);
+    }
     if (index.getStatistics() != null) {
       setIndexStatisticsDetails(createIndexStatisticsDetails(index.getStatistics()));
     }
   }
 
+  public void setIsValid(boolean valid) {
+    this.isValid = String.valueOf(valid);
+  }
+
   public IndexDetails(final String memberId, final String regionPath, final String indexName) {
     assertValidArgument(StringUtils.isNotBlank(memberId),
         "The member having a region with an index must be specified!");
@@ -175,6 +186,10 @@ public class IndexDetails implements Comparable<IndexDetails>, Serializable {
     return regionPath;
   }
 
+  public String getIsValid() {
+    return this.isValid;
+  }
+
   public int compareTo(final IndexDetails indexDetails) {
     int comparisonValue = compare(getMemberName(), indexDetails.getMemberName());
     comparisonValue = (comparisonValue != 0 ? comparisonValue
@@ -223,6 +238,7 @@ public class IndexDetails implements Comparable<IndexDetails>, Serializable {
     buffer.append(", memberName = ").append(getMemberName());
     buffer.append(", regionName = ").append(getRegionName());
     buffer.append(", regionPath = ").append(getRegionPath());
+    buffer.append(", isValid = ").append(getIsValid());
     buffer.append(", projectAttributes = ").append(getProjectionAttributes());
     buffer.append("}");
 
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/data/Portfolio.java b/geode-core/src/test/java/org/apache/geode/cache/query/data/Portfolio.java
index bc9874c..464a822 100644
--- a/geode-core/src/test/java/org/apache/geode/cache/query/data/Portfolio.java
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/data/Portfolio.java
@@ -236,6 +236,10 @@ public class Portfolio implements Serializable, DataSerializable {
     return this.doubleMinValue;
   }
 
+  public void throwExceptionMethod() {
+    throw new IllegalStateException();
+  }
+
   public void fromData(DataInput in) throws IOException, ClassNotFoundException {
     this.ID = in.readInt();
     boolean isNull = DataSerializer.readPrimitiveBoolean(in);
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/dunit/CorruptedIndexIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/cache/query/dunit/CorruptedIndexIntegrationTest.java
new file mode 100644
index 0000000..b7a3742
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/dunit/CorruptedIndexIntegrationTest.java
@@ -0,0 +1,290 @@
+/*
+ * 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.geode.cache.query.dunit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+import org.awaitility.Awaitility;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.PartitionAttributesFactory;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.Index;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.data.Portfolio;
+import org.apache.geode.cache.query.internal.QueryObserverAdapter;
+import org.apache.geode.cache.query.internal.QueryObserverHolder;
+import org.apache.geode.cache.query.internal.index.IndexProtocol;
+import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
+import org.apache.geode.test.junit.categories.DistributedTest;
+
+@Category(DistributedTest.class)
+public class CorruptedIndexIntegrationTest extends JUnit4CacheTestCase {
+
+  @Test
+  public void putMustSucceedAndIndexInvalidatedWhenAPutCorruptsAnIndex() throws Exception {
+
+    String queryString = "SELECT * FROM /REGION_NAME WHERE ID = 3";
+    String regionName = "REGION_NAME";
+
+    Cache cache = getCache();
+
+    Region region =
+        cache.createRegionFactory().setDataPolicy(DataPolicy.PARTITION).create(regionName);
+
+    QueryService queryService = cache.getQueryService();
+    Index idIndex = queryService.createIndex("idIndex", "ID", "/" + regionName);
+    Index exceptionIndex =
+        queryService.createIndex("exceptionIndex", "throwExceptionMethod", "/" + regionName);
+
+    IntStream.rangeClosed(1, 3).forEach(i -> region.put(i, new Portfolio(i)));
+
+
+    assertEquals("Uncorrupted index must have all the entries", 3,
+        idIndex.getStatistics().getNumberOfValues());
+    assertEquals("Corrupted index should not have indexed any entries", 0,
+        exceptionIndex.getStatistics().getNumberOfValues());
+
+    SelectResults results = (SelectResults) queryService.newQuery(queryString).execute();
+
+    assertEquals("Query execution must be successful ", 1, results.size());
+  }
+
+
+  @Test
+  public void indexCreationMustFailIfRegionEntriesAreNotCompatible() throws Exception {
+
+    String queryString = "SELECT * FROM /REGION_NAME WHERE ID = 3";
+    String regionName = "REGION_NAME";
+
+    Cache cache = getCache();
+
+    Region region =
+        cache.createRegionFactory().setDataPolicy(DataPolicy.PARTITION).create(regionName);
+
+    QueryService queryService = cache.getQueryService();
+
+    IntStream.rangeClosed(1, 3).forEach(i -> region.put(i, new Portfolio(i)));
+
+    Index idIndex = queryService.createIndex("idIndex", "ID", "/" + regionName);
+    try {
+      queryService.createIndex("exceptionIndex", "throwExceptionMethod", "/" + regionName);
+      fail();
+    } catch (Exception exception) {
+      System.out.println("Exception expected!");
+    }
+
+    assertEquals("Uncorrupted index must have all the entries ", 3,
+        idIndex.getStatistics().getNumberOfValues());
+
+    SelectResults results = (SelectResults) queryService.newQuery(queryString).execute();
+
+    assertEquals("Query execution must be successful ", 1, results.size());
+  }
+
+  class QueryObserverImpl extends QueryObserverAdapter {
+    boolean isIndexesUsed = false;
+
+    final ArrayList indexesUsed = new ArrayList();
+
+    public void beforeIndexLookup(Index index, int oper, Object key) {
+      indexesUsed.add(index.getName());
+    }
+
+    public void afterIndexLookup(Collection results) {
+      if (results != null) {
+        isIndexesUsed = true;
+      }
+    }
+
+    public void reset() {
+      this.isIndexesUsed = false;
+      this.indexesUsed.clear();
+    }
+  }
+
+  @Test
+  public void putMustSucceedWhenTheRangeIndexIsCorrupted() throws Exception {
+    String regionName = "portfolio";
+    String INDEX_NAME = "key_index1";
+
+    PartitionAttributesFactory partitionAttributes = new PartitionAttributesFactory();
+    partitionAttributes.setTotalNumBuckets(1);
+
+    Cache cache = getCache();
+    Region region = cache.createRegionFactory().setDataPolicy(DataPolicy.PARTITION)
+        .setPartitionAttributes(partitionAttributes.create()).create(regionName);
+
+    Portfolio p = new Portfolio(1, 2);
+    HashMap map1 = new HashMap();
+    map1.put("SUN", 1);
+    map1.put("IBM", 2);
+    map1.put("AOL", 4);
+    p.positions = map1;
+    region.put(1, p);
+
+    QueryService queryService = cache.getQueryService();
+    Index keyIndex1 = queryService.createIndex(INDEX_NAME, "positions[*]", "/portfolio");
+
+    Portfolio p2 = new Portfolio(3, 4);
+    HashMap map2 = new HashMap();
+    map2.put("APPL", 3);
+    map2.put("AOL", "hello");
+    p2.positions = map2;
+
+    region.put(2, p2);
+
+    assertEquals("Put must be successful", 2, region.size());
+
+
+    assertEquals("Index must be invalid at this point ", false, keyIndex1.isValid());
+
+    QueryObserverImpl observer = new QueryObserverImpl();
+    QueryObserverHolder.setInstance(observer);
+
+    SelectResults results = (SelectResults) queryService
+        .newQuery(
+            "select * from /portfolio p where p.positions['AOL'] = 'hello' OR p.positions['IBM'] = 2")
+        .execute();
+
+    assertEquals("Correct results expected from the query execution ", 2, results.size());
+
+    assertEquals("No index must be used while executing the query ", 0,
+        observer.indexesUsed.size());
+
+  }
+
+  @Test
+  public void rangeIndexCreationMustFailIfRegionEntriesAreNotCompatible() throws Exception {
+    String regionName = "portfolio";
+    String INDEX_NAME = "key_index1";
+
+    PartitionAttributesFactory partitionAttributes = new PartitionAttributesFactory();
+    partitionAttributes.setTotalNumBuckets(1);
+
+    Cache cache = getCache();
+    Region region = cache.createRegionFactory().setDataPolicy(DataPolicy.PARTITION)
+        .setPartitionAttributes(partitionAttributes.create()).create(regionName);
+
+    Portfolio p = new Portfolio(1, 2);
+    HashMap map1 = new HashMap();
+    map1.put("SUN", 1);
+    map1.put("IBM", 2);
+    map1.put("AOL", 4);
+    p.positions = map1;
+    region.put(1, p);
+
+    Portfolio p2 = new Portfolio(3, 4);
+    HashMap map2 = new HashMap();
+    map2.put("APPL", 3);
+    map2.put("AOL", "hello");
+    p2.positions = map2;
+
+    region.put(2, p2);
+
+    assertEquals("Put must be successful", 2, region.size());
+
+    QueryService queryService = cache.getQueryService();
+    try {
+      queryService.createIndex(INDEX_NAME, "positions[*]", "/portfolio");
+      fail();
+    } catch (Exception exception) {
+      System.out.println("Expected Exception " + exception);
+    }
+
+    assertEquals("There should be no index present", null,
+        queryService.getIndex(region, INDEX_NAME));
+
+    QueryObserverImpl observer = new QueryObserverImpl();
+    QueryObserverHolder.setInstance(observer);
+
+    SelectResults results = (SelectResults) queryService
+        .newQuery(
+            "select * from /portfolio p where p.positions['AOL'] = 'hello' OR p.positions['IBM'] = 2")
+        .execute();
+
+    assertEquals("Current results expected from the query execution ", 2, results.size());
+
+    assertEquals("No index must be used while executing the query ", 0,
+        observer.indexesUsed.size());
+
+  }
+
+  @Test
+  public void rangeIndexCreationMustPassIfEntriesArePresentInDifferentBucketsAndQueriesMustUseThem()
+      throws Exception {
+    String regionName = "portfolio";
+    String INDEX_NAME = "key_index1";
+
+
+    Cache cache = getCache();
+    Region region =
+        cache.createRegionFactory().setDataPolicy(DataPolicy.PARTITION).create(regionName);
+
+    Portfolio p = new Portfolio(1, 2);
+    HashMap map1 = new HashMap();
+    map1.put("SUN", 1);
+    map1.put("IBM", 2);
+    map1.put("AOL", 4);
+    p.positions = map1;
+    region.put(1, p);
+
+    Portfolio p2 = new Portfolio(3, 4);
+    HashMap map2 = new HashMap();
+    map2.put("APPL", 3);
+    map2.put("AOL", "hello");
+    p2.positions = map2;
+
+    region.put(2, p2);
+
+    assertEquals("Put must be successful", 2, region.size());
+
+    QueryService queryService = cache.getQueryService();
+
+    try {
+      Index keyIndex1 = queryService.createIndex(INDEX_NAME, "positions[*]", "/portfolio");
+      assertEquals("Index must be valid", true, keyIndex1.isValid());
+    } catch (Exception exception) {
+      fail();
+    }
+
+    QueryObserverImpl observer = new QueryObserverImpl();
+    QueryObserverHolder.setInstance(observer);
+
+    SelectResults results = (SelectResults) queryService
+        .newQuery(
+            "select * from /portfolio p where p.positions['AOL'] = 'hello' OR p.positions['IBM'] = 2")
+        .execute();
+
+    assertEquals("Current results expected from the query execution ", 2, results.size());
+
+    assertEquals("Index must be used while executing the query ", 2, observer.indexesUsed.size());
+
+  }
+
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/transaction/QueryAndJtaJUnitTest.java b/geode-core/src/test/java/org/apache/geode/cache/query/transaction/QueryAndJtaJUnitTest.java
index b994733..5859806 100644
--- a/geode-core/src/test/java/org/apache/geode/cache/query/transaction/QueryAndJtaJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/transaction/QueryAndJtaJUnitTest.java
@@ -419,32 +419,6 @@ public class QueryAndJtaJUnitTest {
     }
   }
 
-  @Test
-  public void testFailedIndexUpdateOnCommitForPut() throws Exception {
-    Person.THROW_ON_INDEX = true;
-    AttributesFactory af = new AttributesFactory();
-    af.setDataPolicy(DataPolicy.REPLICATE);
-    SimpleListener sl = new SimpleListener();
-    af.setCacheListener(sl);
-    Region region = cache.createRegion("sample", af.create());
-    qs.createIndex("foo", IndexType.FUNCTIONAL, "index", "/sample");
-    Context ctx = cache.getJNDIContext();
-
-    Integer x = new Integer(0);
-    region.getCache().getCacheTransactionManager().begin();
-    region.create(x, new Person("xyz", 45));
-    try {
-      region.getCache().getCacheTransactionManager().commit();
-      fail("commit should have thrown an exception because the index maintenance threw");
-    } catch (org.apache.geode.cache.query.IndexMaintenanceException ie) {
-      // this is the desired case
-    }
-    Person p = (Person) region.get(x);
-    assertEquals("object shouldn't have made it into region", null, p);
-    assertEquals(0, sl.creates);
-    assertEquals(0, sl.updates);
-  }
-
   private static class SimpleListener extends CacheListenerAdapter {
 
     public int creates = 0;
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsDUnitTest.java
index 415f6f7..383af2f 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/IndexCommandsDUnitTest.java
@@ -111,6 +111,19 @@ public class IndexCommandsDUnitTest {
   }
 
   @Test
+  public void testListIndexValidField() throws Exception {
+    CommandStringBuilder createStringBuilder = new CommandStringBuilder(CliStrings.CREATE_INDEX);
+    createStringBuilder.addOption(CliStrings.CREATE_INDEX__NAME, indexName);
+    createStringBuilder.addOption(CliStrings.CREATE_INDEX__EXPRESSION, "\"h.low\"");
+    createStringBuilder.addOption(CliStrings.CREATE_INDEX__REGION,
+        "\"/" + partitionedRegionName + " s, s.history h\"");
+
+    gfsh.executeAndVerifyCommand(createStringBuilder.toString());
+
+    assertTrue(indexIsListedAsValid());
+  }
+
+  @Test
   public void testCannotCreateIndexWithExistingIndexName() throws Exception {
     createBaseIndexForTesting();
 
@@ -253,6 +266,11 @@ public class IndexCommandsDUnitTest {
     return gfsh.getGfshOutput().contains(indexName);
   }
 
+  private boolean indexIsListedAsValid() throws Exception {
+    gfsh.executeAndVerifyCommand(CliStrings.LIST_INDEX);
+    return gfsh.getGfshOutput().contains("true");
+  }
+
   private static Region<?, ?> createPartitionedRegion(String regionName, Cache cache,
       Class keyConstraint, Class valueConstraint) {
     RegionFactory regionFactory = cache.createRegionFactory();
diff --git a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedSerializables.txt b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedSerializables.txt
index a588268..62fac3b 100755
--- a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedSerializables.txt
+++ b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedSerializables.txt
@@ -512,7 +512,7 @@ org/apache/geode/management/internal/cli/domain/DiskStoreDetails$RegionDetails,f
 org/apache/geode/management/internal/cli/domain/DurableCqNamesResult,true,1,cqNames:java/util/List
 org/apache/geode/management/internal/cli/domain/EvictionAttributesInfo,true,1,evictionAction:java/lang/String,evictionAlgorithm:java/lang/String,evictionMaxValue:int,nonDefaultAttributes:java/util/Map
 org/apache/geode/management/internal/cli/domain/FixedPartitionAttributesInfo,true,1,isPrimary:boolean,numBuckets:int,partitionName:java/lang/String
-org/apache/geode/management/internal/cli/domain/IndexDetails,false,fromClause:java/lang/String,indexName:java/lang/String,indexStatisticsDetails:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,indexType:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexType,indexedExpression:java/lang/String,memberId:java/lang/String,memberName:java/lang/String,projectionAttributes:java/lang/String,regionName:java/lang/String,regionPath:java/lang/String
+org/apache/geode/management/internal/cli/domain/IndexDetails,true,-2198907141534201288,fromClause:java/lang/String,indexName:java/lang/String,indexStatisticsDetails:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,indexType:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexType,indexedExpression:java/lang/String,isValid:java/lang/String,memberId:java/lang/String,memberName:java/lang/String,projectionAttributes:java/lang/String,regionName [...]
 org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,false,numberOfKeys:java/lang/Long,numberOfUpdates:java/lang/Long,numberOfValues:java/lang/Long,totalUpdateTime:java/lang/Long,totalUses:java/lang/Long
 org/apache/geode/management/internal/cli/domain/IndexDetails$IndexType,false,description:java/lang/String
 org/apache/geode/management/internal/cli/domain/IndexInfo,true,1,indexName:java/lang/String,indexType:int,indexedExpression:java/lang/String,regionPath:java/lang/String
@@ -822,4 +822,4 @@ org/apache/geode/redis/internal/executor/list/ListExecutor$ListDirection,false
 org/apache/geode/security/AuthenticationFailedException,true,-8202866472279088879
 org/apache/geode/security/AuthenticationRequiredException,true,4675976651103154919
 org/apache/geode/security/GemFireSecurityException,true,3814254578203076926,cause:java/lang/Throwable
-org/apache/geode/security/NotAuthorizedException,true,419215768216387745,principal:java/security/Principal
+org/apache/geode/security/NotAuthorizedException,true,419215768216387745,principal:java/security/Principal
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].