IndexClient.class,
ActivityNormalizer.class,
ActivityIndex.class,
+ SearchHealth.class,
// LogService
ActivityService.class
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;
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>
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());
import org.sonar.api.ServerComponent;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
}
throw new IllegalStateException("no index for type '"+indexType+"' is registered");
}
+
+ public Collection<Index<?, ?, ?>> allIndices() {
+ return indexComponents.values();
+ }
}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
import org.sonar.server.tester.ServerTester;
import java.util.Date;
+import java.util.Map;
import static org.fest.assertions.Assertions.assertThat;
@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);
+ }
}
}
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() }
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