From 7290ab360212ae2411a50c7bb9ba41572271f1ed Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Tue, 20 May 2025 11:28:40 +0530 Subject: [PATCH] feat: added entry point operations api for the service overview page (#7957) * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page * feat: added entry point operations api for the service overview page --------- Co-authored-by: Nityananda Gohain --- .../app/clickhouseReader/reader.go | 55 +++++++++++++++++++ pkg/query-service/app/http_handler.go | 17 ++++++ pkg/query-service/interfaces/interface.go | 1 + 3 files changed, 73 insertions(+) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index c8377fa43c..08d0dcc0af 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -23,6 +23,7 @@ import ( "github.com/SigNoz/signoz/pkg/valuer" "github.com/uptrace/bun" + errorsV2 "github.com/SigNoz/signoz/pkg/errors" "github.com/google/uuid" "github.com/pkg/errors" @@ -679,6 +680,60 @@ func addExistsOperator(item model.TagQuery, tagMapType string, not bool) (string return fmt.Sprintf(" AND %s (%s)", notStr, strings.Join(tagOperatorPair, " OR ")), args } +func (r *ClickHouseReader) GetEntryPointOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, error) { + // Step 1: Get top operations for the given service + topOps, err := r.GetTopOperations(ctx, queryParams) + if err != nil { + return nil, errorsV2.Wrapf(err, errorsV2.TypeInternal, errorsV2.CodeInternal, "Error in getting Top Operations") + } + if topOps == nil { + return nil, errorsV2.Newf(errorsV2.TypeNotFound, errorsV2.CodeNotFound, "no top operations found") + } + + // Step 2: Get entry point operation names for the given service using GetTopLevelOperations + // instead of running a separate query + serviceName := []string{queryParams.ServiceName} + var startTime, endTime time.Time + if queryParams.Start != nil { + startTime = *queryParams.Start + } + if queryParams.End != nil { + endTime = *queryParams.End + } + topLevelOpsResult, apiErr := r.GetTopLevelOperations(ctx, startTime, endTime, serviceName) + + if apiErr != nil { + return nil, errorsV2.Wrapf(apiErr.Err, errorsV2.TypeInternal, errorsV2.CodeInternal, "failed to get top level operations") + } + + // Create a set of entry point operations + entryPointSet := map[string]struct{}{} + + // Extract operations for the requested service from topLevelOpsResult + if serviceOperations, ok := (*topLevelOpsResult)[queryParams.ServiceName]; ok { + // Skip the first "overflow_operation" if present + startIdx := 0 + if len(serviceOperations) > 0 && serviceOperations[0] == "overflow_operation" { + startIdx = 1 + } + + // Add each operation name to the entry point set + for i := startIdx; i < len(serviceOperations); i++ { + entryPointSet[serviceOperations[i]] = struct{}{} + } + } + + // Step 3: Filter topOps based on entryPointSet (same as original) + var filtered []model.TopOperationsItem + for _, op := range *topOps { + if _, ok := entryPointSet[op.Name]; ok { + filtered = append(filtered, op) + } + } + + return &filtered, nil +} + func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) { namedArgs := []interface{}{ diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 0eac305986..331a1b5bc4 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -540,6 +540,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) { router.HandleFunc("/api/v1/services/list", am.ViewAccess(aH.getServicesList)).Methods(http.MethodGet) router.HandleFunc("/api/v1/service/top_operations", am.ViewAccess(aH.getTopOperations)).Methods(http.MethodPost) router.HandleFunc("/api/v1/service/top_level_operations", am.ViewAccess(aH.getServicesTopLevelOps)).Methods(http.MethodPost) + router.HandleFunc("/api/v1/service/entry_point_operations", am.ViewAccess(aH.getEntryPointOps)).Methods(http.MethodPost) router.HandleFunc("/api/v1/traces/{traceId}", am.ViewAccess(aH.SearchTraces)).Methods(http.MethodGet) router.HandleFunc("/api/v1/usage", am.ViewAccess(aH.getUsage)).Methods(http.MethodGet) router.HandleFunc("/api/v1/dependency_graph", am.ViewAccess(aH.dependencyGraph)).Methods(http.MethodPost) @@ -1618,6 +1619,22 @@ func (aH *APIHandler) getTopOperations(w http.ResponseWriter, r *http.Request) { } +func (aH *APIHandler) getEntryPointOps(w http.ResponseWriter, r *http.Request) { + query, err := parseGetTopOperationsRequest(r) + if err != nil { + render.Error(w, err) + return + } + + result, apiErr := aH.reader.GetEntryPointOperations(r.Context(), query) + if apiErr != nil { + render.Error(w, apiErr) + return + } + + render.Success(w, http.StatusOK, result) +} + func (aH *APIHandler) getUsage(w http.ResponseWriter, r *http.Request) { query, err := parseGetUsageRequest(r) diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 55af995e67..ac07a0a006 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -17,6 +17,7 @@ type Reader interface { GetInstantQueryMetricsResult(ctx context.Context, query *model.InstantQueryMetricsParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetQueryRangeResult(ctx context.Context, query *model.QueryRangeParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetTopLevelOperations(ctx context.Context, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) + GetEntryPointOperations(ctx context.Context, query *model.GetTopOperationsParams) (*[]model.TopOperationsItem, error) GetServices(ctx context.Context, query *model.GetServicesParams) (*[]model.ServiceItem, *model.ApiError) GetTopOperations(ctx context.Context, query *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) GetUsage(ctx context.Context, query *model.GetUsageParams) (*[]model.UsageItem, error)