feat: add support for apdex settings (#3186)

This commit is contained in:
Srikanth Chekuri 2023-07-26 12:27:46 +05:30 committed by GitHub
parent b9409820cc
commit cac637ac88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 201 additions and 0 deletions

View File

@ -0,0 +1,46 @@
package app
import (
"context"
"net/http"
"strings"
"go.signoz.io/signoz/pkg/query-service/dao"
"go.signoz.io/signoz/pkg/query-service/model"
)
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
req, err := parseSetApdexScoreRequest(r)
if aH.HandleError(w, err, http.StatusBadRequest) {
return
}
if err := dao.DB().SetApdexSettings(context.Background(), req); err != nil {
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
return
}
aH.WriteJSON(w, r, map[string]string{"data": "apdex score updated successfully"})
}
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
services := r.URL.Query().Get("services")
apdexSet, err := dao.DB().GetApdexSettings(context.Background(), strings.Split(strings.TrimSpace(services), ","))
if err != nil {
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
return
}
aH.WriteJSON(w, r, apdexSet)
}
func (aH *APIHandler) getLatencyMetricMetadata(w http.ResponseWriter, r *http.Request) {
metricName := r.URL.Query().Get("metricName")
metricMetadata, err := aH.reader.GetLatencyMetricMetadata(r.Context(), metricName, aH.preferDelta)
if err != nil {
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
return
}
aH.WriteJSON(w, r, metricMetadata)
}

View File

@ -5,6 +5,7 @@ import (
"context"
"database/sql"
"encoding/json"
"math"
"fmt"
"io/ioutil"
@ -3829,6 +3830,52 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, req *v3
return &attributeValues, nil
}
func (r *ClickHouseReader) GetLatencyMetricMetadata(ctx context.Context, metricName string, preferDelta bool) (*v3.LatencyMetricMetadataResponse, error) {
query := fmt.Sprintf("SELECT DISTINCT(temporality) from %s.%s WHERE metric_name='%s'", signozMetricDBName, signozTSTableName, metricName)
rows, err := r.db.Query(ctx, query, metricName)
if err != nil {
zap.S().Error(err)
return nil, fmt.Errorf("error while executing query: %s", err.Error())
}
defer rows.Close()
var deltaExists bool
for rows.Next() {
var temporality string
if err := rows.Scan(&temporality); err != nil {
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
}
if temporality == string(v3.Delta) {
deltaExists = true
}
}
query = fmt.Sprintf("SELECT DISTINCT(toFloat64(JSONExtractString(labels, 'le'))) as le from %s.%s WHERE metric_name='%s' ORDER BY le", signozMetricDBName, signozTSTableName, metricName)
rows, err = r.db.Query(ctx, query, metricName)
if err != nil {
zap.S().Error(err)
return nil, fmt.Errorf("error while executing query: %s", err.Error())
}
defer rows.Close()
var leFloat64 []float64
for rows.Next() {
var le float64
if err := rows.Scan(&le); err != nil {
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
}
if math.IsInf(le, 0) {
continue
}
leFloat64 = append(leFloat64, le)
}
return &v3.LatencyMetricMetadataResponse{
Delta: deltaExists && preferDelta,
Le: leFloat64,
}, nil
}
func isColumn(tableStatement, field string) bool {
return strings.Contains(tableStatement, fmt.Sprintf("`%s` ", field))
}

View File

