]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4907 Add search index related info to system info page
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 7 Aug 2014 11:55:45 +0000 (13:55 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 7 Aug 2014 11:55:50 +0000 (13:55 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java
server/sonar-server/src/main/java/org/sonar/server/search/IndexClient.java
server/sonar-server/src/main/java/org/sonar/server/search/IndexHealth.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/search/SearchHealth.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/search/SearchHealthMediumTest.java
server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb

index fe5b19d92ebc213a1d55318a32fc92a5a92d1ba7..caa430bedd75908b496dccd3208c09eb96aa0777 100644 (file)
@@ -224,6 +224,7 @@ class ServerComponents {
       IndexClient.class,
       ActivityNormalizer.class,
       ActivityIndex.class,
+      SearchHealth.class,
 
       // LogService
       ActivityService.class
index 371ea996c47df4087efeded01f8e17fe16c0fa38..d3ab18ae5cbb62c675bda1368140d01a9a8f6567 100644 (file)
@@ -25,6 +25,8 @@ import com.google.common.collect.Multimap;
 import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.action.count.CountRequestBuilder;
+import org.elasticsearch.action.count.CountResponse;
 import org.elasticsearch.action.delete.DeleteRequestBuilder;
 import org.elasticsearch.action.delete.DeleteResponse;
 import org.elasticsearch.action.get.GetRequestBuilder;
@@ -56,17 +58,10 @@ import org.sonar.core.cluster.WorkQueue;
 import org.sonar.core.persistence.Dto;
 
 import javax.annotation.Nullable;
+
 import java.io.IOException;
 import java.io.Serializable;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
+import java.util.*;
 import java.util.concurrent.ExecutionException;
 
 public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serializable>
@@ -185,10 +180,11 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial
     IndexStat stat = new IndexStat();
 
     /** get total document count */
-    stat.setDocumentCount(
-      getClient().prepareCount(this.getIndexName())
-        .setQuery(QueryBuilders.matchAllQuery())
-        .get().getCount());
+    CountRequestBuilder countRequest = getClient().prepareCount(this.getIndexName())
+      .setTypes(this.getIndexType())
+      .setQuery(QueryBuilders.matchAllQuery());
+    CountResponse response = node.execute(countRequest);
+    stat.setDocumentCount(response.getCount());
 
     /** get Management information */
     stat.setLastUpdate(getLastSynchronization());
index 87813d21580ae944989aa7210fd839f3e18f8b82..77e652cbe2b4227716f290743b0089305df69e06 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.search;
 
 import org.sonar.api.ServerComponent;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -52,4 +53,8 @@ public class IndexClient implements ServerComponent {
     }
     throw new IllegalStateException("no index for type '"+indexType+"' is registered");
   }
+
+  public Collection<Index<?, ?, ?>> allIndices() {
+    return indexComponents.values();
+  }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexHealth.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexHealth.java
new file mode 100644 (file)
index 0000000..909d722
--- /dev/null
@@ -0,0 +1,38 @@
+package org.sonar.server.search;
+
+import java.util.Date;
+
+public class IndexHealth {
+  private static final int SEGMENTS_THRESHOLD = 5;
+  private static final double PENDING_DELETION_THRESHOLD = 0.08D;
+
+  String name;
+  long documentCount;
+  Date lastSync;
+  long segmentCount;
+  long pendingDeletion;
+
+  public String getName() {
+    return name;
+  }
+
+  public long getDocumentCount() {
+    return documentCount;
+  }
+
+  public Date getLastSynchronization() {
+    return lastSync;
+  }
+
+  public boolean isOptimized() {
+    return segmentCount < SEGMENTS_THRESHOLD && pendingDeletion < documentCount * PENDING_DELETION_THRESHOLD;
+  }
+
+  public long getSegmentcount() {
+    return segmentCount;
+  }
+
+  public long getPendingDeletion() {
+    return pendingDeletion;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/SearchHealth.java b/server/sonar-server/src/main/java/org/sonar/server/search/SearchHealth.java
new file mode 100644 (file)
index 0000000..c8b2e57
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.search;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequestBuilder;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
+
+import java.util.Map;
+
+public class SearchHealth {
+
+  private ESNode node;
+  private IndexClient indexClient;
+
+  public SearchHealth(ESNode node, IndexClient indexClient) {
+    this.node = node;
+    this.indexClient = indexClient;
+  }
+
+  public NodeHealth getNodeHealth() {
+    return node.getNodeHealth();
+  }
+
+  public Map<String, IndexHealth> getIndexHealth() {
+    Builder<String, IndexHealth> builder = ImmutableMap.builder();
+    for (Index index: indexClient.allIndices()) {
+      IndexStat indexStat = index.getIndexStat();
+      IndexHealth newIndexHealth = new IndexHealth();
+      newIndexHealth.name = index.getIndexName() + "/" + index.getIndexType();
+      newIndexHealth.documentCount = indexStat.getDocumentCount();
+      newIndexHealth.lastSync = indexStat.getLastUpdate();
+
+      IndicesStatsRequestBuilder statRequest = node.client().admin().indices().prepareStats(index.getIndexName())
+        .setTypes(index.getIndexType());
+      IndicesStatsResponse indicesStatsResponse = node.execute(statRequest);
+      newIndexHealth.segmentCount = indicesStatsResponse.getTotal().getSegments().getCount();
+      newIndexHealth.pendingDeletion = indicesStatsResponse.getTotal().getDocs().getDeleted();
+
+      builder.put(newIndexHealth.name, newIndexHealth);
+    }
+    return builder.build();
+  }
+
+}
index 6a3a7a2931bc6731579d97c6b258307322ec56ac..ec865abb3dcf673b8d32e9e2c52471dd296d35b1 100644 (file)
@@ -24,6 +24,7 @@ import org.junit.Test;
 import org.sonar.server.tester.ServerTester;
 
 import java.util.Date;
+import java.util.Map;
 
 import static org.fest.assertions.Assertions.assertThat;
 
@@ -34,15 +35,25 @@ public class SearchHealthMediumTest {
 
   @Test
   public void get_search_health(){
-    ESNode node = tester.get(ESNode.class);
-    NodeHealth nodeHealth = node.getNodeHealth();
+    SearchHealth health = tester.get(SearchHealth.class);
+    Date now = new Date();
+
+    NodeHealth nodeHealth = health.getNodeHealth();
     assertThat(nodeHealth.isClusterAvailable()).isTrue();
     assertThat(nodeHealth.getJvmHeapUsedPercent()).contains("%");
     assertThat(nodeHealth.getFsUsedPercent()).contains("%");
     assertThat(nodeHealth.getJvmThreads()).isGreaterThanOrEqualTo(0L);
     assertThat(nodeHealth.getProcessCpuPercent()).contains("%");
     assertThat(nodeHealth.getOpenFiles()).isGreaterThanOrEqualTo(0L);
-    assertThat(nodeHealth.getJvmUpSince().before(new Date())).isTrue();
+    assertThat(nodeHealth.getJvmUpSince().before(now)).isTrue();
+
+    Map<String, IndexHealth> indexHealth = health.getIndexHealth();
+    assertThat(indexHealth).isNotEmpty();
+    for(IndexHealth index: indexHealth.values()) {
+      assertThat(index.getDocumentCount()).isGreaterThanOrEqualTo(0L);
+      assertThat(index.getLastSynchronization().before(now)).isTrue();
+      assertThat(index.isOptimized()).isIn(true, false);
+    }
   }
 
 }
index 990d7bc6580c1b50c91c19c2ffea44dcab8e7a7f..e68898db2d880d6b4231155948cd2514d920e095 100644 (file)
@@ -78,8 +78,8 @@ class Server
 
   def search_info
     search_info=[]
-    es_node = Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerSearch::ESNode.java_class)
-    node_health = es_node.getNodeHealth()
+    search_health = Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerSearch::SearchHealth.java_class)
+    node_health = search_health.getNodeHealth()
     add_property(search_info, 'Cluster State') { node_health.isClusterAvailable() ? 'Available' : 'Unavailable' }
     add_property(search_info, 'JVM Heap Usage') { node_health.getJvmHeapUsedPercent() }
     add_property(search_info, 'JVM Threads') { node_health.getJvmThreads() }
@@ -87,6 +87,13 @@ class Server
     add_property(search_info, 'Disk Usage') { node_health.getFsUsedPercent() }
     add_property(search_info, 'Open Files') { node_health.getOpenFiles() }
     add_property(search_info, 'CPU Load Average') { node_health.getProcessCpuPercent() }
+
+    search_health.getIndexHealth().each do |name, index_health|
+      add_property(search_info, "Document Count (#{name})") { index_health.getDocumentCount() }
+      add_property(search_info, "Last Sync (#{name})") { index_health.getLastSynchronization() }
+      add_property(search_info, "Optimization (#{name})") { index_health.isOptimized() ? 'Optimized' : "Unoptimized (Segments: #{index_health.getSegmentcount()}, Pending Deletions: #{index_health.getPendingDeletion()})" }
+    end
+
     search_info
   end