From: Jean-Baptiste Lievremont Date: Thu, 7 Aug 2014 11:55:45 +0000 (+0200) Subject: SONAR-4907 Add search index related info to system info page X-Git-Tag: 4.5-RC1~219 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=345b718cfc1b1aaf98dc0457b797cdb39220c86f;p=sonarqube.git SONAR-4907 Add search index related info to system info page --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index fe5b19d92eb..caa430bedd7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -224,6 +224,7 @@ class ServerComponents { IndexClient.class, ActivityNormalizer.class, ActivityIndex.class, + SearchHealth.class, // LogService ActivityService.class diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java index 371ea996c47..d3ab18ae5cb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java @@ -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, KEY extends Serializable> @@ -185,10 +180,11 @@ public abstract class BaseIndex, 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()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexClient.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexClient.java index 87813d21580..77e652cbe2b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexClient.java @@ -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> 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 index 00000000000..909d7229694 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexHealth.java @@ -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 index 00000000000..c8b2e57e5ce --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/search/SearchHealth.java @@ -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 getIndexHealth() { + Builder 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(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/SearchHealthMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/SearchHealthMediumTest.java index 6a3a7a2931b..ec865abb3dc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/search/SearchHealthMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/search/SearchHealthMediumTest.java @@ -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 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); + } } } diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb index 990d7bc6580..e68898db2d8 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb @@ -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