@ -332,6 +332,10 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
router.HandleFunc("/api/v1/dependency_graph", am.ViewAccess(aH.dependencyGraph)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/ttl", am.AdminAccess(aH.setTTL)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/ttl", am.ViewAccess(aH.getTTL)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/settings/apdex", am.AdminAccess(aH.setApdexSettings)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/settings/apdex", am.ViewAccess(aH.getApdexSettings)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/metric_meta", am.ViewAccess(aH.getLatencyMetricMetadata)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/version", am.OpenAccess(aH.getVersion)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/featureFlags", am.OpenAccess(aH.getFeatureFlags)).Methods(http.MethodGet)

View File

@ -726,6 +726,14 @@ func parseInviteRequest(r *http.Request) (*model.InviteRequest, error) {
return &req, nil
}
func parseSetApdexScoreRequest(r *http.Request) (*model.ApdexSettings, error) {
var req model.ApdexSettings
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return &req, nil
}
func parseRegisterRequest(r *http.Request) (*auth.RegisterRequest, error) {
var req auth.RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {

View File

@ -32,6 +32,8 @@ type Queries interface {
GetResetPasswordEntry(ctx context.Context, token string) (*model.ResetPasswordEntry, *model.ApiError)
GetUsersByOrg(ctx context.Context, orgId string) ([]model.UserPayload, *model.ApiError)
GetUsersByGroup(ctx context.Context, groupId string) ([]model.UserPayload, *model.ApiError)
GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError)
}
type Mutations interface {
@ -56,4 +58,6 @@ type Mutations interface {
UpdateUserPassword(ctx context.Context, hash, userId string) *model.ApiError
UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError
SetApdexSettings(ctx context.Context, set *model.ApdexSettings) *model.ApiError
}

View File

@ -0,0 +1,74 @@
package sqlite
import (
"context"
"fmt"
"go.signoz.io/signoz/pkg/query-service/model"
)
const defaultApdexThreshold = 0.5
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError) {
var apdexSettings []model.ApdexSettings
var serviceName string
for i, service := range services {
if i == 0 {
serviceName = fmt.Sprintf("'%s'", service)
} else {
serviceName = fmt.Sprintf("%s, '%s'", serviceName, service)
}
}
query := fmt.Sprintf("SELECT * FROM apdex_settings WHERE service_name IN (%s)", serviceName)
err := mds.db.Select(&apdexSettings, query)
if err != nil {
return nil, &model.ApiError{
Err: err,
}
}
// add default apdex settings for services that don't have any
for _, service := range services {
var found bool
for _, apdexSetting := range apdexSettings {
if apdexSetting.ServiceName == service {
found = true
break
}
}
if !found {
apdexSettings = append(apdexSettings, model.ApdexSettings{
ServiceName: service,
Threshold: defaultApdexThreshold,
})
}
}
return apdexSettings, nil
}
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
fmt.Println("apdexSettings:", apdexSettings)
_, err := mds.db.NamedExec(`
INSERT OR REPLACE INTO apdex_settings (
service_name,
threshold,
exclude_status_codes
) VALUES (
:service_name,
:threshold,
:exclude_status_codes
)`, apdexSettings)
if err != nil {
return &model.ApiError{
Err: err,
}
}
return nil
}

View File

@ -73,6 +73,11 @@ func InitDB(dataSourceName string) (*ModelDaoSqlite, error) {
flags TEXT,
FOREIGN KEY(user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS apdex_settings (
service_name TEXT PRIMARY KEY,
threshold FLOAT NOT NULL,
exclude_status_codes TEXT NOT NULL
);
`
_, err = db.Exec(table_schema)

View File

@ -95,6 +95,8 @@ type Reader interface {
QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error)
CheckClickHouse(ctx context.Context) error
GetLatencyMetricMetadata(context.Context, string, bool) (*v3.LatencyMetricMetadataResponse, error)
}
type Querier interface {

View File

@ -36,6 +36,12 @@ type User struct {
GroupId string `json:"groupId,omitempty" db:"group_id"`
}
type ApdexSettings struct {
ServiceName string `json:"serviceName" db:"service_name"`
Threshold float64 `json:"threshold" db:"threshold"`
ExcludeStatusCodes string `json:"excludeStatusCodes" db:"exclude_status_codes"` // sqlite doesn't support array type
}
type UserFlag map[string]string
func (uf UserFlag) Value() (driver.Value, error) {

View File

@ -663,3 +663,8 @@ func (eq *ExplorerQuery) Validate() error {
}
return eq.CompositeQuery.Validate()
}
type LatencyMetricMetadataResponse struct {
Delta bool `json:"delta"`
Le []float64 `json:"le"`
}