blob: 271315fc9ede19b6f990ef19b47a945605986347 [file] [log] [blame]
/*
* 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.solr.handler.admin;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.io.SolrClientCache;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.RoutingRule;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkCoreNodeProps;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Report low-level details of collection.
*/
public class ColStatus {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final ClusterState clusterState;
private final ZkNodeProps props;
private final SolrClientCache solrClientCache;
public static final String CORE_INFO_PROP = SegmentsInfoRequestHandler.CORE_INFO_PARAM;
public static final String FIELD_INFO_PROP = SegmentsInfoRequestHandler.FIELD_INFO_PARAM;
public static final String SIZE_INFO_PROP = SegmentsInfoRequestHandler.SIZE_INFO_PARAM;
public static final String RAW_SIZE_PROP = SegmentsInfoRequestHandler.RAW_SIZE_PARAM;
public static final String RAW_SIZE_SUMMARY_PROP = SegmentsInfoRequestHandler.RAW_SIZE_SUMMARY_PARAM;
public static final String RAW_SIZE_DETAILS_PROP = SegmentsInfoRequestHandler.RAW_SIZE_DETAILS_PARAM;
public static final String RAW_SIZE_SAMPLING_PERCENT_PROP = SegmentsInfoRequestHandler.RAW_SIZE_SAMPLING_PERCENT_PARAM;
public static final String SEGMENTS_PROP = "segments";
public ColStatus(SolrClientCache solrClientCache, ClusterState clusterState, ZkNodeProps props) {
this.props = props;
this.solrClientCache = solrClientCache;
this.clusterState = clusterState;
}
@SuppressWarnings({"unchecked"})
public void getColStatus(NamedList<Object> results) {
Collection<String> collections;
String col = props.getStr(ZkStateReader.COLLECTION_PROP);
if (col == null) {
collections = new HashSet<>(clusterState.getCollectionStates().keySet());
} else {
collections = Collections.singleton(col);
}
boolean withFieldInfo = props.getBool(FIELD_INFO_PROP, false);
boolean withSegments = props.getBool(SEGMENTS_PROP, false);
boolean withCoreInfo = props.getBool(CORE_INFO_PROP, false);
boolean withSizeInfo = props.getBool(SIZE_INFO_PROP, false);
boolean withRawSizeInfo = props.getBool(RAW_SIZE_PROP, false);
boolean withRawSizeSummary = props.getBool(RAW_SIZE_SUMMARY_PROP, false);
boolean withRawSizeDetails = props.getBool(RAW_SIZE_DETAILS_PROP, false);
Object samplingPercentVal = props.get(RAW_SIZE_SAMPLING_PERCENT_PROP);
Float samplingPercent = samplingPercentVal != null ? Float.parseFloat(String.valueOf(samplingPercentVal)) : null;
if (withRawSizeSummary || withRawSizeDetails) {
withRawSizeInfo = true;
}
if (withFieldInfo || withSizeInfo) {
withSegments = true;
}
for (String collection : collections) {
DocCollection coll = clusterState.getCollectionOrNull(collection);
if (coll == null) {
continue;
}
SimpleOrderedMap<Object> colMap = new SimpleOrderedMap<>();
colMap.add("stateFormat", coll.getStateFormat());
colMap.add("znodeVersion", coll.getZNodeVersion());
Map<String, Object> props = new TreeMap<>(coll.getProperties());
props.remove("shards");
colMap.add("properties", props);
colMap.add("activeShards", coll.getActiveSlices().size());
colMap.add("inactiveShards", coll.getSlices().size() - coll.getActiveSlices().size());
results.add(collection, colMap);
Set<String> nonCompliant = new TreeSet<>();
SimpleOrderedMap<Object> shards = new SimpleOrderedMap<>();
for (Slice s : coll.getSlices()) {
SimpleOrderedMap<Object> sliceMap = new SimpleOrderedMap<>();
shards.add(s.getName(), sliceMap);
SimpleOrderedMap<Object> replicaMap = new SimpleOrderedMap<>();
int totalReplicas = s.getReplicas().size();
int activeReplicas = 0;
int downReplicas = 0;
int recoveringReplicas = 0;
int recoveryFailedReplicas = 0;
for (Replica r : s.getReplicas()) {
// replica may still be marked as ACTIVE even though its node is no longer live
if (! r.isActive(clusterState.getLiveNodes())) {
downReplicas++;
continue;
}
switch (r.getState()) {
case ACTIVE:
activeReplicas++;
break;
case DOWN:
downReplicas++;
break;
case RECOVERING:
recoveringReplicas++;
break;
case RECOVERY_FAILED:
recoveryFailedReplicas++;
break;
}
}
replicaMap.add("total", totalReplicas);
replicaMap.add("active", activeReplicas);
replicaMap.add("down", downReplicas);
replicaMap.add("recovering", recoveringReplicas);
replicaMap.add("recovery_failed", recoveryFailedReplicas);
sliceMap.add("state", s.getState().toString());
if (s.getRange() != null) {
sliceMap.add("range", s.getRange().toString());
}
Map<String, RoutingRule> rules = s.getRoutingRules();
if (rules != null && !rules.isEmpty()) {
sliceMap.add("routingRules", rules);
}
sliceMap.add("replicas", replicaMap);
Replica leader = s.getLeader();
if (leader == null) { // pick the first one
leader = s.getReplicas().size() > 0 ? s.getReplicas().iterator().next() : null;
}
if (leader == null) {
continue;
}
SimpleOrderedMap<Object> leaderMap = new SimpleOrderedMap<>();
sliceMap.add("leader", leaderMap);
leaderMap.add("coreNode", leader.getName());
leaderMap.addAll(leader.getProperties());
if (!leader.isActive(clusterState.getLiveNodes())) {
continue;
}
String url = ZkCoreNodeProps.getCoreUrl(leader);
if (url == null) {
continue;
}
try (SolrClient client = solrClientCache.getHttpSolrClient(url)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.add(CommonParams.QT, "/admin/segments");
params.add(FIELD_INFO_PROP, "true");
params.add(CORE_INFO_PROP, String.valueOf(withCoreInfo));
params.add(SIZE_INFO_PROP, String.valueOf(withSizeInfo));
params.add(RAW_SIZE_PROP, String.valueOf(withRawSizeInfo));
params.add(RAW_SIZE_SUMMARY_PROP, String.valueOf(withRawSizeSummary));
params.add(RAW_SIZE_DETAILS_PROP, String.valueOf(withRawSizeDetails));
if (samplingPercent != null) {
params.add(RAW_SIZE_SAMPLING_PERCENT_PROP, String.valueOf(samplingPercent));
}
QueryRequest req = new QueryRequest(params);
NamedList<Object> rsp = client.request(req);
rsp.remove("responseHeader");
leaderMap.add("segInfos", rsp);
NamedList<Object> segs = (NamedList<Object>)rsp.get("segments");
if (segs != null) {
for (Map.Entry<String, Object> entry : segs) {
NamedList<Object> fields = (NamedList<Object>)((NamedList<Object>)entry.getValue()).get("fields");
if (fields != null) {
for (Map.Entry<String, Object> fEntry : fields) {
Object nc = ((NamedList<Object>)fEntry.getValue()).get("nonCompliant");
if (nc != null) {
nonCompliant.add(fEntry.getKey());
}
}
}
if (!withFieldInfo) {
((NamedList<Object>)entry.getValue()).remove("fields");
}
}
}
if (!withSegments) {
rsp.remove("segments");
}
if (!withFieldInfo) {
rsp.remove("fieldInfoLegend");
}
} catch (SolrServerException | IOException e) {
log.warn("Error getting details of replica segments from {}", url, e);
}
}
if (nonCompliant.isEmpty()) {
nonCompliant.add("(NONE)");
}
colMap.add("schemaNonCompliant", nonCompliant);
colMap.add("shards", shards);
}
}
}