diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 7e57e5ccaf..cd597eca24 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -642,130 +642,6 @@ func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.G } return &serviceItems, nil } - -func (r *ClickHouseReader) GetServiceOverview(ctx context.Context, queryParams *model.GetServiceOverviewParams, skipConfig *model.SkipConfig) (*[]model.ServiceOverviewItem, *model.ApiError) { - - topLevelOps, apiErr := r.GetTopLevelOperations(ctx, skipConfig, *queryParams.Start, *queryParams.End, nil) - if apiErr != nil { - return nil, apiErr - } - ops, ok := (*topLevelOps)[queryParams.ServiceName] - if !ok { - return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("service not found")} - } - - namedArgs := []interface{}{ - clickhouse.Named("interval", strconv.Itoa(int(queryParams.StepSeconds/60))), - clickhouse.Named("start", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), - clickhouse.Named("end", strconv.FormatInt(queryParams.End.UnixNano(), 10)), - clickhouse.Named("serviceName", queryParams.ServiceName), - clickhouse.Named("names", ops), - } - - serviceOverviewItems := []model.ServiceOverviewItem{} - - query := fmt.Sprintf(` - SELECT - toStartOfInterval(timestamp, INTERVAL @interval minute) as time, - quantile(0.99)(durationNano) as p99, - quantile(0.95)(durationNano) as p95, - quantile(0.50)(durationNano) as p50, - count(*) as numCalls - FROM %s.%s - WHERE serviceName = @serviceName AND name In @names AND timestamp>= @start AND timestamp<= @end`, - r.TraceDB, r.indexTable, - ) - args := []interface{}{} - args = append(args, namedArgs...) - - // create TagQuery from TagQueryParams - tags := createTagQueryFromTagQueryParams(queryParams.Tags) - subQuery, argsSubQuery, errStatus := buildQueryWithTagParams(ctx, tags) - query += subQuery - args = append(args, argsSubQuery...) - if errStatus != nil { - return nil, errStatus - } - query += " GROUP BY time ORDER BY time DESC" - err := r.db.Select(ctx, &serviceOverviewItems, query, args...) - - zap.L().Debug("running query", zap.String("query", query)) - - if err != nil { - zap.L().Error("Error in processing sql query", zap.Error(err)) - return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in processing sql query")} - } - - serviceErrorItems := []model.ServiceErrorItem{} - - query = fmt.Sprintf(` - SELECT - toStartOfInterval(timestamp, INTERVAL @interval minute) as time, - count(*) as numErrors - FROM %s.%s - WHERE serviceName = @serviceName AND name In @names AND timestamp>= @start AND timestamp<= @end AND statusCode=2`, - r.TraceDB, r.indexTable, - ) - args = []interface{}{} - args = append(args, namedArgs...) - subQuery, argsSubQuery, errStatus = buildQueryWithTagParams(ctx, tags) - query += subQuery - args = append(args, argsSubQuery...) - if errStatus != nil { - return nil, errStatus - } - query += " GROUP BY time ORDER BY time DESC" - err = r.db.Select(ctx, &serviceErrorItems, query, args...) - - if err != nil { - zap.L().Error("Error in processing sql query", zap.Error(err)) - return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in processing sql query")} - } - - m := make(map[int64]int) - - for j := range serviceErrorItems { - m[int64(serviceErrorItems[j].Time.UnixNano())] = int(serviceErrorItems[j].NumErrors) - } - - for i := range serviceOverviewItems { - serviceOverviewItems[i].Timestamp = int64(serviceOverviewItems[i].Time.UnixNano()) - - if val, ok := m[serviceOverviewItems[i].Timestamp]; ok { - serviceOverviewItems[i].NumErrors = uint64(val) - } - serviceOverviewItems[i].ErrorRate = float64(serviceOverviewItems[i].NumErrors) * 100 / float64(serviceOverviewItems[i].NumCalls) - serviceOverviewItems[i].CallRate = float64(serviceOverviewItems[i].NumCalls) / float64(queryParams.StepSeconds) - } - - return &serviceOverviewItems, nil -} - -func buildFilterArrayQuery(_ context.Context, excludeMap map[string]struct{}, params []string, filter string, query *string, args []interface{}) []interface{} { - for i, e := range params { - filterKey := filter + String(5) - if i == 0 && i == len(params)-1 { - if _, ok := excludeMap[filter]; ok { - *query += fmt.Sprintf(" AND NOT (%s=@%s)", filter, filterKey) - } else { - *query += fmt.Sprintf(" AND (%s=@%s)", filter, filterKey) - } - } else if i == 0 && i != len(params)-1 { - if _, ok := excludeMap[filter]; ok { - *query += fmt.Sprintf(" AND NOT (%s=@%s", filter, filterKey) - } else { - *query += fmt.Sprintf(" AND (%s=@%s", filter, filterKey) - } - } else if i != 0 && i == len(params)-1 { - *query += fmt.Sprintf(" OR %s=@%s)", filter, filterKey) - } else { - *query += fmt.Sprintf(" OR %s=@%s", filter, filterKey) - } - args = append(args, clickhouse.Named(filterKey, e)) - } - return args -} - func getStatusFilters(query string, statusParams []string, excludeMap map[string]struct{}) string { // status can only be two and if both are selected than they are equivalent to none selected diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 2be68ede43..5fb604d7a9 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -508,7 +508,6 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) { // router.HandleFunc("/api/v1/get_percentiles", aH.getApplicationPercentiles).Methods(http.MethodGet) router.HandleFunc("/api/v1/services", am.ViewAccess(aH.getServices)).Methods(http.MethodPost) router.HandleFunc("/api/v1/services/list", am.ViewAccess(aH.getServicesList)).Methods(http.MethodGet) - router.HandleFunc("/api/v1/service/overview", am.ViewAccess(aH.getServiceOverview)).Methods(http.MethodPost) 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/traces/{traceId}", am.ViewAccess(aH.SearchTraces)).Methods(http.MethodGet) @@ -1632,22 +1631,6 @@ func (aH *APIHandler) getUsage(w http.ResponseWriter, r *http.Request) { } -func (aH *APIHandler) getServiceOverview(w http.ResponseWriter, r *http.Request) { - - query, err := parseGetServiceOverviewRequest(r) - if aH.HandleError(w, err, http.StatusBadRequest) { - return - } - - result, apiErr := aH.reader.GetServiceOverview(r.Context(), query, aH.skipConfig) - if apiErr != nil && aH.HandleError(w, apiErr.Err, http.StatusInternalServerError) { - return - } - - aH.WriteJSON(w, r, result) - -} - func (aH *APIHandler) getServicesTopLevelOps(w http.ResponseWriter, r *http.Request) { var start, end time.Time diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index fcf6944234..8cfc621662 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -195,28 +195,6 @@ func parseGetUsageRequest(r *http.Request) (*model.GetUsageParams, error) { } -func parseGetServiceOverviewRequest(r *http.Request) (*model.GetServiceOverviewParams, error) { - - var postData *model.GetServiceOverviewParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - - postData.Start, err = parseTimeStr(postData.StartTime, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndTime, "end") - if err != nil { - return nil, err - } - - postData.Period = fmt.Sprintf("PT%dM", postData.StepSeconds/60) - return postData, nil -} - func parseGetServicesRequest(r *http.Request) (*model.GetServicesParams, error) { var postData *model.GetServicesParams @@ -289,229 +267,6 @@ func DoesExistInSlice(item string, list []string) bool { } return false } - -func parseSpanFilterRequestBody(r *http.Request) (*model.SpanFilterParams, error) { - - var postData *model.SpanFilterParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - - postData.Start, err = parseTimeStr(postData.StartStr, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") - if err != nil { - return nil, err - } - - return postData, nil -} - -func parseFilteredSpansRequest(r *http.Request, aH *APIHandler) (*model.GetFilteredSpansParams, error) { - - var postData *model.GetFilteredSpansParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - - postData.Start, err = parseTimeStr(postData.StartStr, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") - if err != nil { - return nil, err - } - - if postData.Limit == 0 { - postData.Limit = 10 - } - - if len(postData.Order) != 0 { - if postData.Order != baseconstants.Ascending && postData.Order != baseconstants.Descending { - return nil, errors.New("order param is not in correct format") - } - if postData.OrderParam != baseconstants.Duration && postData.OrderParam != baseconstants.Timestamp { - return nil, errors.New("order param is not in correct format") - } - if postData.OrderParam == baseconstants.Duration && !aH.CheckFeature(baseconstants.DurationSort) { - return nil, model.ErrFeatureUnavailable{Key: baseconstants.DurationSort} - } else if postData.OrderParam == baseconstants.Timestamp && !aH.CheckFeature(baseconstants.TimestampSort) { - return nil, model.ErrFeatureUnavailable{Key: baseconstants.TimestampSort} - } - } - tags, err := extractTagKeys(postData.Tags) - if err != nil { - return nil, err - } - postData.Tags = tags - return postData, nil -} - -func parseFilteredSpanAggregatesRequest(r *http.Request) (*model.GetFilteredSpanAggregatesParams, error) { - - var postData *model.GetFilteredSpanAggregatesParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - - postData.Start, err = parseTimeStr(postData.StartStr, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") - if err != nil { - return nil, err - } - - step := postData.StepSeconds - if step == 0 { - return nil, errors.New("step param missing in query") - } - - function := postData.Function - if len(function) == 0 { - return nil, errors.New("function param missing in query") - } else { - if !DoesExistInSlice(function, allowedFunctions) { - return nil, fmt.Errorf("given function: %s is not allowed in query", function) - } - } - - var dimension, aggregationOption string - - switch function { - case "count": - dimension = "calls" - aggregationOption = "count" - case "ratePerSec": - dimension = "calls" - aggregationOption = "rate_per_sec" - case "avg": - dimension = "duration" - aggregationOption = "avg" - case "sum": - dimension = "duration" - aggregationOption = "sum" - case "p50": - dimension = "duration" - aggregationOption = "p50" - case "p90": - dimension = "duration" - aggregationOption = "p90" - case "p95": - dimension = "duration" - aggregationOption = "p95" - case "p99": - dimension = "duration" - aggregationOption = "p99" - case "min": - dimension = "duration" - aggregationOption = "min" - case "max": - dimension = "duration" - aggregationOption = "max" - } - - postData.AggregationOption = aggregationOption - postData.Dimension = dimension - tags, err := extractTagKeys(postData.Tags) - if err != nil { - return nil, err - } - postData.Tags = tags - - return postData, nil -} - -func extractTagKeys(tags []model.TagQueryParam) ([]model.TagQueryParam, error) { - newTags := make([]model.TagQueryParam, 0) - if len(tags) != 0 { - for _, tag := range tags { - customStr := strings.Split(tag.Key, ".(") - if len(customStr) < 2 { - return nil, fmt.Errorf("TagKey param is not valid in query") - } else { - tag.Key = customStr[0] - } - if tag.Operator == model.ExistsOperator || tag.Operator == model.NotExistsOperator { - if customStr[1] == string(model.TagTypeString)+")" { - tag.StringValues = []string{" "} - } else if customStr[1] == string(model.TagTypeBool)+")" { - tag.BoolValues = []bool{true} - } else if customStr[1] == string(model.TagTypeNumber)+")" { - tag.NumberValues = []float64{0} - } else { - return nil, fmt.Errorf("TagKey param is not valid in query") - } - } - newTags = append(newTags, tag) - } - } - return newTags, nil -} - -func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) { - var postData *model.TagFilterParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - - postData.Start, err = parseTimeStr(postData.StartStr, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") - if err != nil { - return nil, err - } - - return postData, nil - -} - -func parseTagValueRequest(r *http.Request) (*model.TagFilterParams, error) { - var postData *model.TagFilterParams - err := json.NewDecoder(r.Body).Decode(&postData) - - if err != nil { - return nil, err - } - if postData.TagKey == (model.TagKey{}) { - return nil, fmt.Errorf("TagKey param missing in query") - } - - if postData.TagKey.Type != model.TagTypeString && postData.TagKey.Type != model.TagTypeBool && postData.TagKey.Type != model.TagTypeNumber { - return nil, fmt.Errorf("tag keys type %s is not supported", postData.TagKey.Type) - } - - if postData.Limit == 0 { - postData.Limit = 100 - } - - postData.Start, err = parseTimeStr(postData.StartStr, "start") - if err != nil { - return nil, err - } - postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end") - if err != nil { - return nil, err - } - - return postData, nil - -} - func parseListErrorsRequest(r *http.Request) (*model.ListErrorsParams, error) { var allowedOrderParams = []string{"exceptionType", "exceptionCount", "firstSeen", "lastSeen", "serviceName"} diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 10c718aa28..1cf32ede02 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -16,7 +16,6 @@ import ( 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) - GetServiceOverview(ctx context.Context, query *model.GetServiceOverviewParams, skipConfig *model.SkipConfig) (*[]model.ServiceOverviewItem, *model.ApiError) GetTopLevelOperations(ctx context.Context, skipConfig *model.SkipConfig, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) GetServices(ctx context.Context, query *model.GetServicesParams, skipConfig *model.SkipConfig) (*[]model.ServiceItem, *model.ApiError) GetTopOperations(ctx context.Context, query *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError)