/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package knn import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "opensearch-cli/client" "opensearch-cli/entity" "opensearch-cli/entity/knn" gw "opensearch-cli/gateway" ) const ( baseURL = "_plugins/_knn" statsURL = baseURL + "/stats" nodeStatsURLTemplate = baseURL + "/%s/stats/%s" warmupIndicesURLTemplate = baseURL + "/warmup/%s" ) //go:generate go run -mod=mod github.com/golang/mock/mockgen -destination=mocks/mock_knn.go -package=mocks . Gateway // Gateway interface to k-NN Plugin type Gateway interface { GetStatistics(ctx context.Context, nodes string, names string) ([]byte, error) WarmupIndices(ctx context.Context, indices string) ([]byte, error) } type gateway struct { gw.HTTPGateway } // New creates new Gateway instance func New(c *client.Client, p *entity.Profile) (Gateway, error) { g, err := gw.NewHTTPGateway(c, p) if err != nil { return nil, err } return &gateway{*g}, nil } //buildStatsURL to construct url for stats func (g *gateway) buildStatsURL(nodes string, names string) (*url.URL, error) { endpoint, err := gw.GetValidEndpoint(g.Profile) if err != nil { return nil, err } path := statsURL // if either of filter parameters are non-empty, use filter template if nodes != "" || names != "" { path = fmt.Sprintf(nodeStatsURLTemplate, nodes, names) } endpoint.Path = path return endpoint, nil } //buildWarmupURL to construct url for warming up indices func (g *gateway) buildWarmupURL(indices string) (*url.URL, error) { endpoint, err := gw.GetValidEndpoint(g.Profile) if err != nil { return nil, err } endpoint.Path = fmt.Sprintf(warmupIndicesURLTemplate, indices) return endpoint, nil } /*GetStatistics provides information about the current status of the KNN Plugin. GET /_plugins/_knn/stats { "_nodes" : { "total" : 1, "successful" : 1, "failed" : 0 }, "cluster_name" : "_run", "circuit_breaker_triggered" : false, "nodes" : { "HYMrXXsBSamUkcAjhjeN0w" : { "eviction_count" : 0, "miss_count" : 1, "graph_memory_usage" : 1, "graph_memory_usage_percentage" : 3.68, "graph_index_requests" : 7, "graph_index_errors" : 1, "knn_query_requests" : 4, "graph_query_requests" : 30, "graph_query_errors" : 15, "indices_in_cache" : { "myindex" : { "graph_memory_usage" : 2, "graph_memory_usage_percentage" : 3.68, "graph_count" : 2 } }, "cache_capacity_reached" : false, "load_exception_count" : 0, "hit_count" : 0, "load_success_count" : 1, "total_load_time" : 2878745, "script_compilations" : 1, "script_compilation_errors" : 0, "script_query_requests" : 534, "script_query_errors" : 0 } } } To filter stats query by nodeID and statName: GET /_plugins/_knn/nodeId1,nodeId2/stats/statName1,statName2 */ func (g gateway) GetStatistics(ctx context.Context, nodes string, names string) ([]byte, error) { statsURL, err := g.buildStatsURL(nodes, names) if err != nil { return nil, err } request, err := g.BuildRequest(ctx, http.MethodGet, nil, statsURL.String(), gw.GetDefaultHeaders()) if err != nil { return nil, err } response, err := g.Call(request, http.StatusOK) if err != nil { return nil, processKNNError(err) } return response, nil } func processKNNError(err error) error { var k knn.ErrorResponse data := fmt.Sprintf("%v", err) responseErr := json.Unmarshal([]byte(data), &k) if responseErr != nil { return err } if len(k.KNNError.RootCause) > 0 { return errors.New(k.KNNError.RootCause[0].Reason) } return err } /* WarmupIndices will perform warmup on given indices GET /_plugins/_knn/warmup/index1,index2,index3?pretty { "_shards" : { "total" : 6, "successful" : 6, "failed" : 0 } } */ func (g gateway) WarmupIndices(ctx context.Context, indices string) ([]byte, error) { warmupURL, err := g.buildWarmupURL(indices) if err != nil { return nil, err } request, err := g.BuildRequest(ctx, http.MethodGet, nil, warmupURL.String(), gw.GetDefaultHeaders()) if err != nil { return nil, err } response, err := g.Call(request, http.StatusOK) if err != nil { return nil, processKNNError(err) } return response, nil }