You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ng...@apache.org on 2021/09/21 07:39:02 UTC

[jackrabbit-oak] branch trunk updated: OAK-9566 | Improve index stats (#372)

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

ngupta pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new d05c9b9  OAK-9566 | Improve index stats (#372)
d05c9b9 is described below

commit d05c9b9e29f252ede85ec42e3e812b59b08f7c33
Author: nit0906 <ni...@gmail.com>
AuthorDate: Tue Sep 21 13:08:57 2021 +0530

    OAK-9566 | Improve index stats (#372)
    
    * OAK-9566 | Improve index stats
    -Added support for Json format
    -Fixed issue with local index dir size - currently with composite node store - it was just reporting size of read only mount
    -Added new stats for indexCreationTimestamp, ReindexCompletionTimestamp, suggestDirSize, If the index has hidden
      property index node and if it has a hidden read only libs mount.
---
 .../jackrabbit/oak/plugins/index/IndexInfo.java    |  32 ++++
 .../oak/plugins/index/IndexInfoServiceImpl.java    |  25 +++
 .../oak/plugins/index/inventory/IndexPrinter.java  | 186 ++++++++++++++++-----
 .../index/property/PropertyIndexInfoProvider.java  |  25 +++
 .../plugins/index/inventory/IndexPrinterTest.java  |  85 +++++++++-
 .../index/lucene/LuceneIndexInfoProvider.java      |  84 +++++++++-
 .../oak/plugins/index/search/IndexDefinition.java  |  11 ++
 7 files changed, 397 insertions(+), 51 deletions(-)

diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfo.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfo.java
index b18619d..d4fcebf 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfo.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfo.java
@@ -90,4 +90,36 @@ public interface IndexInfo {
      */
     @Nullable
     String getIndexDefinitionDiff();
+
+    /**
+     * Determines if a hidden oak libs mount node is present
+     * @return true in case of composite node store with indexed content from read-only part of repository, false otherwise
+     */
+    boolean hasHiddenOakLibsMount();
+
+    /**
+     *Determines if :property-index node is present
+     * @return true if the index is hybrid and has :property-index node, false otherwise.
+     */
+    boolean hasPropertyIndexNode();
+
+    /**
+     * Index suggest data storage size
+     * @return storage size or -1 if unknown
+     */
+    long getSuggestSizeInBytes();
+
+    /**
+     * Time in millis at which index definition was created
+     *
+     * @return time in millis or -1 if unknown
+     */
+    long getCreationTimestamp();
+
+    /**
+     * Time in millis at which index was last re indexed
+     *
+     * @return time in millis or -1 if unknown
+     */
+    long getReindexCompletionTimestamp();
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfoServiceImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfoServiceImpl.java
index 4ecbc04..a4e501f 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfoServiceImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexInfoServiceImpl.java
@@ -178,5 +178,30 @@ public class IndexInfoServiceImpl implements IndexInfoService{
         public String getIndexDefinitionDiff() {
             return null;
         }
+
+        @Override
+        public boolean hasHiddenOakLibsMount() {
+            return false;
+        }
+
+        @Override
+        public boolean hasPropertyIndexNode() {
+            return false;
+        }
+
+        @Override
+        public long getSuggestSizeInBytes() {
+            return -1;
+        }
+
+        @Override
+        public long getCreationTimestamp() {
+            return -1;
+        }
+
+        @Override
+        public long getReindexCompletionTimestamp() {
+            return -1;
+        }
     }
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinter.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinter.java
index 8487c2a..332ff85 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinter.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinter.java
@@ -20,8 +20,11 @@
 package org.apache.jackrabbit.oak.plugins.index.inventory;
 
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
+import java.util.TimeZone;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ArrayListMultimap;
@@ -31,6 +34,7 @@ import org.apache.felix.inventory.Format;
 import org.apache.felix.inventory.InventoryPrinter;
 import org.apache.jackrabbit.oak.api.jmx.IndexStatsMBean;
 import org.apache.jackrabbit.oak.commons.IOUtils;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfo;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
 import org.apache.jackrabbit.oak.plugins.index.IndexInfo;
@@ -44,9 +48,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
 @Component(
         service = InventoryPrinter.class,
         property = {
-            "felix.inventory.printer.name=oak-index-stats",
-            "felix.inventory.printer.title=Oak Index Stats",
-            "felix.inventory.printer.format=TEXT"
+                "felix.inventory.printer.name=oak-index-stats",
+                "felix.inventory.printer.title=Oak Index Stats",
+                "felix.inventory.printer.format=TEXT",
+                "felix.inventory.printer.format=JSON"
         })
 public class IndexPrinter implements InventoryPrinter {
 
@@ -66,97 +71,200 @@ public class IndexPrinter implements InventoryPrinter {
 
     @Override
     public void print(PrintWriter pw, Format format, boolean isZip) {
-        //TODO Highlight if failing
-        printAsyncIndexInfo(pw);
-        printIndexInfo(pw);
+
+        if (format == Format.JSON) {
+            JsopBuilder json = new JsopBuilder();
+            startJsonObject(json);
+            printAsyncIndexInfo(pw, json, format);
+            printIndexInfo(pw, json, format);
+            endJsonObject(json);
+            pw.print(JsopBuilder.prettyPrint(json.toString()));
+        } else {
+            //TODO Highlight if failing
+            printAsyncIndexInfo(pw, null, format);
+            printIndexInfo(pw, null, format);
+        }
     }
 
-    private void printAsyncIndexInfo(PrintWriter pw) {
+    private void printAsyncIndexInfo(PrintWriter pw, JsopBuilder json, Format format) {
         List<String> asyncLanes = ImmutableList.copyOf(asyncIndexInfoService.getAsyncLanes());
         String title = "Async Indexers State";
-        printTitle(pw, title);
-        pw.printf("Number of async indexer lanes : %d%n", asyncLanes.size());
-        pw.println();
+        printTitle(pw, title, format);
+        addJsonKey(json, title);
+        startJsonObject(json);
+        keyValue("Number of async indexer lanes ", asyncLanes.size(), pw, json, format);
+        printWithNewLine(pw, "", format);
+
         for (String lane : asyncLanes) {
-            pw.println(lane);
+            printWithNewLine(pw, lane, format);
+            addJsonKey(json, lane);
             AsyncIndexInfo info = asyncIndexInfoService.getInfo(lane);
+            startJsonObject(json);
             if (info != null) {
-                        pw.printf("    Last indexed to      : %s%n", formatTime(info.getLastIndexedTo()));
+                keyValue("    Last indexed to      ", formatTime(info.getLastIndexedTo()), pw, json, format);
                 IndexStatsMBean stats = info.getStatsMBean();
                 if (stats != null) {
-                        pw.printf("    Status              : %s%n", stats.getStatus());
-                        pw.printf("    Failing             : %s%n", stats.isFailing());
-                        pw.printf("    Paused              : %s%n", stats.isPaused());
+                    keyValue("    Status              ", stats.getStatus(), pw, json, format);
+                    keyValue("    Failing             ", stats.isFailing(), pw, json, format);
+                    keyValue("    Paused              ", stats.isPaused(), pw, json, format);
                     if (stats.isFailing()) {
-                        pw.printf("    Failing since       : %s%n", stats.getFailingSince());
-                        pw.printf("    Latest error        : %s%n", stats.getLatestError());
+                        keyValue("    Failing since       ", stats.getFailingSince(), pw, json, format);
+                        keyValue("    Latest error        ", stats.getLatestError(), pw, json, format);
                     }
                 }
-                pw.println();
+                printWithNewLine(pw, "", format);
             }
+            endJsonObject(json);
         }
+        endJsonObject(json);
     }
 
-    private static void printTitle(PrintWriter pw, String title) {
-        pw.println(title);
-        pw.println(Strings.repeat("=", title.length()));
+    private static void printTitle(PrintWriter pw, String title, Format format) {
+        if (format == Format.TEXT) {
+            pw.println(title);
+            pw.println(Strings.repeat("=", title.length()));
+            pw.println();
+        }
     }
 
-    private void printIndexInfo(PrintWriter pw) {
+    private static void printWithNewLine(PrintWriter pw, String printLine, Format format) {
+        if (format == Format.TEXT) {
+            pw.println(printLine);
+        }
+    }
+
+    private void printIndexInfo(PrintWriter pw, JsopBuilder json, Format format) {
         ListMultimap<String, IndexInfo> infos = ArrayListMultimap.create();
         for (IndexInfo info : indexInfoService.getAllIndexInfo()) {
             infos.put(info.getType(), info);
         }
 
-        pw.printf("Total number of indexes : %d%n", infos.size());
+        keyValue("Total number of indexes ", infos.size(), pw, json, format);
+
         for (String type : infos.keySet()){
             List<IndexInfo> typedInfo = infos.get(type);
             String title = String.format("%s(%d)", type, typedInfo.size());
-            printTitle(pw, title);
-            pw.println();
+            printTitle(pw, title, format);
+            addJsonKey(json, type);
+            startJsonObject(json);
             for (IndexInfo info : typedInfo){
-                printIndexInfo(pw, info);
+                printIndexInfo(pw, json, info, format);
             }
+            endJsonObject(json);
         }
     }
 
-    private static void printIndexInfo(PrintWriter pw, IndexInfo info) {
-        pw.println(info.getIndexPath());
-        pw.printf("    Type                    : %s%n", info.getType());
+
+    private static void printIndexInfo(PrintWriter pw, JsopBuilder json, IndexInfo info, Format format) {
+        if (format == Format.TEXT) {
+            pw.println(info.getIndexPath());
+        }
+        addJsonKey(json, info.getIndexPath());
+        startJsonObject(json);
+
+        keyValue("    Type                    ", info.getType(), pw, json, format);
         if (info.getAsyncLaneName() != null) {
-            pw.printf("    Async                   : true%n");
-            pw.printf("    Async lane name         : %s%n", info.getAsyncLaneName());
+            keyValue("    Async                    ", true, pw, json, format);
+            keyValue("    Async lane name          ", info.getAsyncLaneName(), pw, json, format);
         }
 
         if (info.getIndexedUpToTime() > 0){
-            pw.printf("    Last indexed up to      : %s%n", formatTime(info.getIndexedUpToTime()));
+            keyValue("    Last indexed up to       ", formatTime(info.getIndexedUpToTime()), pw, json, format);
         }
 
         if (info.getLastUpdatedTime() > 0){
-            pw.printf("    Last updated time       : %s%n", formatTime(info.getLastUpdatedTime()));
+            keyValue("     Last updated time       ", formatTime(info.getLastUpdatedTime()), pw, json, format);
+        }
+
+        if (info.getCreationTimestamp() > 0){
+            keyValue("     Creation time           ", formatTime(info.getCreationTimestamp()), pw, json, format);
+        }
+
+        if (info.getReindexCompletionTimestamp() > 0){
+            keyValue("     Reindex completion time ", formatTime(info.getReindexCompletionTimestamp()), pw, json, format);
         }
 
         if (info.getSizeInBytes() >= 0){
-            pw.printf("    Size                    : %s%n", IOUtils.humanReadableByteCount(info.getSizeInBytes()));
+            keyValue("    Size                     ", IOUtils.humanReadableByteCount(info.getSizeInBytes()), pw, json, format);
+            keyValue("    Size (in Bytes)          ", info.getSizeInBytes(), pw, json, format);
+        }
+
+        if (info.getSuggestSizeInBytes() >= 0){
+            keyValue("    Suggest size             ", IOUtils.humanReadableByteCount(info.getSuggestSizeInBytes()), pw, json, format);
+            keyValue("    Suggest size (in Bytes)  ", info.getSuggestSizeInBytes(), pw, json, format);
         }
 
         if (info.getEstimatedEntryCount() >= 0){
-            pw.printf("    Estimated entry count   : %d%n", info.getEstimatedEntryCount());
+            keyValue("    Estimated entry count    ", info.getEstimatedEntryCount(), pw, json, format);
+        }
+
+        if ("lucene".equals(info.getType())) {
+            // Only valid for lucene type indexes, for others it will simply show false.
+            keyValue("    Has hidden oak mount     ", info.hasHiddenOakLibsMount(), pw, json, format);
+            keyValue("    Has property index       ", info.hasPropertyIndexNode(), pw, json, format);
         }
 
         if (info.hasIndexDefinitionChangedWithoutReindexing()) {
-            pw.println("    Index definition changed without reindexing");
             String diff = info.getIndexDefinitionDiff();
             if (diff != null) {
-                pw.println("    "+diff);
+                keyValue("    Index definition changed without reindexing ", diff, pw, json, format);
+                printWithNewLine(pw, "", format);
             }
         }
-        pw.println();
+        endJsonObject(json);
     }
 
     private static String formatTime(long time){
         Calendar cal = Calendar.getInstance();
         cal.setTimeInMillis(time);
-        return ISO8601.format(cal);
+        Date date = cal.getTime();
+        SimpleDateFormat outputFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        outputFmt.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return outputFmt.format(date);
+    }
+
+    private static void keyValue(String key, Object value, PrintWriter pw, JsopBuilder json, Format format) {
+        // In case the key is null or an empty String,
+        // throw an IllegalArgumentException.
+        if (key == null || key.equals("")) {
+            throw new IllegalArgumentException("Unsupported key - can't be null/empty");
+        }
+
+        if (format == Format.JSON) {
+            json.key(key.trim());
+            if (value instanceof  String) {
+                json.value((String)value);
+            } else if (value instanceof  Long) {
+                json.value((Long)value);
+            } else if (value instanceof  Boolean) {
+                json.value((Boolean)value);
+            } else if (value instanceof Integer) {
+                json.value((Integer) value);
+            } else {
+                throw new IllegalArgumentException("Unsupported type of value while creating the json output");
+            }
+        } else if (format == Format.TEXT) {
+            pw.printf(key+":%s%n",value);
+        }
+    }
+
+    // Wrappers around JsonBuilder that will result in NOOP if builder is null -
+    // These are just to avoid the multiple if/else check while handling for both TEXT and JSON formats
+    private static void startJsonObject(JsopBuilder json) {
+        if (json != null) {
+            json.object();
+        }
+    }
+
+    private static void endJsonObject(JsopBuilder json) {
+        if (json != null) {
+            json.endObject();
+        }
+    }
+
+    private static void addJsonKey(JsopBuilder json, String key) {
+        if (json != null) {
+            json.key(key);
+        }
     }
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexInfoProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexInfoProvider.java
index 53a1d29..69da859 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexInfoProvider.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexInfoProvider.java
@@ -145,5 +145,30 @@ public class PropertyIndexInfoProvider implements IndexInfoProvider {
         public String getIndexDefinitionDiff() {
             return null;
         }
+
+        @Override
+        public boolean hasHiddenOakLibsMount() {
+            return false;
+        }
+
+        @Override
+        public boolean hasPropertyIndexNode() {
+            return false;
+        }
+
+        @Override
+        public long getSuggestSizeInBytes() {
+            return -1;
+        }
+
+        @Override
+        public long getCreationTimestamp() {
+            return -1;
+        }
+
+        @Override
+        public long getReindexCompletionTimestamp() {
+            return -1;
+        }
     }
 }
diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinterTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinterTest.java
index a7225b9..0f19469 100644
--- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinterTest.java
+++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/inventory/IndexPrinterTest.java
@@ -21,8 +21,10 @@ package org.apache.jackrabbit.oak.plugins.index.inventory;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Map;
 
 import org.apache.felix.inventory.Format;
+import org.apache.jackrabbit.oak.commons.json.JsonObject;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfo;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
 import org.apache.jackrabbit.oak.plugins.index.IndexInfo;
@@ -31,8 +33,11 @@ import org.junit.Test;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -49,12 +54,31 @@ public class IndexPrinterTest {
         when(asyncInfo.getInfo("foo-async"))
                 .thenReturn(new AsyncIndexInfo("foo-async", 0, 0, false, null));
 
-        String output = getPrintOutput();
+        String output = getPrintOutput(Format.TEXT);
 
         assertThat(output, containsString("foo-async"));
     }
 
     @Test
+    public void asyncIndexInfoJson() throws Exception {
+        when(indexInfo.getAllIndexInfo()).thenReturn(emptyList());
+        when(asyncInfo.getAsyncLanes()).thenReturn(asList("foo-async", "bar-async"));
+        when(asyncInfo.getInfo("foo-async"))
+                .thenReturn(new AsyncIndexInfo("foo-async", 0, 0, false, null));
+
+        String output = getPrintOutput(Format.JSON);
+
+        JsonObject json = JsonObject.fromJson(output, true);
+        Map<String, JsonObject> jsonMap = json.getChildren();
+        assertTrue(jsonMap.keySet().contains("Async Indexers State"));
+        int size  = Integer.parseInt(jsonMap.get("Async Indexers State").getProperties().get("Number of async indexer lanes"));
+        assertEquals(2, size);
+
+        assertTrue(jsonMap.get("Async Indexers State").getChildren().keySet().contains("foo-async"));
+        assertTrue(jsonMap.get("Async Indexers State").getChildren().keySet().contains("bar-async"));
+    }
+
+    @Test
     public void indexInfo() throws Exception{
         when(asyncInfo.getAsyncLanes()).thenReturn(emptyList());
 
@@ -64,16 +88,42 @@ public class IndexPrinterTest {
 
         when(indexInfo.getAllIndexInfo()).thenReturn(asList(info1, info2));
 
-        String output = getPrintOutput();
+        String output = getPrintOutput(Format.TEXT);
         assertThat(output, containsString("/oak:index/fooIndex"));
         assertThat(output, containsString("/oak:index/barIndex"));
         assertThat(output, containsString("async"));
 
     }
 
-    private String getPrintOutput() {
+    @Test
+    public void indexInfoJson() throws Exception{
+        when(asyncInfo.getAsyncLanes()).thenReturn(emptyList());
+
+        TestInfo info1 = new TestInfo("/oak:index/fooIndex", "property");
+        TestInfo info2 = new TestInfo("/oak:index/barIndex", "lucene");
+        info2.laneName = "async";
+
+        when(indexInfo.getAllIndexInfo()).thenReturn(asList(info1, info2));
+
+        String output = getPrintOutput(Format.JSON);
+        JsonObject json = JsonObject.fromJson(output, true);
+        Map<String, JsonObject> jsonMap = json.getChildren();
+        assertTrue(jsonMap.keySet().contains("Async Indexers State"));
+
+        assertEquals(0, jsonMap.get("Async Indexers State").getChildren().size());
+
+        assertTrue(jsonMap.keySet().contains("lucene"));
+        assertTrue(jsonMap.keySet().contains("property"));
+        assertTrue(jsonMap.get("lucene").getChildren().keySet().contains("/oak:index/barIndex"));
+        assertFalse(jsonMap.get("lucene").getChildren().keySet().contains("/oak:index/fooIndex"));
+        assertTrue(jsonMap.get("property").getChildren().keySet().contains("/oak:index/fooIndex"));
+        assertFalse(jsonMap.get("property").getChildren().keySet().contains("/oak:index/barIndex"));
+
+    }
+
+    private String getPrintOutput(Format format) {
         StringWriter sw = new StringWriter();
-        printer.print(new PrintWriter(sw), Format.TEXT, false);
+        printer.print(new PrintWriter(sw), format, false);
         return sw.toString();
     }
 
@@ -131,6 +181,31 @@ public class IndexPrinterTest {
         public String getIndexDefinitionDiff() {
             return null;
         }
+
+        @Override
+        public boolean hasHiddenOakLibsMount() {
+            return false;
+        }
+
+        @Override
+        public boolean hasPropertyIndexNode() {
+            return false;
+        }
+
+        @Override
+        public long getSuggestSizeInBytes() {
+            return 0;
+        }
+
+        @Override
+        public long getCreationTimestamp() {
+            return 0;
+        }
+
+        @Override
+        public long getReindexCompletionTimestamp() {
+            return 0;
+        }
     }
 
 }
\ No newline at end of file
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProvider.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProvider.java
index 0397d34..3f8cde5 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProvider.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexInfoProvider.java
@@ -47,6 +47,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder;
 import org.apache.jackrabbit.util.ISO8601;
+import org.apache.lucene.document.Field;
 import org.apache.lucene.store.Directory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -85,8 +86,10 @@ public class LuceneIndexInfoProvider implements IndexInfoProvider {
         LuceneIndexInfo info = new LuceneIndexInfo(indexPath);
         computeSize(idxState, info);
         computeIndexDefinitionChange(idxState, info);
-        computeLastUpdatedTime(idxState, info);
+        computeStatusNodeInfo(idxState, info);
         computeAsyncIndexInfo(idxState, indexPath, info);
+        checkIfHiddenNodesExists(idxState, info);
+        computeCreationTimestamp(idxState, info);
         return info;
     }
 
@@ -121,22 +124,59 @@ public class LuceneIndexInfoProvider implements IndexInfoProvider {
     private void computeSize(NodeState idxState, LuceneIndexInfo info) throws IOException {
         LuceneIndexDefinition defn = LuceneIndexDefinition.newBuilder(nodeStore.getRoot(), idxState, info.indexPath).build();
         for (String dirName : idxState.getChildNodeNames()) {
-            if (NodeStateUtils.isHidden(dirName) && MultiplexersLucene.isIndexDirName(dirName)) {
-                try (Directory dir = new OakDirectory(new ReadOnlyBuilder(idxState), dirName, defn, true)) {
-                    info.numEntries += DirectoryUtils.getNumDocs(dir);
-                    info.size = DirectoryUtils.dirSize(dir);
+            if (NodeStateUtils.isHidden(dirName)) {
+                // This is true for both read-write index data dir (:data) and the read-only mount (:oak-libs-mount-index-data)
+                if (MultiplexersLucene.isIndexDirName(dirName)) {
+                    try (Directory dir = new OakDirectory(new ReadOnlyBuilder(idxState), dirName, defn, true)) {
+                        info.numEntries += DirectoryUtils.getNumDocs(dir);
+                        info.size += DirectoryUtils.dirSize(dir);
+                    }
+                } else if (MultiplexersLucene.isSuggestIndexDirName(dirName)) {
+                    try (Directory dir = new OakDirectory(new ReadOnlyBuilder(idxState), dirName, defn, true)) {
+                        info.suggestSize += DirectoryUtils.dirSize(dir);
+                    }
                 }
             }
         }
     }
 
-    private static void computeLastUpdatedTime(NodeState idxState, LuceneIndexInfo info) {
+    private static void computeStatusNodeInfo(NodeState idxState, LuceneIndexInfo info) {
         NodeState status = idxState.getChildNode(IndexDefinition.STATUS_NODE);
-        if (status.exists()){
+        if (status.exists()) {
             PropertyState updatedTime = status.getProperty(IndexDefinition.STATUS_LAST_UPDATED);
             if (updatedTime != null) {
                 info.lastUpdatedTime = ISO8601.parse(updatedTime.getValue(Type.DATE)).getTimeInMillis();
             }
+
+            PropertyState reindexCompletionTime = status.getProperty(IndexDefinition.REINDEX_COMPLETION_TIMESTAMP);
+            if (reindexCompletionTime != null) {
+                info.reindexCompletionTimestamp = ISO8601.parse(reindexCompletionTime.getValue(Type.DATE)).getTimeInMillis();
+            }
+        }
+    }
+
+    private static void computeCreationTimestamp(NodeState idxState, LuceneIndexInfo info) {
+        NodeState indexDef = idxState.getChildNode(INDEX_DEFINITION_NODE);
+        if (indexDef.exists()) {
+            PropertyState creationTime = indexDef.getProperty(IndexDefinition.CREATION_TIMESTAMP);
+            if (creationTime != null) {
+                info.creationTimestamp = ISO8601.parse(creationTime.getValue(Type.DATE)).getTimeInMillis();
+            }
+        }
+    }
+
+    private static void checkIfHiddenNodesExists(NodeState idxState, LuceneIndexInfo info) {
+        // Check for hidden oak libs mount node that has indexed content for read only repo in composite store
+        // Also check for hidden property index node :property-index - present in case of hybrid indexes
+        info.hasHiddenOakLibsMount = false;
+        info.hasPropertyIndexNode = false;
+
+        for(String c : idxState.getChildNodeNames()) {
+            if (c.startsWith(IndexDefinition.HIDDEN_OAK_MOUNT_PREFIX)) {
+                info.hasHiddenOakLibsMount = true;
+            } else if (c.equals(IndexDefinition.PROPERTY_INDEX)) {
+                info.hasPropertyIndexNode = true;
+            }
         }
     }
 
@@ -160,6 +200,11 @@ public class LuceneIndexInfoProvider implements IndexInfoProvider {
         long lastUpdatedTime;
         boolean indexDefinitionChanged;
         String indexDiff;
+        boolean hasHiddenOakLibsMount;
+        boolean hasPropertyIndexNode;
+        long suggestSize;
+        long creationTimestamp;
+        long reindexCompletionTimestamp;
 
         public LuceneIndexInfo(String indexPath) {
             this.indexPath = indexPath;
@@ -209,6 +254,31 @@ public class LuceneIndexInfoProvider implements IndexInfoProvider {
         public String getIndexDefinitionDiff() {
             return indexDiff;
         }
+
+        @Override
+        public boolean hasHiddenOakLibsMount() {
+            return hasHiddenOakLibsMount;
+        }
+
+        @Override
+        public boolean hasPropertyIndexNode() {
+            return hasPropertyIndexNode;
+        }
+
+        @Override
+        public long getSuggestSizeInBytes() {
+            return suggestSize;
+        }
+
+        @Override
+        public long getCreationTimestamp() {
+            return creationTimestamp;
+        }
+
+        @Override
+        public long getReindexCompletionTimestamp() {
+            return reindexCompletionTimestamp;
+        }
     }
 
     static class FilteringEqualsDiff extends EqualsDiff {
diff --git a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
index ec6bc80..f49895a 100644
--- a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
+++ b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
@@ -143,6 +143,17 @@ public class IndexDefinition implements Aggregate.AggregateMapper {
     public static final String STATUS_NODE = ":status";
 
     /**
+     * Hidden node under index definition that contains indexed data for read only
+     * part of composite node store.
+     */
+    public static final String HIDDEN_OAK_MOUNT_PREFIX = ":oak:mount-";
+
+    /**
+     * Node name under which all property indexes are created
+     */
+    public static final String PROPERTY_INDEX = ":property-index";
+
+    /**
      * Property on status node which refers to the date when the index was lastUpdated
      * This may not be the same time as when index was closed but the time of checkpoint
      * upto which index is upto date (OAK-6194)