Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-15751: Modify v2 GET /collections/collName to support full ColStatus response #2912

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.client.api.endpoint;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import org.apache.solr.client.api.model.CollectionStatusResponse;

/**
* V2 API definition for fetching collection metadata
*
* <p>This API (GET /v2/collections/collectionName) is analogous to the v1
* /admin/collections?action=COLSTATUS command.
*/
@Path("/collections/{collectionName}")
public interface CollectionStatusApi {

// TODO Query parameters currently match those offered by the v1
// /admin/collections?action=COLSTATUS. Should param names be updated/clarified?
@GET
@Operation(
summary = "Fetches metadata about the specified collection",
tags = {"collections"})
CollectionStatusResponse getCollectionStatus(
@Parameter(description = "The name of the collection return metadata for", required = true)
@PathParam("collectionName")
String collectionName,
@Parameter(
description =
"Boolean flag to include metadata (e.g. index an data directories, IndexWriter configuration, etc.) about each shard leader's core")
@QueryParam("coreInfo")
Boolean coreInfo,
@Parameter(
description =
"Boolean flag to include metadata and statistics about the segments used by each shard leader. Implicitly set to true by 'fieldInfo' and 'sizeInfo'")
@QueryParam("segments")
Boolean segments,
@Parameter(
description =
"Boolean flag to include statistics about the indexed fields present on each shard leader. Implicitly sets the 'segments' flag to 'true'")
@QueryParam("fieldInfo")
Boolean fieldInfo,
@Parameter(
description =
"Boolean flag to include simple estimates of the disk size taken up by each field (e.g. \"id\", \"_version_\") and by each index data structure (e.g. 'storedFields', 'docValues_numeric').")
@QueryParam("rawSize")
Boolean rawSize,
@Parameter(
description =
"Boolean flag to include more involved estimates of the disk size taken up by index data structures, on a per-field basis (e.g. how much data does the \"id\" field contribute to 'storedField' index files). More detail than 'rawSize', less detail than 'rawSizeDetails'.")
@QueryParam("rawSizeSummary")
Boolean rawSizeSummary,
@Parameter(
description =
"Boolean flag to include detailed statistics about the disk size taken up by various fields and data structures. More detail than 'rawSize' and 'rawSizeSummary'.")
@QueryParam("rawSizeDetails")
Boolean rawSizeDetails,
@Parameter(
description =
"Percentage (between 0 and 100) of data to read when estimating index size and statistics. Defaults to 5.0 (i.e. 5%).")
@QueryParam("rawSizeSamplingPercent")
Float rawSizeSamplingPercent,
@Parameter(
description =
"Boolean flag to include information about the largest index files for each Lucene segment. Implicitly sets the 'segment' flag to 'true'")
@QueryParam("sizeInfo")
Boolean sizeInfo)
throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*
* 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.client.api.model;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Response of the CollectionStatusApi.getCollectionStatus() API */
public class CollectionStatusResponse extends SolrJerseyResponse {
gerlowskija marked this conversation as resolved.
Show resolved Hide resolved

@JsonProperty public String name;
@JsonProperty public Integer znodeVersion;
@JsonProperty public Long creationTimeMillis;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Instant and drop "Millis"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change the variable name and probably the type on the deserialized-Java side.

But we'll need to keep the same serialization-time prop name and value to avoid backcompat issues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will Date work in the interim?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, Date can work until SOLR-17608 gets sorted out. If we think "Instant" is generally better, I can switch to 'Date', but add in a TODO comment with the JIRA ticket, so we can find this later on...

@JsonProperty public CollectionMetadata properties;
@JsonProperty public Integer activeShards;
@JsonProperty public Integer inactiveShards;
@JsonProperty public List<String> schemaNonCompliant;

@JsonProperty public Map<String, ShardMetadata> shards;

// Always present in response
public static class CollectionMetadata {
@JsonProperty public String configName;
@JsonProperty public Integer nrtReplicas;
@JsonProperty public Integer pullReplicas;
@JsonProperty public Integer tlogReplicas;
@JsonProperty public Map<String, String> router;
@JsonProperty public Integer replicationFactor;

private Map<String, Object> unknownFields = new HashMap<>();

@JsonAnyGetter
public Map<String, Object> unknownProperties() {
return unknownFields;
}

@JsonAnySetter
public void setUnknownProperty(String field, Object value) {
unknownFields.put(field, value);
}
}

// Always present in response
public static class ShardMetadata {
@JsonProperty public String state; // TODO Make this an enum?
@JsonProperty public String range;
@JsonProperty public ReplicaSummary replicas;
@JsonProperty public LeaderSummary leader;
}

// Always present in response
public static class ReplicaSummary {
@JsonProperty public Integer total;
@JsonProperty public Integer active;
@JsonProperty public Integer down;
@JsonProperty public Integer recovering;

@JsonProperty("recovery_failed")
public Integer recoveryFailed;
}

// Always present in response unless otherwise specified
public static class LeaderSummary {
@JsonProperty public String coreNode;
@JsonProperty public String core;
@JsonProperty public Boolean leader;

@JsonProperty("node_name")
public String nodeName;

@JsonProperty("base_url")
public String baseUrl;

@JsonProperty public String state; // TODO Make this an enum?
@JsonProperty public String type; // TODO Make this an enum?

@JsonProperty("force_set_state")
public Boolean forceSetState;
gerlowskija marked this conversation as resolved.
Show resolved Hide resolved

// Present with coreInfo=true || sizeInfo=true unless otherwise specified
@JsonProperty public SegmentInfo segInfos;

private Map<String, Object> unknownFields = new HashMap<>();

@JsonAnyGetter
public Map<String, Object> unknownProperties() {
return unknownFields;
}

@JsonAnySetter
public void setUnknownProperty(String field, Object value) {
unknownFields.put(field, value);
}
}

// Present with segments=true || coreInfo=true || sizeInfo=true || fieldInfo=true unless otherwise
// specified
public static class SegmentInfo {
@JsonProperty public SegmentSummary info;

// Present with segments=true || sizeInfo=true || fieldInfo=true
@JsonProperty public Map<String, SingleSegmentData> segments;

// Present with rawSize=true
@JsonProperty public RawSize rawSize;

// Present only with fieldInfo=true
@JsonProperty public List<String> fieldInfoLegend;
}

// Present with segment=true || sizeInfo=true
public static class SingleSegmentData {
@JsonProperty public String name;
@JsonProperty public Integer delCount;
@JsonProperty public Integer softDelCount;
@JsonProperty public Boolean hasFieldUpdates;
@JsonProperty public Long sizeInBytes;
@JsonProperty public Integer size;
// A date string of the form "2024-12-17T17:35:18.275Z"
@JsonProperty public String age;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Instant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used Date for now based on PR discussion, see SOLR-17608.

@JsonProperty public String source;
@JsonProperty public String version;
@JsonProperty public Integer createdVersionMajor;
@JsonProperty public String minVersion;
@JsonProperty public SegmentDiagnosticInfo diagnostics;
@JsonProperty public Map<String, Object> attributes;

// Present only when fieldInfo=true
@JsonProperty public Map<String, SegmentSingleFieldInfo> fields;

// Present only when sizeInfo=true
@JsonProperty("largestFiles")
public Map<String, String> largestFilesByName;
}

public static class SegmentSingleFieldInfo {
@JsonProperty public String flags;
@JsonProperty public Integer docCount;
@JsonProperty public Integer termCount;
@JsonProperty public Integer sumDocFreq;
@JsonProperty public Integer sumTotalTermFreq;
@JsonProperty public String schemaType;
@JsonProperty public Map<String, String> nonCompliant;
}

// Present with segments=true
public static class SegmentDiagnosticInfo {
@JsonProperty("os.version")
public String osVersion;

@JsonProperty("lucene.version")
public String luceneVersion;

@JsonProperty public String source;
@JsonProperty public Long timestamp;
dsmiley marked this conversation as resolved.
Show resolved Hide resolved

@JsonProperty("java.runtime.version")
public String javaRuntimeVersion;

@JsonProperty public String os;

@JsonProperty("java.vendor")
public String javaVendor;

@JsonProperty("os.arch")
public String osArchitecture;
}

// Present with rawSize=true unless otherwise specified
public static class RawSize {
@JsonProperty public Map<String, String> fieldsBySize;
@JsonProperty public Map<String, String> typesBySize;

// Present with rawSizeDetails=true
@JsonProperty public Object details;

// Present with rawSizeSummary=true
@JsonProperty public Map<String, Object> summary;
}

// Present with coreInfo=true || sizeInfo=true unless otherwise specified
public static class SegmentSummary {
@JsonProperty public String minSegmentLuceneVersion;
@JsonProperty public String commitLuceneVersion;
@JsonProperty public Integer numSegments;
@JsonProperty public String segmentsFileName;
@JsonProperty public Integer totalMaxDoc;
// Typically keys are 'commitCommandVer' and 'commitTimeMSec'
@JsonProperty public Map<String, String> userData;

// Present for coreInfo=true only
@JsonProperty public CoreSummary core;
}

// Present with coreInfo=true unless otherwise specified
public static class CoreSummary {
@JsonProperty public String startTime;
@JsonProperty public String dataDir;
@JsonProperty public String indexDir;
@JsonProperty public Double sizeInGB;
@JsonProperty public IndexWriterConfigSummary indexWriterConfig;
}

// Present with coreInfo=true unless otherwise specified
public static class IndexWriterConfigSummary {
@JsonProperty public String analyzer;
@JsonProperty public Double ramBufferSizeMB;
@JsonProperty public Integer maxBufferedDocs;
@JsonProperty public String mergedSegmentWarmer;
@JsonProperty public String delPolicy;
@JsonProperty public String commit;
@JsonProperty public String openMode;
@JsonProperty public String similarity;
@JsonProperty public String mergeScheduler;
@JsonProperty public String codec;
@JsonProperty public String infoStream;
@JsonProperty public String mergePolicy;
@JsonProperty public Boolean readerPooling;
@JsonProperty public Integer perThreadHardLimitMB;
@JsonProperty public Boolean useCompoundFile;
@JsonProperty public Boolean commitOnClose;
@JsonProperty public String indexSort;
@JsonProperty public Boolean checkPendingFlushOnUpdate;
@JsonProperty public String softDeletesField;
@JsonProperty public Long maxFullFlushMergeWaitMillis;
@JsonProperty public String leafSorter;
@JsonProperty public String eventListener;
@JsonProperty public String parentField;
@JsonProperty public String writer;
}
}
2 changes: 2 additions & 0 deletions solr/core/src/java/org/apache/solr/api/V2HttpCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ public void call(SolrQueryRequest req, SolrQueryResponse rsp) {

Thread.currentThread().setContextClassLoader(core.getResourceLoader().getClassLoader());
this.path = path = path.substring(prefix.length() + pathSegments.get(1).length() + 2);
// Core-level API, so populate "collection" template val
parts.put(COLLECTION_PROP, origCorename);
Api apiInfo = getApiInfo(core.getRequestHandlers(), path, req.getMethod(), fullPath, parts);
if (isCompositeApi && apiInfo instanceof CompositeApi) {
((CompositeApi) this.api).add(apiInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,16 @@ public void getColStatus(NamedList<Object> results) {
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);
// FieldInfo and SizeInfo imply segments=true, since they add to the data reported about each
// segment
boolean withSegments = props.getBool(SEGMENTS_PROP, false);
withSegments |= withFieldInfo || withSizeInfo;

Object samplingPercentVal = props.get(RAW_SIZE_SAMPLING_PERCENT_PROP);
Float samplingPercent =
samplingPercentVal != null ? Float.parseFloat(String.valueOf(samplingPercentVal)) : null;
Expand All @@ -94,6 +98,7 @@ public void getColStatus(NamedList<Object> results) {
}
boolean getSegments = false;
if (withFieldInfo
|| withSegments
|| withSizeInfo
|| withCoreInfo
|| withRawSizeInfo
Expand Down
Loading
Loading